diff options
Diffstat (limited to 'src/jogl/classes/com/jogamp')
60 files changed, 7415 insertions, 4091 deletions
diff --git a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java index fc8d41660..1a1bd94dd 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java +++ b/src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java @@ -40,7 +40,9 @@ import com.jogamp.graph.geom.plane.AffineTransform; import com.jogamp.graph.geom.plane.Path2F; import com.jogamp.graph.geom.plane.Winding; import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.math.Vert2fImmutable; import com.jogamp.opengl.math.geom.AABBox; /** @@ -128,29 +130,6 @@ public final class OutlineShape implements Comparable<OutlineShape> { } } - /** - * General purpose {@link OutlineShape} visitor. - */ - public static interface Visitor { - /** - * Visiting the given {@link OutlineShape} with it's corresponding {@link AffineTransform}. - * @param shape may be used as is, otherwise a copy shall be made if intended to be modified. - * @param t may be used immediately as is, otherwise a copy shall be made if stored. - */ - public void visit(final OutlineShape shape, final AffineTransform t); - } - - /** - * Constrained {@link OutlineShape} visitor w/o {@link AffineTransform}. - */ - public static interface Visitor2 { - /** - * Visiting the given {@link OutlineShape}. - * @param shape may be used as is, otherwise a copy shall be made if intended to be modified. - */ - public void visit(final OutlineShape shape); - } - /** Initial {@link #getSharpness()} value, which can be modified via {@link #setSharpness(float)}. */ public static final float DEFAULT_SHARPNESS = 0.5f; @@ -164,8 +143,6 @@ public final class OutlineShape implements Comparable<OutlineShape> { */ public static final int DIRTY_TRIANGLES = 1 << 2; - private final Vertex.Factory<? extends Vertex> vertexFactory; - /** The list of {@link Outline}s that are part of this * outline shape. */ @@ -183,14 +160,14 @@ public final class OutlineShape implements Comparable<OutlineShape> { private float sharpness; - private final float[] tmpV1 = new float[3]; - private final float[] tmpV2 = new float[3]; - private final float[] tmpV3 = new float[3]; + private final Vec3f tmpV1 = new Vec3f(); + private final Vec3f tmpV2 = new Vec3f(); + private final Vec3f tmpV3 = new Vec3f(); - /** Create a new Outline based Shape + /** + * Create a new Outline based Shape */ - public OutlineShape(final Vertex.Factory<? extends Vertex> factory) { - this.vertexFactory = factory; + public OutlineShape() { this.outlines = new ArrayList<Outline>(3); this.outlines.add(new Outline()); this.outlineState = VerticesState.UNDEFINED; @@ -241,16 +218,20 @@ public final class OutlineShape implements Comparable<OutlineShape> { dirtyBits |= DIRTY_TRIANGLES | DIRTY_VERTICES; } - /** - * Returns the associated vertex factory of this outline shape - * @return Vertex.Factory object - */ - public final Vertex.Factory<? extends Vertex> vertexFactory() { return vertexFactory; } - + /** Returns the number of {@link Outline}s. */ public final int getOutlineCount() { return outlines.size(); } + /** Returns the total {@link Outline#getVertexCount() vertex number} of all {@link Outline}s. */ + public final int getVertexCount() { + int res = 0; + for(final Outline o : outlines) { + res += o.getVertexCount(); + } + return res; + } + /** * Compute the {@link Winding} of the {@link #getLastOutline()} using the {@link #area(ArrayList)} function over all of its vertices. * @return {@link Winding#CCW} or {@link Winding#CW} @@ -438,7 +419,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { * @see <a href="#windingrules">see winding rules</a> */ public final void addVertex(final float x, final float y, final boolean onCurve) { - addVertex(vertexFactory.create(x, y, 0f, onCurve)); + addVertex(new Vertex(x, y, 0f, onCurve)); } /** @@ -452,7 +433,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { * @see <a href="#windingrules">see winding rules</a> */ public final void addVertex(final int position, final float x, final float y, final boolean onCurve) { - addVertex(position, vertexFactory.create(x, y, 0f, onCurve)); + addVertex(position, new Vertex(x, y, 0f, onCurve)); } /** @@ -465,7 +446,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { * @see <a href="#windingrules">see winding rules</a> */ public final void addVertex(final float x, final float y, final float z, final boolean onCurve) { - addVertex(vertexFactory.create(x, y, z, onCurve)); + addVertex(new Vertex(x, y, z, onCurve)); } /** @@ -479,7 +460,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { * @see <a href="#windingrules">see winding rules</a> */ public final void addVertex(final int position, final float x, final float y, final float z, final boolean onCurve) { - addVertex(position, vertexFactory.create(x, y, z, onCurve)); + addVertex(position, new Vertex(x, y, z, onCurve)); } /** @@ -496,7 +477,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { * @see <a href="#windingrules">see winding rules</a> */ public final void addVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { - addVertex(vertexFactory.create(coordsBuffer, offset, length, onCurve)); + addVertex(new Vertex(coordsBuffer, offset, length, onCurve)); } /** @@ -514,7 +495,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { * @see <a href="#windingrules">see winding rules</a> */ public final void addVertex(final int position, final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { - addVertex(position, vertexFactory.create(coordsBuffer, offset, length, onCurve)); + addVertex(position, new Vertex(coordsBuffer, offset, length, onCurve)); } /** @@ -579,9 +560,9 @@ public final class OutlineShape implements Comparable<OutlineShape> { } { // Skip if last vertex in last outline matching this point -> already connected. - final float[] llc = lo.getVertex(lo_sz-1).getCoord(); - if( llc[0] == points[idx+0] && - llc[1] == points[idx+1] ) { + final Vert2fImmutable llc = lo.getVertex(lo_sz-1); + if( llc.x() == points[idx+0] && + llc.y() == points[idx+1] ) { break; } } @@ -653,9 +634,9 @@ public final class OutlineShape implements Comparable<OutlineShape> { } { // Skip if last vertex in last outline matching this point -> already connected. - final float[] llc = lo.getVertex(0).getCoord(); - if( llc[0] == points[idx+0] && - llc[1] == points[idx+1] ) { + final Vert2fImmutable llc = lo.getVertex(0); + if( llc.x() == points[idx+0] && + llc.y() == points[idx+1] ) { break; } } @@ -791,11 +772,11 @@ public final class OutlineShape implements Comparable<OutlineShape> { VectorUtil.midVec3(tmpV2, tmpV1, tmpV3); //drop off-curve vertex to image on the curve - b.setCoord(tmpV2, 0, 3); + b.setCoord(tmpV2); b.setOnCurve(true); - outline.addVertex(index, vertexFactory.create(tmpV1, 0, 3, false)); - outline.addVertex(index+2, vertexFactory.create(tmpV3, 0, 3, false)); + outline.addVertex(index, new Vertex(tmpV1, false)); + outline.addVertex(index+2, new Vertex(tmpV3, false)); addedVerticeCount += 2; } @@ -833,7 +814,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { } else { overlap = null; } - if( overlaps.contains(currentVertex) || overlap != null ) { + if( null != overlap || overlaps.contains(currentVertex) ) { overlaps.remove(currentVertex); subdivideTriangle(outline, prevV, currentVertex, nextV, i); @@ -935,7 +916,7 @@ public final class OutlineShape implements Comparable<OutlineShape> { 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); + final Vertex v = new Vertex(tmpV1, true); i++; vertexCount++; addedVerticeCount++; @@ -947,8 +928,8 @@ public final class OutlineShape implements Comparable<OutlineShape> { outlines.remove(outline); cc--; count--; - } else if( 0 < vertexCount && - VectorUtil.isVec3Equal( outline.getVertex(0).getCoord(), 0, outline.getLastVertex().getCoord(), 0, FloatUtil.EPSILON )) { + } else if( 0 < vertexCount && + outline.getVertex(0).getCoord().isEqual( outline.getLastVertex().getCoord() ) ) { outline.removeVertex(vertexCount-1); } } @@ -1048,10 +1029,10 @@ public final class OutlineShape implements Comparable<OutlineShape> { * </p> */ public final OutlineShape transform(final AffineTransform t) { - final OutlineShape newOutlineShape = new OutlineShape(vertexFactory); + final OutlineShape newOutlineShape = new OutlineShape(); final int osize = outlines.size(); for(int i=0; i<osize; i++) { - newOutlineShape.addOutline( outlines.get(i).transform(t, vertexFactory) ); + newOutlineShape.addOutline( outlines.get(i).transform(t) ); } return newOutlineShape; } diff --git a/src/jogl/classes/com/jogamp/graph/curve/Region.java b/src/jogl/classes/com/jogamp/graph/curve/Region.java index 0ffcad2c1..28ac1a134 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/Region.java +++ b/src/jogl/classes/com/jogamp/graph/curve/Region.java @@ -33,6 +33,7 @@ import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.concurrent.TimeUnit; import jogamp.opengl.Debug; @@ -44,7 +45,10 @@ import com.jogamp.common.nio.Buffers; import com.jogamp.common.os.Clock; import com.jogamp.common.util.PerfCounterCtrl; import com.jogamp.graph.curve.opengl.GLRegion; +import com.jogamp.opengl.GLCapabilitiesImmutable; import com.jogamp.opengl.GLProfile; +import com.jogamp.opengl.math.Vec3f; +import com.jogamp.opengl.math.Vec4f; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.math.geom.Frustum; import com.jogamp.opengl.util.texture.TextureSequence; @@ -64,6 +68,14 @@ public abstract class Region { /** * Rendering-Mode bit for {@link #getRenderModes() Region} * <p> + * One pass `norm` rendering either using no AA or underlying full-screen AA (fsaa). + * </p> + */ + public static final int NORM_RENDERING_BIT = 0; + + /** + * Rendering-Mode bit for {@link #getRenderModes() Region} + * <p> * MSAA based Anti-Aliasing, a two pass region rendering, slower and more * resource hungry (FBO), but providing fast MSAA in case * the whole scene is not rendered with MSAA. @@ -169,6 +181,13 @@ public abstract class Region { return 0 != (renderModes & Region.COLORTEXTURE_RENDERING_BIT); } + /** + * Returns a unique technical description string for renderModes as follows: + * <pre> + * (vbaa|msaa|norm)[-curve][-cols][-ctex] + * </pre> + * @param renderModes Graph renderModes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)} + */ public static String getRenderModeString(final int renderModes) { final String curveS = hasVariableWeight(renderModes) ? "-curve" : ""; final String cChanS = hasColorChannel(renderModes) ? "-cols" : ""; @@ -182,6 +201,20 @@ public abstract class Region { } } + /** + * Return a unique technical description string for renderModes and sample counts as follows: + * <pre> + * {@link #getRenderModeString(int)}-s{sampleCount}-fsaa{CapsNumSamples} + * </pre> + * + * @param renderModes the used Graph renderModes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)} + * @param graphSampleCount Graph sample count for {@link Region#VBAA_RENDERING_BIT} or {@link Region#MSAA_RENDERING_BIT} + * @param fsaaSampleCount full-screen AA (fsaa) sample count, retrieved e.g. via {@link GLCapabilitiesImmutable#getNumSamples()} + */ + public static String getRenderModeString(final int renderModes, final int graphSampleCount, final int fsaaSampleCount) { + return String.format((Locale)null, "%s-s%02d-fsaa%d", Region.getRenderModeString(renderModes), graphSampleCount, fsaaSampleCount); + } + protected Region(final int regionRenderModes, final boolean use_int32_idx) { this.renderModes = regionRenderModes; this.use_int32_idx = use_int32_idx; @@ -203,36 +236,41 @@ public abstract class Region { public final boolean usesI32Idx() { return this.use_int32_idx; } /** - * Allow the renderer buffers to pre-emptively grow for given vertices- and index counts. - * @param verticesCount number of vertices to hold - * @param indicesCount number of indices to hold + * Increase the renderer buffers if necessary to add given counts of vertices- and index elements. + * <p> + * Buffers will not change if remaining free slots, capacity less position, satisfy count elements. + * </p> + * @param verticesCount number of vertex elements to add if necessary + * @param indicesCount number of index elements to add if necessary + * @return true if buffer size has changed, i.e. grown. Otherwise false. * @see #setBufferCapacity(int, int) * @see #countOutlineShape(OutlineShape, int[]) * @see #countOutlineShapes(List, int[]) */ - public abstract void growBuffer(int verticesCount, int indicesCount); + public abstract boolean growBuffer(int verticesCount, int indicesCount); /** * Set the renderer buffers pre-emptively for given vertices- and index counts. * <p> - * If the buffers already exceeds given numbers, the buffers are unchanged. + * Buffers will not change if given count elements is lower or equal current capacity. * </p> * @param verticesCount number of vertices to hold * @param indicesCount number of indices to hold + * @return true if buffer size has changed, i.e. grown. Otherwise false. * @see #growBuffer(int, int) * @see #countOutlineShape(OutlineShape, int[]) * @see #countOutlineShapes(List, int[]) */ - public abstract void setBufferCapacity(int verticesCount, int indicesCount); + public abstract boolean setBufferCapacity(int verticesCount, int indicesCount); - protected abstract void pushVertex(final float[] coords, final float[] texParams, float[] rgba); - protected abstract void pushVertices(final float[] coords1, final float[] coords2, final float[] coords3, - final float[] texParams1, final float[] texParams2, final float[] texParams3, float[] rgba); + protected abstract void pushVertex(final Vec3f coords, final Vec3f texParams, Vec4f rgba); + protected abstract void pushVertices(final Vec3f coords1, final Vec3f coords2, final Vec3f coords3, + final Vec3f texParams1, final Vec3f texParams2, final Vec3f texParams3, Vec4f rgba); protected abstract void pushIndex(int idx); protected abstract void pushIndices(int idx1, int idx2, int idx3); /** - * Return bit-field of render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence)}. + * Returns bit-field of render modes, see {@link GLRegion#create(GLProfile, int, TextureSequence) create(..)}. */ public final int getRenderModes() { return renderModes; } @@ -242,7 +280,7 @@ public abstract class Region { /** See {@link #MAX_QUALITY} */ public final void setQuality(final int q) { quality=q; } - protected void clearImpl() { + protected final void clearImpl() { dirty = DIRTY_SHAPE | DIRTY_STATE; numVertices = 0; box.reset(); @@ -280,7 +318,7 @@ public abstract class Region { * @see #addOutlineShape(OutlineShape, AffineTransform, float[]) * @see com.jogamp.graph.curve.opengl.RegionRenderer#setColorStatic(com.jogamp.opengl.GL2ES2, float, float, float, float) */ - public boolean hasColorChannel() { + public final boolean hasColorChannel() { return Region.hasColorChannel(renderModes); } @@ -290,7 +328,7 @@ public abstract class Region { * otherwise false. * @see #getRenderModes() */ - public boolean hasColorTexture() { + public final boolean hasColorTexture() { return Region.hasColorTexture(renderModes); } @@ -305,12 +343,9 @@ public abstract class Region { this.frustum = frustum; } - private void pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba) { + private void pushNewVertexImpl(final Vertex vertIn, final AffineTransform transform, final Vec4f rgba) { if( null != transform ) { - final float[] coordsEx1 = new float[3]; - final float[] coordsIn = vertIn.getCoord(); - transform.transform(coordsIn, coordsEx1); - coordsEx1[2] = coordsIn[2]; + final Vec3f coordsEx1 = transform.transform(vertIn.getCoord(), new Vec3f()); box.resize(coordsEx1); pushVertex(coordsEx1, vertIn.getTexCoord(), rgba); } else { @@ -320,20 +355,11 @@ public abstract class Region { numVertices++; } - private void pushNewVerticesImpl(final Vertex vertIn1, final Vertex vertIn2, final Vertex vertIn3, final AffineTransform transform, final float[] rgba) { + private void pushNewVerticesImpl(final Vertex vertIn1, final Vertex vertIn2, final Vertex vertIn3, final AffineTransform transform, final Vec4f rgba) { if( null != transform ) { - final float[] coordsEx1 = new float[3]; - final float[] coordsEx2 = new float[3]; - final float[] coordsEx3 = new float[3]; - final float[] coordsIn1 = vertIn1.getCoord(); - final float[] coordsIn2 = vertIn2.getCoord(); - final float[] coordsIn3 = vertIn3.getCoord(); - transform.transform(coordsIn1, coordsEx1); - transform.transform(coordsIn2, coordsEx2); - transform.transform(coordsIn3, coordsEx3); - coordsEx1[2] = coordsIn1[2]; - coordsEx2[2] = coordsIn2[2]; - coordsEx3[2] = coordsIn3[2]; + final Vec3f coordsEx1 = transform.transform(vertIn1.getCoord(), new Vec3f()); + final Vec3f coordsEx2 = transform.transform(vertIn2.getCoord(), new Vec3f()); + final Vec3f coordsEx3 = transform.transform(vertIn3.getCoord(), new Vec3f()); box.resize(coordsEx1); box.resize(coordsEx2); box.resize(coordsEx3); @@ -350,11 +376,11 @@ public abstract class Region { } @SuppressWarnings("unused") - private void pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform, final float[] rgba) { + private void pushNewVertexIdxImpl(final Vertex vertIn, final AffineTransform transform, final Vec4f rgba) { pushIndex(numVertices); pushNewVertexImpl(vertIn, transform, rgba); } - private void pushNewVerticesIdxImpl(final Vertex vertIn1, final Vertex vertIn2, final Vertex vertIn3, final AffineTransform transform, final float[] rgba) { + private void pushNewVerticesIdxImpl(final Vertex vertIn1, final Vertex vertIn2, final Vertex vertIn3, final AffineTransform transform, final Vec4f rgba) { pushIndices(numVertices, numVertices+1, numVertices+2); pushNewVerticesImpl(vertIn1, vertIn2, vertIn3, transform, rgba); } @@ -365,12 +391,15 @@ public abstract class Region { protected static void put3s(final ShortBuffer b, final short v1, final short v2, final short v3) { b.put(v1); b.put(v2); b.put(v3); } - protected static void put3f(final FloatBuffer b, final float v1, final float v2, final float v3) { - b.put(v1); b.put(v2); b.put(v3); + protected static void put3f(final FloatBuffer b, final Vec3f v) { + b.put(v.x()); b.put(v.y()); b.put(v.z()); } protected static void put4f(final FloatBuffer b, final float v1, final float v2, final float v3, final float v4) { b.put(v1); b.put(v2); b.put(v3); b.put(v4); } + protected static void put4f(final FloatBuffer b, final Vec4f v) { + b.put(v.x()); b.put(v.y()); b.put(v.z()); b.put(v.w()); + } private final AABBox tmpBox = new AABBox(); @@ -453,38 +482,51 @@ public abstract class Region { /** * Count required number of vertices and indices adding to given int[2] `vertIndexCount` array. * <p> - * The region's buffer can be either set using {@link Region#setBufferCapacity(int, int)} or grown using {@link Region#growBuffer(int, int)}. + * The region's buffer can be either set using {@link Region#setBufferCapacity(int, int)}, + * {@link GLRegion#create(GLProfile, int, TextureSequence, int, int) GLRegion ctor w/ initial capacities} + * or grown using {@link Region#growBuffer(int, int)}. + * </p> + * <p> + * Method is utilized in {@link GLRegion#create(GLProfile, int, TextureSequence, com.jogamp.graph.font.Font, CharSequence) GLRegion ctor w/ font + text}, + * computing the initial capacity. * </p> * @param shape the {@link OutlineShape} to count * @param vertIndexCount the int[2] storage where the counted vertices and indices are added, vertices at [0] and indices at [1] + * @return the given int[2] storage for chaining * @see #setBufferCapacity(int, int) * @see #growBuffer(int, int) */ - public final void countOutlineShape(final OutlineShape shape, final int[/*2*/] vertIndexCount) { + public static final int[] countOutlineShape(final OutlineShape shape, final int[/*2*/] vertIndexCount) { + if( null == shape ) { + return vertIndexCount; + } final List<Triangle> trisIn = shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS); final ArrayList<Vertex> vertsIn = shape.getVertices(); { - final int verticeCount = vertsIn.size() + shape.getAddedVerticeCount(); - final int indexCount = trisIn.size() * 3; - vertIndexCount[0] += verticeCount; - vertIndexCount[1] += Math.min( Math.ceil(verticeCount * 0.6), indexCount ); + vertIndexCount[0] += vertsIn.size() + shape.getAddedVerticeCount(); // verticesCount + vertIndexCount[1] += trisIn.size() * 3; // indicesCount } + return vertIndexCount; } /** * Count required number of vertices and indices adding to given int[2] `vertIndexCount` array. * <p> - * The region's buffer can be either set using {@link Region#setBufferCapacity(int, int)} or grown using {@link Region#growBuffer(int, int)}. + * The region's buffer can be either set using {@link Region#setBufferCapacity(int, int)}, + * {@link GLRegion#create(GLProfile, int, TextureSequence, int, int) GLRegion ctor w/ initial capacities} + * or grown using {@link Region#growBuffer(int, int)}. * </p> * @param shapes list of {@link OutlineShape} to count * @param vertIndexCount the int[2] storage where the counted vertices and indices are added, vertices at [0] and indices at [1] + * @return the given int[2] storage for chaining * @see #setBufferCapacity(int, int) * @see #growBuffer(int, int) */ - public final void countOutlineShapes(final List<OutlineShape> shapes, final int[/*2*/] vertIndexCount) { + public static final int[] countOutlineShapes(final List<OutlineShape> shapes, final int[/*2*/] vertIndexCount) { for (int i = 0; i < shapes.size(); i++) { countOutlineShape(shapes.get(i), vertIndexCount); } + return vertIndexCount; } /** @@ -498,7 +540,7 @@ public abstract class Region { * @param t the optional {@link AffineTransform} to be applied on each vertex * @param rgbaColor if {@link #hasColorChannel()} RGBA color must be passed, otherwise value is ignored. */ - public final void addOutlineShape(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor) { + public final void addOutlineShape(final OutlineShape shape, final AffineTransform t, final Vec4f rgbaColor) { if( null != frustum ) { final AABBox shapeBox = shape.getBounds(); final AABBox shapeBoxT; @@ -515,11 +557,14 @@ public abstract class Region { if( null == perf && !DEBUG_INSTANCE ) { addOutlineShape0(shape, t, rgbaColor); } else { + if( null == perf ) { + perfCounter().enable(true); + } addOutlineShape1(shape, t, rgbaColor); } markShapeDirty(); } - private final void addOutlineShape0(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor) { + private final void addOutlineShape0(final OutlineShape shape, final AffineTransform t, final Vec4f rgbaColor) { final List<Triangle> trisIn = shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS); final ArrayList<Vertex> vertsIn = shape.getVertices(); { @@ -556,7 +601,7 @@ public abstract class Region { } } } - private final void addOutlineShape1(final OutlineShape shape, final AffineTransform t, final float[] rgbaColor) { + private final void addOutlineShape1(final OutlineShape shape, final AffineTransform t, final Vec4f rgbaColor) { ++perf.count; final long t0 = Clock.currentNanos(); final List<Triangle> trisIn = shape.getTriangles(OutlineShape.VerticesState.QUADRATIC_NURBS); @@ -651,7 +696,7 @@ public abstract class Region { * @param t the optional {@link AffineTransform} to be applied on each vertex * @param rgbaColor if {@link #hasColorChannel()} RGBA color must be passed, otherwise value is ignored. */ - public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final float[] rgbaColor) { + public final void addOutlineShapes(final List<OutlineShape> shapes, final AffineTransform transform, final Vec4f rgbaColor) { for (int i = 0; i < shapes.size(); i++) { addOutlineShape(shapes.get(i), transform, rgbaColor); } @@ -663,8 +708,14 @@ public abstract class Region { } /** - * Mark this region's shape dirty, i.e. it's - * Vertices, Triangles, and or Lines changed. + * Mark this region's shape dirty, + * i.e. its vertices, triangles, lines and/or color-texture coordinates changed. + * <p> + * The data will be re-uploaded to the GPU at next {@link GLRegion#draw(com.jogamp.opengl.GL2ES2, com.jogamp.graph.curve.opengl.RegionRenderer, int[]) draw(..)}. + * </p> + * <p> + * In 2-pass mode, this implies updating the FBO itself as well. + * </p> */ public final void markShapeDirty() { dirty |= DIRTY_SHAPE; @@ -674,8 +725,11 @@ public abstract class Region { return 0 != ( dirty & DIRTY_SHAPE ) ; } /** - * Mark this region's state dirty, i.e. - * it's render attributes or parameters changed. + * Mark this region's render-state dirty, i.e. re-selecting a shader program regarding color-texture and -channel, + * and rendering the region into the FBO in 2-pass mode. + * <p> + * In 1-pass mode, re-selection of the shader-program is based on color-texture and -channel only. + * </p> */ public final void markStateDirty() { dirty |= DIRTY_STATE; @@ -695,6 +749,6 @@ public abstract class Region { @Override public String toString() { - return "Region["+getRenderModeString(this.renderModes)+", q "+quality+", dirty "+dirty+", vertices "+numVertices+", box "+box+"]"; + return "Region[0x"+Integer.toHexString(hashCode())+", "+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 e02752f73..49daa344b 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java @@ -32,17 +32,28 @@ import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLArrayData;
import com.jogamp.opengl.util.GLArrayDataClient;
import com.jogamp.opengl.util.GLArrayDataEditable;
+import com.jogamp.opengl.util.GLArrayDataServer;
+import com.jogamp.opengl.util.GLArrayDataWrapper;
import com.jogamp.opengl.GLProfile;
+import com.jogamp.opengl.math.Vec3f;
+import com.jogamp.opengl.math.Vec4f;
import jogamp.graph.curve.opengl.VBORegion2PMSAAES2;
import jogamp.graph.curve.opengl.VBORegion2PVBAAES2;
import jogamp.graph.curve.opengl.VBORegionSPES2;
+import jogamp.graph.curve.opengl.shader.AttributeNames;
+import jogamp.opengl.Debug;
import com.jogamp.opengl.util.PMVMatrix;
+import com.jogamp.opengl.util.glsl.ShaderProgram;
import com.jogamp.opengl.util.texture.TextureSequence;
import com.jogamp.graph.curve.Region;
+import com.jogamp.graph.font.Font;
import java.io.PrintStream;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
import com.jogamp.graph.curve.OutlineShape;
@@ -65,22 +76,27 @@ public abstract class GLRegion extends Region { * - Ubuntu Light ~ vertices 100/char, indices 50/char
* - FreeSerif ~ vertices 115/char, indices 61/char
*
- * Now let's assume a minimum of 10 chars will be rendered
+ * However, proper initial size is pre-calculated via ..
+ * - {@link GLRegion#create(GLProfile, int, TextureSequence, Font, CharSequence)}
+ * - {@Link Region#countOutlineShape(OutlineShape, int[])}
+ * - {@link TextRegionUtil#countStringRegion(Font, CharSequence, int[])}
*/
/**
- * Default initial vertices count based on 10 chars w/ FreeSans @ 64 vertices/char avg.
+ * Default initial vertices count {@value}, assuming small sized shapes.
*/
- public static final int defaultVerticesCount = 10*64;
+ public static final int defaultVerticesCount = 64;
/**
- * Default initial indices count based on 10 chars w/ FreeSans @ 33 indices/char avg.
+ * Default initial indices count {@value}, assuming small sized shapes.
*/
- public static final int defaultIndicesCount = 10*33;
+ public static final int defaultIndicesCount = 64;
// private static final float growthFactor = 1.2f; // avg +5% size but 15% more overhead (34% total)
protected static final float growthFactor = GLArrayDataClient.DEFAULT_GROWTH_FACTOR; // avg +20% size, but 15% less CPU overhead compared to 1.2 (19% total)
+ private static final boolean DEBUG_BUFFER = Debug.debug("graph.curve.Buffer");
+
/**
* Create a GLRegion using the passed render mode
*
@@ -108,7 +124,7 @@ public abstract class GLRegion extends Region { }
/**
- * Create a GLRegion using the passed render mode
+ * Create a GLRegion using the passed render mode and default initial buffer sizes {@link #defaultVerticesCount} and {@link #defaultIndicesCount}.
*
* <p> In case {@link Region#VBAA_RENDERING_BIT} is being requested the default texture unit
* {@link Region#DEFAULT_TWO_PASS_TEXTURE_UNIT} is being used.</p>
@@ -120,9 +136,61 @@ public abstract class GLRegion extends Region { return GLRegion.create(glp, renderModes, colorTexSeq, defaultVerticesCount, defaultIndicesCount);
}
+ /**
+ * Create a GLRegion using the passed render mode and pre-calculating its buffer sizes
+ * using {@link Region#countOutlineShape(OutlineShape, int[])}.
+ *
+ * <p> In case {@link Region#VBAA_RENDERING_BIT} is being requested the default texture unit
+ * {@link Region#DEFAULT_TWO_PASS_TEXTURE_UNIT} is being used.</p>
+ * @param glp intended GLProfile to use. Instance may use higher OpenGL features if indicated by GLProfile.
+ * @param renderModes bit-field of modes, e.g. {@link Region#VARWEIGHT_RENDERING_BIT}, {@link Region#VBAA_RENDERING_BIT}
+ * @param colorTexSeq optional {@link TextureSequence} for {@link Region#COLORTEXTURE_RENDERING_BIT} rendering mode.
+ * @param shape the {@link OutlineShape} used to determine {@link GLRegion}'s buffer sizes via {@link Region#countOutlineShape(OutlineShape, int[])}
+ */
+ public static GLRegion create(final GLProfile glp, final int renderModes, final TextureSequence colorTexSeq, final OutlineShape shape) {
+ final int[/*2*/] vertIndexCount = Region.countOutlineShape(shape, new int[2]);
+ return GLRegion.create(glp, renderModes, colorTexSeq, vertIndexCount[0], vertIndexCount[1]);
+ }
+
+ /**
+ * Create a GLRegion using the passed render mode and pre-calculating its buffer sizes
+ * using given font's {@link Font#processString(com.jogamp.graph.font.Font.GlyphVisitor2, CharSequence)}
+ * to {@link #countOutlineShape(OutlineShape, int[])}.
+ *
+ * <p> In case {@link Region#VBAA_RENDERING_BIT} is being requested the default texture unit
+ * {@link Region#DEFAULT_TWO_PASS_TEXTURE_UNIT} is being used.</p>
+ * @param glp intended GLProfile to use. Instance may use higher OpenGL features if indicated by GLProfile.
+ * @param renderModes bit-field of modes, e.g. {@link Region#VARWEIGHT_RENDERING_BIT}, {@link Region#VBAA_RENDERING_BIT}
+ * @param colorTexSeq optional {@link TextureSequence} for {@link Region#COLORTEXTURE_RENDERING_BIT} rendering mode.
+ * @param font Font used to {@link Font#processString(com.jogamp.graph.curve.OutlineShape.Visitor2, CharSequence)} to {@link #countOutlineShape(OutlineShape, int[]) to count initial number of vertices and indices}
+ * @param str the string used to to {@link #countOutlineShape(OutlineShape, int[]) to count initial number of vertices and indices}
+ */
+ public static GLRegion create(final GLProfile glp, final int renderModes, final TextureSequence colorTexSeq, final Font font, final CharSequence str) {
+ final int[] vertIndexCount = { 0, 0 };
+ final Font.GlyphVisitor2 visitor = new Font.GlyphVisitor2() {
+ @Override
+ public final void visit(final char symbol, final Font.Glyph glyph) {
+ Region.countOutlineShape(glyph.getShape(), vertIndexCount);
+ } };
+ font.processString(visitor, str);
+ return GLRegion.create(glp, renderModes, colorTexSeq, vertIndexCount[0], vertIndexCount[1]);
+ }
+
private final int gl_idx_type;
protected final TextureSequence colorTexSeq;
+ // pass-1 common data
+ protected int curVerticesCap = 0;
+ protected int curIndicesCap = 0;
+ protected int growCount = 0;
+
+ /** Interleaved buffer for GLSL attributes: vectices, curveParams and optionally colors */
+ protected GLArrayDataServer vpc_ileave = null;
+ protected GLArrayDataWrapper gca_VerticesAttr = null;
+ protected GLArrayDataWrapper gca_CurveParamsAttr = null;
+ protected GLArrayDataWrapper gca_ColorsAttr = null;
+ protected GLArrayDataServer indicesBuffer = null;
+
protected GLRegion(final GLProfile glp, final int renderModes, final TextureSequence colorTexSeq) {
super(renderModes, glp.isGL2ES3() /* use_int32_idx */);
this.gl_idx_type = usesI32Idx() ? GL.GL_UNSIGNED_INT : GL.GL_UNSIGNED_SHORT;
@@ -131,19 +199,125 @@ public abstract class GLRegion extends Region { protected final int glIdxType() { return this.gl_idx_type; }
- /**
- * Updates a graph region by updating the ogl related
- * 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>
- */
- protected abstract void updateImpl(final GL2ES2 gl);
+ public GLArrayDataServer createInterleaved(final boolean useMappedBuffers, final int comps, final int dataType, final boolean normalized, final int initialSize, final int vboUsage) {
+ if( useMappedBuffers ) {
+ return GLArrayDataServer.createGLSLInterleavedMapped(comps, dataType, normalized, initialSize, vboUsage);
+ } else {
+ return GLArrayDataServer.createGLSLInterleaved(comps, dataType, normalized, initialSize, vboUsage);
+ }
+ }
- protected abstract void destroyImpl(final GL2ES2 gl);
+ public void addInterleavedVertexAndNormalArrays(final GLArrayDataServer array, final int components) {
+ array.addGLSLSubArray("vertices", components, GL.GL_ARRAY_BUFFER);
+ array.addGLSLSubArray("normals", components, GL.GL_ARRAY_BUFFER);
+ }
- protected abstract void clearImpl(final GL2ES2 gl);
+ protected final void initBuffer(final int verticeCount, final int indexCount) {
+ indicesBuffer = GLArrayDataServer.createData(3, glIdxType(), indexCount, GL.GL_STATIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER);
+ indicesBuffer.setGrowthFactor(growthFactor);
+ curIndicesCap = indicesBuffer.getElemCapacity();
+
+ final boolean cc = hasColorChannel();
+ final int totalCompsPerElem = 3 + 3 + (cc ? 4 : 0);
+ vpc_ileave = GLArrayDataServer.createGLSLInterleaved(totalCompsPerElem, GL.GL_FLOAT, false /* normalized */, verticeCount, GL.GL_STATIC_DRAW);
+ vpc_ileave.setGrowthFactor(growthFactor);
+
+ gca_VerticesAttr = vpc_ileave.addGLSLSubArray(AttributeNames.VERTEX_ATTR_NAME, 3, GL.GL_ARRAY_BUFFER);
+ gca_CurveParamsAttr = vpc_ileave.addGLSLSubArray(AttributeNames.CURVEPARAMS_ATTR_NAME, 3, GL.GL_ARRAY_BUFFER);
+ if( cc ) {
+ gca_ColorsAttr = vpc_ileave.addGLSLSubArray(AttributeNames.COLOR_ATTR_NAME, 4, GL.GL_ARRAY_BUFFER);
+ }
+ curVerticesCap = vpc_ileave.getElemCapacity();
+ growCount = 0;
+ }
+
+ @Override
+ public final boolean growBuffer(final int verticesCount, final int indicesCount) {
+ boolean grown = false;
+ if( !DEBUG_BUFFER ) {
+ if( curIndicesCap < indicesBuffer.elemPosition() + indicesCount ) {
+ indicesBuffer.growIfNeeded(indicesCount * indicesBuffer.getCompsPerElem());
+ curIndicesCap = indicesBuffer.getElemCapacity();
+ grown = true;
+ }
+ if( curVerticesCap < vpc_ileave.elemPosition() + verticesCount ) {
+ vpc_ileave.growIfNeeded(verticesCount * vpc_ileave.getCompsPerElem());
+ curVerticesCap = vpc_ileave.getElemCapacity();
+ grown = true;
+ }
+ } else {
+ if( curIndicesCap < indicesBuffer.elemPosition() + indicesCount ) {
+ System.err.printf("GLRegion: Buffer grow - Indices: %d < ( %d = %d + %d ); Status: %s%n",
+ curIndicesCap, indicesBuffer.elemPosition() + indicesCount, indicesBuffer.elemPosition(), indicesCount, indicesBuffer.elemStatsToString());
+
+ indicesBuffer.growIfNeeded(indicesCount * indicesBuffer.getCompsPerElem());
+
+ System.err.println("GLRegion: Grew Indices 0x"+Integer.toHexString(hashCode())+": "+curIndicesCap+" -> "+indicesBuffer.getElemCapacity()+", "+indicesBuffer.elemStatsToString());
+ Thread.dumpStack();
+
+ curIndicesCap = indicesBuffer.getElemCapacity();
+ grown = true;
+ }
+ if( curVerticesCap < vpc_ileave.elemPosition() + verticesCount ) {
+ System.err.printf("GLRegion: Buffer grow - Vertices: %d < ( %d = %d + %d ); Status: %s%n",
+ curVerticesCap, gca_VerticesAttr.elemPosition() + verticesCount, gca_VerticesAttr.elemPosition(), verticesCount, gca_VerticesAttr.elemStatsToString());
+
+ vpc_ileave.growIfNeeded(verticesCount * vpc_ileave.getCompsPerElem());
+
+ System.err.println("GLRegion: Grew Vertices 0x"+Integer.toHexString(hashCode())+": "+curVerticesCap+" -> "+gca_VerticesAttr.getElemCapacity()+", "+gca_VerticesAttr.elemStatsToString());
+
+ curVerticesCap = vpc_ileave.getElemCapacity();
+ grown = true;
+ }
+ }
+ if( grown ) {
+ ++growCount;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public final boolean setBufferCapacity(final int verticesCount, final int indicesCount) {
+ boolean grown = false;
+ if( curIndicesCap < indicesCount ) {
+ indicesBuffer.reserve(indicesCount);
+ curIndicesCap = indicesBuffer.getElemCapacity();
+ grown = true;
+ }
+ if( curVerticesCap < verticesCount ) {
+ vpc_ileave.reserve(verticesCount);
+ curVerticesCap = vpc_ileave.getElemCapacity();
+ grown = true;
+ }
+ return grown;
+ }
- protected static void printAndCount(final PrintStream out, final String name, final GLArrayData data, final int[] size, final int[] capacity) {
+ @Override
+ public final void printBufferStats(final PrintStream out) {
+ final int[] size= { 0 }, capacity= { 0 };
+ out.println("GLRegion: idx32 "+usesI32Idx()+", obj 0x"+Integer.toHexString(hashCode()));
+ printAndCount(out, " indices ", indicesBuffer, size, capacity);
+ out.println();
+ printAndCount(out, " ileave ", vpc_ileave, size, capacity);
+ out.println();
+ {
+ print(out, " - vertices ", gca_VerticesAttr);
+ out.println();
+ print(out, " - params ", gca_CurveParamsAttr);
+ out.println();
+ print(out, " - color ", gca_ColorsAttr);
+ out.println();
+ }
+ final float filled = (float)size[0]/(float)capacity[0];
+ out.printf(" total [bytes %,d / %,d], filled[%.1f%%, left %.1f%%], grow-cnt %d, obj 0x%x%n",
+ size[0], capacity[0], filled*100f, (1f-filled)*100f, growCount, hashCode());
+ // out.printf(" vpc_ileave: %s%n", vpc_ileave.toString());
+ // out.printf(" - vertices: %s%n", gca_VerticesAttr.toString());
+ }
+
+ private static void printAndCount(final PrintStream out, final String name, final GLArrayData data, final int[] size, final int[] capacity) {
out.print(name+"[");
if( null != data ) {
out.print(data.fillStatsToString());
@@ -154,6 +328,76 @@ public abstract class GLRegion extends Region { out.print("null]");
}
}
+ private static void print(final PrintStream out, final String name, final GLArrayData data) {
+ out.print(name+"[");
+ if( null != data ) {
+ out.print(data.fillStatsToString());
+ out.print("]");
+ } else {
+ out.print("null]");
+ }
+ }
+
+ @Override
+ protected final void pushVertex(final Vec3f coords, final Vec3f texParams, final Vec4f rgba) {
+ // NIO array[3] is much slows than group/single
+ // gca_VerticesAttr.putf(coords, 0, 3);
+ // gca_CurveParamsAttr.putf(texParams, 0, 3);
+ // gca_VerticesAttr.put3f(coords.x(), coords.y(), coords.z());
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), coords);
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), texParams);
+ if( hasColorChannel() ) {
+ if( null != rgba ) {
+ put4f((FloatBuffer)vpc_ileave.getBuffer(), rgba);
+ } else {
+ throw new IllegalArgumentException("Null color given for COLOR_CHANNEL rendering mode");
+ }
+ }
+ }
+
+ @Override
+ protected final void pushVertices(final Vec3f coords1, final Vec3f coords2, final Vec3f coords3,
+ final Vec3f texParams1, final Vec3f texParams2, final Vec3f texParams3, final Vec4f rgba) {
+ final boolean cc = hasColorChannel();
+ if( cc && null == rgba ) {
+ throw new IllegalArgumentException("Null color given for COLOR_CHANNEL rendering mode");
+ }
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), coords1);
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), texParams1);
+ if( cc ) {
+ put4f((FloatBuffer)vpc_ileave.getBuffer(), rgba);
+ }
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), coords2);
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), texParams2);
+ if( cc ) {
+ put4f((FloatBuffer)vpc_ileave.getBuffer(), rgba);
+ }
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), coords3);
+ put3f((FloatBuffer)vpc_ileave.getBuffer(), texParams3);
+ if( cc ) {
+ put4f((FloatBuffer)vpc_ileave.getBuffer(), rgba);
+ }
+ }
+
+ @Override
+ protected final void pushIndex(final int idx) {
+ if( usesI32Idx() ) {
+ indicesBuffer.puti(idx);
+ } else {
+ indicesBuffer.puts((short)idx);
+ }
+ }
+
+ @Override
+ protected final void pushIndices(final int idx1, final int idx2, final int idx3) {
+ if( usesI32Idx() ) {
+ // indicesBuffer.put3i(idx1, idx2, idx3);
+ put3i((IntBuffer)indicesBuffer.getBuffer(), idx1, idx2, idx3);
+ } else {
+ // indicesBuffer.put3s((short)idx1, (short)idx2, (short)idx3);
+ put3s((ShortBuffer)indicesBuffer.getBuffer(), (short)idx1, (short)idx2, (short)idx3);
+ }
+ }
/**
* Clears all buffers, i.e. triangles, vertices etc and and resets states accordingly, see {@link GLArrayDataEditable#clear(GL)}.
@@ -165,19 +409,58 @@ public abstract class GLRegion extends Region { * @return this {@link GLRegion} for chaining.
* @see GLArrayDataEditable#clear(GL)
*/
- public GLRegion clear(final GL2ES2 gl) {
+ public final GLRegion clear(final GL2ES2 gl) {
+ lastRenderModes = 0;
+ if(DEBUG_INSTANCE) {
+ System.err.println("GLRegion Clear: " + this);
+ }
+ if( null != indicesBuffer ) {
+ indicesBuffer.clear(gl);
+ }
+ if( null != vpc_ileave ) {
+ vpc_ileave.clear(gl);
+ }
clearImpl(gl);
clearImpl();
return this;
}
+ protected abstract void clearImpl(final GL2ES2 gl);
/**
* Delete and clear the associated OGL objects.
+ * <p>
+ * The {@link ShaderProgram}s references are nullified but not {@link ShaderProgram#destroy(GL2ES2) destroyed}
+ * as they are owned by {@link RegionRenderer}.
+ * </p>
*/
public final void destroy(final GL2ES2 gl) {
clear(gl);
+ if( null != vpc_ileave ) {
+ vpc_ileave.destroy(gl);
+ vpc_ileave = null;
+ }
+ if( null != gca_VerticesAttr ) {
+ gca_VerticesAttr.destroy(gl);
+ gca_VerticesAttr = null;
+ }
+ if( null != gca_CurveParamsAttr ) {
+ gca_CurveParamsAttr.destroy(gl);
+ gca_CurveParamsAttr = null;
+ }
+ if( null != gca_ColorsAttr ) {
+ gca_ColorsAttr.destroy(gl);
+ gca_ColorsAttr = null;
+ }
+ if(null != indicesBuffer) {
+ indicesBuffer.destroy(gl);
+ indicesBuffer = null;
+ }
+ curVerticesCap = 0;
+ curIndicesCap = 0;
+ growCount = 0;
destroyImpl(gl);
}
+ protected abstract void destroyImpl(final GL2ES2 gl);
/**
* Renders the associated OGL objects specifying
@@ -199,22 +482,49 @@ public abstract class GLRegion extends Region { * The <i>alpha</i> component shall be set to zero.
* Note: If {@link GL#GL_BLEND blending} is enabled, the
* {@link RegionRenderer} might need to be
- * {@link RegionRenderer#create(RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) created}
+ * {@link RegionRenderer#create(Vertex.Factory<? extends Vertex>, RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) created}
* with the appropriate {@link RegionRenderer.GLCallback callbacks}.
* </p>
* @param matrix current {@link PMVMatrix}.
* @param renderer the {@link RegionRenderer} to be used
- * @param sampleCount desired multisampling sample count for msaa-rendering.
+ * @param sampleCount desired multisampling sample count for vbaa- or msaa-rendering.
+ * Use -1 for glSelect mode, pass1 w/o any color texture nor channel, use static select color only.
* The actual used scample-count is written back when msaa-rendering is enabled, otherwise the store is untouched.
* @see RegionRenderer#enable(GL2ES2, boolean)
*/
public final void draw(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount) {
+ final int curRenderModes;
+ if( null == sampleCount || 0 == sampleCount[0] ) {
+ // no sampling, reduce to pass1
+ curRenderModes = getRenderModes() & ~( VBAA_RENDERING_BIT | MSAA_RENDERING_BIT );
+ } else if( 0 > sampleCount[0] ) {
+ // negative sampling, hint we perform glSelect: pass1 w/o any color texture nor channel, use static select color only
+ curRenderModes = getRenderModes() & ~( VBAA_RENDERING_BIT | MSAA_RENDERING_BIT | COLORCHANNEL_RENDERING_BIT | COLORTEXTURE_RENDERING_BIT );
+ } else {
+ // normal 2-pass sampling
+ curRenderModes = getRenderModes();
+ }
+ if( lastRenderModes != curRenderModes ) {
+ markShapeDirty();
+ markStateDirty();
+ }
if( isShapeDirty() ) {
- updateImpl(gl);
+ updateImpl(gl, curRenderModes);
}
- drawImpl(gl, renderer, sampleCount);
+ drawImpl(gl, renderer, curRenderModes, sampleCount);
clearDirtyBits(DIRTY_SHAPE|DIRTY_STATE);
+ lastRenderModes = curRenderModes;
}
+ private int lastRenderModes = 0;
+
+ /**
+ * Updates a graph region by updating the ogl related
+ * 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>
+ * @param curRenderModes TODO
+ */
+ protected abstract void updateImpl(final GL2ES2 gl, int curRenderModes);
- protected abstract void drawImpl(final GL2ES2 gl, final RegionRenderer renderer, final int[/*1*/] sampleCount);
+ protected abstract void drawImpl(final GL2ES2 gl, final RegionRenderer renderer, int curRenderModes, 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 6f419704b..4863e97d6 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -35,11 +35,13 @@ import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLES2; import com.jogamp.opengl.GLException; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; +import com.jogamp.opengl.math.Recti; import jogamp.graph.curve.opengl.shader.AttributeNames; import jogamp.graph.curve.opengl.shader.UniformNames; import com.jogamp.opengl.GLExtensions; +import com.jogamp.opengl.GLRendererQuirks; import com.jogamp.opengl.util.glsl.ShaderCode; import com.jogamp.opengl.util.glsl.ShaderProgram; import com.jogamp.opengl.util.texture.TextureSequence; @@ -50,18 +52,23 @@ import com.jogamp.graph.curve.Region; /** * OpenGL {@link Region} renderer - * <p> - * All OpenGL related operations regarding {@link Region}s - * are passed through an instance of this class. - * </p> + * + * All {@link Region} rendering operations utilize a RegionRenderer. + * + * The RegionRenderer owns its {@link RenderState}, a composition. + * + * The RegionRenderer manages and own all used {@link ShaderProgram}s, a composition. + * + * At its {@link #destroy(GL2ES2) destruction}, all {@link ShaderProgram}s and its {@link RenderState} + * will be destroyed and released. */ -public class RegionRenderer { +public final class RegionRenderer { protected static final boolean DEBUG = Region.DEBUG; protected static final boolean DEBUG_INSTANCE = Region.DEBUG_INSTANCE; /** * May be passed to - * {@link RegionRenderer#create(RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) RegionRenderer ctor}, + * {@link RegionRenderer#create(Vertex.Factory<? extends Vertex>, RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) RegionRenderer ctor}, * e.g. * <ul> * <li>{@link RegionRenderer#defaultBlendEnable}</li> @@ -86,7 +93,7 @@ public class RegionRenderer { * 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 #create(Vertex.Factory<? extends Vertex>, RenderState, GLCallback, GLCallback) * @see #enable(GL2ES2, boolean) */ public static final GLCallback defaultBlendEnable = new GLCallback() { @@ -110,7 +117,7 @@ public class RegionRenderer { * <p> * Implementation also clears {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}. * </p> - * @see #create(RenderState, GLCallback, GLCallback) + * @see #create(Vertex.Factory<? extends Vertex>, RenderState, GLCallback, GLCallback) * @see #enable(GL2ES2, boolean) */ public static final GLCallback defaultBlendDisable = new GLCallback() { @@ -127,14 +134,48 @@ public class RegionRenderer { }; /** - * Create a Hardware accelerated Region Renderer. + * Create a hardware accelerated RegionRenderer including its {@link RenderState} composition. + * <p> + * The optional {@link GLCallback}s <code>enableCallback</code> and <code>disableCallback</code> + * maybe used to issue certain tasks at {@link #enable(GL2ES2, boolean)}.<br/> + * For example, instances {@link #defaultBlendEnable} and {@link #defaultBlendDisable} + * can be utilized to enable and disable {@link GL#GL_BLEND}. + * </p> + * @return an instance of Region Renderer + * @see #enable(GL2ES2, boolean) + */ + public static RegionRenderer create() { + return new RegionRenderer(null, null, null); + } + + /** + * Create a hardware accelerated RegionRenderer including its {@link RenderState} composition. + * <p> + * The optional {@link GLCallback}s <code>enableCallback</code> and <code>disableCallback</code> + * maybe used to issue certain tasks at {@link #enable(GL2ES2, boolean)}.<br/> + * For example, instances {@link #defaultBlendEnable} and {@link #defaultBlendDisable} + * can be utilized to enable and disable {@link GL#GL_BLEND}. + * </p> + * @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 + * {@link #enable(GL2ES2, boolean) enable(gl, false)}. + * @return an instance of Region Renderer + * @see #enable(GL2ES2, boolean) + */ + public static RegionRenderer create(final GLCallback enableCallback, final GLCallback disableCallback) { + return new RegionRenderer(enableCallback, disableCallback); + } + + /** + * Create a hardware accelerated RegionRenderer including its {@link RenderState} composition. * <p> * The optional {@link GLCallback}s <code>enableCallback</code> and <code>disableCallback</code> * maybe used to issue certain tasks at {@link #enable(GL2ES2, boolean)}.<br/> * For example, instances {@link #defaultBlendEnable} and {@link #defaultBlendDisable} * can be utilized to enable and disable {@link GL#GL_BLEND}. * </p> - * @param rs the used {@link RenderState} + * @param sharedPMVMatrix optional shared {@link PMVMatrix} to be used for the {@link RenderState} composition. * @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 @@ -142,9 +183,9 @@ public class RegionRenderer { * @return an instance of Region Renderer * @see #enable(GL2ES2, boolean) */ - public static RegionRenderer create(final RenderState rs, final GLCallback enableCallback, - final GLCallback disableCallback) { - return new RegionRenderer(rs, enableCallback, disableCallback); + public static RegionRenderer create(final PMVMatrix sharedPMVMatrix, + final GLCallback enableCallback, final GLCallback disableCallback) { + return new RegionRenderer(sharedPMVMatrix, enableCallback, disableCallback); } private final RenderState rs; @@ -152,35 +193,42 @@ public class RegionRenderer { private final GLCallback enableCallback; private final GLCallback disableCallback; - private final int[] viewport = new int[] { 0, 0, 0, 0 }; + private final Recti viewport = new Recti(); private boolean initialized; private boolean vboSupported = false; public final boolean isInitialized() { return initialized; } - /** Copies the current viewport in given target and returns it for chaining. */ - public final int[/*4*/] getViewport(final int[/*4*/] target) { - System.arraycopy(viewport, 0, target, 0, 4); + /** Copies the current Rect4i viewport in given target and returns it for chaining. */ + public final Recti getViewport(final Recti target) { + target.set(viewport); return target; } - /** Borrows the current viewport w/o copying. */ - public final int[/*4*/] getViewport() { + /** Borrows the current Rect4i viewport w/o copying. */ + public final Recti getViewport() { return viewport; } /** Return width of current viewport */ - public final int getWidth() { return viewport[2]; } + public final int getWidth() { return viewport.width(); } /** Return height of current viewport */ - public final int getHeight() { return viewport[3]; } + public final int getHeight() { return viewport.height(); } + /** Borrow the current {@link PMVMatrix}. */ public final PMVMatrix getMatrix() { return rs.getMatrix(); } ////////////////////////////////////// - /** - * @param rs the used {@link RenderState} - */ - protected RegionRenderer(final RenderState rs, final GLCallback enableCallback, final GLCallback disableCallback) { - this.rs = rs; + protected RegionRenderer(final GLCallback enableCallback, final GLCallback disableCallback) + { + this.rs = new RenderState(null); + this.enableCallback = enableCallback; + this.disableCallback = disableCallback; + } + + protected RegionRenderer(final PMVMatrix sharedPMVMatrix, + final GLCallback enableCallback, final GLCallback disableCallback) + { + this.rs = new RenderState(sharedPMVMatrix); this.enableCallback = enableCallback; this.disableCallback = disableCallback; } @@ -223,6 +271,7 @@ public class RegionRenderer { initialized = true; } + /** Deletes all {@link ShaderProgram}s and nullifies its references including {@link RenderState#destroy(GL2ES2)}. */ public final void destroy(final GL2ES2 gl) { if(!initialized){ if(DEBUG_INSTANCE) { @@ -235,29 +284,51 @@ public class RegionRenderer { sp.destroy(gl); } shaderPrograms.clear(); - rs.destroy(gl); + rs.destroy(); initialized = false; } + /** Return the {@link RenderState} composition. */ public final RenderState getRenderState() { return rs; } /** * Enabling or disabling the {@link #getRenderState() RenderState}'s - * {@link RenderState#getShaderProgram() shader program}. + * current {@link RenderState#getShaderProgram() shader program}. + * <p> + * {@link #useShaderProgram(GL2ES2, int, boolean, int, int, TextureSequence)} + * generates, selects and caches the desired Curve-Graph {@link ShaderProgram} + * and {@link RenderState#setShaderProgram(GL2ES2, ShaderProgram) sets it current} in the {@link RenderState} composition. + * </p> * <p> - * In case enable and disable {@link GLCallback}s are setup via {@link #create(RenderState, GLCallback, GLCallback)}, + * In case enable and disable {@link GLCallback}s are setup via {@link #create(Vertex.Factory<? extends Vertex>, RenderState, GLCallback, GLCallback)}, * they will be called before toggling the shader program. * </p> - * @see #create(RenderState, GLCallback, GLCallback) + * @param gl current GL object + * @param enable if true enable the current {@link ShaderProgram}, otherwise disable. + * @see #create(Vertex.Factory<? extends Vertex>, RenderState, GLCallback, GLCallback) + * @see #useShaderProgram(GL2ES2, int, boolean, int, int, TextureSequence) + * @see RenderState#setShaderProgram(GL2ES2, ShaderProgram) + * @see RenderState#getShaderProgram() */ public final void enable(final GL2ES2 gl, final boolean enable) { + enable(gl, enable, enableCallback, disableCallback); + } + + /** + * Same as {@link #enable(GL2ES2, boolean)} but allowing to force {@link GLCallback}s off. + * @param gl current GL object + * @param enable if true enable the current {@link ShaderProgram}, otherwise disable. + * @param doCallbacks if true (default) perform {@link GLCallback}s, otherwise don't. + * @see #enable(GL2ES2, boolean) + */ + public final void enable(final GL2ES2 gl, final boolean enable, final GLCallback enableCB, final GLCallback disableCB) { if( enable ) { - if( null != enableCallback ) { - enableCallback.run(gl, this); + if( null != enableCB ) { + enableCB.run(gl, this); } } else { - if( null != disableCallback ) { - disableCallback.run(gl, this); + if( null != disableCB ) { + disableCB.run(gl, this); } } if( !enable ) { @@ -271,13 +342,12 @@ public class RegionRenderer { /** * No PMVMatrix operation is performed here. */ - public final void reshapeNotify(final int width, final int height) { - viewport[2] = width; - viewport[3] = height; + public final void reshapeNotify(final int x, final int y, final int width, final int height) { + viewport.set(x, y, width, height); } public final void reshapePerspective(final float angle, final int width, final int height, final float near, final float far) { - reshapeNotify(width, height); + reshapeNotify(0, 0, width, height); final float ratio = (float)width/(float)height; final PMVMatrix p = rs.getMatrix(); p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); @@ -289,7 +359,7 @@ public class RegionRenderer { * Perspective orthogonal, method calls {@link #reshapeNotify(int, int, int, int)}. */ public final void reshapeOrtho(final int width, final int height, final float near, final float far) { - reshapeNotify(width, height); + reshapeNotify(0, 0, width, height); final PMVMatrix p = rs.getMatrix(); p.glMatrixMode(GLMatrixFunc.GL_PROJECTION); p.glLoadIdentity(); @@ -303,12 +373,13 @@ public class RegionRenderer { private static final String SHADER_SRC_SUB = ""; private static final String SHADER_BIN_SUB = "bin"; - private static String GLSL_USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n"; - private static String GLSL_USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n"; - private static String GLSL_DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT "; - private static String GLSL_CONST_SAMPLE_COUNT = "const float sample_count = "; - private static String GLSL_MAIN_BEGIN = "void main (void)\n{\n"; + private static final String GLSL_USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n"; + private static final String GLSL_USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n"; + private static final String GLSL_DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT "; + private static final String GLSL_CONST_SAMPLE_COUNT = "const float sample_count = "; + private static final String GLSL_MAIN_BEGIN = "void main (void)\n{\n"; private static final String gcuTexture2D = "gcuTexture2D"; + private static final String GLSL_USE_DISCARD = "#define USE_DISCARD 1\n"; private String getVersionedShaderName() { return "curverenderer01"; @@ -412,6 +483,11 @@ public class RegionRenderer { private static final int TWO_PASS_BIT = 1 << 31; /** + * Generate, selects and caches the desired Curve-Graph {@link ShaderProgram} according to the given parameters. + * + * The newly generated or cached {@link ShaderProgram} is {@link RenderState#setShaderProgram(GL2ES2, ShaderProgram) set current} in the {@link RenderState} composition + * and can be retrieved via {@link RenderState#getShaderProgram()}. + * * @param gl * @param renderModes * @param pass1 @@ -420,19 +496,27 @@ public class RegionRenderer { * @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. + * @see #enable(GL2ES2, boolean) + * @see RenderState#setShaderProgram(GL2ES2, ShaderProgram) + * @see RenderState#getShaderProgram() */ public final boolean useShaderProgram(final GL2ES2 gl, final int renderModes, 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 hasColorChannel = Region.hasColorChannel( renderModes ); + final boolean hasColorTexture = Region.hasColorTexture( renderModes ) && null != colorTexSeq; + final boolean isPass1ColorTexSeq = pass1 && hasColorTexture; final int colorTexSeqHash; - if( null != colorTexSeq ) { + final String texLookupFuncName; + if( isPass1ColorTexSeq ) { + texLookupFuncName = colorTexSeq.setTextureLookupFunctionName(gcuTexture2D); colorTexSeqHash = colorTexSeq.getTextureFragmentShaderHashCode(); } else { + texLookupFuncName = null; colorTexSeqHash = 0; } - 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 = ( (colorTexSeqHash << 5) - colorTexSeqHash ) + ( sel1.ordinal() | ( HIGH_MASK & renderModes ) | ( isTwoPass ? TWO_PASS_BIT : 0 ) ); @@ -445,7 +529,7 @@ public class RegionRenderer { ShaderProgram sp = (ShaderProgram) shaderPrograms.get( shaderKey ); if( null != sp ) { final boolean spChanged = getRenderState().setShaderProgram(gl, sp); - if(DEBUG) { + if( DEBUG ) { if( spChanged ) { System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id()); } else { @@ -503,11 +587,15 @@ public class RegionRenderer { // GLSL append from here on posFp = -1; - if( Region.hasColorChannel( renderModes ) ) { + if( !gl.getContext().hasRendererQuirk(GLRendererQuirks.GLSLBuggyDiscard) ) { + posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_DISCARD); + } + + if( hasColorChannel ) { posVp = rsVp.insertShaderSource(0, posVp, GLSL_USE_COLOR_CHANNEL); posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_COLOR_CHANNEL); } - if( Region.hasColorTexture( renderModes ) ) { + if( isPass1ColorTexSeq ) { rsVp.insertShaderSource(0, posVp, GLSL_USE_COLOR_TEXTURE); posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_COLOR_TEXTURE); } @@ -526,13 +614,9 @@ public class RegionRenderer { throw new RuntimeException("Failed to read: includes"); } - 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, posFp, GLSL_MAIN_BEGIN); @@ -570,9 +654,10 @@ public class RegionRenderer { 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 %d / %d (changed)%n", + if( DEBUG ) { + System.err.printf("RegionRendererImpl01.useShaderProgram.X1: PUT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed, new)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id()); + // rsFp.dumpShaderSource(System.err); } return true; } 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 2b243e6cf..f38383c83 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/RenderState.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 JogAmp Community. All rights reserved. + * Copyright 2011-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -33,17 +33,22 @@ import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLUniformData; +import com.jogamp.opengl.math.Vec4f; import jogamp.common.os.PlatformPropsImpl; 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.GLArrayDataWrapper; import com.jogamp.opengl.util.PMVMatrix; import com.jogamp.opengl.util.glsl.ShaderProgram; +/** + * The RenderState is owned by {@link RegionRenderer}. + * + * It holds rendering state data like {@link PMVMatrix}, viewport, + * but also the current {@link #getColorStatic(float[]) static color}. + */ public class RenderState { private static final String thisKey = "jogamp.graph.curve.RenderState" ; @@ -82,19 +87,10 @@ public class RenderState { */ public static final int BITHINT_GLOBAL_DEPTH_TEST_ENABLED = 1 << 1 ; - public static RenderState createRenderState(final Vertex.Factory<? extends Vertex> pointFactory) { - return new RenderState(pointFactory, null); - } - - public static RenderState createRenderState(final Vertex.Factory<? extends Vertex> pointFactory, final PMVMatrix pmvMatrix) { - return new RenderState(pointFactory, pmvMatrix); - } - public static final RenderState getRenderState(final GL2ES2 gl) { return (RenderState) gl.getContext().getAttachedObject(thisKey); } - private final Vertex.Factory<? extends Vertex> vertexFactory; private final PMVMatrix pmvMatrix; private final float[] weight; private final FloatBuffer weightBuffer; @@ -110,7 +106,7 @@ public class RenderState { private static int nextID = 1; /** - * Representation of {@link RenderState} data for one {@link ShaderProgram} + * Representation of {@link RenderState} data per {@link ShaderProgram} * as {@link GLUniformData}. * <p> * FIXME: Utilize 'ARB_Uniform_Buffer_Object' where available! @@ -143,7 +139,8 @@ public class RenderState { */ public final boolean update(final GL2ES2 gl, final RenderState rs, final boolean updateLocation, final int renderModes, final boolean pass1, final boolean throwOnError) { if( rs.id() != rsId ) { - gcu_PMVMatrix01.setData(rs.pmvMatrix.glGetPMvMatrixf()); + // Assignment of Renderstate buffers to uniforms (no copy, direct reference) + gcu_PMVMatrix01.setData(rs.pmvMatrix.getSyncPMvMat()); gcu_Weight.setData(rs.weightBuffer); gcu_ColorStatic.setData(rs.colorStaticBuffer); rsId = rs.id(); @@ -186,11 +183,14 @@ public class RenderState { } } - protected RenderState(final Vertex.Factory<? extends Vertex> vertexFactory, final PMVMatrix pmvMatrix) { + /** + * Create a RenderState, a composition of RegionRenderer + * @param sharedPMVMatrix optional shared PMVMatrix, if null using a local instance + */ + /* pp */ RenderState(final PMVMatrix sharedPMVMatrix) { this.id = getNextID(); this.sp = null; - this.vertexFactory = vertexFactory; - this.pmvMatrix = null != pmvMatrix ? pmvMatrix : new PMVMatrix(); + this.pmvMatrix = null != sharedPMVMatrix ? sharedPMVMatrix : new PMVMatrix(); this.weight = new float[1]; this.weightBuffer = FloatBuffer.wrap(weight); this.colorStatic = new float[4]; @@ -199,14 +199,20 @@ public class RenderState { } public final int id() { return id; } + + /** Return the current {@link ShaderProgram} */ public final ShaderProgram getShaderProgram() { return sp; } + + /** Return whether the current {@link ShaderProgram} is {@link ShaderProgram#inUse() in use}. */ 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. + * Sets the current {@link ShaderProgram} and enables it. + * + * If the given {@link ShaderProgram} is not {@link #getShaderProgram() the current}, method returns true, otherwise false. + * * @param gl - * @param spNext + * @param spNext the next current {@link ShaderProgram} to be set and enabled * @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. */ @@ -223,8 +229,6 @@ public class RenderState { return true; } - public final Vertex.Factory<? extends Vertex> getVertexFactory() { return vertexFactory; } - public final PMVMatrix getMatrix() { return pmvMatrix; } public static boolean isWeightValid(final float v) { @@ -238,10 +242,14 @@ public class RenderState { weight[0] = v; } - - public final float[] getColorStatic(final float[] rgbaColor) { - System.arraycopy(colorStatic, 0, rgbaColor, 0, 4); - return rgbaColor; + public final Vec4f getColorStatic(final Vec4f rgbaColor) { + return rgbaColor.set(colorStatic); + } + public final void setColorStatic(final Vec4f rgbaColor){ + colorStatic[0] = rgbaColor.x(); + colorStatic[1] = rgbaColor.y(); + colorStatic[2] = rgbaColor.z(); + colorStatic[3] = rgbaColor.w(); } public final void setColorStatic(final float r, final float g, final float b, final float a){ colorStatic[0] = r; @@ -263,7 +271,8 @@ public class RenderState { if( updateLocation || 0 > data.getLocation() ) { final boolean ok = 0 <= data.setLocation(gl, sp.program()); if( throwOnError && !ok ) { - throw new GLException("Could not locate "+data); + sp.dumpSource(System.err); + throw new GLException("Could not locate "+data.getName()+" in "+sp+", "+data); } return ok; } else { @@ -285,7 +294,8 @@ public class RenderState { if( updateLocation ) { updateData = 0 <= data.setLocation(gl, sp.program()); if( throwOnError && !updateData ) { - throw new GLException("Could not locate "+data); + sp.dumpSource(System.err); + throw new GLException("Could not locate "+data.getName()+" in "+sp+", "+data); } } if( updateData ){ @@ -302,11 +312,12 @@ public class RenderState { * @param throwOnError TODO * @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, final boolean throwOnError) { + public final boolean updateAttributeLoc(final GL2ES2 gl, final boolean updateLocation, final GLArrayDataWrapper data, final boolean throwOnError) { if( updateLocation || 0 > data.getLocation() ) { final boolean ok = 0 <= data.setLocation(gl, sp.program()); if( throwOnError && !ok ) { - throw new GLException("Could not locate "+data); + sp.dumpSource(System.err); + throw new GLException("Could not locate "+data.getName()+" in "+sp+", "+data); } return ok; } else { @@ -325,11 +336,11 @@ public class RenderState { hintBitfield &= ~mask; } - public void destroy(final GL2ES2 gl) { - if( null != sp ) { - sp.destroy(gl); - sp = null; - } + /** + * Only nullifies {@link ShaderProgram} reference owned by {@link RegionRenderer}. + */ + /* pp */ void destroy() { + sp = null; // owned by RegionRenderer } public final RenderState attachTo(final GL2ES2 gl) { 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 8d55c6136..428bb350d 100644 --- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java +++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java @@ -33,10 +33,12 @@ import java.util.Iterator; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLException; +import com.jogamp.opengl.math.Vec4f; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.graph.curve.OutlineShape; import com.jogamp.graph.curve.Region; import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.Font.Glyph; import com.jogamp.graph.geom.plane.AffineTransform; /** @@ -73,6 +75,9 @@ public class TextRegionUtil { * <p> * Origin of rendered text is 0/0 at bottom left. * </p> + * <p> + * The region buffer's size is grown by pre-calculating required size via {@link #countStringRegion(Font, CharSequence, int[])}. + * </p> * @param region the {@link GLRegion} sink * @param font the target {@link Font} * @param transform optional given transform @@ -81,8 +86,8 @@ public class TextRegionUtil { * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] OutlineShape into account. */ public static AABBox addStringToRegion(final Region region, final Font font, final AffineTransform transform, - final CharSequence str, final float[] rgbaColor) { - return addStringToRegion(region, font, transform, str, rgbaColor, new AffineTransform(), new AffineTransform()); + final CharSequence str, final Vec4f rgbaColor) { + return addStringToRegion(true /* preGrowRegion */, region, font, transform, str, rgbaColor, new AffineTransform(), new AffineTransform()); } /** @@ -94,6 +99,9 @@ public class TextRegionUtil { * <p> * Origin of rendered text is 0/0 at bottom left. * </p> + * <p> + * The region buffer's size is grown by pre-calculating required size via {@link #countStringRegion(Font, CharSequence, int[])}. + * </p> * @param region the {@link GLRegion} sink * @param font the target {@link Font} * @param transform optional given transform @@ -104,13 +112,49 @@ public class TextRegionUtil { * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] OutlineShape into account. */ public static AABBox addStringToRegion(final Region region, final Font font, final AffineTransform transform, - final CharSequence str, final float[] rgbaColor, + final CharSequence str, final Vec4f rgbaColor, final AffineTransform temp1, final AffineTransform temp2) { - final OutlineShape.Visitor visitor = new OutlineShape.Visitor() { + return addStringToRegion(true /* preGrowRegion */, region, font, transform, str, rgbaColor, temp1, temp2); + } + + /** + * Add the string in 3D space w.r.t. the font in font em-size [0..1] at the end of the {@link GLRegion} + * while passing the progressed {@link AffineTransform}. + * <p> + * The shapes added to the GLRegion are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor. + * </p> + * <p> + * Origin of rendered text is 0/0 at bottom left. + * </p> + * <p> + * Depending on `preGrowRegion`, the region buffer's size is grown by pre-calculating required size via {@link #countStringRegion(Font, CharSequence, int[])}. + * </p> + * @param preGrowRegion if true, utilizes {@link #countStringRegion(Font, CharSequence, int[])} to pre-calc required buffer size, otherwise not. + * @param region the {@link GLRegion} sink + * @param font the target {@link Font} + * @param transform optional given transform + * @param str string text + * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored. + * @param temp1 temporary AffineTransform storage, mandatory + * @param temp2 temporary AffineTransform storage, mandatory + * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] OutlineShape into account. + */ + public static AABBox addStringToRegion(final boolean preGrowRegion, final Region region, final Font font, final AffineTransform transform, + final CharSequence str, final Vec4f rgbaColor, + final AffineTransform temp1, final AffineTransform temp2) { + final Font.GlyphVisitor visitor = new Font.GlyphVisitor() { @Override - public final void visit(final OutlineShape shape, final AffineTransform t) { - region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null); - } }; + public void visit(final char symbol, final Glyph glyph, final AffineTransform t) { + if( glyph.isWhiteSpace() ) { + return; + } + region.addOutlineShape(glyph.getShape(), t, rgbaColor); + } + }; + if( preGrowRegion ) { + final int[] vertIndCount = countStringRegion(font, str, new int[2]); + region.growBuffer(vertIndCount[0], vertIndCount[1]); + } return font.processString(visitor, transform, str, temp1, temp2); } @@ -119,20 +163,22 @@ public class TextRegionUtil { * <p> * The region's buffer can be either set using {@link Region#setBufferCapacity(int, int)} or grown using {@link Region#growBuffer(int, int)}. * </p> - * @param region the {@link GLRegion} sink * @param font the target {@link Font} * @param str string text * @param vertIndexCount the int[2] storage where the counted vertices and indices are added, vertices at [0] and indices at [1] + * @return the given int[2] storage for chaining * @see Region#setBufferCapacity(int, int) * @see Region#growBuffer(int, int) + * @see #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform) */ - public static void countStringRegion(final Region region, final Font font, final CharSequence str, final int[/*2*/] vertIndexCount) { - final OutlineShape.Visitor2 visitor = new OutlineShape.Visitor2() { + public static int[] countStringRegion(final Font font, final CharSequence str, final int[/*2*/] vertIndexCount) { + final Font.GlyphVisitor2 visitor = new Font.GlyphVisitor2() { @Override - public final void visit(final OutlineShape shape) { - region.countOutlineShape(shape, vertIndexCount); + public final void visit(final char symbol, final Font.Glyph glyph) { + Region.countOutlineShape(glyph.getShape(), vertIndexCount); } }; font.processString(visitor, str); + return vertIndexCount; } /** @@ -146,6 +192,9 @@ public class TextRegionUtil { * <p> * Cached {@link GLRegion}s will be destroyed w/ {@link #clear(GL2ES2)} or to free memory. * </p> + * <p> + * The region's buffer size is pre-calculated via {@link GLRegion#create(com.jogamp.opengl.GLProfile, int, com.jogamp.opengl.util.texture.TextureSequence, Font, CharSequence)} + * </p> * @param gl the current GL state * @param renderer TODO * @param font {@link Font} to be used @@ -158,15 +207,15 @@ public class TextRegionUtil { */ public AABBox drawString3D(final GL2ES2 gl, final RegionRenderer renderer, final Font font, final CharSequence str, - final float[] rgbaColor, final int[/*1*/] sampleCount) { + final Vec4f rgbaColor, final int[/*1*/] sampleCount) { if( !renderer.isInitialized() ) { throw new GLException("TextRendererImpl01: not initialized!"); } GLRegion region = getCachedRegion(font, str); AABBox res; if(null == region) { - region = GLRegion.create(gl.getGLProfile(), renderModes, null); - res = addStringToRegion(region, font, null, str, rgbaColor, tempT1, tempT2); + region = GLRegion.create(gl.getGLProfile(), renderModes, null, font, str); + res = addStringToRegion(false /* preGrowRegion */, region, font, null, str, rgbaColor, tempT1, tempT2); addCachedRegion(gl, font, str, region); } else { res = new AABBox(); @@ -178,10 +227,13 @@ public class TextRegionUtil { /** * Try using {@link #drawString3D(GL2ES2, int, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform)} to reuse {@link AffineTransform} instances. + * <p> + * The region's buffer size is pre-calculated via {@link GLRegion#create(com.jogamp.opengl.GLProfile, int, com.jogamp.opengl.util.texture.TextureSequence, Font, CharSequence)} + * </p> */ public static AABBox drawString3D(final GL2ES2 gl, final int renderModes, final RegionRenderer renderer, final Font font, final CharSequence str, - final float[] rgbaColor, final int[/*1*/] sampleCount) { + final Vec4f rgbaColor, final int[/*1*/] sampleCount) { return drawString3D(gl, renderModes, renderer, font, str, rgbaColor, sampleCount, new AffineTransform(), new AffineTransform()); } @@ -194,6 +246,9 @@ public class TextRegionUtil { * Origin of rendered text is 0/0 at bottom left. * </p> * <p> + * The region's buffer size is pre-calculated via {@link GLRegion#create(com.jogamp.opengl.GLProfile, int, com.jogamp.opengl.util.texture.TextureSequence, Font, CharSequence)} + * </p> + * <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(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform)} @@ -213,12 +268,12 @@ public class TextRegionUtil { */ public static AABBox drawString3D(final GL2ES2 gl, final int renderModes, final RegionRenderer renderer, final Font font, final CharSequence str, - final float[] rgbaColor, final int[/*1*/] sampleCount, final AffineTransform tmp1, final AffineTransform tmp2) { + final Vec4f rgbaColor, final int[/*1*/] sampleCount, final AffineTransform tmp1, final AffineTransform tmp2) { if(!renderer.isInitialized()){ throw new GLException("TextRendererImpl01: not initialized!"); } - final GLRegion region = GLRegion.create(gl.getGLProfile(), renderModes, null); - final AABBox res = addStringToRegion(region, font, null, str, rgbaColor, tmp1, tmp2); + final GLRegion region = GLRegion.create(gl.getGLProfile(), renderModes, null, font, str); + final AABBox res = addStringToRegion(false /* preGrowRegion */, region, font, null, str, rgbaColor, tmp1, tmp2); region.draw(gl, renderer, sampleCount); region.destroy(gl); return res; @@ -226,9 +281,12 @@ public class TextRegionUtil { /** * Try using {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform)} to reuse {@link AffineTransform} instances. + * <p> + * The region buffer's size is grown by pre-calculating required size via {@link #countStringRegion(Font, CharSequence, int[])}. + * </p> */ public static AABBox drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer, - final Font font, final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount) { + final Font font, final CharSequence str, final Vec4f rgbaColor, final int[/*1*/] sampleCount) { return drawString3D(gl, region, renderer, font, str, rgbaColor, sampleCount, new AffineTransform(), new AffineTransform()); } @@ -243,6 +301,9 @@ public class TextRegionUtil { * <p> * Origin of rendered text is 0/0 at bottom left. * </p> + * <p> + * The region buffer's size is grown by pre-calculating required size via {@link #countStringRegion(Font, CharSequence, int[])}. + * </p> * @param gl the current GL state * @param region * @param renderer @@ -257,12 +318,12 @@ public class TextRegionUtil { * @throws Exception if TextRenderer not initialized */ public static AABBox drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer, - final Font font, final CharSequence str, final float[] rgbaColor, + final Font font, final CharSequence str, final Vec4f rgbaColor, final int[/*1*/] sampleCount, final AffineTransform tmp1, final AffineTransform tmp2) { if(!renderer.isInitialized()){ throw new GLException("TextRendererImpl01: not initialized!"); } - final AABBox res = addStringToRegion(region, font, null, str, rgbaColor, tmp1, tmp2); + final AABBox res = addStringToRegion(true /* preGrowRegion */, region, font, null, str, rgbaColor, tmp1, tmp2); region.draw(gl, renderer, sampleCount); return res; } diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java index 1b5452a45..314040adf 100644 --- a/src/jogl/classes/com/jogamp/graph/font/Font.java +++ b/src/jogl/classes/com/jogamp/graph/font/Font.java @@ -145,13 +145,13 @@ public interface Font { * @param dest AABBox instance set to this metrics boundary in font-units * @return the given and set AABBox 'dest' in font units */ - AABBox getBBoxFU(final AABBox dest); + AABBox getBoundsFU(final AABBox dest); /** * @param dest AABBox instance set to this metrics boundary in font em-size [0..1] * @return the given and set AABBox 'dest' in font units */ - AABBox getBBox(final AABBox dest, final float[] tmpV3); + AABBox getBounds(final AABBox dest); } /** @@ -166,36 +166,44 @@ public interface Font { // http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#ba57949e public static final int ID_UNKNOWN = 0; + /** Return the {@link Font} owning this {@link Glyph}. */ + Font getFont(); + /** Return this glyph's ID */ int getID(); /** Return the glyph's name, source from `post` table */ String getName(); + /** Return true if the underlying {@link #getShape()} is a whitespace, otherwise false. */ + boolean isWhiteSpace(); + + /** Return true if the Glyph denotes an undefined {@link #getID()} symbol, i.e. {@link #getName()} == `.notdef`. */ + boolean isUndefined(); + /** * Return the AABBox in font-units, borrowing internal instance. */ - AABBox getBBoxFU(); + AABBox getBoundsFU(); /** * Return the AABBox in font-units, copying into given dest. * @param dest AABBox instance set to this metrics boundary in font-units * @return the given and set AABBox 'dest' in font-units */ - AABBox getBBoxFU(final AABBox dest); + AABBox getBoundsFU(final AABBox dest); /** * Return the AABBox in font em-size [0..1], copying into given dest. * @param dest AABBox instance set to this metrics boundary in font em-size [0..1] - * @param tmpV3 caller provided temporary 3-component vector * @return the given and set AABBox 'dest' in font em-size [0..1] */ - AABBox getBBox(final AABBox dest, float[] tmpV3); + AABBox getBounds(final AABBox dest); /** * Return the AABBox in font em-size [0..1], creating a new copy. */ - AABBox getBBox(); + AABBox getBounds(); /** Return advance in font units, sourced from `hmtx` table. */ int getAdvanceFU(); @@ -246,6 +254,32 @@ public interface Font { String fullString(); } + /** + * General purpose {@link Font.Glyph} visitor. + */ + public static interface GlyphVisitor { + /** + * Visiting the given {@link Font.Glyph} having an {@link OutlineShape} with it's corresponding {@link AffineTransform}. + * @param symbol the character symbol matching the given glyph + * @param glyph {@link Font.Glyph} which contains an {@link OutlineShape} via {@link Font.Glyph#getShape()}. + * @param t may be used immediately as is, otherwise a copy shall be made if stored. + */ + public void visit(final char symbol, final Glyph glyph, final AffineTransform t); + } + + /** + * Constrained {@link Font.Glyph} visitor w/o {@link AffineTransform}. + */ + public static interface GlyphVisitor2 { + /** + * Visiting the given {@link Font.Glyph} having an {@link OutlineShape}. + * @param symbol the character symbol matching the given glyph + * @param glyph {@link Font.Glyph} which contains an {@link OutlineShape} via {@link Font.Glyph#getShape()}. + */ + public void visit(final char symbol, final Glyph glyph); + } + + String getName(final int nameIndex); /** Shall return the family and subfamily name, separated a dash. @@ -287,8 +321,13 @@ public interface Font { Metrics getMetrics(); + /** Return the {@link Glyph} ID mapped to given `symbol`, usually UTF16 unicode. Returned ID can be used to retrieve the {@link Glyph} via {@link #getGlyph(int)}. */ int getGlyphID(final char symbol); + /** Return number of {@link Glyph} IDs available, i.e. retrievable via {@link #getGlyph(int)} [0..count). */ + int getGlyphCount(); + + /** Return the {@link Glyph} using given ID, see {@link #getGlyphCount()}. */ Glyph getGlyph(final int glyph_id); int getNumGlyphs(); @@ -312,7 +351,20 @@ public interface Font { int getLineHeightFU(); /** - * Return line height in font em-size [0..1] + * Return line height, baseline-to-baseline in em-size [0..1], composed from `hhea' table entries. + * <pre> + * return ascent - descent + linegap; + * </pre> + * or + * <pre> + * // lineGap positive value + * // descent negative value + * // ascent positive value + * return ascent - descent + linegap; + * </pre> + * @see Metrics#getAscent() + * @see Metrics#getDescent() + * @see Metrics#getLineGap() */ float getLineHeight(); @@ -406,50 +458,55 @@ public interface Font { * <p> * This method is only exposed to validate the produced {@link OutlineShape} against {@link #getGlyphBounds(CharSequence)}. * </p> + * @param transform optional given transform * @param string string text + * @param tmp1 temp {@link AffineTransform} to be reused + * @param tmp2 temp {@link AffineTransform} to be reused * @return the bounding box of the given string in font-units [0..1] - * @see #getGlyphShapeBounds(AffineTransform, CharSequence) + * @see #getGlyphShapeBounds(CharSequence) * @see #getGlyphBounds(CharSequence) * @see #getMetricBounds(CharSequence) */ - AABBox getGlyphShapeBounds(final CharSequence string); + AABBox getGlyphShapeBounds(final AffineTransform transform, final CharSequence string, final AffineTransform tmp1, final AffineTransform tmp2); boolean isPrintableChar(final char c); /** - * Try using {@link #processString(com.jogamp.graph.curve.OutlineShape.Visitor, AffineTransform, CharSequence, AffineTransform, AffineTransform)} + * Try using {@link #processString(GlyphVisitor, AffineTransform, CharSequence, AffineTransform, AffineTransform)} * to reuse {@link AffineTransform} instances. + * @see #processString(GlyphVisitor, AffineTransform, CharSequence, AffineTransform, AffineTransform) */ - AABBox processString(final OutlineShape.Visitor visitor, final AffineTransform transform, + AABBox processString(final Font.GlyphVisitor visitor, final AffineTransform transform, final CharSequence string); /** - * Visit each {@link Glyph}'s {@link OutlineShape} of the string with the {@link OutlineShape.Visitor} + * Visit each {@link Glyph} and perhaps its {@link OutlineShape} of the string with the {@link Font.GlyphVisitor} * while passing the progressed {@link AffineTransform}. * <p> * The processed shapes are in font em-size [0..1], but can be adjusted with the given transform, progressed and passed to the visitor. * </p> - * @param visitor handling each glyph's outline shape in font em-size [0..1] and the given {@link AffineTransform} - * @param transform optional given transform + * @param visitor handling each {@link Font.Glyph} and perhaps its {@link OutlineShape} in font em-size [0..1] and the given {@link AffineTransform} + * @param transform optional given transform for size and position * @param font the target {@link Font} * @param string string text * @param temp1 temporary AffineTransform storage, mandatory * @param temp2 temporary AffineTransform storage, mandatory * @return the bounding box of the given string by taking each glyph's font em-sized [0..1] {@link OutlineShape} into account. + * @see #processString(GlyphVisitor, AffineTransform, CharSequence) */ - AABBox processString(final OutlineShape.Visitor visitor, final AffineTransform transform, + AABBox processString(final Font.GlyphVisitor visitor, final AffineTransform transform, final CharSequence string, final AffineTransform temp1, final AffineTransform temp2); /** - * Visit each {@link Glyph}'s {@link OutlineShape} of the string with the constrained {@link OutlineShape.Visitor2}. + * Visit each {@link Glyph} and perhaps its {@link OutlineShape} of the string with the constrained {@link Font.GlyphVisitor2}. * <p> * The processed shapes are in font em-size [0..1]. * </p> - * @param visitor handling each glyph's outline shape in font em-size [0..1] + * @param visitor handling each {@link Font.Glyph} and perhaps its {@link OutlineShape} in font em-size [0..1] * @param string string text */ - void processString(final OutlineShape.Visitor2 visitor, final CharSequence string); + void processString(final Font.GlyphVisitor2 visitor, final CharSequence string); /** Returns {@link #getFullFamilyName()} */ @Override diff --git a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java index ac42274f6..9b9762a05 100644 --- a/src/jogl/classes/com/jogamp/graph/font/FontFactory.java +++ b/src/jogl/classes/com/jogamp/graph/font/FontFactory.java @@ -147,7 +147,7 @@ public class FontFactory { InputStream bis = null; try { tempFile[0] = IOUtil.createTempFile( "jogl.font", ".ttf", false); - streamLen[0] = IOUtil.copyStream2File(stream, tempFile[0], -1); + streamLen[0] = IOUtil.copyStream2File(stream, tempFile[0]); if( 0 == streamLen[0] ) { throw new IOException("Font stream has zero bytes"); } diff --git a/src/jogl/classes/com/jogamp/graph/font/FontScale.java b/src/jogl/classes/com/jogamp/graph/font/FontScale.java index a0f2ef4e0..da8df807f 100644 --- a/src/jogl/classes/com/jogamp/graph/font/FontScale.java +++ b/src/jogl/classes/com/jogamp/graph/font/FontScale.java @@ -1,48 +1,100 @@ +/** + * Copyright 2010-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ package com.jogamp.graph.font; /** - * Simple static font scale methods for unit conversion. + * Simple static font scale methods for unit conversions. + * + * PostScript - current DTP point system used e.g in CSS (Cascading Style Sheets). + * - 1 point = 1pt = 1/72in (cala) = 0.3528 mm + * - 1 pica = 1pc = 12pt= 1/6in (cala) = 4.233(3) mm */ public class FontScale { /** - * Converts typical font size in points (per-inch) and screen resolution in dpi to font size in pixels (per-inch), - * which can be used for pixel-size font scaling operations. - * <p> - * Note that 1em == size of the selected font.<br/> - * In case the pixel per em size is required (advance etc), - * the resulting pixel-size (per-inch) of this method shall be used if rendering directly into the screen resolution! - * </p> + * Converts the the given points size to inch, dividing by {@code 72} points per inch. + * <pre> + 1 points = 1/72 inch + * + * </pre> + */ + public static float ptToInch(final float points) { + return points / 72f /* points per inch */; + } + + /** + * Converts the the given points size to mm, dividing by {@code 72 * 25.4} points per inch. * <pre> - Font Scale Formula: 1 inch = 25.4 mm + 1 points = 1/72 inch + 1 points = 1/72 * 25.4 mm + * + * </pre> + */ + public static float ptToMM(final float points) { + return points / 72f /* points per inch */ * 25.4f /* mm_per_inch */; + } - 1 inch = 72 points - pointSize: [point] = [1/72 inch] + /** + * Converts typical font size in points and screen resolution in dpi (pixels-per-inch) to font size in pixels, + * which can be used for pixel-size font scaling operations. + * <pre> + Font Scale Formula: + 1 points = 1/72 inch - [1] Scale := pointSize * resolution / ( 72 points per inch * units_per_em ) - [2] PixelSize := pointSize * resolution / ( 72 points per inch ) - [3] Scale := PixelSize / units_per_em + pixels = points / 72 * res_dpi * </pre> - * @param font_sz_pt in points (per-inch) - * @param res_dpi display resolution in dots-per-inch - * @return pixel-per-inch, pixelSize scale factor for font operations. + * @param points in points + * @param res_dpi display resolution in pixels-per-inch + * @return pixelSize scale factor for font operations. * @see #toPixels2(float, float) */ - public static float toPixels(final float font_sz_pt /* points per inch */, final float res_dpi /* dots per inch */) { - return ( font_sz_pt / 72f /* points per inch */ ) * res_dpi; + public static float toPixels(final float points /* points */, final float res_dpi /* pixels per inch */) { + return ptToInch( points ) * res_dpi; } /** - * Converts typical font size in points-per-inch and screen resolution in points-per-mm to font size in pixels (per-inch), + * Converts typical font size in points and screen resolution in pixels (pixels-per-mm) to font size in pixels, * which can be used for pixel-size font scaling operations. - * - * @param font_sz_pt in points (per-inch) - * @param res_ppmm display resolution in dots-per-mm - * @return pixel-per-inch, pixelSize scale factor for font operations. + * <pre> + Font Scale Formula: + 1 inch = 25.4 mm + 1 points = 1/72 inch + 1 points = 1/72 * 25.4 mm + + pixels = points / 72 * 25.4 * res_ppmm + * </pre> + * @param points in points + * @param res_ppmm display resolution in pixels-per-mm + * @return pixelSize scale factor for font operations. * @see #toPixels(float, float) */ - public static float toPixels2(final float font_sz_pt /* points per inch */, final float res_ppmm /* pixels per mm */) { - return ( font_sz_pt / 72f /* points per inch */ ) * ( res_ppmm * 25.4f /* mm per inch */ ) ; + public static float toPixels2(final float points /* points */, final float res_ppmm /* pixels per mm */) { + return ptToMM( points ) * res_ppmm; } /** @@ -50,7 +102,7 @@ public class FontScale { * @param ppmm float[2] [1/mm] value * @return return [1/inch] value */ - public static float[/*2*/] perMMToPerInch(final float[/*2*/] ppmm) { + public static float[/*2*/] ppmmToPPI(final float[/*2*/] ppmm) { ppmm[0] *= 25.4f; ppmm[1] *= 25.4f; return ppmm; @@ -67,4 +119,27 @@ public class FontScale { res[1] = ppmm[1] * 25.4f; return res; } + + /** + * Converts [1/inch] to [1/mm] in place + * @param ppi float[2] [1/inch] value + * @return return [1/mm] value + */ + public static float[/*2*/] ppiToPPMM(final float[/*2*/] ppi) { + ppi[0] /= 25.4f; + ppi[1] /= 25.4f; + return ppi; + } + + /** + * Converts [1/inch] to [1/mm] into res storage + * @param ppi float[2] [1/inch] value + * @param res the float[2] result storage + * @return return [1/mm] value, i.e. the given res storage + */ + public static float[/*2*/] ppiToPPMM(final float[/*2*/] ppi, final float[/*2*/] res) { + res[0] = ppi[0] / 25.4f; + res[1] = ppi[1] / 25.4f; + return res; + } } diff --git a/src/jogl/classes/com/jogamp/graph/geom/Outline.java b/src/jogl/classes/com/jogamp/graph/geom/Outline.java index bd25aeccc..a0999baa1 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Outline.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Outline.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -268,7 +268,7 @@ public class Outline implements Comparable<Outline> { if( !isEmpty() ) { final Vertex first = vertices.get(0); final Vertex last = getLastVertex(); - if( !VectorUtil.isVec3Equal( first.getCoord(), 0, last.getCoord(), 0, FloatUtil.EPSILON ) ) { + if( !first.getCoord().isEqual( last.getCoord() ) ) { if( closeTail ) { vertices.add(first.clone()); } else { @@ -283,12 +283,12 @@ public class Outline implements Comparable<Outline> { /** * Return a transformed instance with all vertices are copied and transformed. */ - public final Outline transform(final AffineTransform t, final Vertex.Factory<? extends Vertex> vertexFactory) { + public final Outline transform(final AffineTransform t) { final Outline newOutline = new Outline(); final int vsize = vertices.size(); for(int i=0; i<vsize; i++) { final Vertex v = vertices.get(i); - newOutline.addVertex(t.transform(v, vertexFactory.create())); + newOutline.addVertex(t.transform(v, new Vertex())); } newOutline.closed = this.closed; return newOutline; diff --git a/src/jogl/classes/com/jogamp/graph/geom/SVertex.java b/src/jogl/classes/com/jogamp/graph/geom/SVertex.java deleted file mode 100644 index dc6982025..000000000 --- a/src/jogl/classes/com/jogamp/graph/geom/SVertex.java +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright 2010 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.graph.geom; - -import com.jogamp.opengl.math.FloatUtil; -import com.jogamp.opengl.math.VectorUtil; - -/** A Simple Vertex Implementation. Where the coordinates, and other attributes are - * float based, and the coordinates and texture coordinates are saved in two float arrays. - * - */ -public class SVertex implements Vertex { - private int id; - protected boolean onCurve; - protected final float[] coord = new float[3]; - private final float[] texCoord = new float[3]; - - static final Factory factory = new Factory(); - - public static Factory factory() { return factory; } - - public static class Factory implements Vertex.Factory<SVertex> { - @Override - public SVertex create() { - return new SVertex(); - } - - public SVertex create(final Vertex src) { - return new SVertex(src); - } - - @Override - public SVertex create(final int id, final boolean onCurve, final float[] texCoordsBuffer) { - return new SVertex(id, onCurve, texCoordsBuffer); - } - - @Override - public SVertex create(final float x, final float y, final float z, final boolean onCurve) { - return new SVertex(x, y, z, onCurve); - } - - @Override - public SVertex create(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { - return new SVertex(coordsBuffer, offset, length, onCurve); - } - } - - public SVertex() { - this.id = Integer.MAX_VALUE; - } - - public SVertex(final Vertex src) { - this.id = Integer.MAX_VALUE; - System.arraycopy(src.getCoord(), 0, coord, 0, 3); - System.arraycopy(src.getTexCoord(), 0, texCoord, 0, 3); - setOnCurve(src.isOnCurve()); - } - - public SVertex(final int id, final boolean onCurve, final float[] texCoordsBuffer) { - this.id = id; - this.onCurve = onCurve; - System.arraycopy(texCoordsBuffer, 0, texCoord, 0, 3); - } - - public SVertex(final float x, final float y, final float z, final boolean onCurve) { - this.id = Integer.MAX_VALUE; - setCoord(x, y, z); - setOnCurve(onCurve); - } - - public SVertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { - this.id = Integer.MAX_VALUE; - setCoord(coordsBuffer, offset, length); - setOnCurve(onCurve); - } - - @Override - public final void setCoord(final float x, final float y, final float z) { - coord[0] = x; - coord[1] = y; - coord[2] = z; - } - - @Override - public final void setCoord(final float[] coordsBuffer, final int offset, final int length) { - System.arraycopy(coordsBuffer, offset, coord, 0, length); - } - - @Override - public int getCoordCount() { - return 3; - } - - @Override - public final float[] getCoord() { - return coord; - } - - @Override - public final void setX(final float x) { - this.coord[0] = x; - } - - @Override - public final void setY(final float y) { - this.coord[1] = y; - } - - @Override - public final void setZ(final float z) { - this.coord[2] = z; - } - - @Override - public final float getX() { - return this.coord[0]; - } - - @Override - public final float getY() { - return this.coord[1]; - } - - @Override - public final float getZ() { - return this.coord[2]; - } - - @Override - public final boolean isOnCurve() { - return onCurve; - } - - @Override - public final void setOnCurve(final boolean onCurve) { - this.onCurve = onCurve; - } - - @Override - public final int getId(){ - return id; - } - - @Override - public final void setId(final int id){ - this.id = id; - } - - @Override - public boolean equals(final Object obj) { - if( obj == this) { - return true; - } - if( null == obj || !(obj instanceof Vertex) ) { - return false; - } - final Vertex v = (Vertex) obj; - return this == v || - isOnCurve() == v.isOnCurve() && - VectorUtil.isVec3Equal(getTexCoord(), 0, v.getTexCoord(), 0, FloatUtil.EPSILON) && - VectorUtil.isVec3Equal(getCoord(), 0, v.getCoord(), 0, FloatUtil.EPSILON) ; - } - @Override - public final int hashCode() { - throw new InternalError("hashCode not designed"); - } - - @Override - public final float[] getTexCoord() { - return texCoord; - } - - @Override - public final void setTexCoord(final float s, final float t, final float p) { - texCoord[0] = s; - texCoord[1] = t; - texCoord[2] = p; - } - - @Override - public final void setTexCoord(final float[] texCoordsBuffer, final int offset, final int length) { - System.arraycopy(texCoordsBuffer, offset, texCoord, 0, length); - } - - /** - * @return deep clone of this Vertex elements - */ - @Override - public SVertex clone(){ - return new SVertex(this); // OK to not call super.clone(), using own copy-ctor - } - - @Override - public String toString() { - return "[ID: " + id + ", onCurve: " + onCurve + - ": p " + coord[0] + ", " + coord[1] + ", " + coord[2] + - ", t " + texCoord[0] + ", " + texCoord[1] + ", " + texCoord[2] + "]"; - } -} diff --git a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java index 1c63c4005..6b07501a6 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Triangle.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Triangle.java @@ -64,11 +64,11 @@ public class Triangle { /** * Returns a transformed a clone of this instance using the given AffineTransform. */ - public Triangle transform(final AffineTransform t, final Vertex.Factory<? extends Vertex> vertexFactory) { + public Triangle transform(final AffineTransform t) { final Triangle tri = new Triangle(id, boundaryEdges, boundaryVertices); - tri.vertices[0] = t.transform(vertices[0], vertexFactory.create()); - tri.vertices[1] = t.transform(vertices[1], vertexFactory.create()); - tri.vertices[2] = t.transform(vertices[2], vertexFactory.create()); + tri.vertices[0] = t.transform(vertices[0], new Vertex()); + tri.vertices[1] = t.transform(vertices[1], new Vertex()); + tri.vertices[2] = t.transform(vertices[2], new Vertex()); return tri; } @@ -83,9 +83,9 @@ public class Triangle { * Returns true if all vertices are lines, i.e. zero tex-coord, otherwise false. */ public final boolean isLine() { - return VectorUtil.isVec2Zero(vertices[0].getTexCoord(), 0) && - VectorUtil.isVec2Zero(vertices[1].getTexCoord(), 0) && - VectorUtil.isVec2Zero(vertices[2].getTexCoord(), 0) ; + return VectorUtil.isVec2Zero(vertices[0].getTexCoord()) && + VectorUtil.isVec2Zero(vertices[1].getTexCoord()) && + VectorUtil.isVec2Zero(vertices[2].getTexCoord()) ; } public int getId() { diff --git a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java index e9c8dd193..e5fe76c28 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/Vertex.java +++ b/src/jogl/classes/com/jogamp/graph/geom/Vertex.java @@ -1,5 +1,5 @@ /** - * Copyright 2011 JogAmp Community. All rights reserved. + * Copyright 2011-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -27,64 +27,185 @@ */ package com.jogamp.graph.geom; +import com.jogamp.opengl.math.Vec2f; +import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.math.Vert3fImmutable; /** - * A Vertex with custom memory layout using custom factory. + * A Vertex exposing Vec3f vertex- and texture-coordinates. */ -public interface Vertex extends Vert3fImmutable, Cloneable { +public final class Vertex implements Vert3fImmutable, Cloneable { + private int id; + private boolean onCurve; + private final Vec3f coord = new Vec3f(); + private final Vec3f texCoord = new Vec3f(); + + public Vertex() { + this.id = Integer.MAX_VALUE; + } - public static interface Factory <T extends Vertex> { - T create(); + public Vertex(final Vertex src) { + this.id = Integer.MAX_VALUE; + coord.set(src.getCoord()); + texCoord.set(src.getTexCoord()); + setOnCurve(src.isOnCurve()); + } - T create(Vertex src); + public Vertex(final int id, final boolean onCurve, final Vec3f texCoord) { + this.id = id; + this.onCurve = onCurve; + this.texCoord.set(texCoord); + } - T create(int id, boolean onCurve, float[] texCoordsBuffer); + public Vertex(final int id, final boolean onCurve, final float texCoordX, final float texCoordY, final float texCoordZ) { + this.id = id; + this.onCurve = onCurve; + this.texCoord.set(texCoordX, texCoordY, texCoordZ); + } - T create(float x, float y, float z, boolean onCurve); + public Vertex(final Vec3f coord, final boolean onCurve) { + this.id = Integer.MAX_VALUE; + this.coord.set(coord); + setOnCurve(onCurve); + } - T create(float[] coordsBuffer, int offset, int length, boolean onCurve); + public Vertex(final Vec2f coord, final boolean onCurve) { + this.id = Integer.MAX_VALUE; + this.coord.set(coord, 0f); + setOnCurve(onCurve); } - void setCoord(float x, float y, float z); + public Vertex(final float x, final float y, final boolean onCurve) { + this(x, y, 0, onCurve); + } - /** - * @see System#arraycopy(Object, int, Object, int, int) for thrown IndexOutOfBoundsException - */ - void setCoord(float[] coordsBuffer, int offset, int length); + public Vertex(final float[] coordsBuffer, final int offset, final int length, final boolean onCurve) { + this(coordsBuffer[offset+0], coordsBuffer[offset+1], 2 < length ? coordsBuffer[offset+2] : 0f, onCurve); + } - void setX(float x); + public Vertex(final float x, final float y, final float z, final boolean onCurve) { + this.id = Integer.MAX_VALUE; + coord.set(x, y, z); + setOnCurve(onCurve); + } - void setY(float y); + public final void setCoord(final Vec3f coord) { + this.coord.set(coord); + } - void setZ(float z); + public void setCoord(final Vec2f coord) { + this.coord.set(coord, 0f); + } + + public final void setCoord(final float x, final float y, final float z) { + coord.set(x, y, z); + } - boolean isOnCurve(); + public final void setCoord(final float x, final float y) { + coord.set(x, y, 0f); + } - void setOnCurve(boolean onCurve); + @Override + public int getCoordCount() { + return 3; + } - int getId(); + @Override + public final Vec3f getCoord() { + return coord; + } - void setId(int id); + public final void setX(final float x) { + coord.setX(x); + } - float[] getTexCoord(); + public final void setY(final float y) { + coord.setY(y); + } - void setTexCoord(float s, float t, float p); + public final void setZ(final float z) { + coord.setZ(z); + } - /** - * @see System#arraycopy(Object, int, Object, int, int) for thrown IndexOutOfBoundsException - */ - void setTexCoord(float[] texCoordsBuffer, int offset, int length); + @Override + public final float x() { + return coord.x(); + } + + @Override + public final float y() { + return coord.y(); + } + + @Override + public final float z() { + return coord.z(); + } + + public final boolean isOnCurve() { + return onCurve; + } + + public final void setOnCurve(final boolean onCurve) { + this.onCurve = onCurve; + } + + public final int getId(){ + return id; + } + + public final void setId(final int id){ + this.id = id; + } /** * @param obj the Object to compare this Vertex with * @return true if {@code obj} is a Vertex and not null, on-curve flag is equal and has same vertex- and tex-coords. */ @Override - boolean equals(Object obj); + public boolean equals(final Object obj) { + if( obj == this) { + return true; + } + if( null == obj || !(obj instanceof Vertex) ) { + return false; + } + final Vertex v = (Vertex) obj; + return this == v || + isOnCurve() == v.isOnCurve() && + getTexCoord().isEqual( v.getTexCoord() ) && + getCoord().isEqual( v.getCoord() ); + } + + @Override + public final int hashCode() { + throw new InternalError("hashCode not designed"); + } + + public final Vec3f getTexCoord() { + return texCoord; + } + + public final void setTexCoord(final Vec3f v) { + texCoord.set(v); + } + + public final void setTexCoord(final float s, final float t, final float p) { + texCoord.set(s, t, p); + } /** - * @return deep clone of this Vertex + * @return deep clone of this Vertex elements */ - Vertex clone(); + @Override + public Vertex clone(){ + return new Vertex(this); // OK to not call super.clone(), using own copy-ctor + } + + @Override + public String toString() { + return "[ID: " + id + ", onCurve: " + onCurve + + ": p " + coord + + ", t " + texCoord + "]"; + } } diff --git a/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java b/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java index 62cda0322..434746240 100644 --- a/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java +++ b/src/jogl/classes/com/jogamp/graph/geom/plane/AffineTransform.java @@ -16,6 +16,7 @@ */ /** * @author Denis M. Kishenko + * @author Sven Gothel, (c) 2010-2023 */ package com.jogamp.graph.geom.plane; @@ -23,6 +24,8 @@ package com.jogamp.graph.geom.plane; import com.jogamp.graph.geom.Vertex; import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Vec2f; +import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.math.geom.AABBox; public class AffineTransform implements Cloneable { @@ -53,17 +56,17 @@ public class AffineTransform implements Cloneable { /** * The values of transformation matrix */ - float m00; - float m10; - float m01; - float m11; - float m02; - float m12; + private float m00; + private float m10; + private float m01; + private float m11; + private float m02; + private float m12; /** * The transformation <code>type</code> */ - transient int type; + private transient int type; public AffineTransform() { setToIdentity(); @@ -404,10 +407,10 @@ public class AffineTransform implements Cloneable { * @return dst for chaining */ public final AABBox transform(final AABBox src, final AABBox dst) { - final float[] srcLo = src.getLow(); - final float[] srcHi = src.getHigh(); - dst.setSize(srcLo[0] * m00 + srcLo[1] * m01 + m02, srcLo[0] * m10 + srcLo[1] * m11 + m12, srcLo[2], - srcHi[0] * m00 + srcHi[1] * m01 + m02, srcHi[0] * m10 + srcHi[1] * m11 + m12, srcHi[2]); + final Vec3f lo = src.getLow(); + final Vec3f hi = src.getHigh(); + dst.setSize(lo.x() * m00 + lo.y() * m01 + m02, lo.x() * m10 + lo.y() * m11 + m12, lo.z(), + hi.x() * m00 + hi.y() * m01 + m02, hi.x() * m10 + hi.y() * m11 + m12, hi.z()); return dst; } @@ -417,9 +420,9 @@ public class AffineTransform implements Cloneable { * @return dst for chaining */ public final Vertex transform(final Vertex src, final Vertex dst) { - final float x = src.getX(); - final float y = src.getY(); - dst.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, src.getZ()); + final float x = src.x(); + final float y = src.y(); + dst.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, src.z()); return dst; } @@ -430,9 +433,9 @@ public class AffineTransform implements Cloneable { if (dstPoint == null) { throw new IllegalArgumentException("dst["+dstOff+"] is null"); } - final float x = srcPoint.getX(); - final float y = srcPoint.getY(); - dstPoint.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, srcPoint.getZ()); + final float x = srcPoint.x(); + final float y = srcPoint.y(); + dstPoint.setCoord(x * m00 + y * m01 + m02, x * m10 + y * m11 + m12, srcPoint.z()); dst[dstOff++] = dstPoint; } } @@ -475,15 +478,42 @@ public class AffineTransform implements Cloneable { } /** + * @param src source of transformation + * @param dst destination of transformation, maybe be equal to <code>src</code> + * @return dst for chaining + */ + public final Vec2f transform(final Vec2f src, final Vec2f dst) { + final float x = src.x(); + final float y = src.y(); + dst.setX( x * m00 + y * m01 + m02 ); + dst.setY( x * m10 + y * m11 + m12 ); + return dst; + } + + /** + * @param src source of transformation + * @param dst destination of transformation, maybe be equal to <code>src</code> + * @return dst for chaining + */ + public final Vec3f transform(final Vec3f src, final Vec3f dst) { + final float x = src.x(); + final float y = src.y(); + dst.setX( x * m00 + y * m01 + m02 ); + dst.setY( x * m10 + y * m11 + m12 ); + dst.setZ( src.z() ); // just copy z + return dst; + } + + /** * * @param src * @param dst * @return return dst for chaining */ public final Vertex deltaTransform(final Vertex src, final Vertex dst) { - final float x = src.getX(); - final float y = src.getY(); - dst.setCoord(x * m00 + y * m01, x * m10 + y * m11, src.getZ()); + final float x = src.x(); + final float y = src.y(); + dst.setCoord(x * m00 + y * m01, x * m10 + y * m11, src.z()); return dst; } @@ -508,9 +538,9 @@ public class AffineTransform implements Cloneable { if (FloatUtil.abs(det) < ZERO) { throw new NoninvertibleTransformException(determinantIsZero); } - final float x = src.getX() - m02; - final float y = src.getY() - m12; - dst.setCoord((x * m11 - y * m01) / det, (y * m00 - x * m10) / det, src.getZ()); + final float x = src.x() - m02; + final float y = src.y() - m12; + dst.setCoord((x * m11 - y * m01) / det, (y * m00 - x * m10) / det, src.z()); return dst; } diff --git a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawable.java b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawable.java index 385acf082..a2a04f3fe 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLAutoDrawable.java +++ b/src/jogl/classes/com/jogamp/opengl/GLAutoDrawable.java @@ -175,7 +175,7 @@ public interface GLAutoDrawable extends GLDrawable { * <li>If the old context was current on this thread, it is being released after disassociating this auto-drawable.</li> * <li>If the new context was current on this thread, it is being released before associating this auto-drawable * and made current afterwards.</li> - * <li>Implementation may issue {@link #makeCurrent()} and {@link #release()} while drawable reassociation.</li> + * <li>Implementation may issue {@link GLContext#makeCurrent()} and {@link GLContext#release()} while drawable reassociation.</li> * <li>The user shall take extra care of thread synchronization, * i.e. lock the involved {@link GLAutoDrawable auto-drawable's} * {@link GLAutoDrawable#getUpstreamLock() upstream-locks} and {@link GLAutoDrawable#getNativeSurface() surfaces} @@ -417,6 +417,13 @@ public interface GLAutoDrawable extends GLDrawable { * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} * methods have been called. * <p> + * The given {@link GLRunnable#run(GLAutoDrawable)} shall return true to indicate + * that the GL [back] framebuffer remains intact by this runnable. <br/> + * If returning false {@link GLAutoDrawable} will call + * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} + * of all registered {@link GLEventListener}s once more to reinstate the framebuffer. + * </p> + * <p> * If no {@link GLAnimatorControl} is animating (default),<br> * or if the current thread is the animator thread,<br> * a {@link #display()} call is issued after enqueue the <code>GLRunnable</code>, diff --git a/src/jogl/classes/com/jogamp/opengl/GLEventListener.java b/src/jogl/classes/com/jogamp/opengl/GLEventListener.java index 8c5dfd3b3..6b3786b0d 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLEventListener.java +++ b/src/jogl/classes/com/jogamp/opengl/GLEventListener.java @@ -93,10 +93,10 @@ public interface GLEventListener extends EventListener { * </p> * * @param drawable the triggering {@link GLAutoDrawable} - * @param x viewport x-coord in pixel units - * @param y viewport y-coord in pixel units - * @param width viewport width in pixel units - * @param height viewport height in pixel units + * @param x lower left corner of the viewport rectangle in pixel units + * @param y lower left corner of the viewport rectangle in pixel units + * @param width width of the viewport rectangle in pixel units + * @param height height of the viewport rectangle in pixel units */ public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height); } diff --git a/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java b/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java index a2d0f5fdb..7d150c517 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java +++ b/src/jogl/classes/com/jogamp/opengl/GLOffscreenAutoDrawable.java @@ -30,8 +30,6 @@ package com.jogamp.opengl; import com.jogamp.nativewindow.NativeWindowException; -import com.jogamp.opengl.FBObject; - /** * Platform-independent {@link GLAutoDrawable} specialization, * exposing offscreen functionality. diff --git a/src/jogl/classes/com/jogamp/opengl/GLRunnable.java b/src/jogl/classes/com/jogamp/opengl/GLRunnable.java index 97a72d6cd..f8bd56fdc 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLRunnable.java +++ b/src/jogl/classes/com/jogamp/opengl/GLRunnable.java @@ -41,6 +41,13 @@ package com.jogamp.opengl; * The OpenGL context is current while executing the GLRunnable. * </p> * <p> + * {@link GLRunnable#run(GLAutoDrawable)} shall return true to indicate + * that the GL [back] framebuffer remains intact by this runnable. <br/> + * If returning false {@link GLAutoDrawable} will call + * {@link GLEventListener#display(GLAutoDrawable) display(GLAutoDrawable)} + * of all registered {@link GLEventListener}s once more to reinstate the framebuffer. + * </p> + * <p> * This might be useful to inject OpenGL commands from an I/O event listener. * </p> */ diff --git a/src/jogl/classes/com/jogamp/opengl/GLUniformData.java b/src/jogl/classes/com/jogamp/opengl/GLUniformData.java index 55a2e0cf1..c4cc3fe8f 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLUniformData.java +++ b/src/jogl/classes/com/jogamp/opengl/GLUniformData.java @@ -1,18 +1,52 @@ - +/** + * Copyright 2009-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ package com.jogamp.opengl; -import java.nio.*; +import java.nio.Buffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.util.SyncAction; +import com.jogamp.opengl.util.SyncBuffer; -public class GLUniformData { +/** + * GLSL uniform data wrapper encapsulating data to be uploaded to the GPU as a uniform. + */ +public final class GLUniformData { /** * int atom * * Number of objects is 1 * + * @param name the uniform name as used in the shader */ public GLUniformData(final String name, final int val) { initScalar(name, 1, Integer.valueOf(val)); @@ -23,6 +57,7 @@ public class GLUniformData { * * Number of objects is 1 * + * @param name the uniform name as used in the shader */ public GLUniformData(final String name, final float val) { initScalar(name, 1, Float.valueOf(val)); @@ -33,10 +68,12 @@ public class GLUniformData { * * Number of objects is calculated by data.limit()/components * + * @param name the uniform name as used in the shader * @param components number of elements of one object, ie 4 for GL_FLOAT_VEC4, + * @param data the data */ public GLUniformData(final String name, final int components, final IntBuffer data) { - initBuffer(name, components, data); + initBuffer(name, components, data, null); } /** @@ -44,14 +81,29 @@ public class GLUniformData { * * Number of objects is calculated by data.limit()/components * + * @param name the uniform name as used in the shader * @param components number of elements of one object, ie 4 for GL_FLOAT_VEC4, + * @param data the underlying data */ public GLUniformData(final String name, final int components, final FloatBuffer data) { - initBuffer(name, components, data); + initBuffer(name, components, data, null); + } + + /** + * Multiple IntBuffer or FloatBuffer Vector + * + * Number of objects is calculated by data.limit()/components + * + * @param name the uniform name as used in the shader + * @param components number of elements of one object, ie 4 for GL_FLOAT_VEC4, + * @param syncBuffer {@link SyncBuffer} providing {@link SyncAction} and {@link Buffer}, allowing to sync the buffer with the underlying data, see {@link #getBuffer()} + */ + public GLUniformData(final String name, final int components, final SyncBuffer syncBuffer) { + initBuffer(name, components, syncBuffer.getBuffer(), syncBuffer.getAction()); } private GLUniformData(final int components, final String name) { - initBuffer(name, components, null); + initBuffer(name, components, null, null); } public static GLUniformData creatEmptyVector(final String name, final int components) { @@ -59,7 +111,7 @@ public class GLUniformData { } public static GLUniformData creatEmptyMatrix(final String name, final int rows, final int columns) { - return new GLUniformData(name, rows, columns, null); + return new GLUniformData(name, rows, columns, (FloatBuffer)null); } /** @@ -67,17 +119,35 @@ public class GLUniformData { * * Number of objects is calculated by data.limit()/(rows*columns) * + * @param name the uniform name as used in the shader * @param rows the matrix rows * @param column the matrix column + * @param data the underlying data */ public GLUniformData(final String name, final int rows, final int columns, final FloatBuffer data) { - initBuffer(name, rows, columns, data); + initBuffer(name, rows, columns, data, null); + } + + /** + * Multiple FloatBuffer Matrix + * + * Number of objects is calculated by data.limit()/(rows*columns) + * + * @param name the uniform name as used in the shader + * @param rows the matrix rows + * @param column the matrix column + * @param syncBuffer {@link SyncBuffer} providing {@link SyncAction} and {@link Buffer}, allowing to sync the buffer with the underlying data, see {@link #getBuffer()} + */ + public GLUniformData(final String name, final int rows, final int columns, final SyncBuffer syncBuffer) { + initBuffer(name, rows, columns, syncBuffer.getBuffer(), syncBuffer.getAction()); } public GLUniformData setData(final int data) { initScalar(Integer.valueOf(data)); return this; } public GLUniformData setData(final float data) { initScalar(Float.valueOf(data)); return this; } - public GLUniformData setData(final IntBuffer data) { initBuffer(data); return this; } - public GLUniformData setData(final FloatBuffer data) { initBuffer(data); return this; } + + public GLUniformData setData(final IntBuffer data) { initBuffer(data, null); return this; } + public GLUniformData setData(final FloatBuffer data) { initBuffer(data, null); return this; } + public GLUniformData setData(final SyncBuffer syncedBuffer) { initBuffer(syncedBuffer.getBuffer(), syncedBuffer.getAction()); return this; } public int intValue() { return ((Integer)data).intValue(); }; public float floatValue() { return ((Float)data).floatValue(); }; @@ -115,16 +185,16 @@ public class GLUniformData { return toString(null).toString(); } - private void initBuffer(final String name, final int rows, final int columns, final Buffer buffer) { + private void initBuffer(final String name, final int rows, final int columns, final Buffer buffer, final SyncAction syncAction) { if( 2>rows || rows>4 || 2>columns || columns>4 ) { throw new GLException("rowsXcolumns must be within [2..4]X[2..4], is: "+rows+"X"+columns); } this.name=name; this.rows=rows; this.columns=columns; - this.isMatrix=true; + this.bits=BIT_MATRIX; this.location=-1; - initBuffer(buffer); + initBuffer(buffer, syncAction); } private void initScalar(final String name, final int components, final Object data) { if( 1>components || components>4 ) { @@ -133,27 +203,27 @@ public class GLUniformData { this.name=name; this.columns=components; this.rows=1; - this.isMatrix=false; + this.bits=0; this.location=-1; initScalar(data); } - private void initBuffer(final String name, final int components, final Buffer buffer) { + private void initBuffer(final String name, final int components, final Buffer buffer, final SyncAction syncAction) { if( 1>components || components>4 ) { throw new GLException("components must be within [1..4], is: "+components); } this.name=name; this.columns=components; this.rows=1; - this.isMatrix=false; + this.bits=0; this.location=-1; - initBuffer(buffer); + initBuffer(buffer, syncAction); } private void initScalar(final Object data) { if(data instanceof Buffer) { - initBuffer((Buffer)data); + initBuffer((Buffer)data, null); } else if( null != data ) { - if(isMatrix) { + if( isMatrix() ) { throw new GLException("Atom type not allowed for matrix : "+this); } this.count=1; @@ -164,8 +234,9 @@ public class GLUniformData { } } - private void initBuffer(final Buffer buffer) { + private void initBuffer(final Buffer buffer, final SyncAction syncAction) { if( null != buffer ) { + this.bits |= BIT_BUFFER; final int sz = rows*columns; if(buffer.remaining()<sz || 0!=buffer.remaining()%sz) { throw new GLException("remaining data buffer size invalid: buffer: "+buffer.toString()+"\n\t"+this); @@ -176,8 +247,10 @@ public class GLUniformData { this.count=0; this.data=null; } + this.syncAction = syncAction; } + /** Return the uniform name as used in the shader */ public String getName() { return name; } public int getLocation() { return location; } @@ -189,7 +262,7 @@ public class GLUniformData { public int setLocation(final int location) { this.location=location; return location; } /** - * Retrieves the location of the shader uniform from the linked shader program. + * Retrieves the location of the shader uniform with {@link #getName()} from the linked shader program. * <p> * No validation is performed within the implementation. * </p> @@ -203,26 +276,54 @@ public class GLUniformData { return location; } + /** + * Returns the data object. + * <p> + * In case a {@link SyncAction} has been set, + * it is invoked to {@link SyncAction#sync() synchronize} the object with the underlying data before returning the object. + * </p> + * @return the data object. + * @see SyncAction#sync() + */ public Object getObject() { + if( null != syncAction ) { + syncAction.sync(); + } return data; } + + /** + * Returns the data buffer. + * <p> + * In case a {@link SyncAction} has been set, + * it is invoked to {@link SyncAction#sync() synchronize} the buffer with the underlying data before returning the buffer. + * </p> + * @return the data buffer. + * @see SyncAction#sync() + */ public Buffer getBuffer() { + if( null != syncAction ) { + syncAction.sync(); + } return (data instanceof Buffer)?(Buffer)data:null; } - public boolean isBuffer() { - return (data instanceof Buffer); - } - public boolean isMatrix() { return isMatrix; } + + public boolean isMatrix() { return 0 != ( BIT_MATRIX & bits ); } + public boolean isBuffer() { return 0 != ( BIT_BUFFER & bits ); } public int count() { return count; } public int components() { return rows*columns; } public int rows() { return rows; } public int columns() { return columns; } + private static final short BIT_MATRIX = 0b0000000000000001; + private static final short BIT_BUFFER = 0b0000000000000010; + private String name; private int location; private int rows, columns; private int count; private Object data; - private boolean isMatrix; + private short bits; + private SyncAction syncAction; } diff --git a/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java index 602671f6e..735a2a21c 100644 --- a/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/awt/GLCanvas.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2010 JogAmp Community. All rights reserved. + * Copyright (c) 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -175,10 +175,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing private volatile JAWTWindow jawtWindow; // the JAWTWindow presentation of this AWT Canvas, bound to the 'drawable' lifecycle private volatile GLContextImpl context; // volatile: avoid locking for read-only access private volatile boolean sendReshape = false; // volatile: maybe written by EDT w/o locking - private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; // copy of the cstr args, mainly for recreation private final GLCapabilitiesImmutable capsReqUser; @@ -564,9 +561,10 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing function properly. <P> <B>Overrides:</B> - <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ - @SuppressWarnings("deprecation") - @Override + <DL><DD><CODE>addNotify</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> + */ + @SuppressWarnings("deprecation") + @Override public void addNotify() { final RecursiveLock _lock = lock; _lock.lock(); @@ -634,62 +632,51 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } } + /** + * {@inheritDoc} + * <p> + * This implementation returns false, i.e. not supporting manual change of pixel-scale. + * </p> + */ @Override - public final boolean setSurfaceScale(final float[] pixelScale) { - System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); - if( isRealized() && isShowing ) { - Threading.invoke(true, setSurfaceScaleOnEDTAction, getTreeLock()); - return true; - } else { - return false; - } - } - private final Runnable setSurfaceScaleOnEDTAction = new Runnable() { - @Override - public void run() { - final RecursiveLock _lock = lock; - _lock.lock(); - try { - if( null != drawable && drawable.isRealized() ) { - if( setSurfaceScaleImpl(jawtWindow) ) { - reshapeImpl(getWidth(), getHeight()); - if( !helper.isAnimatorAnimatingOnOtherThread() ) { - helper.invokeGL(drawable, context, displayAction, initAction); // display - } - } - } - } finally { - _lock.unlock(); - } - } }; + public final boolean canSetSurfaceScale() { return false; } - private final boolean setSurfaceScaleImpl(final ScalableSurface ns) { - if(DEBUG) { - System.err.printf("GLCanvas.setSurfaceScaleImpl reqPixelScale %.2f %.2f, hasPixelScale %.2f %.2f\n", - reqPixelScale[0], reqPixelScale[1], hasPixelScale[0], hasPixelScale[1]); - } - - if( ns.setSurfaceScale(hasPixelScale) ) { - ns.getCurrentSurfaceScale(hasPixelScale); - return true; - } else { - return false; - } + /** + * {@inheritDoc} + * <p> + * Ignored for an AWT widget since pixelScale is dictated by AWT mechanisms. + * </p> + */ + @Override + public final boolean setSurfaceScale(final float[] pixelScale) { + return false; } private final boolean updatePixelScale() { if( jawtWindow.hasPixelScaleChanged() ) { - jawtWindow.getMaximumSurfaceScale(maxPixelScale); - jawtWindow.getMinimumSurfaceScale(minPixelScale); - return setSurfaceScaleImpl(jawtWindow); + if(DEBUG) { + final float[] old = { hasPixelScale[0], hasPixelScale[1] }; + jawtWindow.getCurrentSurfaceScale(hasPixelScale); + System.err.printf("GLCanvas.updatePixelScale hasPixelScale %.2f %.2f -> %.2f %.2f\n", old[0], old[1], hasPixelScale[0], hasPixelScale[1]); + } else { + jawtWindow.getCurrentSurfaceScale(hasPixelScale); + } + return true; } else { return false; } } + /** + * {@inheritDoc} + * <p> + * Returns {@link ScalableSurface#AUTOMAX_PIXELSCALE}, always. + * </p> + */ @Override public final float[] getRequestedSurfaceScale(final float[] result) { - System.arraycopy(reqPixelScale, 0, result, 0, 2); + result[0] = ScalableSurface.AUTOMAX_PIXELSCALE; + result[1] = ScalableSurface.AUTOMAX_PIXELSCALE; return result; } @@ -699,15 +686,28 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing return result; } + /** + * {@inheritDoc} + * <p> + * Returns 1.0, always. + * </p> + */ @Override public float[] getMinimumSurfaceScale(final float[] result) { - System.arraycopy(minPixelScale, 0, result, 0, 2); + result[0] = 1f; + result[1] = 1f; return result; } + /** + * {@inheritDoc} + * <p> + * Returns {@link #getCurrentSurfaceScale(float[])}. + * </p> + */ @Override public float[] getMaximumSurfaceScale(final float[] result) { - System.arraycopy(maxPixelScale, 0, result, 0, 2); + System.arraycopy(hasPixelScale, 0, result, 0, 2); return result; } @@ -723,12 +723,9 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing jawtWindow.setShallUseOffscreenLayer(shallUseOffscreenLayer); jawtWindow.lockSurface(); try { - jawtWindow.setSurfaceScale(reqPixelScale); drawable = (GLDrawableImpl) GLDrawableFactory.getFactory(capsReqUser.getGLProfile()).createGLDrawable(jawtWindow); createContextImpl(drawable); jawtWindow.getCurrentSurfaceScale(hasPixelScale); - jawtWindow.getMinimumSurfaceScale(minPixelScale); - jawtWindow.getMaximumSurfaceScale(maxPixelScale); } finally { jawtWindow.unlockSurface(); } @@ -1388,10 +1385,6 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing } hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; if(null != awtConfig) { final AbstractGraphicsConfiguration aconfig = awtConfig.getNativeGraphicsConfiguration(); diff --git a/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java b/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java index 20dc71958..fb0df6aaf 100644 --- a/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/com/jogamp/opengl/awt/GLJPanel.java @@ -1,6 +1,6 @@ /* * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2010 JogAmp Community. All rights reserved. + * Copyright (c) 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -263,10 +263,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private boolean handleReshape = false; private boolean sendReshape = true; - private final float[] minPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - private final float[] maxPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; private final float[] hasPixelScale = new float[] { ScalableSurface.IDENTITY_PIXELSCALE, ScalableSurface.IDENTITY_PIXELSCALE }; - private final float[] reqPixelScale = new float[] { ScalableSurface.AUTOMAX_PIXELSCALE, ScalableSurface.AUTOMAX_PIXELSCALE }; /** For handling reshape events lazily: reshapeWidth -> panelWidth -> backend.width in pixel units (scaled) */ private int reshapeWidth; @@ -397,6 +394,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing public final boolean initializeBackend(final boolean offthread) { if( offthread ) { new InterruptSource.Thread(null, null, getThreadName()+"-GLJPanel_Init") { + @Override public void run() { if( !isInitialized ) { initializeBackendImpl(); @@ -581,59 +579,52 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } + /** + * {@inheritDoc} + * <p> + * This implementation returns false, i.e. not supporting manual change of pixel-scale. + * </p> + */ @Override - public final boolean setSurfaceScale(final float[] pixelScale) { // HiDPI support - System.arraycopy(pixelScale, 0, reqPixelScale, 0, 2); - final Backend b = backend; - if ( isInitialized && null != b && isShowing ) { - if( isShowing || ( printActive && isVisible() ) ) { - if (EventQueue.isDispatchThread()) { - setSurfaceScaleAction.run(); - } else { - try { - EventQueue.invokeAndWait(setSurfaceScaleAction); - } catch (final Exception e) { - throw new GLException(e); - } - } - } - return true; - } else { - return false; - } - } - private final Runnable setSurfaceScaleAction = new Runnable() { - @Override - public void run() { - final Backend b = backend; - if( null != b && setSurfaceScaleImpl(b) ) { - if( !helper.isAnimatorAnimatingOnOtherThread() ) { - paintImmediatelyAction.run(); // display - } - } - } - }; + public final boolean canSetSurfaceScale() { return false; } - private final boolean setSurfaceScaleImpl(final Backend b) { - if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null) ) { - reshapeImpl(getWidth(), getHeight()); - updateWrappedSurfaceScale(b.getDrawable()); - return true; - } + /** + * {@inheritDoc} + * <p> + * Ignored for an AWT widget since pixelScale is dictated by AWT mechanisms. + * </p> + */ + @Override + public final boolean setSurfaceScale(final float[] pixelScale) { // HiDPI support return false; } private final boolean updatePixelScale(final Backend b) { - if( JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale) ) { - return setSurfaceScaleImpl(b); - } else { - return false; - } + final float[] min = { 1, 1 }; + final float[] max = { hasPixelScale[0], hasPixelScale[1] }; + if( JAWTUtil.getPixelScale(getGraphicsConfiguration(), min, max) ) { + if( DEBUG ) { + System.err.printf("GLJPanel.updatePixelScale %.2f %.2f -> %.2f %.2f\n", hasPixelScale[0], hasPixelScale[1], max[0], max[1]); + } + System.arraycopy(max, 0, hasPixelScale, 0, 2); + reshapeImpl(getWidth(), getHeight()); + updateWrappedSurfaceScale(b.getDrawable()); + return true; + } else { + return false; + } } + /** + * {@inheritDoc} + * <p> + * Returns {@link ScalableSurface#AUTOMAX_PIXELSCALE}, always. + * </p> + */ @Override public final float[] getRequestedSurfaceScale(final float[] result) { - System.arraycopy(reqPixelScale, 0, result, 0, 2); + result[0] = ScalableSurface.AUTOMAX_PIXELSCALE; + result[1] = ScalableSurface.AUTOMAX_PIXELSCALE; return result; } @@ -643,15 +634,28 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing return result; } + /** + * {@inheritDoc} + * <p> + * Returns 1.0, always. + * </p> + */ @Override public float[] getMinimumSurfaceScale(final float[] result) { - System.arraycopy(minPixelScale, 0, result, 0, 2); + result[0] = 1f; + result[1] = 1f; return result; } + /** + * {@inheritDoc} + * <p> + * Returns {@link #getCurrentSurfaceScale(float[])}. + * </p> + */ @Override public float[] getMaximumSurfaceScale(final float[] result) { - System.arraycopy(maxPixelScale, 0, result, 0, 2); + System.arraycopy(hasPixelScale, 0, result, 0, 2); return result; } @@ -667,8 +671,17 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing awtWindowClosingProtocol.addClosingListener(); // HiDPI support - JAWTUtil.getPixelScale(getGraphicsConfiguration(), minPixelScale, maxPixelScale); - SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG ? getClass().getSimpleName() : null); + { + final float[] min = { 1, 1 }; + final float[] max = { hasPixelScale[0], hasPixelScale[1] }; + if( JAWTUtil.getPixelScale(getGraphicsConfiguration(), min, max) ) { + if( DEBUG ) { + System.err.printf("GLJPanel.addNotify: pixelScale %.2f %.2f -> %.2f %.2f\n", hasPixelScale[0], hasPixelScale[1], max[0], max[1]); + } + System.arraycopy(max, 0, hasPixelScale, 0, 2); + reshapeImpl(getWidth(), getHeight()); + } + } if (DEBUG) { System.err.println(getThreadName()+": GLJPanel.addNotify()"); @@ -688,10 +701,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing dispose(null); hasPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; hasPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - minPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - minPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; - maxPixelScale[0] = ScalableSurface.IDENTITY_PIXELSCALE; - maxPixelScale[1] = ScalableSurface.IDENTITY_PIXELSCALE; super.removeNotify(); } @@ -1345,7 +1354,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if( !isInitialized ) { if( handleReshape ) { if (DEBUG) { - System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend.1: ["+(printActive?"printing":"paint")+"] "+ + System.err.println(getThreadName()+": GLJPanel.initializeBackendImpl.1: ["+(printActive?"printing":"paint")+"] "+ panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr() + " -> " + reshapeWidth+"x"+reshapeHeight+" @ scale "+getPixelScaleStr()); } @@ -1354,7 +1363,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing handleReshape = false; } else { if (DEBUG) { - System.err.println(getThreadName()+": GLJPanel.createAndInitializeBackend.0: ["+(printActive?"printing":"paint")+"] "+ + System.err.println(getThreadName()+": GLJPanel.initializeBackendImpl.0: ["+(printActive?"printing":"paint")+"] "+ panelWidth+"x"+panelHeight+" @ scale "+getPixelScaleStr()); } } diff --git a/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java index 73244cb13..004562767 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/math/FloatUtil.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -35,8 +35,6 @@ import com.jogamp.opengl.GLException; import jogamp.opengl.Debug; import com.jogamp.common.os.Platform; -import com.jogamp.opengl.math.geom.AABBox; -import com.jogamp.opengl.math.geom.Frustum; /** * Basic Float math utility functions. @@ -44,20 +42,26 @@ import com.jogamp.opengl.math.geom.Frustum; * Implementation assumes linear matrix layout in column-major order * matching OpenGL's implementation, illustration: * <pre> - Row-Major Column-Major (OpenGL): + Row-Major Column-Major (OpenGL): - | 0 1 2 3 | | 0 4 8 12 | - | | | | - | 4 5 6 7 | | 1 5 9 13 | - M = | | M = | | - | 8 9 10 11 | | 2 6 10 14 | - | | | | - | 12 13 14 15 | | 3 7 11 15 | + | 0 1 2 tx | + | | + | 4 5 6 ty | + M = | | + | 8 9 10 tz | + | | + | 12 13 14 15 | - C R C R + R C R C m[0*4+3] = tx; m[0+4*3] = tx; m[1*4+3] = ty; m[1+4*3] = ty; m[2*4+3] = tz; m[2+4*3] = tz; + + RC (std subscript order) RC (std subscript order) + m03 = tx; m03 = tx; + m13 = ty; m13 = ty; + m23 = tz; m23 = tz; + * </pre> * </p> * <p> @@ -71,7 +75,7 @@ import com.jogamp.opengl.math.geom.Frustum; * Implementation utilizes unrolling of small vertices and matrices wherever possible * while trying to access memory in a linear fashion for performance reasons, see: * <ul> - * <li><a href="https://code.google.com/p/java-matrix-benchmark/">java-matrix-benchmark</a></li> + * <li><a href="https://lessthanoptimal.github.io/Java-Matrix-Benchmark/">java-matrix-benchmark</a></li> * <li><a href="https://github.com/lessthanoptimal/ejml">EJML Efficient Java Matrix Library</a></li> * </ul> * </p> @@ -81,40 +85,12 @@ public final class FloatUtil { // // Matrix Ops + // Only a subset will remain here, try using Matrix4f and perhaps PMVMatrix, SyncMatrix4f16 or SyncMatrices4f16 // /** * Make matrix an identity matrix * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @return given matrix for chaining - */ - public static float[] makeIdentity(final float[] m, final int m_offset) { - m[m_offset+0+4*0] = 1f; - m[m_offset+1+4*0] = 0f; - m[m_offset+2+4*0] = 0f; - m[m_offset+3+4*0] = 0f; - - m[m_offset+0+4*1] = 0f; - m[m_offset+1+4*1] = 1f; - m[m_offset+2+4*1] = 0f; - m[m_offset+3+4*1] = 0f; - - m[m_offset+0+4*2] = 0f; - m[m_offset+1+4*2] = 0f; - m[m_offset+2+4*2] = 1f; - m[m_offset+3+4*2] = 0f; - - m[m_offset+0+4*3] = 0f; - m[m_offset+1+4*3] = 0f; - m[m_offset+2+4*3] = 0f; - m[m_offset+3+4*3] = 1f; - return m; - } - - /** - * Make matrix an identity matrix - * @param m 4x4 matrix in column-major order (also result) * @return given matrix for chaining */ public static float[] makeIdentity(final float[] m) { @@ -153,43 +129,6 @@ public final class FloatUtil { * All matrix fields are only set if <code>initM</code> is <code>true</code>. * </p> * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @param initM if true, given matrix will be initialized w/ identity matrix, - * otherwise only the diagonal and last-row is set. - * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix - * for {@link #makeScale(float[], int, boolean, float, float, float) scaling} - * and {@link #makeTranslation(float[], int, boolean, float, float, float) translation}, - * while leaving the other fields untouched for performance reasons. - * @return given matrix for chaining - */ - public static float[] makeTranslation(final float[] m, final int m_offset, final boolean initM, final float tx, final float ty, final float tz) { - if( initM ) { - makeIdentity(m, m_offset); - } else { - m[m_offset+0+4*0] = 1; - m[m_offset+1+4*1] = 1; - m[m_offset+2+4*2] = 1; - m[m_offset+3+4*3] = 1; - } - m[m_offset+0+4*3] = tx; - m[m_offset+1+4*3] = ty; - m[m_offset+2+4*3] = tz; - return m; - } - - /** - * Make a translation matrix in column-major order from the given axis deltas - * <pre> - Translation matrix (Column Order): - 1 0 0 0 - 0 1 0 0 - 0 0 1 0 - x y z 1 - * </pre> - * <p> - * All matrix fields are only set if <code>initM</code> is <code>true</code>. - * </p> - * @param m 4x4 matrix in column-major order (also result) * @param initM if true, given matrix will be initialized w/ identity matrix, * otherwise only the diagonal and last-row is set. * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix @@ -226,43 +165,6 @@ public final class FloatUtil { * All matrix fields are only set if <code>initM</code> is <code>true</code>. * </p> * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @param initM if true, given matrix will be initialized w/ identity matrix, - * otherwise only the diagonal and last-row is set. - * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix - * for {@link #makeScale(float[], int, boolean, float, float, float) scaling} - * and {@link #makeTranslation(float[], int, boolean, float, float, float) translation}, - * while leaving the other fields untouched for performance reasons. - * @return given matrix for chaining - */ - public static float[] makeScale(final float[] m, final int m_offset, final boolean initM, final float sx, final float sy, final float sz) { - if( initM ) { - makeIdentity(m, m_offset); - } else { - m[m_offset+0+4*3] = 0; - m[m_offset+1+4*3] = 0; - m[m_offset+2+4*3] = 0; - m[m_offset+3+4*3] = 1; - } - m[m_offset+0+4*0] = sx; - m[m_offset+1+4*1] = sy; - m[m_offset+2+4*2] = sz; - return m; - } - - /** - * Make a scale matrix in column-major order from the given axis factors - * <pre> - Scale matrix (Any Order): - x 0 0 0 - 0 y 0 0 - 0 0 z 0 - 0 0 0 1 - * </pre> - * <p> - * All matrix fields are only set if <code>initM</code> is <code>true</code>. - * </p> - * @param m 4x4 matrix in column-major order (also result) * @param initM if true, given matrix will be initialized w/ identity matrix, * otherwise only the diagonal and last-row is set. * The latter can be utilized to share a once {@link #makeIdentity(float[], int) identity set} matrix @@ -287,189 +189,6 @@ public final class FloatUtil { } /** - * Make a rotation matrix from the given axis and angle in radians. - * <pre> - Rotation matrix (Column Order): - xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0 - xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0 - xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0 - 0 0 0 1 - * </pre> - * <p> - * All matrix fields are set. - * </p> - * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a> - * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @return given matrix for chaining - */ - public static float[] makeRotationAxis(final float[] m, final int m_offset, final float angrad, float x, float y, float z, final float[] tmpVec3f) { - final float c = cos(angrad); - final float ic= 1.0f - c; - final float s = sin(angrad); - - tmpVec3f[0]=x; tmpVec3f[1]=y; tmpVec3f[2]=z; - VectorUtil.normalizeVec3(tmpVec3f); - x = tmpVec3f[0]; y = tmpVec3f[1]; z = tmpVec3f[2]; - - final float xy = x*y; - final float xz = x*z; - final float xs = x*s; - final float ys = y*s; - final float yz = y*z; - final float zs = z*s; - m[0+0*4+m_offset] = x*x*ic+c; - m[1+0*4+m_offset] = xy*ic+zs; - m[2+0*4+m_offset] = xz*ic-ys; - m[3+0*4+m_offset] = 0; - - m[0+1*4+m_offset] = xy*ic-zs; - m[1+1*4+m_offset] = y*y*ic+c; - m[2+1*4+m_offset] = yz*ic+xs; - m[3+1*4+m_offset] = 0; - - m[0+2*4+m_offset] = xz*ic+ys; - m[1+2*4+m_offset] = yz*ic-xs; - m[2+2*4+m_offset] = z*z*ic+c; - m[3+2*4+m_offset] = 0; - - m[0+3*4+m_offset] = 0f; - m[1+3*4+m_offset] = 0f; - m[2+3*4+m_offset] = 0f; - m[3+3*4+m_offset] = 1f; - - return m; - } - - /** - * Make a concatenated rotation matrix in column-major order from the given Euler rotation angles in radians. - * <p> - * The rotations are applied in the given order: - * <ul> - * <li>y - heading</li> - * <li>z - attitude</li> - * <li>x - bank</li> - * </ul> - * </p> - * <p> - * All matrix fields are set. - * </p> - * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @param bankX the Euler pitch angle in radians. (rotation about the X axis) - * @param headingY the Euler yaw angle in radians. (rotation about the Y axis) - * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis) - * @return given matrix for chaining - * <p> - * Implementation does not use Quaternion and hence is exposed to - * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a> - * </p> - * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q36">Matrix-FAQ Q36</a> - * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a> - */ - public static float[] makeRotationEuler(final float[] m, final int m_offset, final float bankX, final float headingY, final float attitudeZ) { - // Assuming the angles are in radians. - final float ch = cos(headingY); - final float sh = sin(headingY); - final float ca = cos(attitudeZ); - final float sa = sin(attitudeZ); - final float cb = cos(bankX); - final float sb = sin(bankX); - - m[0+0*4+m_offset] = ch*ca; - m[1+0*4+m_offset] = sa; - m[2+0*4+m_offset] = -sh*ca; - m[3+0*4+m_offset] = 0; - - m[0+1*4+m_offset] = sh*sb - ch*sa*cb; - m[1+1*4+m_offset] = ca*cb; - m[2+1*4+m_offset] = sh*sa*cb + ch*sb; - m[3+1*4+m_offset] = 0; - - m[0+2*4+m_offset] = ch*sa*sb + sh*cb; - m[1+2*4+m_offset] = -ca*sb; - m[2+2*4+m_offset] = -sh*sa*sb + ch*cb; - m[3+2*4+m_offset] = 0; - - m[0+3*4+m_offset] = 0; - m[1+3*4+m_offset] = 0; - m[2+3*4+m_offset] = 0; - m[3+3*4+m_offset] = 1; - - return m; - } - - /** - * Make given matrix the orthogonal matrix based on given parameters. - * <pre> - Ortho matrix (Column Order): - 2/dx 0 0 0 - 0 2/dy 0 0 - 0 0 2/dz 0 - tx ty tz 1 - * </pre> - * <p> - * All matrix fields are only set if <code>initM</code> is <code>true</code>. - * </p> - * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @param initM if true, given matrix will be initialized w/ identity matrix, - * otherwise only the orthogonal fields are set. - * @param left - * @param right - * @param bottom - * @param top - * @param zNear - * @param zFar - * @return given matrix for chaining - */ - public static float[] makeOrtho(final float[] m, final int m_offset, final boolean initM, - final float left, final float right, - final float bottom, final float top, - final float zNear, final float zFar) { - if( initM ) { - // m[m_offset+0+4*0] = 1f; - m[m_offset+1+4*0] = 0f; - m[m_offset+2+4*0] = 0f; - m[m_offset+3+4*0] = 0f; - - m[m_offset+0+4*1] = 0f; - // m[m_offset+1+4*1] = 1f; - m[m_offset+2+4*1] = 0f; - m[m_offset+3+4*1] = 0f; - - m[m_offset+0+4*2] = 0f; - m[m_offset+1+4*2] = 0f; - // m[m_offset+2+4*2] = 1f; - m[m_offset+3+4*2] = 0f; - - // m[m_offset+0+4*3] = 0f; - // m[m_offset+1+4*3] = 0f; - // m[m_offset+2+4*3] = 0f; - // m[m_offset+3+4*3] = 1f; - } - final float dx=right-left; - final float dy=top-bottom; - final float dz=zFar-zNear; - final float tx=-1.0f*(right+left)/dx; - final float ty=-1.0f*(top+bottom)/dy; - final float tz=-1.0f*(zFar+zNear)/dz; - - m[m_offset+0+4*0] = 2.0f/dx; - - m[m_offset+1+4*1] = 2.0f/dy; - - m[m_offset+2+4*2] = -2.0f/dz; - - m[m_offset+0+4*3] = tx; - m[m_offset+1+4*3] = ty; - m[m_offset+2+4*3] = tz; - m[m_offset+3+4*3] = 1f; - - return m; - } - - /** * Make given matrix the frustum matrix based on given parameters. * <pre> Frustum matrix (Column Order): @@ -580,35 +299,6 @@ public final class FloatUtil { } /** - * Make given matrix the perspective {@link #makeFrustum(float[], int, boolean, float, float, float, float, float, float) frustum} - * matrix based on given parameters. - * <p> - * All matrix fields are only set if <code>initM</code> is <code>true</code>. - * </p> - * - * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @param initM if true, given matrix will be initialized w/ identity matrix, - * otherwise only the frustum fields are set. - * @param fovhv {@link FovHVHalves} field of view in both directions, may not be centered, either in radians or tangent - * @param zNear - * @param zFar - * @return given matrix for chaining - * @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear} - * @see #makeFrustum(float[], int, boolean, float, float, float, float, float, float) - * @see Frustum#updateByFovDesc(float[], int, boolean, Frustum.FovDesc) - */ - public static float[] makePerspective(final float[] m, final int m_offset, final boolean initM, - final FovHVHalves fovhv, final float zNear, final float zFar) throws GLException { - final FovHVHalves fovhvTan = fovhv.toTangents(); // use tangent of half-fov ! - final float top = fovhvTan.top * zNear; - final float bottom = -1.0f * fovhvTan.bottom * zNear; - final float left = -1.0f * fovhvTan.left * zNear; - final float right = fovhvTan.right * zNear; - return makeFrustum(m, m_offset, initM, left, right, bottom, top, zNear, zFar); - } - - /** * Make given matrix the <i>look-at</i> matrix based on given parameters. * <p> * Consist out of two matrix multiplications: @@ -722,7 +412,7 @@ public final class FloatUtil { * @param mat4Tmp temp float[16] storage * @return given matrix <code>m</code> for chaining or <code>null</code> if either delta value is <= zero. */ - public static float[] makePick(final float[] m, final int m_offset, + public static float[] makePick(final float[] m, final float x, final float y, final float deltaX, final float deltaY, final int[] viewport, final int viewport_offset, @@ -732,13 +422,13 @@ public final class FloatUtil { } /* Translate and scale the picked region to the entire window */ - makeTranslation(m, m_offset, true, + makeTranslation(m, true, (viewport[2+viewport_offset] - 2 * (x - viewport[0+viewport_offset])) / deltaX, (viewport[3+viewport_offset] - 2 * (y - viewport[1+viewport_offset])) / deltaY, 0); makeScale(mat4Tmp, true, viewport[2+viewport_offset] / deltaX, viewport[3+viewport_offset] / deltaY, 1.0f); - multMatrix(m, m_offset, mat4Tmp, 0); + multMatrix(m, mat4Tmp); return m; } @@ -746,42 +436,6 @@ public final class FloatUtil { * Transpose the given matrix. * * @param msrc 4x4 matrix in column-major order, the source - * @param msrc_offset offset in given array <i>msrc</i>, i.e. start of the 4x4 matrix - * @param mres 4x4 matrix in column-major order, the result - * @param mres_offset offset in given array <i>mres</i>, i.e. start of the 4x4 matrix - * @return given result matrix <i>mres</i> for chaining - */ - public static float[] transposeMatrix(final float[] msrc, final int msrc_offset, final float[] mres, final int mres_offset) { - mres[mres_offset+0] = msrc[msrc_offset+0*4]; - mres[mres_offset+1] = msrc[msrc_offset+1*4]; - mres[mres_offset+2] = msrc[msrc_offset+2*4]; - mres[mres_offset+3] = msrc[msrc_offset+3*4]; - - final int i4_1 = 1*4; - mres[mres_offset+0+i4_1] = msrc[msrc_offset+1+0*4]; - mres[mres_offset+1+i4_1] = msrc[msrc_offset+1+1*4]; - mres[mres_offset+2+i4_1] = msrc[msrc_offset+1+2*4]; - mres[mres_offset+3+i4_1] = msrc[msrc_offset+1+3*4]; - - final int i4_2 = 2*4; - mres[mres_offset+0+i4_2] = msrc[msrc_offset+2+0*4]; - mres[mres_offset+1+i4_2] = msrc[msrc_offset+2+1*4]; - mres[mres_offset+2+i4_2] = msrc[msrc_offset+2+2*4]; - mres[mres_offset+3+i4_2] = msrc[msrc_offset+2+3*4]; - - final int i4_3 = 3*4; - mres[mres_offset+0+i4_3] = msrc[msrc_offset+3+0*4]; - mres[mres_offset+1+i4_3] = msrc[msrc_offset+3+1*4]; - mres[mres_offset+2+i4_3] = msrc[msrc_offset+3+2*4]; - mres[mres_offset+3+i4_3] = msrc[msrc_offset+3+3*4]; - - return mres; - } - - /** - * Transpose the given matrix. - * - * @param msrc 4x4 matrix in column-major order, the source * @param mres 4x4 matrix in column-major order, the result * @return given result matrix <i>mres</i> for chaining */ @@ -815,40 +469,6 @@ public final class FloatUtil { /** * Returns the determinant of the given matrix * @param m 4x4 matrix in column-major order, the source - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @return the matrix determinant - */ - public static float matrixDeterminant(final float[] m, final int m_offset) { - float a11 = m[ 1+4*1 + m_offset ]; - float a21 = m[ 2+4*1 + m_offset ]; - float a31 = m[ 3+4*1 + m_offset ]; - float a12 = m[ 1+4*2 + m_offset ]; - float a22 = m[ 2+4*2 + m_offset ]; - float a32 = m[ 3+4*2 + m_offset ]; - float a13 = m[ 1+4*3 + m_offset ]; - float a23 = m[ 2+4*3 + m_offset ]; - float a33 = m[ 3+4*3 + m_offset ]; - - float ret = 0; - ret += m[ 0 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); - a11 = m[ 1+4*0 + m_offset ]; - a21 = m[ 2+4*0 + m_offset ]; - a31 = m[ 3+4*0 + m_offset ]; - ret -= m[ 0+4*1 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); - a12 = m[ 1+4*1 + m_offset ]; - a22 = m[ 2+4*1 + m_offset ]; - a32 = m[ 3+4*1 + m_offset ]; - ret += m[ 0+4*2 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); - a13 = m[ 1+4*2 + m_offset ]; - a23 = m[ 2+4*2 + m_offset ]; - a33 = m[ 3+4*2 + m_offset ]; - ret -= m[ 0+4*3 + m_offset ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); - return ret; - } - - /** - * Returns the determinant of the given matrix - * @param m 4x4 matrix in column-major order, the source * @return the matrix determinant */ public static float matrixDeterminant(final float[] m) { @@ -878,94 +498,6 @@ public final class FloatUtil { ret -= m[ 0+4*3 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31)); return ret; } - - /** - * Invert the given matrix. - * <p> - * Returns <code>null</code> if inversion is not possible, - * e.g. matrix is singular due to a bad matrix. - * </p> - * - * @param msrc 4x4 matrix in column-major order, the source - * @param msrc_offset offset in given array <i>msrc</i>, i.e. start of the 4x4 matrix - * @param mres 4x4 matrix in column-major order, the result - may be <code>msrc</code> (in-place) - * @param mres_offset offset in given array <i>mres</i>, i.e. start of the 4x4 matrix - may be <code>msrc_offset</code> (in-place) - * @return given result matrix <i>mres</i> for chaining if successful, otherwise <code>null</code>. See above. - */ - public static float[] invertMatrix(final float[] msrc, final int msrc_offset, final float[] mres, final int mres_offset) { - final float scale; - { - float max = Math.abs(msrc[0]); - - for( int i = 1; i < 16; i++ ) { - final float a = Math.abs(msrc[i]); - if( a > max ) max = a; - } - if( 0 == max ) { - return null; - } - scale = 1.0f/max; - } - - final float a11 = msrc[0+4*0+msrc_offset]*scale; - final float a21 = msrc[1+4*0+msrc_offset]*scale; - final float a31 = msrc[2+4*0+msrc_offset]*scale; - final float a41 = msrc[3+4*0+msrc_offset]*scale; - final float a12 = msrc[0+4*1+msrc_offset]*scale; - final float a22 = msrc[1+4*1+msrc_offset]*scale; - final float a32 = msrc[2+4*1+msrc_offset]*scale; - final float a42 = msrc[3+4*1+msrc_offset]*scale; - final float a13 = msrc[0+4*2+msrc_offset]*scale; - final float a23 = msrc[1+4*2+msrc_offset]*scale; - final float a33 = msrc[2+4*2+msrc_offset]*scale; - final float a43 = msrc[3+4*2+msrc_offset]*scale; - final float a14 = msrc[0+4*3+msrc_offset]*scale; - final float a24 = msrc[1+4*3+msrc_offset]*scale; - final float a34 = msrc[2+4*3+msrc_offset]*scale; - final float a44 = msrc[3+4*3+msrc_offset]*scale; - - final float m11 = + a22*(a33*a44 - a34*a43) - a23*(a32*a44 - a34*a42) + a24*(a32*a43 - a33*a42); - final float m12 = -( + a21*(a33*a44 - a34*a43) - a23*(a31*a44 - a34*a41) + a24*(a31*a43 - a33*a41)); - final float m13 = + a21*(a32*a44 - a34*a42) - a22*(a31*a44 - a34*a41) + a24*(a31*a42 - a32*a41); - final float m14 = -( + a21*(a32*a43 - a33*a42) - a22*(a31*a43 - a33*a41) + a23*(a31*a42 - a32*a41)); - final float m21 = -( + a12*(a33*a44 - a34*a43) - a13*(a32*a44 - a34*a42) + a14*(a32*a43 - a33*a42)); - final float m22 = + a11*(a33*a44 - a34*a43) - a13*(a31*a44 - a34*a41) + a14*(a31*a43 - a33*a41); - final float m23 = -( + a11*(a32*a44 - a34*a42) - a12*(a31*a44 - a34*a41) + a14*(a31*a42 - a32*a41)); - final float m24 = + a11*(a32*a43 - a33*a42) - a12*(a31*a43 - a33*a41) + a13*(a31*a42 - a32*a41); - final float m31 = + a12*(a23*a44 - a24*a43) - a13*(a22*a44 - a24*a42) + a14*(a22*a43 - a23*a42); - final float m32 = -( + a11*(a23*a44 - a24*a43) - a13*(a21*a44 - a24*a41) + a14*(a21*a43 - a23*a41)); - final float m33 = + a11*(a22*a44 - a24*a42) - a12*(a21*a44 - a24*a41) + a14*(a21*a42 - a22*a41); - final float m34 = -( + a11*(a22*a43 - a23*a42) - a12*(a21*a43 - a23*a41) + a13*(a21*a42 - a22*a41)); - final float m41 = -( + a12*(a23*a34 - a24*a33) - a13*(a22*a34 - a24*a32) + a14*(a22*a33 - a23*a32)); - final float m42 = + a11*(a23*a34 - a24*a33) - a13*(a21*a34 - a24*a31) + a14*(a21*a33 - a23*a31); - final float m43 = -( + a11*(a22*a34 - a24*a32) - a12*(a21*a34 - a24*a31) + a14*(a21*a32 - a22*a31)); - final float m44 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31); - - final float det = (a11*m11 + a12*m12 + a13*m13 + a14*m14)/scale; - - if( 0 == det ) { - return null; - } - - mres[0+4*0+mres_offset] = m11 / det; - mres[1+4*0+mres_offset] = m12 / det; - mres[2+4*0+mres_offset] = m13 / det; - mres[3+4*0+mres_offset] = m14 / det; - mres[0+4*1+mres_offset] = m21 / det; - mres[1+4*1+mres_offset] = m22 / det; - mres[2+4*1+mres_offset] = m23 / det; - mres[3+4*1+mres_offset] = m24 / det; - mres[0+4*2+mres_offset] = m31 / det; - mres[1+4*2+mres_offset] = m32 / det; - mres[2+4*2+mres_offset] = m33 / det; - mres[3+4*2+mres_offset] = m34 / det; - mres[0+4*3+mres_offset] = m41 / det; - mres[1+4*3+mres_offset] = m42 / det; - mres[2+4*3+mres_offset] = m43 / det; - mres[3+4*3+mres_offset] = m44 / det; - return mres; - } - /** * Invert the given matrix. * <p> @@ -1027,27 +559,27 @@ public final class FloatUtil { final float m44 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31); final float det = (a11*m11 + a12*m12 + a13*m13 + a14*m14)/scale; - if( 0 == det ) { return null; } - - mres[0+4*0] = m11 / det; - mres[1+4*0] = m12 / det; - mres[2+4*0] = m13 / det; - mres[3+4*0] = m14 / det; - mres[0+4*1] = m21 / det; - mres[1+4*1] = m22 / det; - mres[2+4*1] = m23 / det; - mres[3+4*1] = m24 / det; - mres[0+4*2] = m31 / det; - mres[1+4*2] = m32 / det; - mres[2+4*2] = m33 / det; - mres[3+4*2] = m34 / det; - mres[0+4*3] = m41 / det; - mres[1+4*3] = m42 / det; - mres[2+4*3] = m43 / det; - mres[3+4*3] = m44 / det; + final float invdet = 1.0f / det; + + mres[0+4*0] = m11 * invdet; + mres[1+4*0] = m12 * invdet; + mres[2+4*0] = m13 * invdet; + mres[3+4*0] = m14 * invdet; + mres[0+4*1] = m21 * invdet; + mres[1+4*1] = m22 * invdet; + mres[2+4*1] = m23 * invdet; + mres[3+4*1] = m24 * invdet; + mres[0+4*2] = m31 * invdet; + mres[1+4*2] = m32 * invdet; + mres[2+4*2] = m33 * invdet; + mres[3+4*2] = m34 * invdet; + mres[0+4*3] = m41 * invdet; + mres[1+4*3] = m42 * invdet; + mres[2+4*3] = m43 * invdet; + mres[3+4*3] = m44 * invdet; return mres; } @@ -1072,12 +604,12 @@ public final class FloatUtil { * @param vec4Tmp2 4 component vector for temp storage * @return true if successful, otherwise false (z is 1) */ - public static boolean mapObjToWinCoords(final float objx, final float objy, final float objz, - final float[] modelMatrix, final int modelMatrix_offset, - final float[] projMatrix, final int projMatrix_offset, - final int[] viewport, final int viewport_offset, - final float[] win_pos, final int win_pos_offset, - final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { + public static boolean mapObjToWin(final float objx, final float objy, final float objz, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final float[] win_pos, final int win_pos_offset, + final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { vec4Tmp1[0] = objx; vec4Tmp1[1] = objy; vec4Tmp1[2] = objz; @@ -1110,56 +642,6 @@ public final class FloatUtil { } /** - * Map object coordinates to window coordinates. - * <p> - * Traditional <code>gluProject</code> implementation. - * </p> - * - * @param objx - * @param objy - * @param objz - * @param mat4PMv [projection] x [modelview] matrix, i.e. P x Mv - * @param viewport 4 component viewport vector - * @param viewport_offset - * @param win_pos 3 component window coordinate, the result - * @param win_pos_offset - * @param vec4Tmp1 4 component vector for temp storage - * @param vec4Tmp2 4 component vector for temp storage - * @return true if successful, otherwise false (z is 1) - */ - public static boolean mapObjToWinCoords(final float objx, final float objy, final float objz, - final float[/*16*/] mat4PMv, - final int[] viewport, final int viewport_offset, - final float[] win_pos, final int win_pos_offset, - final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { - vec4Tmp2[0] = objx; - vec4Tmp2[1] = objy; - vec4Tmp2[2] = objz; - vec4Tmp2[3] = 1.0f; - - // vec4Tmp1 = P * Mv * o - multMatrixVec(mat4PMv, vec4Tmp2, vec4Tmp1); - - if (vec4Tmp1[3] == 0.0f) { - return false; - } - - vec4Tmp1[3] = (1.0f / vec4Tmp1[3]) * 0.5f; - - // Map x, y and z to range 0-1 - vec4Tmp1[0] = vec4Tmp1[0] * vec4Tmp1[3] + 0.5f; - vec4Tmp1[1] = vec4Tmp1[1] * vec4Tmp1[3] + 0.5f; - vec4Tmp1[2] = vec4Tmp1[2] * vec4Tmp1[3] + 0.5f; - - // Map x,y to viewport - win_pos[0+win_pos_offset] = vec4Tmp1[0] * viewport[2+viewport_offset] + viewport[0+viewport_offset]; - win_pos[1+win_pos_offset] = vec4Tmp1[1] * viewport[3+viewport_offset] + viewport[1+viewport_offset]; - win_pos[2+win_pos_offset] = vec4Tmp1[2]; - - return true; - } - - /** * Map window coordinates to object coordinates. * <p> * Traditional <code>gluUnProject</code> implementation. @@ -1180,12 +662,12 @@ public final class FloatUtil { * @param mat4Tmp2 16 component matrix for temp storage * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) */ - public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz, - final float[] modelMatrix, final int modelMatrix_offset, - final float[] projMatrix, final int projMatrix_offset, - final int[] viewport, final int viewport_offset, - final float[] obj_pos, final int obj_pos_offset, - final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2) { + public static boolean mapWinToObj(final float winx, final float winy, final float winz, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final float[] obj_pos, final int obj_pos_offset, + final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2) { // mat4Tmp1 = P x Mv multMatrix(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, mat4Tmp1, 0); @@ -1227,137 +709,6 @@ public final class FloatUtil { /** * Map window coordinates to object coordinates. * <p> - * Traditional <code>gluUnProject</code> implementation. - * </p> - * - * @param winx - * @param winy - * @param winz - * @param mat4PMvI inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv) - * @param viewport 4 component viewport vector - * @param viewport_offset - * @param obj_pos 3 component object coordinate, the result - * @param obj_pos_offset - * @param vec4Tmp1 4 component vector for temp storage - * @param vec4Tmp2 4 component vector for temp storage - * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) - */ - public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz, - final float[/*16*/] mat4PMvI, - final int[] viewport, final int viewport_offset, - final float[] obj_pos, final int obj_pos_offset, - final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { - vec4Tmp1[0] = winx; - vec4Tmp1[1] = winy; - vec4Tmp1[2] = winz; - vec4Tmp1[3] = 1.0f; - - // Map x and y from window coordinates - vec4Tmp1[0] = (vec4Tmp1[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; - vec4Tmp1[1] = (vec4Tmp1[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; - - // Map to range -1 to 1 - vec4Tmp1[0] = vec4Tmp1[0] * 2 - 1; - vec4Tmp1[1] = vec4Tmp1[1] * 2 - 1; - vec4Tmp1[2] = vec4Tmp1[2] * 2 - 1; - - // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 - multMatrixVec(mat4PMvI, vec4Tmp1, vec4Tmp2); - - if (vec4Tmp2[3] == 0.0) { - return false; - } - - vec4Tmp2[3] = 1.0f / vec4Tmp2[3]; - - obj_pos[0+obj_pos_offset] = vec4Tmp2[0] * vec4Tmp2[3]; - obj_pos[1+obj_pos_offset] = vec4Tmp2[1] * vec4Tmp2[3]; - obj_pos[2+obj_pos_offset] = vec4Tmp2[2] * vec4Tmp2[3]; - - return true; - } - - /** - * Map two window coordinates to two object coordinates, - * distinguished by their z component. - * - * @param winx - * @param winy - * @param winz1 - * @param winz2 - * @param mat4PMvI inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv) - * @param viewport 4 component viewport vector - * @param viewport_offset - * @param obj1_pos 3 component object coordinate, the result for winz1 - * @param obj1_pos_offset - * @param obj2_pos 3 component object coordinate, the result for winz2 - * @param obj2_pos_offset - * @param vec4Tmp1 4 component vector for temp storage - * @param vec4Tmp2 4 component vector for temp storage - * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) - */ - public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz1, final float winz2, - final float[/*16*/] mat4PMvI, - final int[] viewport, final int viewport_offset, - final float[] obj1_pos, final int obj1_pos_offset, - final float[] obj2_pos, final int obj2_pos_offset, - final float[/*4*/] vec4Tmp1, final float[/*4*/] vec4Tmp2) { - vec4Tmp1[0] = winx; - vec4Tmp1[1] = winy; - vec4Tmp1[3] = 1.0f; - - // Map x and y from window coordinates - vec4Tmp1[0] = (vec4Tmp1[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; - vec4Tmp1[1] = (vec4Tmp1[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset]; - - // Map to range -1 to 1 - vec4Tmp1[0] = vec4Tmp1[0] * 2 - 1; - vec4Tmp1[1] = vec4Tmp1[1] * 2 - 1; - - // - // winz1 - // - vec4Tmp1[2] = winz1; - vec4Tmp1[2] = vec4Tmp1[2] * 2 - 1; - - // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 - multMatrixVec(mat4PMvI, vec4Tmp1, vec4Tmp2); - - if (vec4Tmp2[3] == 0.0) { - return false; - } - - vec4Tmp2[3] = 1.0f / vec4Tmp2[3]; - - obj1_pos[0+obj1_pos_offset] = vec4Tmp2[0] * vec4Tmp2[3]; - obj1_pos[1+obj1_pos_offset] = vec4Tmp2[1] * vec4Tmp2[3]; - obj1_pos[2+obj1_pos_offset] = vec4Tmp2[2] * vec4Tmp2[3]; - - // - // winz2 - // - vec4Tmp1[2] = winz2; - vec4Tmp1[2] = vec4Tmp1[2] * 2 - 1; - - // object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2 - multMatrixVec(mat4PMvI, vec4Tmp1, vec4Tmp2); - - if (vec4Tmp2[3] == 0.0) { - return false; - } - - vec4Tmp2[3] = 1.0f / vec4Tmp2[3]; - - obj2_pos[0+obj2_pos_offset] = vec4Tmp2[0] * vec4Tmp2[3]; - obj2_pos[1+obj2_pos_offset] = vec4Tmp2[1] * vec4Tmp2[3]; - obj2_pos[2+obj2_pos_offset] = vec4Tmp2[2] * vec4Tmp2[3]; - - return true; - } - - /** - * Map window coordinates to object coordinates. - * <p> * Traditional <code>gluUnProject4</code> implementation. * </p> * @@ -1379,13 +730,13 @@ public final class FloatUtil { * @param mat4Tmp2 16 component matrix for temp storage * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) */ - public static boolean mapWinToObjCoords(final float winx, final float winy, final float winz, final float clipw, - final float[] modelMatrix, final int modelMatrix_offset, - final float[] projMatrix, final int projMatrix_offset, - final int[] viewport, final int viewport_offset, - final float near, final float far, - final float[] obj_pos, final int obj_pos_offset, - final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2) { + public static boolean mapWinToObj4(final float winx, final float winy, final float winz, final float clipw, + final float[] modelMatrix, final int modelMatrix_offset, + final float[] projMatrix, final int projMatrix_offset, + final int[] viewport, final int viewport_offset, + final float near, final float far, + final float[] obj_pos, final int obj_pos_offset, + final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2) { // mat4Tmp1 = P x Mv multMatrix(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, mat4Tmp1, 0); @@ -1397,7 +748,7 @@ public final class FloatUtil { mat4Tmp2[0] = winx; mat4Tmp2[1] = winy; mat4Tmp2[2] = winz; - mat4Tmp2[3] = 1.0f; + mat4Tmp2[3] = clipw; // Map x and y from window coordinates mat4Tmp2[0] = (mat4Tmp2[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset]; @@ -1417,8 +768,6 @@ public final class FloatUtil { return false; } - mat4Tmp2[3+raw_off] = 1.0f / mat4Tmp2[3+raw_off]; - obj_pos[0+obj_pos_offset] = mat4Tmp2[0+raw_off]; obj_pos[1+obj_pos_offset] = mat4Tmp2[1+raw_off]; obj_pos[2+obj_pos_offset] = mat4Tmp2[2+raw_off]; @@ -1427,67 +776,13 @@ public final class FloatUtil { return true; } - - /** - * Map two window coordinates w/ shared X/Y and distinctive Z - * to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i> - * using a {@link AABBox#getRayIntersection(Ray, float[]) bounding box}. - * <p> - * Notes for picking <i>winz0</i> and <i>winz1</i>: - * <ul> - * <li>see {@link #getZBufferEpsilon(int, float, float)}</li> - * <li>see {@link #getZBufferValue(int, float, float, float)}</li> - * <li>see {@link #getOrthoWinZ(float, float, float)}</li> - * </ul> - * </p> - * @param winx - * @param winy - * @param winz0 - * @param winz1 - * @param modelMatrix 4x4 modelview matrix - * @param modelMatrix_offset - * @param projMatrix 4x4 projection matrix - * @param projMatrix_offset - * @param viewport 4 component viewport vector - * @param viewport_offset - * @param ray storage for the resulting {@link Ray} - * @param mat4Tmp1 16 component matrix for temp storage - * @param mat4Tmp2 16 component matrix for temp storage - * @param vec4Tmp2 4 component vector for temp storage - * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity) - */ - public static boolean mapWinToRay(final float winx, final float winy, final float winz0, final float winz1, - final float[] modelMatrix, final int modelMatrix_offset, - final float[] projMatrix, final int projMatrix_offset, - final int[] viewport, final int viewport_offset, - final Ray ray, - final float[/*16*/] mat4Tmp1, final float[/*16*/] mat4Tmp2, final float[/*4*/] vec4Tmp2) { - // mat4Tmp1 = P x Mv - multMatrix(projMatrix, projMatrix_offset, modelMatrix, modelMatrix_offset, mat4Tmp1, 0); - - // mat4Tmp1 = Inv(P x Mv) - if ( null == invertMatrix(mat4Tmp1, mat4Tmp1) ) { - return false; - } - if( mapWinToObjCoords(winx, winy, winz0, winz1, mat4Tmp1, - viewport, viewport_offset, - ray.orig, 0, ray.dir, 0, - mat4Tmp2, vec4Tmp2) ) { - VectorUtil.normalizeVec3( VectorUtil.subVec3(ray.dir, ray.dir, ray.orig) ); - return true; - } else { - return false; - } - } - /** * Multiply matrix: [d] = [a] x [b] * @param a 4x4 matrix in column-major order * @param b 4x4 matrix in column-major order * @param d result a*b in column-major order - * @return given result matrix <i>d</i> for chaining */ - public static float[] multMatrix(final float[] a, final int a_off, final float[] b, final int b_off, final float[] d, final int d_off) { + public static void multMatrix(final float[] a, final int a_off, final float[] b, final int b_off, final float[] d, final int d_off) { final float b00 = b[b_off+0+0*4]; final float b10 = b[b_off+1+0*4]; final float b20 = b[b_off+2+0*4]; @@ -1540,8 +835,6 @@ public final class FloatUtil { d[d_off+3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; d[d_off+3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; d[d_off+3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; - - return d; } /** @@ -1612,9 +905,8 @@ public final class FloatUtil { * Multiply matrix: [a] = [a] x [b] * @param a 4x4 matrix in column-major order (also result) * @param b 4x4 matrix in column-major order - * @return given result matrix <i>a</i> for chaining */ - public static float[] multMatrix(final float[] a, final int a_off, final float[] b, final int b_off) { + public static void multMatrix(final float[] a, final int a_off, final float[] b, final int b_off) { final float b00 = b[b_off+0+0*4]; final float b10 = b[b_off+1+0*4]; final float b20 = b[b_off+2+0*4]; @@ -1667,8 +959,6 @@ public final class FloatUtil { a[a_off+3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ; a[a_off+3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ; a[a_off+3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ; - - return a; } /** @@ -1778,11 +1068,10 @@ public final class FloatUtil { * @param m_in_off * @param v_in 4-component column-vector * @param v_out m_in * v_in - * @return given result vector <i>v_out</i> for chaining */ - public static float[] multMatrixVec(final float[] m_in, final int m_in_off, - final float[] v_in, final int v_in_off, - final float[] v_out, final int v_out_off) { + public static void multMatrixVec(final float[] m_in, final int m_in_off, + final float[] v_in, final int v_in_off, + final float[] v_out, final int v_out_off) { // (one matrix row in column-major order) X (column vector) v_out[0 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off ] + v_in[1+v_in_off] * m_in[1*4+m_in_off ] + v_in[2+v_in_off] * m_in[2*4+m_in_off ] + v_in[3+v_in_off] * m_in[3*4+m_in_off ]; @@ -1798,8 +1087,31 @@ public final class FloatUtil { final int m_in_off_3 = 3+m_in_off; v_out[3 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_3] + v_in[1+v_in_off] * m_in[1*4+m_in_off_3] + v_in[2+v_in_off] * m_in[2*4+m_in_off_3] + v_in[3+v_in_off] * m_in[3*4+m_in_off_3]; + } - return v_out; + /** + * @param m_in 4x4 matrix in column-major order + * @param m_in_off + * @param v_in 4-component column-vector + * @param v_out m_in * v_in + */ + public static void multMatrixVec(final float[] m_in, final int m_in_off, + final float[] v_in, final float[] v_out) { + // (one matrix row in column-major order) X (column vector) + v_out[0] = v_in[0] * m_in[0*4+m_in_off ] + v_in[1] * m_in[1*4+m_in_off ] + + v_in[2] * m_in[2*4+m_in_off ] + v_in[3] * m_in[3*4+m_in_off ]; + + final int m_in_off_1 = 1+m_in_off; + v_out[1] = v_in[0] * m_in[0*4+m_in_off_1] + v_in[1] * m_in[1*4+m_in_off_1] + + v_in[2] * m_in[2*4+m_in_off_1] + v_in[3] * m_in[3*4+m_in_off_1]; + + final int m_in_off_2 = 2+m_in_off; + v_out[2] = v_in[0] * m_in[0*4+m_in_off_2] + v_in[1] * m_in[1*4+m_in_off_2] + + v_in[2] * m_in[2*4+m_in_off_2] + v_in[3] * m_in[3*4+m_in_off_2]; + + final int m_in_off_3 = 3+m_in_off; + v_out[3] = v_in[0] * m_in[0*4+m_in_off_3] + v_in[1] * m_in[1*4+m_in_off_3] + + v_in[2] * m_in[2*4+m_in_off_3] + v_in[3] * m_in[3*4+m_in_off_3]; } /** @@ -1845,46 +1157,29 @@ public final class FloatUtil { } /** - * Copy the named column of the given column-major matrix to v_out. - * <p> - * v_out may be 3 or 4 components long, hence the 4th row may not be stored. - * </p> - * @param m_in input column-major matrix - * @param m_in_off offset to input matrix - * @param column named column to copy - * @param v_out the column-vector storage, at least 3 components long - * @param v_out_off offset to storage + * Affine 3f-vector transformation by 4x4 matrix + * + * 4x4 matrix multiplication with 3-component vector, + * using {@code 1} for for {@code v_in[3]} and dropping {@code v_out[3]}, + * which shall be {@code 1}. + * + * @param m_in 4x4 matrix in column-major order + * @param m_in_off + * @param v_in 3-component column-vector + * @param v_out m_in * v_in, 3-component column-vector * @return given result vector <i>v_out</i> for chaining */ - public static float[] copyMatrixColumn(final float[] m_in, final int m_in_off, final int column, final float[] v_out, final int v_out_off) { - v_out[0+v_out_off]=m_in[0+column*4+m_in_off]; - v_out[1+v_out_off]=m_in[1+column*4+m_in_off]; - v_out[2+v_out_off]=m_in[2+column*4+m_in_off]; - if( v_out.length > 3+v_out_off ) { - v_out[3+v_out_off]=m_in[3+column*4+m_in_off]; - } - return v_out; - } + public static float[] multMatrixVec3(final float[] m_in, final float[] v_in, final float[] v_out) { + // (one matrix row in column-major order) X (column vector) + v_out[0] = v_in[0] * m_in[0*4 ] + v_in[1] * m_in[1*4 ] + + v_in[2] * m_in[2*4 ] + 1f * m_in[3*4 ]; + + v_out[1] = v_in[0] * m_in[0*4+1] + v_in[1] * m_in[1*4+1] + + v_in[2] * m_in[2*4+1] + 1f * m_in[3*4+1]; + + v_out[2] = v_in[0] * m_in[0*4+2] + v_in[1] * m_in[1*4+2] + + v_in[2] * m_in[2*4+2] + 1f * m_in[3*4+2]; - /** - * Copy the named row of the given column-major matrix to v_out. - * <p> - * v_out may be 3 or 4 components long, hence the 4th column may not be stored. - * </p> - * @param m_in input column-major matrix - * @param m_in_off offset to input matrix - * @param row named row to copy - * @param v_out the row-vector storage, at least 3 components long - * @param v_out_off offset to storage - * @return given result vector <i>v_out</i> for chaining - */ - public static float[] copyMatrixRow(final float[] m_in, final int m_in_off, final int row, final float[] v_out, final int v_out_off) { - v_out[0+v_out_off]=m_in[row+0*4+m_in_off]; - v_out[1+v_out_off]=m_in[row+1*4+m_in_off]; - v_out[2+v_out_off]=m_in[row+2*4+m_in_off]; - if( v_out.length > 3+v_out_off ) { - v_out[3+v_out_off]=m_in[row+3*4+m_in_off]; - } return v_out; } @@ -1908,11 +1203,11 @@ public final class FloatUtil { final int a0 = aOffset + a.position(); if(rowMajorOrder) { for(int c=0; c<columns; c++) { - sb.append( String.format((Locale)null, f+" ", a.get( a0 + row*columns + c ) ) ); + sb.append( String.format((Locale)null, f+", ", a.get( a0 + row*columns + c ) ) ); } } else { for(int r=0; r<columns; r++) { - sb.append( String.format((Locale)null, f+" ", a.get( a0 + row + r*rows ) ) ); + sb.append( String.format((Locale)null, f+", ", a.get( a0 + row + r*rows ) ) ); } } return sb; @@ -1936,11 +1231,11 @@ public final class FloatUtil { } if(rowMajorOrder) { for(int c=0; c<columns; c++) { - sb.append( String.format((Locale)null, f+" ", a[ aOffset + row*columns + c ] ) ); + sb.append( String.format((Locale)null, f+", ", a[ aOffset + row*columns + c ] ) ); } } else { for(int r=0; r<columns; r++) { - sb.append( String.format((Locale)null, f+" ", a[ aOffset + row + r*rows ] ) ); + sb.append( String.format((Locale)null, f+", ", a[ aOffset + row + r*rows ] ) ); } } return sb; @@ -1963,11 +1258,15 @@ public final class FloatUtil { sb = new StringBuilder(); } final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; + sb.append(prefix).append("{ "); for(int i=0; i<rows; i++) { - sb.append(prefix).append("[ "); + if( 0 < i ) { + sb.append(prefix).append(" "); + } matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); - sb.append("]").append(Platform.getNewline()); + sb.append(System.lineSeparator()); } + sb.append(prefix).append("}").append(System.lineSeparator()); return sb; } @@ -1988,71 +1287,15 @@ public final class FloatUtil { sb = new StringBuilder(); } final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; + sb.append(prefix).append("{ "); for(int i=0; i<rows; i++) { - sb.append(prefix).append("[ "); - matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); - sb.append("]").append(Platform.getNewline()); - } - return sb; - } - - /** - * @param sb optional passed StringBuilder instance to be used - * @param rowPrefix optional prefix for each row - * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} - * @param a 4x4 matrix in column major order (OpenGL) - * @param aOffset offset to <code>a</code>'s current position - * @param b 4x4 matrix in column major order (OpenGL) - * @param bOffset offset to <code>a</code>'s current position - * @param rows - * @param columns - * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) - * @return side by side representation - */ - public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f, - final FloatBuffer a, final int aOffset, final FloatBuffer b, final int bOffset, - final int rows, final int columns, final boolean rowMajorOrder) { - if(null == sb) { - sb = new StringBuilder(); - } - final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; - for(int i=0; i<rows; i++) { - sb.append(prefix).append("[ "); - matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); - sb.append("=?= "); - matrixRowToString(sb, f, b, bOffset, rows, columns, rowMajorOrder, i); - sb.append("]").append(Platform.getNewline()); - } - return sb; - } - - /** - * @param sb optional passed StringBuilder instance to be used - * @param rowPrefix optional prefix for each row - * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} - * @param a 4x4 matrix in column major order (OpenGL) - * @param aOffset offset to <code>a</code>'s current position - * @param b 4x4 matrix in column major order (OpenGL) - * @param bOffset offset to <code>a</code>'s current position - * @param rows - * @param columns - * @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL) - * @return side by side representation - */ - public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f, - final float[] a, final int aOffset, final float[] b, final int bOffset, - final int rows, final int columns, final boolean rowMajorOrder) { - if(null == sb) { - sb = new StringBuilder(); - } - final String prefix = ( null == rowPrefix ) ? "" : rowPrefix; - for(int i=0; i<rows; i++) { - sb.append(prefix).append("[ "); + if( 0 < i ) { + sb.append(prefix).append(" "); + } matrixRowToString(sb, f, a, aOffset, rows, columns, rowMajorOrder, i); - sb.append("=?= "); - matrixRowToString(sb, f, b, bOffset, rows, columns, rowMajorOrder, i); - sb.append("]").append(Platform.getNewline()); + sb.append(System.lineSeparator()); } + sb.append(prefix).append("}").append(System.lineSeparator()); return sb; } @@ -2121,6 +1364,16 @@ public final class FloatUtil { /** The value PI^2. */ public final static float SQUARED_PI = PI * PI; + /** Converts arc-degree to radians */ + public static float adegToRad(final float arc_degree) { + return arc_degree * PI / 180.0f; + } + + /** Converts radians to arc-degree */ + public static float radToADeg(final float rad) { + return rad * 180.0f / PI; + } + /** * Epsilon for floating point {@value}, as once computed via {@link #getMachineEpsilon()} on an AMD-64 CPU. * <p> @@ -2257,6 +1510,14 @@ public final class FloatUtil { return Math.abs(a) < epsilon; } + /** + * Return true if value is zero, i.e. it's absolute value < {@link #EPSILON}. + * @see #EPSILON + */ + public static boolean isZero(final float a) { + return Math.abs(a) < FloatUtil.EPSILON; + } + public static float abs(final float a) { return java.lang.Math.abs(a); } public static float pow(final float a, final float b) { return (float) java.lang.Math.pow(a, b); } @@ -2314,10 +1575,10 @@ public final class FloatUtil { /** * Returns orthogonal distance - * (1f/zNear-1f/orthoDist)/(1f/zNear-1f/zFar); + * (1f/zNear-1f/orthoZ) / (1f/zNear-1f/zFar); */ public static float getOrthoWinZ(final float orthoZ, final float zNear, final float zFar) { - return (1f/zNear-1f/orthoZ)/(1f/zNear-1f/zFar); + return (1f/zNear-1f/orthoZ) / (1f/zNear-1f/zFar); } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java b/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java deleted file mode 100644 index a080d4442..000000000 --- a/src/jogl/classes/com/jogamp/opengl/math/Matrix4.java +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Copyright 2014 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ - -package com.jogamp.opengl.math; - -import com.jogamp.opengl.GLException; -import com.jogamp.opengl.fixedfunc.GLMatrixFunc; - -import com.jogamp.opengl.util.PMVMatrix; - -/** - * Simple float array-backed float 4x4 matrix - * exposing {@link FloatUtil} matrix functionality in an object oriented manner. - * <p> - * Unlike {@link PMVMatrix}, this class only represents one single matrix - * without a complete {@link GLMatrixFunc} implementation, - * allowing this class to be more lightweight. - * </p> - * <p> - * Implementation is not mature - WIP and subject to change. - * </p> - */ -public class Matrix4 { - - public Matrix4() { - matrix = new float[16]; - matrixTxSx = new float[16]; - mat4Tmp1 = new float[16]; - vec4Tmp1 = new float[4]; - FloatUtil.makeIdentity(matrixTxSx); - loadIdentity(); - } - - public final float[] getMatrix() { - return matrix; - } - - public final void loadIdentity() { - FloatUtil.makeIdentity(matrix); - } - - /** - * Multiply matrix: [this] = [this] x [m] - * @param m 4x4 matrix in column-major order - */ - public final void multMatrix(final float[] m, final int m_offset) { - FloatUtil.multMatrix(matrix, 0, m, m_offset); - } - - /** - * Multiply matrix: [this] = [this] x [m] - * @param m 4x4 matrix in column-major order - */ - public final void multMatrix(final float[] m) { - FloatUtil.multMatrix(matrix, m); - } - - /** - * Multiply matrix: [this] = [this] x [m] - * @param m 4x4 matrix in column-major order - */ - public final void multMatrix(final Matrix4 m) { - FloatUtil.multMatrix(matrix, m.getMatrix()); - } - - /** - * @param v_in 4-component column-vector - * @param v_out this * v_in - */ - public final void multVec(final float[] v_in, final float[] v_out) { - FloatUtil.multMatrixVec(matrix, v_in, v_out); - } - - /** - * @param v_in 4-component column-vector - * @param v_out this * v_in - */ - public final void multVec(final float[] v_in, final int v_in_offset, final float[] v_out, final int v_out_offset) { - FloatUtil.multMatrixVec(matrix, 0, v_in, v_in_offset, v_out, v_out_offset); - } - - public final void translate(final float x, final float y, final float z) { - multMatrix(FloatUtil.makeTranslation(matrixTxSx, false, x, y, z)); - } - - public final void scale(final float x, final float y, final float z) { - multMatrix(FloatUtil.makeScale(matrixTxSx, false, x, y, z)); - } - - public final void rotate(final float angrad, final float x, final float y, final float z) { - multMatrix(FloatUtil.makeRotationAxis(mat4Tmp1, 0, angrad, x, y, z, vec4Tmp1)); - } - - /** - * Rotate the current matrix with the given {@link Quaternion}'s rotation {@link Quaternion#toMatrix(float[], int) matrix representation}. - */ - public final void rotate(final Quaternion quat) { - multMatrix(quat.toMatrix(mat4Tmp1, 0)); - } - - public final void transpose() { - System.arraycopy(matrix, 0, mat4Tmp1, 0, 16); - FloatUtil.transposeMatrix(mat4Tmp1, matrix); - } - - public final float determinant() { - return FloatUtil.matrixDeterminant(matrix); - } - - public final boolean invert() { - return null != FloatUtil.invertMatrix(matrix, matrix); - } - - public final void makeOrtho(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { - multMatrix( FloatUtil.makeOrtho(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar) ); - } - - /** - * @param left - * @param right - * @param bottom - * @param top - * @param zNear - * @param zFar - * @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear} - * or {@code left == right}, or {@code bottom == top}. - * @see FloatUtil#makeFrustum(float[], int, boolean, float, float, float, float, float, float) - */ - public final void makeFrustum(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) throws GLException { - multMatrix( FloatUtil.makeFrustum(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar) ); - } - - /** - * @param fovy_rad - * @param aspect - * @param zNear - * @param zFar - * @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear} - * @see FloatUtil#makePerspective(float[], int, boolean, float, float, float, float) - */ - public final void makePerspective(final float fovy_rad, final float aspect, final float zNear, final float zFar) throws GLException { - multMatrix( FloatUtil.makePerspective(mat4Tmp1, 0, true, fovy_rad, aspect, zNear, zFar) ); - } - - private final float[] matrix, matrixTxSx; - private final float[] mat4Tmp1, vec4Tmp1; -} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Matrix4f.java b/src/jogl/classes/com/jogamp/opengl/math/Matrix4f.java new file mode 100644 index 000000000..77971b72d --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Matrix4f.java @@ -0,0 +1,2153 @@ +/** + * Copyright 2014-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +import java.nio.FloatBuffer; + +import com.jogamp.opengl.math.geom.AABBox; +import com.jogamp.opengl.math.geom.Frustum; +import com.jogamp.opengl.math.geom.Frustum.Plane; + +/** + * Basic 4x4 float matrix implementation using fields for intensive use-cases (host operations). + * <p> + * Implementation covers {@link FloatUtil} matrix functionality, exposed in an object oriented manner. + * </p> + * <p> + * Unlike {@link com.jogamp.opengl.util.PMVMatrix PMVMatrix}, this class only represents one single matrix + * without a complete {@link com.jogamp.opengl.fixedfunc.GLMatrixFunc GLMatrixFunc} implementation. + * </p> + * <p> + * For array operations the layout is expected in column-major order + * matching OpenGL's implementation, illustration: + * <pre> + Row-Major Column-Major (OpenGL): + + | 0 1 2 tx | + | | + | 4 5 6 ty | + M = | | + | 8 9 10 tz | + | | + | 12 13 14 15 | + + R C R C + m[0*4+3] = tx; m[0+4*3] = tx; + m[1*4+3] = ty; m[1+4*3] = ty; + m[2*4+3] = tz; m[2+4*3] = tz; + + RC (std subscript order) RC (std subscript order) + m03 = tx; m03 = tx; + m13 = ty; m13 = ty; + m23 = tz; m23 = tz; + + * </pre> + * </p> + * <p> + * <ul> + * <li><a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html">Matrix-FAQ</a></li> + * <li><a href="https://en.wikipedia.org/wiki/Matrix_%28mathematics%29">Wikipedia-Matrix</a></li> + * <li><a href="http://www.euclideanspace.com/maths/algebra/matrix/index.htm">euclideanspace.com-Matrix</a></li> + * </ul> + * </p> + * <p> + * Implementation utilizes unrolling of small vertices and matrices wherever possible + * while trying to access memory in a linear fashion for performance reasons, see: + * <ul> + * <li><a href="https://lessthanoptimal.github.io/Java-Matrix-Benchmark/">java-matrix-benchmark</a></li> + * <li><a href="https://github.com/lessthanoptimal/ejml">EJML Efficient Java Matrix Library</a></li> + * </ul> + * </p> + * @see com.jogamp.opengl.util.PMVMatrix + * @see FloatUtil + */ +public class Matrix4f { + + /** + * Creates a new identity matrix. + */ + public Matrix4f() { + m00 = m11 = m22 = m33 = 1.0f; + // remaining fields have default init to zero + } + + /** + * Creates a new matrix copying the values of the given {@code src} matrix. + */ + public Matrix4f(final Matrix4f src) { + load(src); + } + + /** + * Creates a new matrix based on given float[4*4] column major order. + * @param m 4x4 matrix in column-major order + */ + public Matrix4f(final float[] m) { + load(m); + } + + /** + * Creates a new matrix based on given float[4*4] column major order. + * @param m 4x4 matrix in column-major order + * @param m_off offset for matrix {@code m} + */ + public Matrix4f(final float[] m, final int m_off) { + load(m, m_off); + } + + /** + * Creates a new matrix based on given {@link FloatBuffer} 4x4 column major order. + * @param m 4x4 matrix in column-major order + */ + public Matrix4f(final FloatBuffer m) { + load(m); + } + + // + // Write to Matrix via set(..) or load(..) + // + + /** Sets the {@code i}th component with float {@code v} 0 <= i < 16 */ + public void set(final int i, final float v) { + switch (i) { + case 0+4*0: m00 = v; break; + case 1+4*0: m10 = v; break; + case 2+4*0: m20 = v; break; + case 3+4*0: m30 = v; break; + + case 0+4*1: m01 = v; break; + case 1+4*1: m11 = v; break; + case 2+4*1: m21 = v; break; + case 3+4*1: m31 = v; break; + + case 0+4*2: m02 = v; break; + case 1+4*2: m12 = v; break; + case 2+4*2: m22 = v; break; + case 3+4*2: m32 = v; break; + + case 0+4*3: m03 = v; break; + case 1+4*3: m13 = v; break; + case 2+4*3: m23 = v; break; + case 3+4*3: m33 = v; break; + default: throw new IndexOutOfBoundsException(); + } + } + + /** + * Set this matrix to identity. + * <pre> + Translation matrix (Column Order): + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 + 0 0 0 1 + * </pre> + * @return this matrix for chaining + */ + public final Matrix4f loadIdentity() { + m00 = m11 = m22 = m33 = 1.0f; + m01 = m02 = m03 = + m10 = m12 = m13 = + m20 = m21 = m23 = + m30 = m31 = m32 = 0.0f; + return this; + } + + /** + * Load the values of the given matrix {@code b} to this matrix. + * @param src the source values + * @return this matrix for chaining + */ + public Matrix4f load(final Matrix4f src) { + m00 = src.m00; m10 = src.m10; m20 = src.m20; m30 = src.m30; + m01 = src.m01; m11 = src.m11; m21 = src.m21; m31 = src.m31; + m02 = src.m02; m12 = src.m12; m22 = src.m22; m32 = src.m32; + m03 = src.m03; m13 = src.m13; m23 = src.m23; m33 = src.m33; + return this; + } + + /** + * Load the values of the given matrix {@code src} to this matrix. + * @param src 4x4 matrix float[16] in column-major order + * @return this matrix for chaining + */ + public Matrix4f load(final float[] src) { + m00 = src[0+0*4]; // column 0 + m10 = src[1+0*4]; + m20 = src[2+0*4]; + m30 = src[3+0*4]; + m01 = src[0+1*4]; // column 1 + m11 = src[1+1*4]; + m21 = src[2+1*4]; + m31 = src[3+1*4]; + m02 = src[0+2*4]; // column 2 + m12 = src[1+2*4]; + m22 = src[2+2*4]; + m32 = src[3+2*4]; + m03 = src[0+3*4]; // column 3 + m13 = src[1+3*4]; + m23 = src[2+3*4]; + m33 = src[3+3*4]; + return this; + } + + /** + * Load the values of the given matrix {@code src} to this matrix. + * @param src 4x4 matrix float[16] in column-major order + * @param src_off offset for matrix {@code src} + * @return this matrix for chaining + */ + public Matrix4f load(final float[] src, final int src_off) { + m00 = src[src_off+0+0*4]; + m10 = src[src_off+1+0*4]; + m20 = src[src_off+2+0*4]; + m30 = src[src_off+3+0*4]; + m01 = src[src_off+0+1*4]; + m11 = src[src_off+1+1*4]; + m21 = src[src_off+2+1*4]; + m31 = src[src_off+3+1*4]; + m02 = src[src_off+0+2*4]; + m12 = src[src_off+1+2*4]; + m22 = src[src_off+2+2*4]; + m32 = src[src_off+3+2*4]; + m03 = src[src_off+0+3*4]; + m13 = src[src_off+1+3*4]; + m23 = src[src_off+2+3*4]; + m33 = src[src_off+3+3*4]; + return this; + } + + /** + * Load the values of the given matrix {@code src} to this matrix. + * <p> + * Implementation uses relative {@link FloatBuffer#get()}, + * hence caller may want to issue {@link FloatBuffer#reset()} thereafter. + * </p> + * @param src 4x4 matrix {@link FloatBuffer} in column-major order + * @return this matrix for chaining + */ + public Matrix4f load(final FloatBuffer src) { + m00 = src.get(); + m10 = src.get(); + m20 = src.get(); + m30 = src.get(); + m01 = src.get(); + m11 = src.get(); + m21 = src.get(); + m31 = src.get(); + m02 = src.get(); + m12 = src.get(); + m22 = src.get(); + m32 = src.get(); + m03 = src.get(); + m13 = src.get(); + m23 = src.get(); + m33 = src.get(); + return this; + } + + // + // Read out Matrix via get(..) + // + + /** Gets the {@code i}th component, 0 <= i < 16 */ + public float get(final int i) { + switch (i) { + case 0+4*0: return m00; + case 1+4*0: return m10; + case 2+4*0: return m20; + case 3+4*0: return m30; + + case 0+4*1: return m01; + case 1+4*1: return m11; + case 2+4*1: return m21; + case 3+4*1: return m31; + + case 0+4*2: return m02; + case 1+4*2: return m12; + case 2+4*2: return m22; + case 3+4*2: return m32; + + case 0+4*3: return m03; + case 1+4*3: return m13; + case 2+4*3: return m23; + case 3+4*3: return m33; + + default: throw new IndexOutOfBoundsException(); + } + } + + /** + * Get the named column of the given column-major matrix to v_out. + * @param column named column to copy + * @param v_out the column-vector storage + * @return given result vector <i>v_out</i> for chaining + */ + public Vec4f getColumn(final int column, final Vec4f v_out) { + v_out.set( get(0+column*4), + get(1+column*4), + get(2+column*4), + get(3+column*4) ); + return v_out; + } + + /** + * Get the named column of the given column-major matrix to v_out. + * @param column named column to copy + * @param v_out the column-vector storage + * @return given result vector <i>v_out</i> for chaining + */ + public Vec3f getColumn(final int column, final Vec3f v_out) { + v_out.set( get(0+column*4), + get(1+column*4), + get(2+column*4) ); + return v_out; + } + + /** + * Get the named row of the given column-major matrix to v_out. + * @param row named row to copy + * @param v_out the row-vector storage + * @return given result vector <i>v_out</i> for chaining + */ + public Vec4f getRow(final int row, final Vec4f v_out) { + v_out.set( get(row+0*4), + get(row+1*4), + get(row+2*4), + get(row+3*4) ); + return v_out; + } + + /** + * Get the named row of the given column-major matrix to v_out. + * @param row named row to copy + * @param v_out the row-vector storage + * @return given result vector <i>v_out</i> for chaining + */ + public Vec3f getRow(final int row, final Vec3f v_out) { + v_out.set( get(row+0*4), + get(row+1*4), + get(row+2*4) ); + return v_out; + } + + /** + * Get this matrix into the given float[16] array at {@code dst_off} in column major order. + * + * @param dst float[16] array storage in column major order + * @param dst_off offset + * @return {@code dst} for chaining + */ + public float[] get(final float[] dst, final int dst_off) { + dst[dst_off+0+0*4] = m00; + dst[dst_off+1+0*4] = m10; + dst[dst_off+2+0*4] = m20; + dst[dst_off+3+0*4] = m30; + dst[dst_off+0+1*4] = m01; + dst[dst_off+1+1*4] = m11; + dst[dst_off+2+1*4] = m21; + dst[dst_off+3+1*4] = m31; + dst[dst_off+0+2*4] = m02; + dst[dst_off+1+2*4] = m12; + dst[dst_off+2+2*4] = m22; + dst[dst_off+3+2*4] = m32; + dst[dst_off+0+3*4] = m03; + dst[dst_off+1+3*4] = m13; + dst[dst_off+2+3*4] = m23; + dst[dst_off+3+3*4] = m33; + return dst; + } + + /** + * Get this matrix into the given float[16] array in column major order. + * + * @param dst float[16] array storage in column major order + * @return {@code dst} for chaining + */ + public float[] get(final float[] dst) { + dst[0+0*4] = m00; + dst[1+0*4] = m10; + dst[2+0*4] = m20; + dst[3+0*4] = m30; + dst[0+1*4] = m01; + dst[1+1*4] = m11; + dst[2+1*4] = m21; + dst[3+1*4] = m31; + dst[0+2*4] = m02; + dst[1+2*4] = m12; + dst[2+2*4] = m22; + dst[3+2*4] = m32; + dst[0+3*4] = m03; + dst[1+3*4] = m13; + dst[2+3*4] = m23; + dst[3+3*4] = m33; + return dst; + } + + /** + * Get this matrix into the given {@link FloatBuffer} in column major order. + * <p> + * Implementation uses relative {@link FloatBuffer#put(float)}, + * hence caller may want to issue {@link FloatBuffer#reset()} thereafter. + * </p> + * + * @param dst {@link FloatBuffer} array storage in column major order + * @return {@code dst} for chaining + */ + public FloatBuffer get(final FloatBuffer dst) { + dst.put( m00 ); + dst.put( m10 ); + dst.put( m20 ); + dst.put( m30 ); + dst.put( m01 ); + dst.put( m11 ); + dst.put( m21 ); + dst.put( m31 ); + dst.put( m02 ); + dst.put( m12 ); + dst.put( m22 ); + dst.put( m32 ); + dst.put( m03 ); + dst.put( m13 ); + dst.put( m23 ); + dst.put( m33 ); + return dst; + } + + // + // Basic matrix operations + // + + /** + * Returns the determinant of this matrix + * @return the matrix determinant + */ + public float determinant() { + float ret = 0; + ret += m00 * ( + m11*(m22*m33 - m23*m32) - m12*(m21*m33 - m23*m31) + m13*(m21*m32 - m22*m31)); + ret -= m01 * ( + m10*(m22*m33 - m23*m32) - m12*(m20*m33 - m23*m30) + m13*(m20*m32 - m22*m30)); + ret += m02 * ( + m10*(m21*m33 - m23*m31) - m11*(m20*m33 - m23*m30) + m13*(m20*m31 - m21*m30)); + ret -= m03 * ( + m10*(m21*m32 - m22*m31) - m11*(m20*m32 - m22*m30) + m12*(m20*m31 - m21*m30)); + return ret; + } + + /** + * Transpose this matrix. + * + * @return this matrix for chaining + */ + public final Matrix4f transpose() { + float tmp; + + tmp = m10; + m10 = m01; + m01 = tmp; + + tmp = m20; + m20 = m02; + m02 = tmp; + + tmp = m30; + m30 = m03; + m03 = tmp; + + tmp = m21; + m21 = m12; + m12 = tmp; + + tmp = m31; + m31 = m13; + m13 = tmp; + + tmp = m32; + m32 = m23; + m23 = tmp; + + return this; + } + + /** + * Transpose the given {@code src} matrix into this matrix. + * + * @param src source 4x4 matrix + * @return this matrix (result) for chaining + */ + public final Matrix4f transpose(final Matrix4f src) { + if( src == this ) { + return transpose(); + } + m00 = src.m00; + m10 = src.m01; + m20 = src.m02; + m30 = src.m03; + + m01 = src.m10; + m11 = src.m11; + m21 = src.m12; + m31 = src.m13; + + m02 = src.m20; + m12 = src.m21; + m22 = src.m22; + m32 = src.m23; + + m03 = src.m30; + m13 = src.m31; + m23 = src.m32; + m33 = src.m33; + return this; + } + + /** + * Invert this matrix. + * @return false if this matrix is singular and inversion not possible, otherwise true + */ + public boolean invert() { + final float scale; + try { + scale = mulScale(); + } catch(final ArithmeticException aex) { + return false; // max was 0 + } + final float a00 = m00*scale; + final float a10 = m10*scale; + final float a20 = m20*scale; + final float a30 = m30*scale; + + final float a01 = m01*scale; + final float a11 = m11*scale; + final float a21 = m21*scale; + final float a31 = m31*scale; + + final float a02 = m02*scale; + final float a12 = m12*scale; + final float a22 = m22*scale; + final float a32 = m32*scale; + + final float a03 = m03*scale; + final float a13 = m13*scale; + final float a23 = m23*scale; + final float a33 = m33*scale; + + final float b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31); + final float b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30)); + final float b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30); + final float b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30)); + + final float b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31)); + final float b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30); + final float b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30)); + final float b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30); + + final float b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31); + final float b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30)); + final float b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30); + final float b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30)); + + final float b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21)); + final float b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20); + final float b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20)); + final float b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20); + + final float det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale; + if( 0 == det ) { + return false; + } + final float invdet = 1.0f / det; + + m00 = b00 * invdet; + m10 = b01 * invdet; + m20 = b02 * invdet; + m30 = b03 * invdet; + + m01 = b10 * invdet; + m11 = b11 * invdet; + m21 = b12 * invdet; + m31 = b13 * invdet; + + m02 = b20 * invdet; + m12 = b21 * invdet; + m22 = b22 * invdet; + m32 = b23 * invdet; + + m03 = b30 * invdet; + m13 = b31 * invdet; + m23 = b32 * invdet; + m33 = b33 * invdet; + return true; + } + + /** + * Invert the {@code src} matrix values into this matrix + * @param src the source matrix, which values are to be inverted + * @return false if {@code src} matrix is singular and inversion not possible, otherwise true + */ + public boolean invert(final Matrix4f src) { + final float scale; + try { + scale = src.mulScale(); + } catch(final ArithmeticException aex) { + return false; // max was 0 + } + final float a00 = src.m00*scale; + final float a10 = src.m10*scale; + final float a20 = src.m20*scale; + final float a30 = src.m30*scale; + + final float a01 = src.m01*scale; + final float a11 = src.m11*scale; + final float a21 = src.m21*scale; + final float a31 = src.m31*scale; + + final float a02 = src.m02*scale; + final float a12 = src.m12*scale; + final float a22 = src.m22*scale; + final float a32 = src.m32*scale; + + final float a03 = src.m03*scale; + final float a13 = src.m13*scale; + final float a23 = src.m23*scale; + final float a33 = src.m33*scale; + + final float b00 = + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31); + final float b01 = -( + a10*(a22*a33 - a23*a32) - a12*(a20*a33 - a23*a30) + a13*(a20*a32 - a22*a30)); + final float b02 = + a10*(a21*a33 - a23*a31) - a11*(a20*a33 - a23*a30) + a13*(a20*a31 - a21*a30); + final float b03 = -( + a10*(a21*a32 - a22*a31) - a11*(a20*a32 - a22*a30) + a12*(a20*a31 - a21*a30)); + + final float b10 = -( + a01*(a22*a33 - a23*a32) - a02*(a21*a33 - a23*a31) + a03*(a21*a32 - a22*a31)); + final float b11 = + a00*(a22*a33 - a23*a32) - a02*(a20*a33 - a23*a30) + a03*(a20*a32 - a22*a30); + final float b12 = -( + a00*(a21*a33 - a23*a31) - a01*(a20*a33 - a23*a30) + a03*(a20*a31 - a21*a30)); + final float b13 = + a00*(a21*a32 - a22*a31) - a01*(a20*a32 - a22*a30) + a02*(a20*a31 - a21*a30); + + final float b20 = + a01*(a12*a33 - a13*a32) - a02*(a11*a33 - a13*a31) + a03*(a11*a32 - a12*a31); + final float b21 = -( + a00*(a12*a33 - a13*a32) - a02*(a10*a33 - a13*a30) + a03*(a10*a32 - a12*a30)); + final float b22 = + a00*(a11*a33 - a13*a31) - a01*(a10*a33 - a13*a30) + a03*(a10*a31 - a11*a30); + final float b23 = -( + a00*(a11*a32 - a12*a31) - a01*(a10*a32 - a12*a30) + a02*(a10*a31 - a11*a30)); + + final float b30 = -( + a01*(a12*a23 - a13*a22) - a02*(a11*a23 - a13*a21) + a03*(a11*a22 - a12*a21)); + final float b31 = + a00*(a12*a23 - a13*a22) - a02*(a10*a23 - a13*a20) + a03*(a10*a22 - a12*a20); + final float b32 = -( + a00*(a11*a23 - a13*a21) - a01*(a10*a23 - a13*a20) + a03*(a10*a21 - a11*a20)); + final float b33 = + a00*(a11*a22 - a12*a21) - a01*(a10*a22 - a12*a20) + a02*(a10*a21 - a11*a20); + + final float det = (a00*b00 + a01*b01 + a02*b02 + a03*b03) / scale; + + if( 0 == det ) { + return false; + } + final float invdet = 1.0f / det; + + m00 = b00 * invdet; + m10 = b01 * invdet; + m20 = b02 * invdet; + m30 = b03 * invdet; + + m01 = b10 * invdet; + m11 = b11 * invdet; + m21 = b12 * invdet; + m31 = b13 * invdet; + + m02 = b20 * invdet; + m12 = b21 * invdet; + m22 = b22 * invdet; + m32 = b23 * invdet; + + m03 = b30 * invdet; + m13 = b31 * invdet; + m23 = b32 * invdet; + m33 = b33 * invdet; + return true; + } + + private final float mulScale() { + /** + // No Hotspot intrinsic Math.* optimization for at least Math.max(), + // hence this chunk is slower. + float max = Math.abs(m00); + + max = Math.max(max, Math.abs(m01)); + max = Math.max(max, Math.abs(m02)); + ... etc + */ + float a = Math.abs(m00); + float max = a; + a = Math.abs(m01); if( a > max ) max = a; + a = Math.abs(m02); if( a > max ) max = a; + a = Math.abs(m03); if( a > max ) max = a; + + a = Math.abs(m10); if( a > max ) max = a; + a = Math.abs(m11); if( a > max ) max = a; + a = Math.abs(m12); if( a > max ) max = a; + a = Math.abs(m13); if( a > max ) max = a; + + a = Math.abs(m20); if( a > max ) max = a; + a = Math.abs(m21); if( a > max ) max = a; + a = Math.abs(m22); if( a > max ) max = a; + a = Math.abs(m23); if( a > max ) max = a; + + a = Math.abs(m30); if( a > max ) max = a; + a = Math.abs(m31); if( a > max ) max = a; + a = Math.abs(m32); if( a > max ) max = a; + a = Math.abs(m33); if( a > max ) max = a; + + return 1.0f/max; + } + + /** + * Multiply matrix: [this] = [this] x [b] + * @param b 4x4 matrix + * @return this matrix for chaining + * @see #mul(Matrix4f, Matrix4f) + */ + public final Matrix4f mul(final Matrix4f b) { + // return mul(new Matrix4f(this), b); // <- roughly half speed + float ai0=m00; // row-0, m[0+0*4] + float ai1=m01; + float ai2=m02; + float ai3=m03; + m00 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ; + m01 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ; + m02 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ; + m03 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ; + + ai0=m10; //row-1, m[1+0*4] + ai1=m11; + ai2=m12; + ai3=m13; + m10 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ; + m11 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ; + m12 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ; + m13 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ; + + ai0=m20; // row-2, m[2+0*4] + ai1=m21; + ai2=m22; + ai3=m23; + m20 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ; + m21 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ; + m22 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ; + m23 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ; + + ai0=m30; // row-3, m[3+0*4] + ai1=m31; + ai2=m32; + ai3=m33; + m30 = ai0 * b.m00 + ai1 * b.m10 + ai2 * b.m20 + ai3 * b.m30 ; + m31 = ai0 * b.m01 + ai1 * b.m11 + ai2 * b.m21 + ai3 * b.m31 ; + m32 = ai0 * b.m02 + ai1 * b.m12 + ai2 * b.m22 + ai3 * b.m32 ; + m33 = ai0 * b.m03 + ai1 * b.m13 + ai2 * b.m23 + ai3 * b.m33 ; + return this; + } + + /** + * Multiply matrix: [this] = [a] x [b] + * @param a 4x4 matrix, can't be this matrix + * @param b 4x4 matrix, can't be this matrix + * @return this matrix for chaining + * @see #mul(Matrix4f) + */ + public final Matrix4f mul(final Matrix4f a, final Matrix4f b) { + // row-0, m[0+0*4] + m00 = a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30 ; + m01 = a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31 ; + m02 = a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32 ; + m03 = a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33 ; + + //row-1, m[1+0*4] + m10 = a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30 ; + m11 = a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31 ; + m12 = a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32 ; + m13 = a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33 ; + + // row-2, m[2+0*4] + m20 = a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30 ; + m21 = a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31 ; + m22 = a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32 ; + m23 = a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33 ; + + // row-3, m[3+0*4] + m30 = a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30 ; + m31 = a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31 ; + m32 = a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32 ; + m33 = a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 ; + + return this; + } + + /** + * @param v_in 4-component column-vector, can be v_out for in-place transformation + * @param v_out this * v_in + * @returns v_out for chaining + */ + public final Vec4f mulVec4f(final Vec4f v_in, final Vec4f v_out) { + // (one matrix row in column-major order) X (column vector) + final float x = v_in.x(), y = v_in.y(), z = v_in.z(), w = v_in.w(); + v_out.set( x * m00 + y * m01 + z * m02 + w * m03, + x * m10 + y * m11 + z * m12 + w * m13, + x * m20 + y * m21 + z * m22 + w * m23, + x * m30 + y * m31 + z * m32 + w * m33 ); + return v_out; + } + + /** + * @param v_inout 4-component column-vector input and output, i.e. in-place transformation + * @returns v_inout for chaining + */ + public final Vec4f mulVec4f(final Vec4f v_inout) { + // (one matrix row in column-major order) X (column vector) + final float x = v_inout.x(), y = v_inout.y(), z = v_inout.z(), w = v_inout.w(); + v_inout.set( x * m00 + y * m01 + z * m02 + w * m03, + x * m10 + y * m11 + z * m12 + w * m13, + x * m20 + y * m21 + z * m22 + w * m23, + x * m30 + y * m31 + z * m32 + w * m33 ); + return v_inout; + } + + /** + * Affine 3f-vector transformation by 4x4 matrix + * + * 4x4 matrix multiplication with 3-component vector, + * using {@code 1} for for {@code v_in.w()} and dropping {@code v_out.w()}, + * which shall be {@code 1}. + * + * @param v_in 3-component column-vector {@link Vec3f}, can be v_out for in-place transformation + * @param v_out m_in * v_in, 3-component column-vector {@link Vec3f} + * @returns v_out for chaining + */ + public final Vec3f mulVec3f(final Vec3f v_in, final Vec3f v_out) { + // (one matrix row in column-major order) X (column vector) + final float x = v_in.x(), y = v_in.y(), z = v_in.z(); + v_out.set( x * m00 + y * m01 + z * m02 + 1f * m03, + x * m10 + y * m11 + z * m12 + 1f * m13, + x * m20 + y * m21 + z * m22 + 1f * m23 ); + return v_out; + } + + /** + * Affine 3f-vector transformation by 4x4 matrix + * + * 4x4 matrix multiplication with 3-component vector, + * using {@code 1} for for {@code v_inout.w()} and dropping {@code v_inout.w()}, + * which shall be {@code 1}. + * + * @param v_inout 3-component column-vector {@link Vec3f} input and output, i.e. in-place transformation + * @returns v_inout for chaining + */ + public final Vec3f mulVec3f(final Vec3f v_inout) { + // (one matrix row in column-major order) X (column vector) + final float x = v_inout.x(), y = v_inout.y(), z = v_inout.z(); + v_inout.set( x * m00 + y * m01 + z * m02 + 1f * m03, + x * m10 + y * m11 + z * m12 + 1f * m13, + x * m20 + y * m21 + z * m22 + 1f * m23 ); + return v_inout; + } + + // + // Matrix setTo...(), affine + basic + // + + /** + * Set this matrix to translation. + * <pre> + Translation matrix (Column Order): + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 + x y z 1 + * </pre> + * @param x x-axis translate + * @param y y-axis translate + * @param z z-axis translate + * @return this matrix for chaining + */ + public final Matrix4f setToTranslation(final float x, final float y, final float z) { + m00 = m11 = m22 = m33 = 1.0f; + m03 = x; + m13 = y; + m23 = z; + m01 = m02 = + m10 = m12 = + m20 = m21 = + m30 = m31 = m32 = 0.0f; + return this; + } + + /** + * Set this matrix to translation. + * <pre> + Translation matrix (Column Order): + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 + x y z 1 + * </pre> + * @param t translate Vec3f + * @return this matrix for chaining + */ + public final Matrix4f setToTranslation(final Vec3f t) { + return setToTranslation(t.x(), t.y(), t.z()); + } + + /** + * Set this matrix to scale. + * <pre> + Scale matrix (Any Order): + x 0 0 0 + 0 y 0 0 + 0 0 z 0 + 0 0 0 1 + * </pre> + * @param x x-axis scale + * @param y y-axis scale + * @param z z-axis scale + * @return this matrix for chaining + */ + public final Matrix4f setToScale(final float x, final float y, final float z) { + m33 = 1.0f; + m00 = x; + m11 = y; + m22 = z; + m01 = m02 = m03 = + m10 = m12 = m13 = + m20 = m21 = m23 = + m30 = m31 = m32 = 0.0f; + return this; + } + + /** + * Set this matrix to scale. + * <pre> + Scale matrix (Any Order): + x 0 0 0 + 0 y 0 0 + 0 0 z 0 + 0 0 0 1 + * </pre> + * @param s scale Vec3f + * @return this matrix for chaining + */ + public final Matrix4f setToScale(final Vec3f s) { + return setToScale(s.x(), s.y(), s.z()); + } + + /** + * Set this matrix to rotation from the given axis and angle in radians. + * <pre> + Rotation matrix (Column Order): + xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0 + xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0 + xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0 + 0 0 0 1 + * </pre> + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a> + * @param ang_rad angle in radians + * @param x x of rotation axis + * @param y y of rotation axis + * @param z z of rotation axis + * @return this matrix for chaining + */ + public final Matrix4f setToRotationAxis(final float ang_rad, float x, float y, float z) { + final float c = FloatUtil.cos(ang_rad); + final float ic= 1.0f - c; + final float s = FloatUtil.sin(ang_rad); + + final Vec3f tmp = new Vec3f(x, y, z).normalize(); + x = tmp.x(); y = tmp.y(); z = tmp.z(); + + final float xy = x*y; + final float xz = x*z; + final float xs = x*s; + final float ys = y*s; + final float yz = y*z; + final float zs = z*s; + m00 = x*x*ic+c; + m10 = xy*ic+zs; + m20 = xz*ic-ys; + m30 = 0; + + m01 = xy*ic-zs; + m11 = y*y*ic+c; + m21 = yz*ic+xs; + m31 = 0; + + m02 = xz*ic+ys; + m12 = yz*ic-xs; + m22 = z*z*ic+c; + m32 = 0; + + m03 = 0f; + m13 = 0f; + m23 = 0f; + m33 = 1f; + + return this; + } + + /** + * Set this matrix to rotation from the given axis and angle in radians. + * <pre> + Rotation matrix (Column Order): + xx(1-c)+c xy(1-c)+zs xz(1-c)-ys 0 + xy(1-c)-zs yy(1-c)+c yz(1-c)+xs 0 + xz(1-c)+ys yz(1-c)-xs zz(1-c)+c 0 + 0 0 0 1 + * </pre> + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a> + * @param ang_rad angle in radians + * @param axis rotation axis + * @return this matrix for chaining + */ + public final Matrix4f setToRotationAxis(final float ang_rad, final Vec3f axis) { + return setToRotationAxis(ang_rad, axis.x(), axis.y(), axis.z()); + } + + /** + * Set this matrix to rotation from the given Euler rotation angles in radians. + * <p> + * The rotations are applied in the given order: + * <ul> + * <li>y - heading</li> + * <li>z - attitude</li> + * <li>x - bank</li> + * </ul> + * </p> + * @param bankX the Euler pitch angle in radians. (rotation about the X axis) + * @param headingY the Euler yaw angle in radians. (rotation about the Y axis) + * @param attitudeZ the Euler roll angle in radians. (rotation about the Z axis) + * @return this matrix for chaining + * <p> + * Implementation does not use Quaternion and hence is exposed to + * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a>, + * consider using {@link #setToRotation(Quaternion)}. + * </p> + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q36">Matrix-FAQ Q36</a> + * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a> + * @see #setToRotation(Quaternion) + */ + public Matrix4f setToRotationEuler(final float bankX, final float headingY, final float attitudeZ) { + // Assuming the angles are in radians. + final float ch = FloatUtil.cos(headingY); + final float sh = FloatUtil.sin(headingY); + final float ca = FloatUtil.cos(attitudeZ); + final float sa = FloatUtil.sin(attitudeZ); + final float cb = FloatUtil.cos(bankX); + final float sb = FloatUtil.sin(bankX); + + m00 = ch*ca; + m10 = sa; + m20 = -sh*ca; + m30 = 0; + + m01 = sh*sb - ch*sa*cb; + m11 = ca*cb; + m21 = sh*sa*cb + ch*sb; + m31 = 0; + + m02 = ch*sa*sb + sh*cb; + m12 = -ca*sb; + m22 = -sh*sa*sb + ch*cb; + m32 = 0; + + m03 = 0; + m13 = 0; + m23 = 0; + m33 = 1; + + return this; + } + + /** + * Set this matrix to rotation from the given Euler rotation angles in radians. + * <p> + * The rotations are applied in the given order: + * <ul> + * <li>y - heading</li> + * <li>z - attitude</li> + * <li>x - bank</li> + * </ul> + * </p> + * @param angradXYZ euler angle vector in radians holding x-bank, y-heading and z-attitude + * @return this quaternion for chaining. + * <p> + * Implementation does not use Quaternion and hence is exposed to + * <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q34">Gimbal-Lock</a>, + * consider using {@link #setToRotation(Quaternion)}. + * </p> + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q36">Matrix-FAQ Q36</a> + * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm">euclideanspace.com-eulerToMatrix</a> + * @see #setToRotation(Quaternion) + */ + public Matrix4f setToRotationEuler(final Vec3f angradXYZ) { + return setToRotationEuler(angradXYZ.x(), angradXYZ.y(), angradXYZ.z()); + } + + /** + * Set this matrix to rotation using the given Quaternion. + * <p> + * Implementation Details: + * <ul> + * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * <li> The fields [m00 .. m22] define the rotation</li> + * </ul> + * </p> + * + * @param q the Quaternion representing the rotation + * @return this matrix for chaining + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a> + * @see Quaternion#toMatrix(float[]) + * @see #getRotation() + */ + public final Matrix4f setToRotation(final Quaternion q) { + // pre-multiply scaled-reciprocal-magnitude to reduce multiplications + final float norm = q.magnitudeSquared(); + if ( FloatUtil.isZero(norm, FloatUtil.EPSILON) ) { + // identity matrix -> srecip = 0f + loadIdentity(); + return this; + } + final float srecip; + if ( FloatUtil.isEqual(1f, norm, FloatUtil.EPSILON) ) { + srecip = 2f; + } else { + srecip = 2.0f / norm; + } + + final float x = q.x(); + final float y = q.y(); + final float z = q.z(); + final float w = q.w(); + + final float xs = srecip * x; + final float ys = srecip * y; + final float zs = srecip * z; + + final float xx = x * xs; + final float xy = x * ys; + final float xz = x * zs; + final float xw = xs * w; + final float yy = y * ys; + final float yz = y * zs; + final float yw = ys * w; + final float zz = z * zs; + final float zw = zs * w; + + m00 = 1f - ( yy + zz ); + m01 = ( xy - zw ); + m02 = ( xz + yw ); + m03 = 0f; + + m10 = ( xy + zw ); + m11 = 1f - ( xx + zz ); + m12 = ( yz - xw ); + m13 = 0f; + + m20 = ( xz - yw ); + m21 = ( yz + xw ); + m22 = 1f - ( xx + yy ); + m23 = 0f; + + m30 = m31 = m32 = 0f; + m33 = 1f; + return this; + } + + /** + * Returns the rotation [m00 .. m22] fields converted to a Quaternion. + * @param res resulting Quaternion + * @return the resulting Quaternion for chaining. + * @see Quaternion#setFromMatrix(float, float, float, float, float, float, float, float, float) + * @see #setToRotation(Quaternion) + */ + public final Quaternion getRotation(final Quaternion res) { + res.setFromMatrix(m00, m01, m02, m10, m11, m12, m20, m21, m22); + return res; + } + + /** + * Set this matrix to orthogonal projection. + * <pre> + Ortho matrix (Column Order): + 2/dx 0 0 0 + 0 2/dy 0 0 + 0 0 2/dz 0 + tx ty tz 1 + * </pre> + * @param left + * @param right + * @param bottom + * @param top + * @param zNear + * @param zFar + * @return this matrix for chaining + */ + public Matrix4f setToOrtho(final float left, final float right, + final float bottom, final float top, + final float zNear, final float zFar) { + { + // m00 = m11 = m22 = m33 = 1f; + m10 = m20 = m30 = 0f; + m01 = m21 = m31 = 0f; + m02 = m12 = m32 = 0f; + // m03 = m13 = m23 = 0f; + } + final float dx=right-left; + final float dy=top-bottom; + final float dz=zFar-zNear; + final float tx=-1.0f*(right+left)/dx; + final float ty=-1.0f*(top+bottom)/dy; + final float tz=-1.0f*(zFar+zNear)/dz; + + m00 = 2.0f/dx; + m11 = 2.0f/dy; + m22 = -2.0f/dz; + + m03 = tx; + m13 = ty; + m23 = tz; + m33 = 1f; + + return this; + } + + /** + * Set this matrix to frustum. + * <pre> + Frustum matrix (Column Order): + 2*zNear/dx 0 0 0 + 0 2*zNear/dy 0 0 + A B C -1 + 0 0 D 0 + * </pre> + * @param left + * @param right + * @param bottom + * @param top + * @param zNear + * @param zFar + * @return this matrix for chaining + * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear} + * or {@code left == right}, or {@code bottom == top}. + */ + public Matrix4f setToFrustum(final float left, final float right, + final float bottom, final float top, + final float zNear, final float zFar) throws IllegalArgumentException { + if( zNear <= 0.0f || zFar <= zNear ) { + throw new IllegalArgumentException("Requirements zNear > 0 and zFar > zNear, but zNear "+zNear+", zFar "+zFar); + } + if( left == right || top == bottom) { + throw new IllegalArgumentException("GL_INVALID_VALUE: top,bottom and left,right must not be equal"); + } + { + // m00 = m11 = m22 = m33 = 1f; + m10 = m20 = m30 = 0f; + m01 = m21 = m31 = 0f; + m03 = m13 = 0f; + } + final float zNear2 = 2.0f*zNear; + final float dx=right-left; + final float dy=top-bottom; + final float dz=zFar-zNear; + final float A=(right+left)/dx; + final float B=(top+bottom)/dy; + final float C=-1.0f*(zFar+zNear)/dz; + final float D=-2.0f*(zFar*zNear)/dz; + + m00 = zNear2/dx; + m11 = zNear2/dy; + + m02 = A; + m12 = B; + m22 = C; + m32 = -1.0f; + + m23 = D; + m33 = 0f; + + return this; + } + + /** + * Set this matrix to perspective {@link #setToFrustum(float, float, float, float, float, float) frustum} projection. + * + * @param fovy_rad angle in radians + * @param aspect aspect ratio width / height + * @param zNear + * @param zFar + * @return this matrix for chaining + * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear} + * @see #setToFrustum(float, float, float, float, float, float) + */ + public Matrix4f setToPerspective(final float fovy_rad, final float aspect, final float zNear, final float zFar) throws IllegalArgumentException { + final float top = FloatUtil.tan(fovy_rad/2f) * zNear; // use tangent of half-fov ! + final float bottom = -1.0f * top; // -1f * fovhvTan.top * zNear + final float left = aspect * bottom; // aspect * -1f * fovhvTan.top * zNear + final float right = aspect * top; // aspect * fovhvTan.top * zNear + return setToFrustum(left, right, bottom, top, zNear, zFar); + } + + /** + * Set this matrix to perspective {@link #setToFrustum(float, float, float, float, float, float) frustum} projection. + * + * @param fovhv {@link FovHVHalves} field of view in both directions, may not be centered, either in radians or tangent + * @param zNear + * @param zFar + * @return this matrix for chaining + * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear} + * @see #setToFrustum(float, float, float, float, float, float) + * @see Frustum#updateByFovDesc(Matrix4f, com.jogamp.opengl.math.geom.Frustum.FovDesc) + */ + public Matrix4f setToPerspective(final FovHVHalves fovhv, final float zNear, final float zFar) throws IllegalArgumentException { + final FovHVHalves fovhvTan = fovhv.toTangents(); // use tangent of half-fov ! + final float top = fovhvTan.top * zNear; + final float bottom = -1.0f * fovhvTan.bottom * zNear; + final float left = -1.0f * fovhvTan.left * zNear; + final float right = fovhvTan.right * zNear; + return setToFrustum(left, right, bottom, top, zNear, zFar); + } + + /** + * Calculate the frustum planes in world coordinates + * using this premultiplied P*MV (column major order) matrix. + * <p> + * Frustum plane's normals will point to the inside of the viewing frustum, + * as required by this class. + * </p> + * <p> + * Usually called by {@link Frustum#updateFrustumPlanes(Matrix4f)}. + * </p> + */ + public void updateFrustumPlanes(final Frustum frustum) { + // Left: a = m41 + m11, b = m42 + m12, c = m43 + m13, d = m44 + m14 - [1..4] column-major + // Left: a = m30 + m00, b = m31 + m01, c = m32 + m02, d = m33 + m03 - [0..3] column-major + { + final Frustum.Plane p = frustum.getPlanes()[Frustum.LEFT]; + final Vec3f p_n = p.n; + p_n.set( m30 + m00, + m31 + m01, + m32 + m02 ); + p.d = m33 + m03; + } + + // Right: a = m41 - m11, b = m42 - m12, c = m43 - m13, d = m44 - m14 - [1..4] column-major + // Right: a = m30 - m00, b = m31 - m01, c = m32 - m02, d = m33 - m03 - [0..3] column-major + { + final Frustum.Plane p = frustum.getPlanes()[Frustum.RIGHT]; + final Vec3f p_n = p.n; + p_n.set( m30 - m00, + m31 - m01, + m32 - m02 ); + p.d = m33 - m03; + } + + // Bottom: a = m41m21, b = m42m22, c = m43m23, d = m44m24 - [1..4] column-major + // Bottom: a = m30m10, b = m31m11, c = m32m12, d = m33m13 - [0..3] column-major + { + final Frustum.Plane p = frustum.getPlanes()[Frustum.BOTTOM]; + final Vec3f p_n = p.n; + p_n.set( m30 + m10, + m31 + m11, + m32 + m12 ); + p.d = m33 + m13; + } + + // Top: a = m41 - m21, b = m42 - m22, c = m43 - m23, d = m44 - m24 - [1..4] column-major + // Top: a = m30 - m10, b = m31 - m11, c = m32 - m12, d = m33 - m13 - [0..3] column-major + { + final Frustum.Plane p = frustum.getPlanes()[Frustum.TOP]; + final Vec3f p_n = p.n; + p_n.set( m30 - m10, + m31 - m11, + m32 - m12 ); + p.d = m33 - m13; + } + + // Near: a = m41m31, b = m42m32, c = m43m33, d = m44m34 - [1..4] column-major + // Near: a = m30m20, b = m31m21, c = m32m22, d = m33m23 - [0..3] column-major + { + final Frustum.Plane p = frustum.getPlanes()[Frustum.NEAR]; + final Vec3f p_n = p.n; + p_n.set( m30 + m20, + m31 + m21, + m32 + m22 ); + p.d = m33 + m23; + } + + // Far: a = m41 - m31, b = m42 - m32, c = m43 - m33, d = m44 - m34 - [1..4] column-major + // Far: a = m30 - m20, b = m31 - m21, c = m32m22, d = m33m23 - [0..3] column-major + { + final Frustum.Plane p = frustum.getPlanes()[Frustum.FAR]; + final Vec3f p_n = p.n; + p_n.set( m30 - m20, + m31 - m21, + m32 - m22 ); + p.d = m33 - m23; + } + + // Normalize all planes + for (int i = 0; i < 6; ++i) { + final Plane p = frustum.getPlanes()[i]; + final Vec3f p_n = p.n; + final float invLen = 1f / p_n.length(); + p_n.scale(invLen); + p.d *= invLen; + } + } + + /** + * Set this matrix to the <i>look-at</i> matrix based on given parameters. + * <p> + * Consist out of two matrix multiplications: + * <pre> + * <b>R</b> = <b>L</b> x <b>T</b>, + * with <b>L</b> for <i>look-at</i> matrix and + * <b>T</b> for eye translation. + * + * Result <b>R</b> can be utilized for <i>projection or modelview</i> multiplication, i.e. + * <b>M</b> = <b>M</b> x <b>R</b>, + * with <b>M</b> being the <i>projection or modelview</i> matrix. + * </pre> + * </p> + * @param eye 3 component eye vector + * @param center 3 component center vector + * @param up 3 component up vector + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public Matrix4f setToLookAt(final Vec3f eye, final Vec3f center, final Vec3f up, final Matrix4f tmp) { + // normalized forward! + final Vec3f fwd = new Vec3f( center.x() - eye.x(), + center.y() - eye.y(), + center.z() - eye.z() ).normalize(); + + /* Side = forward x up, normalized */ + final Vec3f side = fwd.cross(up).normalize(); + + /* Recompute up as: up = side x forward */ + final Vec3f up2 = side.cross(fwd); + + m00 = side.x(); + m10 = up2.x(); + m20 = -fwd.x(); + m30 = 0; + + m01 = side.y(); + m11 = up2.y(); + m21 = -fwd.y(); + m31 = 0; + + m02 = side.z(); + m12 = up2.z(); + m22 = -fwd.z(); + m32 = 0; + + m03 = 0; + m13 = 0; + m23 = 0; + m33 = 1; + + return mul( tmp.setToTranslation( -eye.x(), -eye.y(), -eye.z() ) ); + } + + /** + * Set this matrix to the <i>pick</i> matrix based on given parameters. + * <p> + * Traditional <code>gluPickMatrix</code> implementation. + * </p> + * <p> + * Consist out of two matrix multiplications: + * <pre> + * <b>R</b> = <b>T</b> x <b>S</b>, + * with <b>T</b> for viewport translation matrix and + * <b>S</b> for viewport scale matrix. + * + * Result <b>R</b> can be utilized for <i>projection</i> multiplication, i.e. + * <b>P</b> = <b>P</b> x <b>R</b>, + * with <b>P</b> being the <i>projection</i> matrix. + * </pre> + * </p> + * <p> + * To effectively use the generated pick matrix for picking, + * call {@link #setToPick(float, float, float, float, Recti, Matrix4f) setToPick(..)} + * and multiply a {@link #setToPerspective(float, float, float, float) custom perspective matrix} + * by this pick matrix. Then you may load the result onto the perspective matrix stack. + * </p> + * @param x the center x-component of a picking region in window coordinates + * @param y the center y-component of a picking region in window coordinates + * @param deltaX the width of the picking region in window coordinates. + * @param deltaY the height of the picking region in window coordinates. + * @param viewport Rect4i viewport + * @param mat4Tmp temp storage + * @return this matrix for chaining or {@code null} if either delta value is <= zero. + */ + public Matrix4f setToPick(final float x, final float y, final float deltaX, final float deltaY, + final Recti viewport, final Matrix4f mat4Tmp) { + if (deltaX <= 0 || deltaY <= 0) { + return null; + } + /* Translate and scale the picked region to the entire window */ + setToTranslation( ( viewport.width() - 2 * ( x - viewport.x() ) ) / deltaX, + ( viewport.height() - 2 * ( y - viewport.y() ) ) / deltaY, + 0); + mat4Tmp.setToScale( viewport.width() / deltaX, viewport.height() / deltaY, 1.0f ); + return mul(mat4Tmp); + } + + // + // Matrix affine operations using setTo..() + // + + /** + * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(float, float, float, float) axis-rotation matrix}. + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a> + * @param angrad angle in radians + * @param x x of rotation axis + * @param y y of rotation axis + * @param z z of rotation axis + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f rotate(final float ang_rad, final float x, final float y, final float z, final Matrix4f tmp) { + return mul( tmp.setToRotationAxis(ang_rad, x, y, z) ); + } + + /** + * Rotate this matrix about give axis and angle in radians, i.e. multiply by {@link #setToRotationAxis(float, Vec3f) axis-rotation matrix}. + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q38">Matrix-FAQ Q38</a> + * @param angrad angle in radians + * @param axis rotation axis + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f rotate(final float ang_rad, final Vec3f axis, final Matrix4f tmp) { + return mul( tmp.setToRotationAxis(ang_rad, axis) ); + } + + /** + * Rotate this matrix with the given {@link Quaternion}, i.e. multiply by {@link #setToRotation(Quaternion) Quaternion's rotation matrix}. + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f rotate(final Quaternion quat, final Matrix4f tmp) { + return mul( tmp.setToRotation(quat) ); + } + + /** + * Translate this matrix, i.e. multiply by {@link #setToTranslation(float, float, float) translation matrix}. + * @param x x translation + * @param y y translation + * @param z z translation + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f translate(final float x, final float y, final float z, final Matrix4f tmp) { + return mul( tmp.setToTranslation(x, y, z) ); + } + + /** + * Translate this matrix, i.e. multiply by {@link #setToTranslation(Vec3f) translation matrix}. + * @param t translation Vec3f + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f translate(final Vec3f t, final Matrix4f tmp) { + return mul( tmp.setToTranslation(t) ); + } + + /** + * Scale this matrix, i.e. multiply by {@link #setToScale(float, float, float) scale matrix}. + * @param x x scale + * @param y y scale + * @param z z scale + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f scale(final float x, final float y, final float z, final Matrix4f tmp) { + return mul( tmp.setToScale(x, y, z) ); + } + + /** + * Scale this matrix, i.e. multiply by {@link #setToScale(float, float, float) scale matrix}. + * @param s scale for x-, y- and z-axis + * @param tmp temporary Matrix4f used for multiplication + * @return this matrix for chaining + */ + public final Matrix4f scale(final float s, final Matrix4f tmp) { + return mul( tmp.setToScale(s, s, s) ); + } + + // + // Matrix Stack + // + + /** + * Push the matrix to it's stack, while preserving this matrix values. + * @see #pop() + */ + public final void push() { + stack.push(this); + } + + /** + * Pop the current matrix from it's stack, replacing this matrix values. + * @see #push() + */ + public final void pop() { + stack.pop(this); + } + + // + // equals + // + + /** + * Equals check using a given {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @param epsilon consider using {@link FloatUtil#EPSILON} + * @return true if all components differ less than {@code epsilon}, otherwise false. + */ + public boolean isEqual(final Matrix4f o, final float epsilon) { + if( this == o ) { + return true; + } else { + return FloatUtil.isEqual(m00, o.m00, epsilon) && + FloatUtil.isEqual(m01, o.m01, epsilon) && + FloatUtil.isEqual(m02, o.m02, epsilon) && + FloatUtil.isEqual(m03, o.m03, epsilon) && + FloatUtil.isEqual(m10, o.m10, epsilon) && + FloatUtil.isEqual(m11, o.m11, epsilon) && + FloatUtil.isEqual(m12, o.m12, epsilon) && + FloatUtil.isEqual(m13, o.m13, epsilon) && + FloatUtil.isEqual(m20, o.m20, epsilon) && + FloatUtil.isEqual(m21, o.m21, epsilon) && + FloatUtil.isEqual(m22, o.m22, epsilon) && + FloatUtil.isEqual(m23, o.m23, epsilon) && + FloatUtil.isEqual(m30, o.m30, epsilon) && + FloatUtil.isEqual(m31, o.m31, epsilon) && + FloatUtil.isEqual(m32, o.m32, epsilon) && + FloatUtil.isEqual(m33, o.m33, epsilon); + } + } + + /** + * Equals check using {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @return true if all components differ less than {@link FloatUtil#EPSILON}, otherwise false. + */ + public boolean isEqual(final Matrix4f o) { + return isEqual(o, FloatUtil.EPSILON); + } + + @Override + public boolean equals(final Object o) { + if( o instanceof Matrix4f ) { + return isEqual((Matrix4f)o, FloatUtil.EPSILON); + } else { + return false; + } + } + + // + // Static multi Matrix ops + // + + /** + * Map object coordinates to window coordinates. + * <p> + * Traditional <code>gluProject</code> implementation. + * </p> + * + * @param obj object position, 3 component vector + * @param mMv modelview matrix + * @param mP projection matrix + * @param viewport Rect4i viewport + * @param winPos 3 component window coordinate, the result + * @return true if successful, otherwise false (z is 1) + */ + public static boolean mapObjToWin(final Vec3f obj, final Matrix4f mMv, final Matrix4f mP, + final Recti viewport, final Vec3f winPos) + { + final Vec4f vec4Tmp1 = new Vec4f(obj, 1f); + + // vec4Tmp2 = Mv * o + // rawWinPos = P * vec4Tmp2 + // rawWinPos = P * ( Mv * o ) + // rawWinPos = P * Mv * o + final Vec4f vec4Tmp2 = mMv.mulVec4f(vec4Tmp1, new Vec4f()); + final Vec4f rawWinPos = mP.mulVec4f(vec4Tmp2, vec4Tmp1); + + if (rawWinPos.w() == 0.0f) { + return false; + } + + final float s = ( 1.0f / rawWinPos.w() ) * 0.5f; + + // Map x, y and z to range 0-1 (w is ignored) + rawWinPos.scale(s).add(0.5f, 0.5f, 0.5f, 0f); + + // Map x,y to viewport + winPos.set( rawWinPos.x() * viewport.width() + viewport.x(), + rawWinPos.y() * viewport.height() + viewport.y(), + rawWinPos.z() ); + + return true; + } + + /** + * Map object coordinates to window coordinates. + * <p> + * Traditional <code>gluProject</code> implementation. + * </p> + * + * @param obj object position, 3 component vector + * @param mPMv [projection] x [modelview] matrix, i.e. P x Mv + * @param viewport Rect4i viewport + * @param winPos 3 component window coordinate, the result + * @return true if successful, otherwise false (z is 1) + */ + public static boolean mapObjToWin(final Vec3f obj, final Matrix4f mPMv, + final Recti viewport, final Vec3f winPos) + { + final Vec4f vec4Tmp2 = new Vec4f(obj, 1f); + + // rawWinPos = P * Mv * o + final Vec4f rawWinPos = mPMv.mulVec4f(vec4Tmp2, new Vec4f()); + + if (rawWinPos.w() == 0.0f) { + return false; + } + + final float s = ( 1.0f / rawWinPos.w() ) * 0.5f; + + // Map x, y and z to range 0-1 (w is ignored) + rawWinPos.scale(s).add(0.5f, 0.5f, 0.5f, 0f); + + // Map x,y to viewport + winPos.set( rawWinPos.x() * viewport.width() + viewport.x(), + rawWinPos.y() * viewport.height() + viewport.y(), + rawWinPos.z() ); + + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param mMv 4x4 modelview matrix + * @param mP 4x4 projection matrix + * @param viewport Rect4i viewport + * @param objPos 3 component object coordinate, the result + * @param mat4Tmp 16 component matrix for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObj(final float winx, final float winy, final float winz, + final Matrix4f mMv, final Matrix4f mP, + final Recti viewport, + final Vec3f objPos, + final Matrix4f mat4Tmp) + { + // invPMv = Inv(P x Mv) + final Matrix4f invPMv = mat4Tmp.mul(mP, mMv); + if( !invPMv.invert() ) { + return false; + } + + final Vec4f winPos = new Vec4f(winx, winy, winz, 1f); + + // Map x and y from window coordinates + winPos.add(-viewport.x(), -viewport.y(), 0f, 0f).scale(1f/viewport.width(), 1f/viewport.height(), 1f, 1f); + + // Map to range -1 to 1 + winPos.scale(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f); + + // rawObjPos = Inv(P x Mv) * winPos + final Vec4f rawObjPos = invPMv.mulVec4f(winPos, new Vec4f()); + + if ( rawObjPos.w() == 0.0f ) { + return false; + } + objPos.set( rawObjPos.scale( 1f / rawObjPos.w() ) ); + + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false + * @param viewport Rect4i viewport + * @param objPos 3 component object coordinate, the result + * @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObj(final float winx, final float winy, final float winz, + final Matrix4f invPMv, + final Recti viewport, + final Vec3f objPos) + { + if( null == invPMv ) { + return false; + } + final Vec4f winPos = new Vec4f(winx, winy, winz, 1f); + + // Map x and y from window coordinates + winPos.add(-viewport.x(), -viewport.y(), 0f, 0f).scale(1f/viewport.width(), 1f/viewport.height(), 1f, 1f); + + // Map to range -1 to 1 + winPos.scale(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f); + + // rawObjPos = Inv(P x Mv) * winPos + final Vec4f rawObjPos = invPMv.mulVec4f(winPos, new Vec4f()); + + if ( rawObjPos.w() == 0.0f ) { + return false; + } + objPos.set( rawObjPos.scale( 1f / rawObjPos.w() ) ); + + return true; + } + + /** + * Map two window coordinates to two object coordinates, + * distinguished by their z component. + * <p> + * Traditional <code>gluUnProject</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz1 + * @param winz2 + * @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false + * @param viewport Rect4i viewport vector + * @param objPos1 3 component object coordinate, the result + * @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObj(final float winx, final float winy, final float winz1, final float winz2, + final Matrix4f invPMv, + final Recti viewport, + final Vec3f objPos1, final Vec3f objPos2) + { + if( null == invPMv ) { + return false; + } + final Vec4f winPos = new Vec4f(winx, winy, winz1, 1f); + + // Map x and y from window coordinates + winPos.add(-viewport.x(), -viewport.y(), 0f, 0f).scale(1f/viewport.width(), 1f/viewport.height(), 1f, 1f); + + // Map to range -1 to 1 + winPos.scale(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f); + + // rawObjPos = Inv(P x Mv) * winPos1 + final Vec4f rawObjPos = invPMv.mulVec4f(winPos, new Vec4f()); + + if ( rawObjPos.w() == 0.0f ) { + return false; + } + objPos1.set( rawObjPos.scale( 1f / rawObjPos.w() ) ); + + // + // winz2 + // + // Map Z to range -1 to 1 + winPos.setZ( winz2 * 2f - 1f ); + + // rawObjPos = Inv(P x Mv) * winPos2 + invPMv.mulVec4f(winPos, rawObjPos); + + if ( rawObjPos.w() == 0.0f ) { + return false; + } + objPos2.set( rawObjPos.scale( 1f / rawObjPos.w() ) ); + + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject4</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param clipw + * @param mMv 4x4 modelview matrix + * @param mP 4x4 projection matrix + * @param viewport Rect4i viewport vector + * @param near + * @param far + * @param obj_pos 4 component object coordinate, the result + * @param mat4Tmp 16 component matrix for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObj4(final float winx, final float winy, final float winz, final float clipw, + final Matrix4f mMv, final Matrix4f mP, + final Recti viewport, + final float near, final float far, + final Vec4f objPos, + final Matrix4f mat4Tmp) + { + // invPMv = Inv(P x Mv) + final Matrix4f invPMv = mat4Tmp.mul(mP, mMv); + if( !invPMv.invert() ) { + return false; + } + + final Vec4f winPos = new Vec4f(winx, winy, winz, clipw); + + // Map x and y from window coordinates + winPos.add(-viewport.x(), -viewport.y(), -near, 0f).scale(1f/viewport.width(), 1f/viewport.height(), 1f/(far-near), 1f); + + // Map to range -1 to 1 + winPos.scale(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f); + + // objPos = Inv(P x Mv) * winPos + invPMv.mulVec4f(winPos, objPos); + + if ( objPos.w() == 0.0f ) { + return false; + } + return true; + } + + /** + * Map window coordinates to object coordinates. + * <p> + * Traditional <code>gluUnProject4</code> implementation. + * </p> + * + * @param winx + * @param winy + * @param winz + * @param clipw + * @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false + * @param viewport Rect4i viewport vector + * @param near + * @param far + * @param obj_pos 4 component object coordinate, the result + * @return true if successful, otherwise false (null invert matrix, or becomes infinity due to zero z) + */ + public static boolean mapWinToObj4(final float winx, final float winy, final float winz, final float clipw, + final Matrix4f invPMv, + final Recti viewport, + final float near, final float far, + final Vec4f objPos) + { + if( null == invPMv ) { + return false; + } + final Vec4f winPos = new Vec4f(winx, winy, winz, clipw); + + // Map x and y from window coordinates + winPos.add(-viewport.x(), -viewport.y(), -near, 0f).scale(1f/viewport.width(), 1f/viewport.height(), 1f/(far-near), 1f); + + // Map to range -1 to 1 + winPos.scale(2f, 2f, 2f, 1f).add(-1f, -1f, -1f, 0f); + + // objPos = Inv(P x Mv) * winPos + invPMv.mulVec4f(winPos, objPos); + + if ( objPos.w() == 0.0f ) { + return false; + } + return true; + } + + /** + * Map two window coordinates w/ shared X/Y and distinctive Z + * to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i> + * using a {@link AABBox#getRayIntersection(Vec3f, Ray, float, boolean)}. + * <p> + * Notes for picking <i>winz0</i> and <i>winz1</i>: + * <ul> + * <li>see {@link FloatUtil#getZBufferEpsilon(int, float, float)}</li> + * <li>see {@link FloatUtil#getZBufferValue(int, float, float, float)}</li> + * <li>see {@link FloatUtil#getOrthoWinZ(float, float, float)}</li> + * </ul> + * </p> + * @param winx + * @param winy + * @param winz0 + * @param winz1 + * @param mMv 4x4 modelview matrix + * @param mP 4x4 projection matrix + * @param viewport Rect4i viewport + * @param ray storage for the resulting {@link Ray} + * @param mat4Tmp1 16 component matrix for temp storage + * @param mat4Tmp2 16 component matrix for temp storage + * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity) + */ + public static boolean mapWinToRay(final float winx, final float winy, final float winz0, final float winz1, + final Matrix4f mMv, final Matrix4f mP, + final Recti viewport, + final Ray ray, + final Matrix4f mat4Tmp1, final Matrix4f mat4Tmp2) { + // invPMv = Inv(P x Mv) + final Matrix4f invPMv = mat4Tmp1.mul(mP, mMv); + if( !invPMv.invert() ) { + return false; + } + + if( mapWinToObj(winx, winy, winz0, winz1, invPMv, viewport, ray.orig, ray.dir) ) { + ray.dir.sub(ray.orig).normalize(); + return true; + } else { + return false; + } + } + + /** + * Map two window coordinates w/ shared X/Y and distinctive Z + * to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i> + * using a {@link AABBox#getRayIntersection(Vec3f, Ray, float, boolean)}. + * <p> + * Notes for picking <i>winz0</i> and <i>winz1</i>: + * <ul> + * <li>see {@link FloatUtil#getZBufferEpsilon(int, float, float)}</li> + * <li>see {@link FloatUtil#getZBufferValue(int, float, float, float)}</li> + * <li>see {@link FloatUtil#getOrthoWinZ(float, float, float)}</li> + * </ul> + * </p> + * @param winx + * @param winy + * @param winz0 + * @param winz1 + * @param invPMv inverse [projection] x [modelview] matrix, i.e. Inv(P x Mv), if null method returns false + * @param viewport Rect4i viewport + * @param ray storage for the resulting {@link Ray} + * @return true if successful, otherwise false (null invert matrix, or becomes z is infinity) + */ + public static boolean mapWinToRay(final float winx, final float winy, final float winz0, final float winz1, + final Matrix4f invPMv, + final Recti viewport, + final Ray ray) { + if( mapWinToObj(winx, winy, winz0, winz1, invPMv, viewport, ray.orig, ray.dir) ) { + ray.dir.sub(ray.orig).normalize(); + return true; + } else { + return false; + } + } + + // + // String and internals + // + + /** + * @param sb optional passed StringBuilder instance to be used + * @param rowPrefix optional prefix for each row + * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} + * @return matrix string representation + */ + public StringBuilder toString(final StringBuilder sb, final String rowPrefix, final String f) { + final float[] tmp = new float[16]; + this.get(tmp); + return FloatUtil.matrixToString(sb, rowPrefix, f,tmp, 0, 4, 4, false /* rowMajorOrder */); + } + + @Override + public String toString() { + return toString(null, null, "%10.5f").toString(); + } + + private float m00, m10, m20, m30; + private float m01, m11, m21, m31; + private float m02, m12, m22, m32; + private float m03, m13, m23, m33; + + final Stack stack = new Stack(0, 16*16); // start w/ zero size, growSize is half GL-min size (32) + + private static class Stack { + private int position; + private float[] buffer; + private final int growSize; + + /** + * @param initialSize initial size + * @param growSize grow size if {@link #position()} is reached, maybe <code>0</code> + * in which case an {@link IndexOutOfBoundsException} is thrown. + */ + public Stack(final int initialSize, final int growSize) { + this.position = 0; + this.growSize = growSize; + this.buffer = new float[initialSize]; + } + + private final void growIfNecessary(final int length) throws IndexOutOfBoundsException { + if( position + length > buffer.length ) { + if( 0 >= growSize ) { + throw new IndexOutOfBoundsException("Out of fixed stack size: "+this); + } + final float[] newBuffer = + new float[buffer.length + growSize]; + System.arraycopy(buffer, 0, newBuffer, 0, position); + buffer = newBuffer; + } + } + + public final Matrix4f push(final Matrix4f src) throws IndexOutOfBoundsException { + growIfNecessary(16); + src.get(buffer, position); + position += 16; + return src; + } + + public final Matrix4f pop(final Matrix4f dest) throws IndexOutOfBoundsException { + position -= 16; + dest.load(buffer, position); + return dest; + } + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java b/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java index 430450767..a285774f8 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java +++ b/src/jogl/classes/com/jogamp/opengl/math/Quaternion.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -99,7 +99,7 @@ public class Quaternion { return FloatUtil.sqrt(magnitudeSQ); } - public final float getW() { + public final float w() { return w; } @@ -107,7 +107,7 @@ public class Quaternion { this.w = w; } - public final float getX() { + public final float x() { return x; } @@ -115,7 +115,7 @@ public class Quaternion { this.x = x; } - public final float getY() { + public final float y() { return y; } @@ -123,7 +123,7 @@ public class Quaternion { this.y = y; } - public final float getZ() { + public final float z() { return z; } @@ -142,15 +142,15 @@ public class Quaternion { * Returns the dot product of this quaternion with the given quaternion */ public final float dot(final Quaternion quat) { - return dot(quat.getX(), quat.getY(), quat.getZ(), quat.getW()); + return dot(quat.x(), quat.y(), quat.z(), quat.w()); } /** * Returns <code>true</code> if this quaternion has identity. * <p> * Implementation uses {@link FloatUtil#EPSILON epsilon} to compare - * {@link #getW() W} {@link FloatUtil#isEqual(float, float, float) against 1f} and - * {@link #getX() X}, {@link #getY() Y} and {@link #getZ() Z} + * {@link #w() W} {@link FloatUtil#isEqual(float, float, float) against 1f} and + * {@link #x() X}, {@link #y() Y} and {@link #z() Z} * {@link FloatUtil#isZero(float, float) against zero}. * </p> */ @@ -404,12 +404,12 @@ public class Quaternion { * </ul> * </p> * For details see {@link #rotateByEuler(float, float, float)}. - * @param angradXYZ euler angel array in radians + * @param angradXYZ euler angle array in radians * @return this quaternion for chaining. * @see #rotateByEuler(float, float, float) */ - public final Quaternion rotateByEuler(final float[] angradXYZ) { - return rotateByEuler(angradXYZ[0], angradXYZ[1], angradXYZ[2]); + public final Quaternion rotateByEuler(final Vec3f angradXYZ) { + return rotateByEuler(angradXYZ.x(), angradXYZ.y(), angradXYZ.z()); } /** @@ -450,48 +450,42 @@ public class Quaternion { /*** * Rotate the given vector by this quaternion + * @param vecIn vector to be rotated + * @param vecOut result storage for rotated vector, maybe equal to vecIn for in-place rotation * - * @param vecOut result float[3] storage for rotated vector, maybe equal to vecIn for in-place rotation - * @param vecOutOffset offset in result storage - * @param vecIn float[3] vector to be rotated - * @param vecInOffset offset in vecIn * @return the given vecOut store for chaining * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q63">Matrix-FAQ Q63</a> */ - public final float[] rotateVector(final float[] vecOut, final int vecOutOffset, final float[] vecIn, final int vecInOffset) { - if ( VectorUtil.isVec3Zero(vecIn, vecInOffset, FloatUtil.EPSILON) ) { - vecOut[0+vecOutOffset] = 0f; - vecOut[1+vecOutOffset] = 0f; - vecOut[2+vecOutOffset] = 0f; + public final Vec3f rotateVector(final Vec3f vecIn, final Vec3f vecOut) { + if( vecIn.isZero() ) { + vecOut.set(0, 0, 0); } else { - final float vecX = vecIn[0+vecInOffset]; - final float vecY = vecIn[1+vecInOffset]; - final float vecZ = vecIn[2+vecInOffset]; + final float vecX = vecIn.x(); + final float vecY = vecIn.y(); + final float vecZ = vecIn.z(); final float x_x = x*x; final float y_y = y*y; final float z_z = z*z; final float w_w = w*w; - vecOut[0+vecOutOffset] = w_w * vecX - + x_x * vecX - - z_z * vecX - - y_y * vecX - + 2f * ( y*w*vecZ - z*w*vecY + y*x*vecY + z*x*vecZ ); + vecOut.setX( w_w * vecX + + x_x * vecX + - z_z * vecX + - y_y * vecX + + 2f * ( y*w*vecZ - z*w*vecY + y*x*vecY + z*x*vecZ ) ); ; - vecOut[1+vecOutOffset] = y_y * vecY - - z_z * vecY - + w_w * vecY - - x_x * vecY - + 2f * ( x*y*vecX + z*y*vecZ + w*z*vecX - x*w*vecZ ); - ; - - vecOut[2+vecOutOffset] = z_z * vecZ - - y_y * vecZ - - x_x * vecZ - + w_w * vecZ - + 2f * ( x*z*vecX + y*z*vecY - w*y*vecX + w*x*vecY ); - ; + vecOut.setY( y_y * vecY + - z_z * vecY + + w_w * vecY + - x_x * vecY + + 2f * ( x*y*vecX + z*y*vecZ + w*z*vecX - x*w*vecZ ) );; + + vecOut.setZ( z_z * vecZ + - y_y * vecZ + - x_x * vecZ + + w_w * vecZ + + 2f * ( x*z*vecX + y*z*vecY - w*y*vecX + w*x*vecY ) ); } return vecOut; } @@ -593,21 +587,19 @@ public class Quaternion { * @return this quaternion for chaining. * @see <a href="http://www.euclideanspace.com/maths/algebra/vectors/lookat/index.htm">euclideanspace.com-LookUp</a> */ - public Quaternion setLookAt(final float[] directionIn, final float[] upIn, - final float[] xAxisOut, final float[] yAxisOut, final float[] zAxisOut) { + public Quaternion setLookAt(final Vec3f directionIn, final Vec3f upIn, + final Vec3f xAxisOut, final Vec3f yAxisOut, final Vec3f zAxisOut) { // Z = norm(dir) - VectorUtil.normalizeVec3(zAxisOut, directionIn); + zAxisOut.set(directionIn).normalize(); // X = upIn x Z // (borrow yAxisOut for upNorm) - VectorUtil.normalizeVec3(yAxisOut, upIn); - VectorUtil.crossVec3(xAxisOut, yAxisOut, zAxisOut); - VectorUtil.normalizeVec3(xAxisOut); + yAxisOut.set(upIn).normalize(); + xAxisOut.cross(yAxisOut, zAxisOut).normalize(); // Y = Z x X // - VectorUtil.crossVec3(yAxisOut, zAxisOut, xAxisOut); - VectorUtil.normalizeVec3(yAxisOut); + yAxisOut.cross(zAxisOut, xAxisOut).normalize(); /** final float m00 = xAxisOut[0]; @@ -642,42 +634,42 @@ public class Quaternion { * </p> * @param v1 not normalized * @param v2 not normalized - * @param tmpPivotVec float[3] temp storage for cross product - * @param tmpNormalVec float[3] temp storage to normalize vector + * @param tmpPivotVec temp storage for cross product + * @param tmpNormalVec temp storage to normalize vector * @return this quaternion for chaining. */ - public final Quaternion setFromVectors(final float[] v1, final float[] v2, final float[] tmpPivotVec, final float[] tmpNormalVec) { - final float factor = VectorUtil.normVec3(v1) * VectorUtil.normVec3(v2); + public final Quaternion setFromVectors(final Vec3f v1, final Vec3f v2, final Vec3f tmpPivotVec, final Vec3f tmpNormalVec) { + final float factor = v1.length() * v2.length(); if ( FloatUtil.isZero(factor, FloatUtil.EPSILON ) ) { return setIdentity(); } else { - final float dot = VectorUtil.dotVec3(v1, v2) / factor; // normalize + final float dot = v1.dot(v2) / factor; // normalize final float theta = FloatUtil.acos(Math.max(-1.0f, Math.min(dot, 1.0f))); // clipping [-1..1] - VectorUtil.crossVec3(tmpPivotVec, v1, v2); + tmpPivotVec.cross(v1, v2); - if ( dot < 0.0f && FloatUtil.isZero( VectorUtil.normVec3(tmpPivotVec), FloatUtil.EPSILON ) ) { + if ( dot < 0.0f && FloatUtil.isZero( tmpPivotVec.length(), FloatUtil.EPSILON ) ) { // Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector // perpendicular to this vector will rotate vector a onto vector b. // // The following guarantees the dot-product will be 0.0. int dominantIndex; - if (Math.abs(v1[0]) > Math.abs(v1[1])) { - if (Math.abs(v1[0]) > Math.abs(v1[2])) { + if (Math.abs(v1.x()) > Math.abs(v1.y())) { + if (Math.abs(v1.x()) > Math.abs(v1.z())) { dominantIndex = 0; } else { dominantIndex = 2; } } else { - if (Math.abs(v1[1]) > Math.abs(v1[2])) { + if (Math.abs(v1.y()) > Math.abs(v1.z())) { dominantIndex = 1; } else { dominantIndex = 2; } } - tmpPivotVec[dominantIndex] = -v1[(dominantIndex + 1) % 3]; - tmpPivotVec[(dominantIndex + 1) % 3] = v1[dominantIndex]; - tmpPivotVec[(dominantIndex + 2) % 3] = 0f; + tmpPivotVec.set( dominantIndex, -v1.get( (dominantIndex + 1) % 3 ) ); + tmpPivotVec.set( (dominantIndex + 1) % 3, v1.get( dominantIndex ) ); + tmpPivotVec.set( (dominantIndex + 2) % 3, 0f ); } return setFromAngleAxis(theta, tmpPivotVec, tmpNormalVec); } @@ -698,41 +690,41 @@ public class Quaternion { * </p> * @param v1 normalized * @param v2 normalized - * @param tmpPivotVec float[3] temp storage for cross product + * @param tmpPivotVec temp storage for cross product * @return this quaternion for chaining. */ - public final Quaternion setFromNormalVectors(final float[] v1, final float[] v2, final float[] tmpPivotVec) { - final float factor = VectorUtil.normVec3(v1) * VectorUtil.normVec3(v2); + public final Quaternion setFromNormalVectors(final Vec3f v1, final Vec3f v2, final Vec3f tmpPivotVec) { + final float factor = v1.length() * v2.length(); if ( FloatUtil.isZero(factor, FloatUtil.EPSILON ) ) { return setIdentity(); } else { - final float dot = VectorUtil.dotVec3(v1, v2) / factor; // normalize + final float dot = v1.dot(v2) / factor; // normalize final float theta = FloatUtil.acos(Math.max(-1.0f, Math.min(dot, 1.0f))); // clipping [-1..1] - VectorUtil.crossVec3(tmpPivotVec, v1, v2); + tmpPivotVec.cross(v1, v2); - if ( dot < 0.0f && FloatUtil.isZero( VectorUtil.normVec3(tmpPivotVec), FloatUtil.EPSILON ) ) { + if ( dot < 0.0f && FloatUtil.isZero( tmpPivotVec.length(), FloatUtil.EPSILON ) ) { // Vectors parallel and opposite direction, therefore a rotation of 180 degrees about any vector // perpendicular to this vector will rotate vector a onto vector b. // // The following guarantees the dot-product will be 0.0. int dominantIndex; - if (Math.abs(v1[0]) > Math.abs(v1[1])) { - if (Math.abs(v1[0]) > Math.abs(v1[2])) { + if (Math.abs(v1.x()) > Math.abs(v1.y())) { + if (Math.abs(v1.x()) > Math.abs(v1.z())) { dominantIndex = 0; } else { dominantIndex = 2; } } else { - if (Math.abs(v1[1]) > Math.abs(v1[2])) { + if (Math.abs(v1.y()) > Math.abs(v1.z())) { dominantIndex = 1; } else { dominantIndex = 2; } } - tmpPivotVec[dominantIndex] = -v1[(dominantIndex + 1) % 3]; - tmpPivotVec[(dominantIndex + 1) % 3] = v1[dominantIndex]; - tmpPivotVec[(dominantIndex + 2) % 3] = 0f; + tmpPivotVec.set( dominantIndex, -v1.get( (dominantIndex + 1) % 3 ) ); + tmpPivotVec.set( (dominantIndex + 1) % 3, v1.get( dominantIndex ) ); + tmpPivotVec.set( (dominantIndex + 2) % 3, 0f ); } return setFromAngleNormalAxis(theta, tmpPivotVec); } @@ -748,14 +740,14 @@ public class Quaternion { * </p> * @param angle rotation angle (rads) * @param vector axis vector not normalized - * @param tmpV3f float[3] temp storage to normalize vector + * @param tmpV3f temp storage to normalize vector * @return this quaternion for chaining. * * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a> - * @see #toAngleAxis(float[]) + * @see #toAngleAxis(Vec3f) */ - public final Quaternion setFromAngleAxis(final float angle, final float[] vector, final float[] tmpV3f) { - VectorUtil.normalizeVec3(tmpV3f, vector); + public final Quaternion setFromAngleAxis(final float angle, final Vec3f vector, final Vec3f tmpV3f) { + tmpV3f.set(vector).normalize(); return setFromAngleNormalAxis(angle, tmpV3f); } @@ -772,17 +764,17 @@ public class Quaternion { * @return this quaternion for chaining. * * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56">Matrix-FAQ Q56</a> - * @see #toAngleAxis(float[]) + * @see #toAngleAxis(Vec3f) */ - public final Quaternion setFromAngleNormalAxis(final float angle, final float[] vector) { - if ( VectorUtil.isVec3Zero(vector, 0, FloatUtil.EPSILON) ) { + public final Quaternion setFromAngleNormalAxis(final float angle, final Vec3f vector) { + if( vector.isZero() ) { setIdentity(); } else { final float halfangle = angle * 0.5f; final float sin = FloatUtil.sin(halfangle); - x = vector[0] * sin; - y = vector[1] * sin; - z = vector[2] * sin; + x = vector.x() * sin; + y = vector.y() * sin; + z = vector.z() * sin; w = FloatUtil.cos(halfangle); } return this; @@ -791,24 +783,22 @@ public class Quaternion { /** * Transform the rotational quaternion to axis based rotation angles * - * @param axis float[3] storage for computed axis + * @param axis storage for computed axis * @return the rotation angle in radians - * @see #setFromAngleAxis(float, float[], float[]) + * @see #setFromAngleAxis(float, Vec3f, Vec3f) */ - public final float toAngleAxis(final float[] axis) { + public final float toAngleAxis(final Vec3f axis) { final float sqrLength = x*x + y*y + z*z; float angle; if ( FloatUtil.isZero(sqrLength, FloatUtil.EPSILON) ) { // length is ~0 angle = 0.0f; - axis[0] = 1.0f; - axis[1] = 0.0f; - axis[2] = 0.0f; + axis.set( 1.0f, 0.0f, 0.0f ); } else { angle = FloatUtil.acos(w) * 2.0f; final float invLength = 1.0f / FloatUtil.sqrt(sqrLength); - axis[0] = x * invLength; - axis[1] = y * invLength; - axis[2] = z * invLength; + axis.set( x * invLength, + y * invLength, + z * invLength ); } return angle; } @@ -816,7 +806,7 @@ public class Quaternion { /** * Initializes this quaternion from the given Euler rotation array <code>angradXYZ</code> in radians. * <p> - * The <code>angradXYZ</code> array is laid out in natural order: + * The <code>angradXYZ</code> vector is laid out in natural order: * <ul> * <li>x - bank</li> * <li>y - heading</li> @@ -824,12 +814,12 @@ public class Quaternion { * </ul> * </p> * For details see {@link #setFromEuler(float, float, float)}. - * @param angradXYZ euler angel array in radians + * @param angradXYZ euler angle vector in radians holding x-bank, y-heading and z-attitude * @return this quaternion for chaining. * @see #setFromEuler(float, float, float) */ - public final Quaternion setFromEuler(final float[] angradXYZ) { - return setFromEuler(angradXYZ[0], angradXYZ[1], angradXYZ[2]); + public final Quaternion setFromEuler(final Vec3f angradXYZ) { + return setFromEuler(angradXYZ.x(), angradXYZ.y(), angradXYZ.z()); } /** @@ -857,7 +847,7 @@ public class Quaternion { * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60">Matrix-FAQ Q60</a> * @see <a href="http://vered.rose.utoronto.ca/people/david_dir/GEMS/GEMS.html">Gems</a> * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">euclideanspace.com-eulerToQuaternion</a> - * @see #toEuler(float[]) + * @see #toEuler(Vec3f) */ public final Quaternion setFromEuler(final float bankX, final float headingY, final float attitudeZ) { if ( VectorUtil.isZero(bankX, headingY, attitudeZ, FloatUtil.EPSILON) ) { @@ -889,58 +879,45 @@ public class Quaternion { /** * Transform this quaternion to Euler rotation angles in radians (pitchX, yawY and rollZ). + * <p> + * The <code>result</code> array is laid out in natural order: + * <ul> + * <li>x - bank</li> + * <li>y - heading</li> + * <li>z - attitude</li> + * </ul> + * </p> * - * @param result the float[] array storing the computed angles. - * @return the double[] array, filled with heading, attitude and bank in that order.. + * @param result euler angle result vector for radians x-bank, y-heading and z-attitude + * @return the Vec3f `result` filled with x-bank, y-heading and z-attitude * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">euclideanspace.com-quaternionToEuler</a> * @see #setFromEuler(float, float, float) */ - public float[] toEuler(final float[] result) { + public Vec3f toEuler(final Vec3f result) { final float sqw = w*w; final float sqx = x*x; final float sqy = y*y; final float sqz = z*z; - final float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise - // is correction factor + final float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise, is correction factor final float test = x*y + z*w; if (test > 0.499f * unit) { // singularity at north pole - result[0] = 0f; - result[1] = 2f * FloatUtil.atan2(x, w); - result[2] = FloatUtil.HALF_PI; + result.set( 0f, // x-bank + 2f * FloatUtil.atan2(x, w), // y-heading + FloatUtil.HALF_PI ); // z-attitude } else if (test < -0.499f * unit) { // singularity at south pole - result[0] = 0f; - result[1] = -2 * FloatUtil.atan2(x, w); - result[2] = -FloatUtil.HALF_PI; + result.set( 0f, // x-bank + -2 * FloatUtil.atan2(x, w), // y-heading + -FloatUtil.HALF_PI ); // z-attitude } else { - result[0] = FloatUtil.atan2(2f * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); - result[1] = FloatUtil.atan2(2f * y * w - 2 * x * z, sqx - sqy - sqz + sqw); - result[2] = FloatUtil.asin( 2f * test / unit); + result.set( FloatUtil.atan2(2f * x * w - 2 * y * z, -sqx + sqy - sqz + sqw), // x-bank + FloatUtil.atan2(2f * y * w - 2 * x * z, sqx - sqy - sqz + sqw), // y-heading + FloatUtil.asin( 2f * test / unit) ); // z-attitude } return result; } /** - * Initializes this quaternion from a 4x4 column rotation matrix - * <p> - * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/> - * <a href="http://mathworld.wolfram.com/MatrixTrace.html">MatrixTrace</a>. - * </p> - * <p> - * Buggy <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55">Matrix-FAQ Q55</a> - * </p> - * - * @param m 4x4 column matrix - * @return this quaternion for chaining. - * @see #toMatrix(float[], int) - */ - public final Quaternion setFromMatrix(final float[] m, final int m_off) { - return setFromMatrix(m[0+0*4+m_off], m[0+1*4+m_off], m[0+2*4+m_off], - m[1+0*4+m_off], m[1+1*4+m_off], m[1+2*4+m_off], - m[2+0*4+m_off], m[2+1*4+m_off], m[2+2*4+m_off]); - } - - /** * Compute the quaternion from a 3x3 column rotation matrix * <p> * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/> @@ -951,7 +928,7 @@ public class Quaternion { * </p> * * @return this quaternion for chaining. - * @see #toMatrix(float[], int) + * @see #setFromMatrix(Matrix4f) */ public Quaternion setFromMatrix(final float m00, final float m01, final float m02, final float m10, final float m11, final float m12, @@ -996,6 +973,24 @@ public class Quaternion { } /** + * Compute the quaternion from a 3x3 column rotation matrix + * <p> + * See <a href="ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z">Graphics Gems Code</a>,<br/> + * <a href="http://mathworld.wolfram.com/MatrixTrace.html">MatrixTrace</a>. + * </p> + * <p> + * Buggy <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55">Matrix-FAQ Q55</a> + * </p> + * + * @return this quaternion for chaining. + * @see Matrix4f#getRotation(Quaternion) + * @see #setFromMatrix(float, float, float, float, float, float, float, float, float) + */ + public Quaternion setFromMatrix(final Matrix4f m) { + return m.getRotation(this); + } + + /** * Transform this quaternion to a normalized 4x4 column matrix representing the rotation. * <p> * Implementation Details: @@ -1005,17 +1000,17 @@ public class Quaternion { * </p> * * @param matrix float[16] store for the resulting normalized column matrix 4x4 - * @param mat_offset * @return the given matrix store * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a> - * @see #setFromMatrix(float[], int) + * @see #setFromMatrix(Matrix4f) + * @see #setFromMatrix(float, float, float, float, float, float, float, float, float) */ - public final float[] toMatrix(final float[] matrix, final int mat_offset) { + public final float[] toMatrix(final float[] matrix) { // pre-multiply scaled-reciprocal-magnitude to reduce multiplications final float norm = magnitudeSquared(); if ( FloatUtil.isZero(norm, FloatUtil.EPSILON) ) { // identity matrix -> srecip = 0f - return FloatUtil.makeIdentity(matrix, mat_offset); + return FloatUtil.makeIdentity(matrix); } final float srecip; if ( FloatUtil.isEqual(1f, norm, FloatUtil.EPSILON) ) { @@ -1038,81 +1033,45 @@ public class Quaternion { final float zz = z * zs; final float zw = zs * w; - matrix[0+0*4+mat_offset] = 1f - ( yy + zz ); - matrix[0+1*4+mat_offset] = ( xy - zw ); - matrix[0+2*4+mat_offset] = ( xz + yw ); - matrix[0+3*4+mat_offset] = 0f; - - matrix[1+0*4+mat_offset] = ( xy + zw ); - matrix[1+1*4+mat_offset] = 1f - ( xx + zz ); - matrix[1+2*4+mat_offset] = ( yz - xw ); - matrix[1+3*4+mat_offset] = 0f; - - matrix[2+0*4+mat_offset] = ( xz - yw ); - matrix[2+1*4+mat_offset] = ( yz + xw ); - matrix[2+2*4+mat_offset] = 1f - ( xx + yy ); - matrix[2+3*4+mat_offset] = 0f; - - matrix[3+0*4+mat_offset] = 0f; - matrix[3+1*4+mat_offset] = 0f; - matrix[3+2*4+mat_offset] = 0f; - matrix[3+3*4+mat_offset] = 1f; + matrix[0+0*4] = 1f - ( yy + zz ); + matrix[0+1*4] = ( xy - zw ); + matrix[0+2*4] = ( xz + yw ); + matrix[0+3*4] = 0f; + + matrix[1+0*4] = ( xy + zw ); + matrix[1+1*4] = 1f - ( xx + zz ); + matrix[1+2*4] = ( yz - xw ); + matrix[1+3*4] = 0f; + + matrix[2+0*4] = ( xz - yw ); + matrix[2+1*4] = ( yz + xw ); + matrix[2+2*4] = 1f - ( xx + yy ); + matrix[2+3*4] = 0f; + + matrix[3+0*4] = 0f; + matrix[3+1*4] = 0f; + matrix[3+2*4] = 0f; + matrix[3+3*4] = 1f; return matrix; } /** - * @param index the 3x3 rotation matrix column to retrieve from this quaternion (normalized). Must be between 0 and 2. - * @param result the vector object to store the result in. - * @return the result column-vector for chaining. + * Transform this quaternion to a normalized 4x4 column matrix representing the rotation. + * <p> + * Implementation Details: + * <ul> + * <li> makes identity matrix if {@link #magnitudeSquared()} is {@link FloatUtil#isZero(float, float) is zero} using {@link FloatUtil#EPSILON epsilon}</li> + * </ul> + * </p> + * + * @param matrix store for the resulting normalized column matrix 4x4 + * @return the given matrix store + * @see <a href="http://web.archive.org/web/20041029003853/http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54">Matrix-FAQ Q54</a> + * @see #setFromMatrix(float, float, float, float, float, float, float, float, float) + * @see Matrix4f#setToRotation(Quaternion) */ - public float[] copyMatrixColumn(final int index, final float[] result, final int resultOffset) { - // pre-multipliy scaled-reciprocal-magnitude to reduce multiplications - final float norm = magnitudeSquared(); - final float srecip; - if ( FloatUtil.isZero(norm, FloatUtil.EPSILON) ) { - srecip= 0f; - } else if ( FloatUtil.isEqual(1f, norm, FloatUtil.EPSILON) ) { - srecip= 2f; - } else { - srecip= 2.0f / norm; - } - - // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs - // will be used 2-4 times each. - final float xs = x * srecip; - final float ys = y * srecip; - final float zs = z * srecip; - final float xx = x * xs; - final float xy = x * ys; - final float xz = x * zs; - final float xw = w * xs; - final float yy = y * ys; - final float yz = y * zs; - final float yw = w * ys; - final float zz = z * zs; - final float zw = w * zs; - - // using s=2/norm (instead of 1/norm) saves 3 multiplications by 2 here - switch (index) { - case 0: - result[0+resultOffset] = 1.0f - (yy + zz); - result[1+resultOffset] = xy + zw; - result[2+resultOffset] = xz - yw; - break; - case 1: - result[0+resultOffset] = xy - zw; - result[1+resultOffset] = 1.0f - (xx + zz); - result[2+resultOffset] = yz + xw; - break; - case 2: - result[0+resultOffset] = xz + yw; - result[1+resultOffset] = yz - xw; - result[2+resultOffset] = 1.0f - (xx + yy); - break; - default: - throw new IllegalArgumentException("Invalid column index. " + index); - } - return result; + public final Matrix4f toMatrix(final Matrix4f matrix) { + return matrix.setToRotation(this); } /** @@ -1126,10 +1085,10 @@ public class Quaternion { * @param zAxis vector representing the <i>orthogonal</i> z-axis of the coordinate system. * @return this quaternion for chaining. */ - public final Quaternion setFromAxes(final float[] xAxis, final float[] yAxis, final float[] zAxis) { - return setFromMatrix(xAxis[0], yAxis[0], zAxis[0], - xAxis[1], yAxis[1], zAxis[1], - xAxis[2], yAxis[2], zAxis[2]); + public final Quaternion setFromAxes(final Vec3f xAxis, final Vec3f yAxis, final Vec3f zAxis) { + return setFromMatrix(xAxis.x(), yAxis.x(), zAxis.x(), + xAxis.y(), yAxis.y(), zAxis.y(), + xAxis.z(), yAxis.z(), zAxis.z()); } /** @@ -1140,11 +1099,11 @@ public class Quaternion { * @param zAxis vector representing the <i>orthogonal</i> z-axis of the coordinate system. * @param tmpMat4 temporary float[4] matrix, used to transform this quaternion to a matrix. */ - public void toAxes(final float[] xAxis, final float[] yAxis, final float[] zAxis, final float[] tmpMat4) { - toMatrix(tmpMat4, 0); - FloatUtil.copyMatrixColumn(tmpMat4, 0, 2, zAxis, 0); - FloatUtil.copyMatrixColumn(tmpMat4, 0, 1, yAxis, 0); - FloatUtil.copyMatrixColumn(tmpMat4, 0, 0, xAxis, 0); + public void toAxes(final Vec3f xAxis, final Vec3f yAxis, final Vec3f zAxis, final Matrix4f tmpMat4) { + tmpMat4.setToRotation(this); + tmpMat4.getColumn(2, zAxis); + tmpMat4.getColumn(1, yAxis); + tmpMat4.getColumn(0, xAxis); } /** @@ -1154,6 +1113,7 @@ public class Quaternion { * @param m 3x3 column matrix * @return true if representing a rotational matrix, false otherwise */ + @Deprecated public final boolean isRotationMatrix3f(final float[] m) { final float epsilon = 0.01f; // margin to allow for rounding errors if (FloatUtil.abs(m[0] * m[3] + m[3] * m[4] + m[6] * m[7]) > epsilon) @@ -1171,6 +1131,7 @@ public class Quaternion { return (FloatUtil.abs(determinant3f(m) - 1) < epsilon); } + @Deprecated private final float determinant3f(final float[] m) { return m[0] * m[4] * m[8] + m[3] * m[7] * m[2] + m[6] * m[1] * m[5] - m[0] * m[7] * m[5] - m[3] * m[1] * m[8] - m[6] * m[4] * m[2]; @@ -1193,17 +1154,18 @@ public class Quaternion { return false; } final Quaternion comp = (Quaternion) o; - return Math.abs(x - comp.getX()) <= ALLOWED_DEVIANCE && - Math.abs(y - comp.getY()) <= ALLOWED_DEVIANCE && - Math.abs(z - comp.getZ()) <= ALLOWED_DEVIANCE && - Math.abs(w - comp.getW()) <= ALLOWED_DEVIANCE; + return Math.abs(x - comp.x()) <= ALLOWED_DEVIANCE && + Math.abs(y - comp.y()) <= ALLOWED_DEVIANCE && + Math.abs(z - comp.z()) <= ALLOWED_DEVIANCE && + Math.abs(w - comp.w()) <= ALLOWED_DEVIANCE; } @Override public final int hashCode() { throw new InternalError("hashCode not designed"); } + @Override public String toString() { - return "Quaternion[x "+x+", y "+y+", z "+z+", w "+w+"]"; + return "Quat[x "+x+", y "+y+", z "+z+", w "+w+"]"; } } diff --git a/src/jogl/classes/com/jogamp/opengl/math/Ray.java b/src/jogl/classes/com/jogamp/opengl/math/Ray.java index 4d651d1c3..25a7d9a70 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/Ray.java +++ b/src/jogl/classes/com/jogamp/opengl/math/Ray.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 JogAmp Community. All rights reserved. + * Copyright 2014-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -47,13 +47,14 @@ import com.jogamp.opengl.math.geom.AABBox; * </p> */ public class Ray { - /** Origin of Ray, float[3]. */ - public final float[] orig = new float[3]; + /** Origin of Ray. */ + public final Vec3f orig = new Vec3f(); - /** Normalized direction vector of ray, float[3]. */ - public final float[] dir = new float[3]; + /** Normalized direction vector of ray. */ + public final Vec3f dir = new Vec3f(); + @Override public String toString() { - return "Ray[orig["+orig[0]+", "+orig[1]+", "+orig[2]+"], dir["+dir[0]+", "+dir[1]+", "+dir[2]+"]]"; + return "Ray[orig["+orig+"], dir["+dir+"]]"; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/math/Recti.java b/src/jogl/classes/com/jogamp/opengl/math/Recti.java new file mode 100644 index 000000000..58f5e5e77 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Recti.java @@ -0,0 +1,134 @@ +/** + * Copyright 2022-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * Rectangle with x, y, width and height integer components. + */ +public final class Recti { + private int x; + private int y; + private int width; + private int height; + + public Recti() {} + + public Recti(final Recti o) { + set(o); + } + + public Recti copy() { + return new Recti(this); + } + + public Recti(final int[/*4*/] xywh) { + set(xywh); + } + + public Recti(final int x, final int y, final int width, final int height) { + set(x, y, width, height); + } + + /** this = o, returns this. */ + public void set(final Recti o) { + this.x = o.x; + this.y = o.y; + this.width = o.width; + this.height= o.height; + } + + /** this = { x, y, width, height }, returns this. */ + public void set(final int x, final int y, final int width, final int height) { + this.x = x; + this.y = y; + this.width = width; + this.height= height; + } + + /** this = xywh, returns this. */ + public Recti set(final int[/*2*/] xywh) { + this.x = xywh[0]; + this.y = xywh[1]; + this.width = xywh[2]; + this.height= xywh[3]; + return this; + } + + /** xywh = this, returns xy. */ + public int[] get(final int[/*4*/] xywh) { + xywh[0] = this.x; + xywh[1] = this.y; + xywh[2] = this.width; + xywh[3] = this.height; + return xywh; + } + + public int x() { return x; } + public int y() { return y; } + public int width() { return width; } + public int height() { return height; } + + public void setX(final int x) { this.x = x; } + public void setY(final int y) { this.y = y; } + public void setWidth(final int width) { this.width = width; } + public void setHeight(final int height) { this.height = height; } + + /** Return true if all components are zero. */ + public boolean isZero() { + return 0 == x && 0 == y; + } + + /** + * Equals check. + * @param o comparison value + * @return true if all components are equal + */ + public boolean isEqual(final Recti o) { + if( this == o ) { + return true; + } else { + return x == o.x && y == o.y && + width == o.width && height == o.height; + } + } + + @Override + public boolean equals(final Object o) { + if( o instanceof Recti ) { + return isEqual((Recti)o); + } else { + return false; + } + } + + @Override + public String toString() { + return x + " / " + y + " " + width + " x " + height; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vec2f.java b/src/jogl/classes/com/jogamp/opengl/math/Vec2f.java new file mode 100644 index 000000000..47d1a78dc --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Vec2f.java @@ -0,0 +1,377 @@ +/** + * Copyright 2022-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * 2D Vector based upon two float components. + * + * Implementation borrowed from [gfxbox2](https://jausoft.com/cgit/cs_class/gfxbox2.git/tree/include/pixel/pixel2f.hpp#n29) + * and its data layout from JOAL's Vec3f. + */ +public final class Vec2f { + private float x; + private float y; + + public static Vec2f from_length_angle(final float magnitude, final float radians) { + return new Vec2f((float)(magnitude * Math.cos(radians)), (float)(magnitude * Math.sin(radians))); + } + + public Vec2f() {} + + public Vec2f(final Vec2f o) { + set(o); + } + + /** Creating new Vec2f using Vec3f, dropping z. */ + public Vec2f(final Vec3f o) { + set(o); + } + + public Vec2f copy() { + return new Vec2f(this); + } + + public Vec2f(final float[/*2*/] xy) { + set(xy); + } + + public Vec2f(final float x, final float y) { + set(x, y); + } + + /** this = o, returns this. */ + public void set(final Vec2f o) { + this.x = o.x; + this.y = o.y; + } + + /** this = o while dropping z, returns this. */ + public void set(final Vec3f o) { + this.x = o.x(); + this.y = o.y(); + } + + /** this = { x, y }, returns this. */ + public void set(final float x, final float y) { + this.x = x; + this.y = y; + } + + /** this = xy, returns this. */ + public Vec2f set(final float[/*2*/] xy) { + this.x = xy[0]; + this.y = xy[1]; + return this; + } + + /** Sets the ith component, 0 <= i < 2 */ + public void set(final int i, final float val) { + switch (i) { + case 0: x = val; break; + case 1: y = val; break; + default: throw new IndexOutOfBoundsException(); + } + } + + /** xy = this, returns xy. */ + public float[] get(final float[/*2*/] xy) { + xy[0] = this.x; + xy[1] = this.y; + return xy; + } + + /** Gets the ith component, 0 <= i < 2 */ + public float get(final int i) { + switch (i) { + case 0: return x; + case 1: return y; + default: throw new IndexOutOfBoundsException(); + } + } + + public float x() { return x; } + public float y() { return y; } + + public void setX(final float x) { this.x = x; } + public void setY(final float y) { this.y = y; } + + /** this = max(this, m), returns this. */ + public Vec2f max(final Vec2f m) { + this.x = Math.max(this.x, m.x); + this.y = Math.max(this.y, m.y); + return this; + } + /** this = min(this, m), returns this. */ + public Vec2f min(final Vec2f m) { + this.x = Math.min(this.x, m.x); + this.y = Math.min(this.y, m.y); + return this; + } + + /** Returns this * val; creates new vector */ + public Vec2f mul(final float val) { + return new Vec2f(this).scale(val); + } + + /** this = a * b, returns this. */ + public Vec2f mul(final Vec2f a, final Vec2f b) { + x = a.x * b.x; + y = a.y * b.y; + return this; + } + + /** this = this * s, returns this. */ + public Vec2f scale(final float s) { + x *= s; + y *= s; + return this; + } + + /** this = this * { sx, sy }, returns this. */ + public Vec2f scale(final float sx, final float sy) { + x *= sx; + y *= sy; + return this; + } + + /** Returns this + arg; creates new vector */ + public Vec2f plus(final Vec2f arg) { + return new Vec2f(this).add(arg); + } + + /** this = a + b, returns this. */ + public Vec2f plus(final Vec2f a, final Vec2f b) { + x = a.x + b.x; + y = a.y + b.y; + return this; + } + + /** this = this + { dx, dy }, returns this. */ + public Vec2f add(final float dx, final float dy) { + x += dx; + y += dy; + return this; + } + + /** this = this + b, returns this. */ + public Vec2f add(final Vec2f b) { + x += b.x; + y += b.y; + return this; + } + + /** Returns this - arg; creates new vector */ + public Vec2f minus(final Vec2f arg) { + return new Vec2f(this).sub(arg); + } + + /** this = a - b, returns this. */ + public Vec2f minus(final Vec2f a, final Vec2f b) { + x = a.x - b.x; + y = a.y - b.y; + return this; + } + + /** this = this - b, returns this. */ + public Vec2f sub(final Vec2f b) { + x -= b.x; + y -= b.y; + return this; + } + + /** Return true if all components are zero, i.e. it's absolute value < {@link #EPSILON}. */ + public boolean isZero() { + return FloatUtil.isZero(x) && FloatUtil.isZero(y); + } + + public void rotate(final float radians, final Vec2f ctr) { + final float cos = (float)Math.cos(radians); + final float sin = (float)Math.sin(radians); + rotate(sin, cos, ctr); + } + + public void rotate(final float sin, final float cos, final Vec2f ctr) { + final float x0 = x - ctr.x; + final float y0 = y - ctr.y; + final float tmp = x0 * cos - y0 * sin + ctr.x; + y = x0 * sin + y0 * cos + ctr.y; + x = tmp; + } + + /** + * Return the length of this vector, a.k.a the <i>norm</i> or <i>magnitude</i> + */ + public float length() { + return (float) Math.sqrt(lengthSq()); + } + + /** + * Return the squared length of this vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public float lengthSq() { + return x*x + y*y; + } + + /** + * Return the direction angle of this vector in radians + */ + public float angle() { + // Utilize atan2 taking y=sin(a) and x=cos(a), resulting in proper direction angle for all quadrants. + return (float) Math.atan2(y, x); + } + + /** + * Normalize this vector in place + */ + public Vec2f normalize() { + final float lengthSq = lengthSq(); + if ( FloatUtil.isZero( lengthSq ) ) { + x = 0.0f; + y = 0.0f; + } else { + final float invSqr = 1.0f / (float)Math.sqrt(lengthSq); + x *= invSqr; + y *= invSqr; + } + return this; + } + + /** + * Return the squared distance between this vector and the given one. + * <p> + * When comparing the relative distance between two points it is usually sufficient to compare the squared + * distances, thus avoiding an expensive square root operation. + * </p> + */ + public float distSq(final Vec2f o) { + final float dx = x - o.x; + final float dy = y - o.y; + return dx*dx + dy*dy; + } + + /** + * Return the distance between this vector and the given one. + */ + public float dist(final Vec2f o) { + return (float)Math.sqrt(distSq(o)); + } + + + /** + * Return the dot product of this vector and the given one + * @return the dot product as float + */ + public float dot(final Vec2f arg) { + return x * arg.x + y * arg.y; + } + + /** + * Returns cross product of this vectors and the given one, i.e. *this x o. + * + * The 2D cross product is identical with the 2D perp dot product. + * + * @return the resulting scalar + */ + public float cross(final Vec2f o) { + return x * o.y - y * o.x; + } + + /** + * Return the cosines of the angle between two vectors + */ + public float cosAngle(final Vec2f o) { + return dot(o) / ( length() * o.length() ) ; + } + + /** + * Return the angle between two vectors in radians + */ + public float angle(final Vec2f o) { + return (float) Math.acos( cosAngle(o) ); + } + + /** + * Return the counter-clock-wise (CCW) normal of this vector, i.e. perp(endicular) vector + */ + public Vec2f normal_ccw() { + return new Vec2f(-y, x); + } + + /** + * Equals check using a given {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @param epsilon consider using {@link FloatUtil#EPSILON} + * @return true if all components differ less than {@code epsilon}, otherwise false. + */ + public boolean isEqual(final Vec2f o, final float epsilon) { + if( this == o ) { + return true; + } else { + return FloatUtil.isEqual(x, o.x, epsilon) && + FloatUtil.isEqual(y, o.y, epsilon); + } + } + + /** + * Equals check using {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @return true if all components differ less than {@link FloatUtil#EPSILON}, otherwise false. + */ + public boolean isEqual(final Vec2f o) { + return isEqual(o, FloatUtil.EPSILON); + } + + @Override + public boolean equals(final Object o) { + if( o instanceof Vec2f ) { + return isEqual((Vec2f)o, FloatUtil.EPSILON); + } else { + return false; + } + } + + @Override + public String toString() { + return x + " / " + y; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vec2i.java b/src/jogl/classes/com/jogamp/opengl/math/Vec2i.java new file mode 100644 index 000000000..9e70a502f --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Vec2i.java @@ -0,0 +1,153 @@ +/** + * Copyright 2022-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * 2D Vector based upon two integer components. + */ +public final class Vec2i { + private int x; + private int y; + + public Vec2i() {} + + public Vec2i(final Vec2i o) { + set(o); + } + + public Vec2i copy() { + return new Vec2i(this); + } + + public Vec2i(final int[/*2*/] xy) { + set(xy); + } + + public Vec2i(final int x, final int y) { + set(x, y); + } + + /** this = o, returns this. */ + public void set(final Vec2i o) { + this.x = o.x; + this.y = o.y; + } + + /** this = { x, y }, returns this. */ + public void set(final int x, final int y) { + this.x = x; + this.y = y; + } + + /** this = xy, returns this. */ + public Vec2i set(final int[/*2*/] xy) { + this.x = xy[0]; + this.y = xy[1]; + return this; + } + + /** xy = this, returns xy. */ + public int[] get(final int[/*2*/] xy) { + xy[0] = this.x; + xy[1] = this.y; + return xy; + } + + public int x() { return x; } + public int y() { return y; } + + public void setX(final int x) { this.x = x; } + public void setY(final int y) { this.y = y; } + + /** Return true if all components are zero. */ + public boolean isZero() { + return 0 == x && 0 == y; + } + + /** + * Return the length of this vector, a.k.a the <i>norm</i> or <i>magnitude</i> + */ + public int length() { + return (int) Math.sqrt(lengthSq()); + } + + /** + * Return the squared length of this vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public int lengthSq() { + return x*x + y*y; + } + + /** + * Return the squared distance between this vector and the given one. + * <p> + * When comparing the relative distance between two points it is usually sufficient to compare the squared + * distances, thus avoiding an expensive square root operation. + * </p> + */ + public int distSq(final Vec2i o) { + final int dx = x - o.x; + final int dy = y - o.y; + return dx*dx + dy*dy; + } + + /** + * Return the distance between this vector and the given one. + */ + public int dist(final Vec2i o) { + return (int)Math.sqrt(distSq(o)); + } + + /** + * Equals check. + * @param o comparison value + * @return true if all components are equal + */ + public boolean isEqual(final Vec2i o) { + if( this == o ) { + return true; + } else { + return x == o.x && y == o.y; + } + } + + @Override + public boolean equals(final Object o) { + if( o instanceof Vec2i ) { + return isEqual((Vec2i)o); + } else { + return false; + } + } + + @Override + public String toString() { + return x + " / " + y; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vec3f.java b/src/jogl/classes/com/jogamp/opengl/math/Vec3f.java new file mode 100644 index 000000000..eb1144c07 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Vec3f.java @@ -0,0 +1,393 @@ +/** + * Copyright 2022-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * 3D Vector based upon three float components. + * + * Implementation borrowed from [gfxbox2](https://jausoft.com/cgit/cs_class/gfxbox2.git/tree/include/pixel/pixel3f.hpp#n29) + * and its data layout from JOAL's Vec3f. + */ +public final class Vec3f { + public static final Vec3f ONE = new Vec3f(1f, 1f, 1f); + public static final Vec3f UNIT_Y = new Vec3f(0f, 1f, 0f); + public static final Vec3f UNIT_Y_NEG = new Vec3f(0f, -1f, 0f); + public static final Vec3f UNIT_Z = new Vec3f(0f, 0f, 1f); + public static final Vec3f UNIT_Z_NEG = new Vec3f(0f, 0f, -1f); + + private float x; + private float y; + private float z; + + public Vec3f() {} + + public Vec3f(final Vec3f o) { + set(o); + } + + /** Creating new Vec3f using Vec4f, dropping w. */ + public Vec3f(final Vec4f o) { + set(o); + } + + /** Creating new Vec3f using { Vec2f, z}. */ + public Vec3f(final Vec2f o, final float z) { + set(o, z); + } + + public Vec3f copy() { + return new Vec3f(this); + } + + public Vec3f(final float[/*3*/] xyz) { + set(xyz); + } + + public Vec3f(final float x, final float y, final float z) { + set(x, y, z); + } + + /** this = o, returns this. */ + public Vec3f set(final Vec3f o) { + this.x = o.x; + this.y = o.y; + this.z = o.z; + return this; + } + + /** this = { o, z }, returns this. */ + public Vec3f set(final Vec2f o, final float z) { + this.x = o.x(); + this.y = o.y(); + this.z = z; + return this; + } + + /** this = o while dropping w, returns this. */ + public Vec3f set(final Vec4f o) { + this.x = o.x(); + this.y = o.y(); + this.z = o.z(); + return this; + } + + /** this = { x, y, z }, returns this. */ + public Vec3f set(final float x, final float y, final float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + /** this = xyz, returns this. */ + public Vec3f set(final float[/*3*/] xyz) { + this.x = xyz[0]; + this.y = xyz[1]; + this.z = xyz[2]; + return this; + } + + /** Sets the ith component, 0 <= i < 3 */ + public void set(final int i, final float val) { + switch (i) { + case 0: x = val; break; + case 1: y = val; break; + case 2: z = val; break; + default: throw new IndexOutOfBoundsException(); + } + } + + /** xyz = this, returns xyz. */ + public float[] get(final float[/*3*/] xyz) { + xyz[0] = this.x; + xyz[1] = this.y; + xyz[2] = this.z; + return xyz; + } + + /** Gets the ith component, 0 <= i < 3 */ + public float get(final int i) { + switch (i) { + case 0: return x; + case 1: return y; + case 2: return z; + default: throw new IndexOutOfBoundsException(); + } + } + + public float x() { return x; } + public float y() { return y; } + public float z() { return z; } + + public void setX(final float x) { this.x = x; } + public void setY(final float y) { this.y = y; } + public void setZ(final float z) { this.z = z; } + + /** this = max(this, m), returns this. */ + public Vec3f max(final Vec3f m) { + this.x = Math.max(this.x, m.x); + this.y = Math.max(this.y, m.y); + this.z = Math.max(this.z, m.z); + return this; + } + /** this = min(this, m), returns this. */ + public Vec3f min(final Vec3f m) { + this.x = Math.min(this.x, m.x); + this.y = Math.min(this.y, m.y); + this.z = Math.min(this.z, m.z); + return this; + } + + /** Returns this * val; creates new vector */ + public Vec3f mul(final float val) { + return new Vec3f(this).scale(val); + } + + /** this = a * b, returns this. */ + public Vec3f mul(final Vec3f a, final Vec3f b) { + x = a.x * b.x; + y = a.y * b.y; + z = a.z * b.z; + return this; + } + + /** this = this * s, returns this. */ + public Vec3f scale(final float s) { + x *= s; + y *= s; + z *= s; + return this; + } + + /** this = this * { sx, sy, sz }, returns this. */ + public Vec3f scale(final float sx, final float sy, final float sz) { + x *= sx; + y *= sy; + z *= sz; + return this; + } + + /** Returns this + arg; creates new vector */ + public Vec3f plus(final Vec3f arg) { + return new Vec3f(this).add(arg); + } + + /** this = a + b, returns this. */ + public Vec3f plus(final Vec3f a, final Vec3f b) { + x = a.x + b.x; + y = a.y + b.y; + z = a.z + b.z; + return this; + } + + /** this = this + { dx, dy, dz }, returns this. */ + public Vec3f add(final float dx, final float dy, final float dz) { + x += dx; + y += dy; + z += dz; + return this; + } + + /** this = this + b, returns this. */ + public Vec3f add(final Vec3f b) { + x += b.x; + y += b.y; + z += b.z; + return this; + } + + /** Returns this - arg; creates new vector */ + public Vec3f minus(final Vec3f arg) { + return new Vec3f(this).sub(arg); + } + + /** this = a - b, returns this. */ + public Vec3f minus(final Vec3f a, final Vec3f b) { + x = a.x - b.x; + y = a.y - b.y; + z = a.z - b.z; + return this; + } + + /** this = this - b, returns this. */ + public Vec3f sub(final Vec3f b) { + x -= b.x; + y -= b.y; + z -= b.z; + return this; + } + + /** Return true if all components are zero, i.e. it's absolute value < {@link #EPSILON}. */ + public boolean isZero() { + return FloatUtil.isZero(x) && FloatUtil.isZero(y) && FloatUtil.isZero(z); + } + + /** + * Return the length of this vector, a.k.a the <i>norm</i> or <i>magnitude</i> + */ + public float length() { + return (float) Math.sqrt(lengthSq()); + } + + /** + * Return the squared length of this vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public float lengthSq() { + return x*x + y*y + z*z; + } + + /** + * Normalize this vector in place + */ + public Vec3f normalize() { + final float lengthSq = lengthSq(); + if ( FloatUtil.isZero( lengthSq ) ) { + x = 0.0f; + y = 0.0f; + z = 0.0f; + } else { + final float invSqr = 1.0f / (float)Math.sqrt(lengthSq); + x *= invSqr; + y *= invSqr; + z *= invSqr; + } + return this; + } + + /** + * Return the squared distance between this vector and the given one. + * <p> + * When comparing the relative distance between two points it is usually sufficient to compare the squared + * distances, thus avoiding an expensive square root operation. + * </p> + */ + public float distSq(final Vec3f o) { + final float dx = x - o.x; + final float dy = y - o.y; + final float dz = z - o.z; + return dx*dx + dy*dy + dz*dz; + } + + /** + * Return the distance between this vector and the given one. + */ + public float dist(final Vec3f o) { + return (float)Math.sqrt(distSq(o)); + } + + + /** + * Return the dot product of this vector and the given one + * @return the dot product as float + */ + public float dot(final Vec3f o) { + return x*o.x + y*o.y + z*o.z; + } + + /** Returns this cross arg; creates new vector */ + public Vec3f cross(final Vec3f arg) { + return new Vec3f().cross(this, arg); + } + + /** this = a cross b. NOTE: "this" must be a different vector than + both a and b. */ + public Vec3f cross(final Vec3f a, final Vec3f b) { + x = a.y * b.z - a.z * b.y; + y = a.z * b.x - a.x * b.z; + z = a.x * b.y - a.y * b.x; + return this; + } + + /** + * Return the cosines of the angle between two vectors + */ + public float cosAngle(final Vec3f o) { + return dot(o) / ( length() * o.length() ) ; + } + + /** + * Return the angle between two vectors in radians + */ + public float angle(final Vec3f o) { + return (float) Math.acos( cosAngle(o) ); + } + + /** + * Equals check using a given {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @param epsilon consider using {@link FloatUtil#EPSILON} + * @return true if all components differ less than {@code epsilon}, otherwise false. + */ + public boolean isEqual(final Vec3f o, final float epsilon) { + if( this == o ) { + return true; + } else { + return FloatUtil.isEqual(x, o.x, epsilon) && + FloatUtil.isEqual(y, o.y, epsilon) && + FloatUtil.isEqual(z, o.z, epsilon); + } + } + + /** + * Equals check using {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @return true if all components differ less than {@link FloatUtil#EPSILON}, otherwise false. + */ + public boolean isEqual(final Vec3f o) { + return isEqual(o, FloatUtil.EPSILON); + } + + @Override + public boolean equals(final Object o) { + if( o instanceof Vec3f ) { + return isEqual((Vec3f)o, FloatUtil.EPSILON); + } else { + return false; + } + } + + @Override + public String toString() { + return x + " / " + y + " / " + z; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vec4f.java b/src/jogl/classes/com/jogamp/opengl/math/Vec4f.java new file mode 100644 index 000000000..f86fe5dad --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/math/Vec4f.java @@ -0,0 +1,384 @@ +/** + * Copyright 2022-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package com.jogamp.opengl.math; + +/** + * 4D Vector based upon four float components. + * + * Implementation borrowed from [gfxbox2](https://jausoft.com/cgit/cs_class/gfxbox2.git/tree/include/pixel/pixel3f.hpp#n29) + * and its data layout from JOAL's Vec3f. + */ +public final class Vec4f { + private float x; + private float y; + private float z; + private float w; + + public Vec4f() {} + + public Vec4f(final Vec4f o) { + set(o); + } + + /** Creating new Vec4f using { o, w }. */ + public Vec4f(final Vec3f o, final float w) { + set(o, w); + } + + public Vec4f copy() { + return new Vec4f(this); + } + + public Vec4f(final float[/*4*/] xyzw) { + set(xyzw); + } + + public Vec4f(final float x, final float y, final float z, final float w) { + set(x, y, z, w); + } + + /** this = o, returns this. */ + public Vec4f set(final Vec4f o) { + this.x = o.x; + this.y = o.y; + this.z = o.z; + this.w = o.w; + return this; + } + + /** this = { o, w }, returns this. */ + public Vec4f set(final Vec3f o, final float w) { + this.x = o.x(); + this.y = o.y(); + this.z = o.z(); + this.w = w; + return this; + } + + /** this = { x, y, z, w }, returns this. */ + public Vec4f set(final float x, final float y, final float z, final float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + return this; + } + + /** this = xyzw, returns this. */ + public Vec4f set(final float[/*4*/] xyzw) { + this.x = xyzw[0]; + this.y = xyzw[1]; + this.z = xyzw[2]; + this.w = xyzw[3]; + return this; + } + + /** Sets the ith component, 0 <= i < 4 */ + public void set(final int i, final float val) { + switch (i) { + case 0: x = val; break; + case 1: y = val; break; + case 2: z = val; break; + case 3: w = val; break; + default: throw new IndexOutOfBoundsException(); + } + } + + /** xyzw = this, returns xyzw. */ + public float[] get(final float[/*4*/] xyzw) { + xyzw[0] = this.x; + xyzw[1] = this.y; + xyzw[2] = this.z; + xyzw[3] = this.w; + return xyzw; + } + + /** Gets the ith component, 0 <= i < 4 */ + public float get(final int i) { + switch (i) { + case 0: return x; + case 1: return y; + case 2: return z; + case 3: return w; + default: throw new IndexOutOfBoundsException(); + } + } + + public float x() { return x; } + public float y() { return y; } + public float z() { return z; } + public float w() { return w; } + + public void setX(final float x) { this.x = x; } + public void setY(final float y) { this.y = y; } + public void setZ(final float z) { this.z = z; } + public void setW(final float w) { this.w = w; } + + /** this = max(this, m), returns this. */ + public Vec4f max(final Vec4f m) { + this.x = Math.max(this.x, m.x); + this.y = Math.max(this.y, m.y); + this.z = Math.max(this.z, m.z); + this.w = Math.max(this.w, m.w); + return this; + } + /** this = min(this, m), returns this. */ + public Vec4f min(final Vec4f m) { + this.x = Math.min(this.x, m.x); + this.y = Math.min(this.y, m.y); + this.z = Math.min(this.z, m.z); + this.w = Math.min(this.w, m.w); + return this; + } + + /** Returns this * val; creates new vector */ + public Vec4f mul(final float val) { + return new Vec4f(this).scale(val); + } + + /** this = a * b, returns this. */ + public Vec4f mul(final Vec4f a, final Vec4f b) { + x = a.x * b.x; + y = a.y * b.y; + z = a.z * b.z; + w = a.w * b.w; + return this; + } + + /** this = this * s, returns this. */ + public Vec4f scale(final float s) { + x *= s; + y *= s; + z *= s; + w *= s; + return this; + } + + /** this = this * { sx, sy, sz, sw }, returns this. */ + public Vec4f scale(final float sx, final float sy, final float sz, final float sw) { + x *= sx; + y *= sy; + z *= sz; + w *= sw; + return this; + } + + /** Returns this + arg; creates new vector */ + public Vec4f plus(final Vec4f arg) { + return new Vec4f(this).add(arg); + } + + /** this = a + b, returns this. */ + public Vec4f plus(final Vec4f a, final Vec4f b) { + x = a.x + b.x; + y = a.y + b.y; + z = a.z + b.z; + w = a.w + b.w; + return this; + } + + /** this = this + { dx, dy, dz, dw }, returns this. */ + public Vec4f add(final float dx, final float dy, final float dz, final float dw) { + x += dx; + y += dy; + z += dz; + w += dw; + return this; + } + + /** this = this + b, returns this. */ + public Vec4f add(final Vec4f b) { + x += b.x; + y += b.y; + z += b.z; + w += b.w; + return this; + } + + /** Returns this - arg; creates new vector */ + public Vec4f minus(final Vec4f arg) { + return new Vec4f(this).sub(arg); + } + + /** this = a - b, returns this. */ + public Vec4f minus(final Vec4f a, final Vec4f b) { + x = a.x - b.x; + y = a.y - b.y; + z = a.z - b.z; + w = a.w - b.w; + return this; + } + + /** this = this - b, returns this. */ + public Vec4f sub(final Vec4f b) { + x -= b.x; + y -= b.y; + z -= b.z; + w -= b.w; + return this; + } + + /** Return true if all components are zero, i.e. it's absolute value < {@link #EPSILON}. */ + public boolean isZero() { + return FloatUtil.isZero(x) && FloatUtil.isZero(y) && FloatUtil.isZero(z) && FloatUtil.isZero(w); + } + + /** + * Return the length of this vector, a.k.a the <i>norm</i> or <i>magnitude</i> + */ + public float length() { + return (float) Math.sqrt(lengthSq()); + } + + /** + * Return the squared length of this vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> + */ + public float lengthSq() { + return x*x + y*y + z*z + w*w; + } + + /** + * Normalize this vector in place + */ + public Vec4f normalize() { + final float lengthSq = lengthSq(); + if ( FloatUtil.isZero( lengthSq ) ) { + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 0.0f; + } else { + final float invSqr = 1.0f / (float)Math.sqrt(lengthSq); + x *= invSqr; + y *= invSqr; + z *= invSqr; + w *= invSqr; + } + return this; + } + + /** + * Return the squared distance between this vector and the given one. + * <p> + * When comparing the relative distance between two points it is usually sufficient to compare the squared + * distances, thus avoiding an expensive square root operation. + * </p> + */ + public float distSq(final Vec4f o) { + final float dx = x - o.x; + final float dy = y - o.y; + final float dz = z - o.z; + final float dw = w - o.w; + return dx*dx + dy*dy + dz*dz + dw*dw; + } + + /** + * Return the distance between this vector and the given one. + */ + public float dist(final Vec4f o) { + return (float)Math.sqrt(distSq(o)); + } + + + /** + * Return the dot product of this vector and the given one + * @return the dot product as float + */ + public float dot(final Vec4f o) { + return x*o.x + y*o.y + z*o.z + w*o.w; + } + + /** + * Return the cosines of the angle between two vectors + */ + public float cosAngle(final Vec4f o) { + return dot(o) / ( length() * o.length() ) ; + } + + /** + * Return the angle between two vectors in radians + */ + public float angle(final Vec4f o) { + return (float) Math.acos( cosAngle(o) ); + } + + /** + * Equals check using a given {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @param epsilon consider using {@link FloatUtil#EPSILON} + * @return true if all components differ less than {@code epsilon}, otherwise false. + */ + public boolean isEqual(final Vec4f o, final float epsilon) { + if( this == o ) { + return true; + } else { + return FloatUtil.isEqual(x, o.x, epsilon) && + FloatUtil.isEqual(y, o.y, epsilon) && + FloatUtil.isEqual(z, o.z, epsilon) && + FloatUtil.isEqual(w, o.w, epsilon); + } + } + + /** + * Equals check using {@link FloatUtil#EPSILON} value and {@link FloatUtil#isEqual(float, float, float)}. + * <p> + * Implementation considers following corner cases: + * <ul> + * <li>NaN == NaN</li> + * <li>+Inf == +Inf</li> + * <li>-Inf == -Inf</li> + * </ul> + * @param o comparison value + * @return true if all components differ less than {@link FloatUtil#EPSILON}, otherwise false. + */ + public boolean isEqual(final Vec4f o) { + return isEqual(o, FloatUtil.EPSILON); + } + + @Override + public boolean equals(final Object o) { + if( o instanceof Vec4f ) { + return isEqual((Vec4f)o, FloatUtil.EPSILON); + } else { + return false; + } + } + + @Override + public String toString() { + return x + " / " + y + " / " + z + " / " + w; + } +} diff --git a/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java index a07153155..d3b2c3cfd 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -32,145 +32,11 @@ import java.util.ArrayList; import com.jogamp.graph.geom.plane.Winding; public final class VectorUtil { - - public static final float[] VEC3_ONE = { 1f, 1f, 1f }; - public static final float[] VEC3_ZERO = { 0f, 0f, 0f }; - public static final float[] VEC3_UNIT_Y = { 0f, 1f, 0f }; - public static final float[] VEC3_UNIT_Y_NEG = { 0f, -1f, 0f }; - public static final float[] VEC3_UNIT_Z = { 0f, 0f, 1f }; - public static final float[] VEC3_UNIT_Z_NEG = { 0f, 0f, -1f }; - - /** - * Copies a vector of length 2 - * @param dst output vector - * @param dstOffset offset of dst in array - * @param src input vector - * @param srcOffset offset of src in array - * @return copied output vector for chaining - */ - public static float[] copyVec2(final float[] dst, final int dstOffset, final float[] src, final int srcOffset) - { - System.arraycopy(src, srcOffset, dst, dstOffset, 2); - return dst; - } - - /** - * Copies a vector of length 3 - * @param dst output vector - * @param dstOffset offset of dst in array - * @param src input vector - * @param srcOffset offset of src in array - * @return copied output vector for chaining - */ - public static float[] copyVec3(final float[] dst, final int dstOffset, final float[] src, final int srcOffset) - { - System.arraycopy(src, srcOffset, dst, dstOffset, 3); - return dst; - } - - /** - * Copies a vector of length 4 - * @param dst output vector - * @param dstOffset offset of dst in array - * @param src input vector - * @param srcOffset offset of src in array - * @return copied output vector for chaining - */ - public static float[] copyVec4(final float[] dst, final int dstOffset, final float[] src, final int srcOffset) - { - System.arraycopy(src, srcOffset, dst, dstOffset, 4); - return dst; - } - - /** - * Return true if both vectors are equal w/o regarding an epsilon. - * <p> - * Implementation uses {@link FloatUtil#isEqual(float, float)}, see API doc for details. - * </p> - */ - public static boolean isVec2Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset) { - return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset]) && - FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset]) ; - } - - /** - * Return true if both vectors are equal w/o regarding an epsilon. - * <p> - * Implementation uses {@link FloatUtil#isEqual(float, float)}, see API doc for details. - * </p> - */ - public static boolean isVec3Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset) { - return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset]) && - FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset]) && - FloatUtil.isEqual(vec1[2+vec1Offset], vec2[2+vec2Offset]) ; - } - - /** - * Return true if both vectors are equal, i.e. their absolute delta < <code>epsilon</code>. - * <p> - * Implementation uses {@link FloatUtil#isEqual(float, float, float)}, see API doc for details. - * </p> - */ - public static boolean isVec2Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset, final float epsilon) { - return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset], epsilon) && - FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset], epsilon) ; - } - /** - * Return true if both vectors are equal, i.e. their absolute delta < <code>epsilon</code>. - * <p> - * Implementation uses {@link FloatUtil#isEqual(float, float, float)}, see API doc for details. - * </p> - */ - public static boolean isVec3Equal(final float[] vec1, final int vec1Offset, final float[] vec2, final int vec2Offset, final float epsilon) { - return FloatUtil.isEqual(vec1[0+vec1Offset], vec2[0+vec2Offset], epsilon) && - FloatUtil.isEqual(vec1[1+vec1Offset], vec2[1+vec2Offset], epsilon) && - FloatUtil.isEqual(vec1[2+vec1Offset], vec2[2+vec2Offset], epsilon) ; - } - - /** - * Return true if vector is zero, no {@link FloatUtil#EPSILON} is taken into consideration. - */ - public static boolean isVec2Zero(final float[] vec, final int vecOffset) { - return 0f == vec[0+vecOffset] && 0f == vec[1+vecOffset]; - } - - /** - * Return true if vector is zero, no {@link FloatUtil#EPSILON} is taken into consideration. - */ - public static boolean isVec3Zero(final float[] vec, final int vecOffset) { - return 0f == vec[0+vecOffset] && 0f == vec[1+vecOffset] && 0f == vec[2+vecOffset]; - } - - /** - * Return true if vector is zero, i.e. it's absolute components < <code>epsilon</code>. - * <p> - * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. - * </p> - */ - public static boolean isVec2Zero(final float[] vec, final int vecOffset, final float epsilon) { - return isZero(vec[0+vecOffset], vec[1+vecOffset], epsilon); - } - - /** - * Return true if vector is zero, i.e. it's absolute components < <code>epsilon</code>. - * <p> - * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. - * </p> - */ - public static boolean isVec3Zero(final float[] vec, final int vecOffset, final float epsilon) { - return isZero(vec[0+vecOffset], vec[1+vecOffset], vec[2+vecOffset], epsilon); - } - - /** - * Return true if all two vector components are zero, i.e. it's their absolute value < <code>epsilon</code>. - * <p> - * Implementation uses {@link FloatUtil#isZero(float, float)}, see API doc for details. - * </p> + * Return true if 2D vector components are zero, no {@link FloatUtil#EPSILON} is taken into consideration. */ - public static boolean isZero(final float x, final float y, final float epsilon) { - return FloatUtil.isZero(x, epsilon) && - FloatUtil.isZero(y, epsilon) ; + public static boolean isVec2Zero(final Vec3f vec) { + return 0f == vec.x() && 0f == vec.y(); } /** @@ -207,34 +73,6 @@ public final class VectorUtil { } /** - * Return the dot product of two points - * @param vec1 vector 1 - * @param vec2 vector 2 - * @return the dot product as float - */ - public static float dotVec3(final float[] vec1, final float[] vec2) { - return vec1[0]*vec2[0] + vec1[1]*vec2[1] + vec1[2]*vec2[2]; - } - - /** - * Return the cosines of the angle between to vectors - * @param vec1 vector 1 - * @param vec2 vector 2 - */ - public static float cosAngleVec3(final float[] vec1, final float[] vec2) { - return dotVec3(vec1, vec2) / ( normVec3(vec1) * normVec3(vec2) ) ; - } - - /** - * Return the angle between to vectors in radians - * @param vec1 vector 1 - * @param vec2 vector 2 - */ - public static float angleVec3(final float[] vec1, final float[] vec2) { - return FloatUtil.acos(cosAngleVec3(vec1, vec2)); - } - - /** * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> */ public static float normSquareVec2(final float[] vec) { @@ -244,16 +82,6 @@ public final class VectorUtil { /** * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> */ - public static float normSquareVec2(final float[] vec, final int offset) { - float v = vec[0+offset]; - final float r = v*v; - v = vec[1+offset]; - return r + v*v; - } - - /** - * Return the squared length of a vector, a.k.a the squared <i>norm</i> or squared <i>magnitude</i> - */ public static float normSquareVec3(final float[] vec) { return vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]; } @@ -278,73 +106,6 @@ public final class VectorUtil { } /** - * Return the length of a vector, a.k.a the <i>norm</i> or <i>magnitude</i> - */ - public static float normVec3(final float[] vec) { - return FloatUtil.sqrt(normSquareVec3(vec)); - } - - /** - * Normalize a vector - * @param result output vector, may be vector (in-place) - * @param vector input vector - * @return normalized output vector - * @return result vector for chaining - */ - public static float[] normalizeVec2(final float[] result, final float[] vector) { - final float lengthSq = normSquareVec2(vector); - if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { - result[0] = 0f; - result[1] = 0f; - } else { - final float invSqr = 1f / FloatUtil.sqrt(lengthSq); - result[0] = vector[0] * invSqr; - result[1] = vector[1] * invSqr; - } - return result; - } - - /** - * Normalize a vector in place - * @param vector input vector - * @return normalized output vector - */ - public static float[] normalizeVec2(final float[] vector) { - final float lengthSq = normSquareVec2(vector); - if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { - vector[0] = 0f; - vector[1] = 0f; - } else { - final float invSqr = 1f / FloatUtil.sqrt(lengthSq); - vector[0] *= invSqr; - vector[1] *= invSqr; - } - return vector; - } - - /** - * Normalize a vector - * @param result output vector, may be vector (in-place) - * @param vector input vector - * @return normalized output vector - * @return result vector for chaining - */ - public static float[] normalizeVec3(final float[] result, final float[] vector) { - final float lengthSq = normSquareVec3(vector); - if ( FloatUtil.isZero(lengthSq, FloatUtil.EPSILON) ) { - result[0] = 0f; - result[1] = 0f; - result[2] = 0f; - } else { - final float invSqr = 1f / FloatUtil.sqrt(lengthSq); - result[0] = vector[0] * invSqr; - result[1] = vector[1] * invSqr; - result[2] = vector[2] * invSqr; - } - return result; - } - - /** * Normalize a vector in place * @param vector input vector * @return normalized output vector @@ -401,35 +162,6 @@ public final class VectorUtil { * Scales a vector by param using given result float[], result = vector * scale * @param result vector for the result, may be vector (in-place) * @param vector input vector - * @param scale single scale constant for all vector components - * @return result vector for chaining - */ - public static float[] scaleVec3(final float[] result, final float[] vector, final float scale) { - result[0] = vector[0] * scale; - result[1] = vector[1] * scale; - result[2] = vector[2] * scale; - return result; - } - - /** - * Scales a vector by param using given result float[], result = vector * scale - * @param result vector for the result, may be vector (in-place) - * @param vector input vector - * @param scale 3 component scale constant for each vector component - * @return result vector for chaining - */ - public static float[] scaleVec3(final float[] result, final float[] vector, final float[] scale) - { - result[0] = vector[0] * scale[0]; - result[1] = vector[1] * scale[1]; - result[2] = vector[2] * scale[2]; - return result; - } - - /** - * Scales a vector by param using given result float[], result = vector * scale - * @param result vector for the result, may be vector (in-place) - * @param vector input vector * @param scale 2 component scale constant for each vector component * @return result vector for chaining */ @@ -457,35 +189,6 @@ public final class VectorUtil { * Divides a vector by param using given result float[], result = vector / scale * @param result vector for the result, may be vector (in-place) * @param vector input vector - * @param scale single scale constant for all vector components - * @return result vector for chaining - */ - public static float[] divVec3(final float[] result, final float[] vector, final float scale) { - result[0] = vector[0] / scale; - result[1] = vector[1] / scale; - result[2] = vector[2] / scale; - return result; - } - - /** - * Divides a vector by param using given result float[], result = vector / scale - * @param result vector for the result, may be vector (in-place) - * @param vector input vector - * @param scale 3 component scale constant for each vector component - * @return result vector for chaining - */ - public static float[] divVec3(final float[] result, final float[] vector, final float[] scale) - { - result[0] = vector[0] / scale[0]; - result[1] = vector[1] / scale[1]; - result[2] = vector[2] / scale[2]; - return result; - } - - /** - * Divides a vector by param using given result float[], result = vector / scale - * @param result vector for the result, may be vector (in-place) - * @param vector input vector * @param scale 2 component scale constant for each vector component * @return result vector for chaining */ @@ -510,20 +213,6 @@ public final class VectorUtil { } /** - * Adds two vectors, result = v1 + v2 - * @param result float[3] result vector, may be either v1 or v2 (in-place) - * @param v1 vector 1 - * @param v2 vector 2 - * @return result vector for chaining - */ - public static float[] addVec3(final float[] result, final float[] v1, final float[] v2) { - result[0] = v1[0] + v2[0]; - result[1] = v1[1] + v2[1]; - result[2] = v1[2] + v2[2]; - return result; - } - - /** * Subtracts two vectors, result = v1 - v2 * @param result float[2] result vector, may be either v1 or v2 (in-place) * @param v1 vector 1 @@ -537,34 +226,6 @@ public final class VectorUtil { } /** - * Subtracts two vectors, result = v1 - v2 - * @param result float[3] result vector, may be either v1 or v2 (in-place) - * @param v1 vector 1 - * @param v2 vector 2 - * @return result vector for chaining - */ - public static float[] subVec3(final float[] result, final float[] v1, final float[] v2) { - result[0] = v1[0] - v2[0]; - result[1] = v1[1] - v2[1]; - result[2] = v1[2] - v2[2]; - return result; - } - - /** - * cross product vec1 x vec2 - * @param v1 vector 1 - * @param v2 vector 2 - * @return the resulting vector - */ - public static float[] crossVec3(final float[] result, final float[] v1, final float[] v2) - { - result[0] = v1[1] * v2[2] - v1[2] * v2[1]; - result[1] = v1[2] * v2[0] - v1[0] * v2[2]; - result[2] = v1[0] * v2[1] - v1[1] * v2[0]; - return result; - } - - /** * cross product vec1 x vec2 * @param v1 vector 1 * @param v2 vector 2 @@ -579,55 +240,15 @@ public final class VectorUtil { } /** - * Multiplication of column-major 4x4 matrix with vector - * @param colMatrix column matrix (4x4) - * @param vec vector(x,y,z) - * @return result - */ - public static float[] mulColMat4Vec3(final float[] result, final float[] colMatrix, final float[] vec) - { - result[0] = vec[0]*colMatrix[0] + vec[1]*colMatrix[4] + vec[2]*colMatrix[8] + colMatrix[12]; - result[1] = vec[0]*colMatrix[1] + vec[1]*colMatrix[5] + vec[2]*colMatrix[9] + colMatrix[13]; - result[2] = vec[0]*colMatrix[2] + vec[1]*colMatrix[6] + vec[2]*colMatrix[10] + colMatrix[14]; - - return result; - } - - /** - * Matrix Vector multiplication - * @param rawMatrix column matrix (4x4) - * @param vec vector(x,y,z) - * @return result - */ - public static float[] mulRowMat4Vec3(final float[] result, final float[] rawMatrix, final float[] vec) - { - result[0] = vec[0]*rawMatrix[0] + vec[1]*rawMatrix[1] + vec[2]*rawMatrix[2] + rawMatrix[3]; - result[1] = vec[0]*rawMatrix[4] + vec[1]*rawMatrix[5] + vec[2]*rawMatrix[6] + rawMatrix[7]; - result[2] = vec[0]*rawMatrix[8] + vec[1]*rawMatrix[9] + vec[2]*rawMatrix[10] + rawMatrix[11]; - - return result; - } - - /** - * Calculate the midpoint of two values - * @param p1 first value - * @param p2 second vale - * @return midpoint - */ - public static float mid(final float p1, final float p2) { - return (p1+p2)*0.5f; - } - - /** * Calculate the midpoint of two points * @param p1 first point vector * @param p2 second point vector * @return midpoint */ - public static float[] midVec3(final float[] result, final float[] p1, final float[] p2) { - result[0] = (p1[0] + p2[0])*0.5f; - result[1] = (p1[1] + p2[1])*0.5f; - result[2] = (p1[2] + p2[2])*0.5f; + public static Vec3f midVec3(final Vec3f result, final Vec3f p1, final Vec3f p2) { + result.set( (p1.x() + p2.x())*0.5f, + (p1.y() + p2.y())*0.5f, + (p1.z() + p2.z())*0.5f ); return result; } @@ -638,8 +259,8 @@ public final class VectorUtil { * @param c vector 3 * @return the determinant value */ - public static float determinantVec3(final float[] a, final float[] b, final float[] c) { - return a[0]*b[1]*c[2] + a[1]*b[2]*c[0] + a[2]*b[0]*c[1] - a[0]*b[2]*c[1] - a[1]*b[0]*c[2] - a[2]*b[1]*c[0]; + public static float determinantVec3(final Vec3f a, final Vec3f b, final Vec3f c) { + return a.x()*b.y()*c.z() + a.y()*b.z()*c.x() + a.z()*b.x()*c.y() - a.x()*b.z()*c.y() - a.y()*b.x()*c.z() - a.z()*b.y()*c.x(); } /** @@ -649,7 +270,7 @@ public final class VectorUtil { * @param v3 vertex 3 * @return true if collinear, false otherwise */ - public static boolean isCollinearVec3(final float[] v1, final float[] v2, final float[] v3) { + public static boolean isCollinearVec3(final Vec3f v1, final Vec3f v2, final Vec3f v3) { return FloatUtil.isZero( determinantVec3(v1, v2, v3), FloatUtil.EPSILON ); } @@ -663,14 +284,10 @@ public final class VectorUtil { * vertices a, b, c. from paper by Guibas and Stolfi (1985). */ public static boolean isInCircleVec2(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d) { - final float[] A = a.getCoord(); - final float[] B = b.getCoord(); - final float[] C = c.getCoord(); - final float[] D = d.getCoord(); - return (A[0] * A[0] + A[1] * A[1]) * triAreaVec2(B, C, D) - - (B[0] * B[0] + B[1] * B[1]) * triAreaVec2(A, C, D) + - (C[0] * C[0] + C[1] * C[1]) * triAreaVec2(A, B, D) - - (D[0] * D[0] + D[1] * D[1]) * triAreaVec2(A, B, C) > 0; + return (a.x() * a.x() + a.y() * a.y()) * triAreaVec2(b, c, d) - + (b.x() * b.x() + b.y() * b.y()) * triAreaVec2(a, c, d) + + (c.x() * c.x() + c.y() * c.y()) * triAreaVec2(a, b, d) - + (d.x() * d.x() + d.y() * d.y()) * triAreaVec2(a, b, c) > 0; } /** @@ -682,47 +299,34 @@ public final class VectorUtil { * is positive if the triangle is oriented counterclockwise. */ public static float triAreaVec2(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c){ - final float[] A = a.getCoord(); - final float[] B = b.getCoord(); - final float[] C = c.getCoord(); - return (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]); - } - - /** - * Computes oriented area of a triangle - * @param A first vertex - * @param B second vertex - * @param C third vertex - * @return compute twice the area of the oriented triangle (a,b,c), the area - * is positive if the triangle is oriented counterclockwise. - */ - public static float triAreaVec2(final float[] A, final float[] B, final float[] C){ - return (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1])*(C[0] - A[0]); + return (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x()); } /** - * Check if a vertex is in triangle using - * barycentric coordinates computation. + * Check if a vertex is in triangle using barycentric coordinates computation. * @param a first triangle vertex * @param b second triangle vertex * @param c third triangle vertex * @param p the vertex in question + * @param ac temporary storage + * @param ab temporary storage + * @param ap temporary storage * @return true if p is in triangle (a, b, c), false otherwise. */ - public static boolean isInTriangleVec3(final float[] a, final float[] b, final float[] c, - final float[] p, - final float[] ac, final float[] ab, final float[] ap){ + public static boolean isInTriangleVec3(final Vec3f a, final Vec3f b, final Vec3f c, + final Vec3f p, + final Vec3f ac, final Vec3f ab, final Vec3f ap){ // Compute vectors - subVec3(ac, c, a); //v0 - subVec3(ab, b, a); //v1 - subVec3(ap, p, a); //v2 + ac.minus( c, a); // v0 + ab.minus( b, a); // v1 + ap.minus( p, a); // v2 // Compute dot products - final float dotAC_AC = dotVec3(ac, ac); - final float dotAC_AB = dotVec3(ac, ab); - final float dotAB_AB = dotVec3(ab, ab); - final float dotAC_AP = dotVec3(ac, ap); - final float dotAB_AP = dotVec3(ab, ap); + final float dotAC_AC = ac.dot(ac); + final float dotAC_AB = ac.dot(ab); + final float dotAB_AB = ab.dot(ab); + final float dotAC_AP = ac.dot(ap); + final float dotAB_AP = ab.dot(ap); // Compute barycentric coordinates final float invDenom = 1 / (dotAC_AC * dotAB_AB - dotAC_AB * dotAC_AB); @@ -734,37 +338,36 @@ public final class VectorUtil { } /** - * Check if one of three vertices are in triangle using - * barycentric coordinates computation. + * Check if one of three vertices are in triangle using barycentric coordinates computation. * @param a first triangle vertex * @param b second triangle vertex * @param c third triangle vertex * @param p1 the vertex in question * @param p2 the vertex in question * @param p3 the vertex in question - * @param tmpAC - * @param tmpAB - * @param tmpAP + * @param ac temporary storage + * @param ab temporary storage + * @param ap temporary storage * @return true if p1 or p2 or p3 is in triangle (a, b, c), false otherwise. */ - public static boolean isVec3InTriangle3(final float[] a, final float[] b, final float[] c, - final float[] p1, final float[] p2, final float[] p3, - final float[] tmpAC, final float[] tmpAB, final float[] tmpAP){ + public static boolean isVec3InTriangle3(final Vec3f a, final Vec3f b, final Vec3f c, + final Vec3f p1, final Vec3f p2, final Vec3f p3, + final Vec3f ac, final Vec3f ab, final Vec3f ap){ // Compute vectors - subVec3(tmpAC, c, a); //v0 - subVec3(tmpAB, b, a); //v1 + ac.minus(c, a); // v0 + ab.minus(b, a); // v1 // Compute dot products - final float dotAC_AC = dotVec3(tmpAC, tmpAC); - final float dotAC_AB = dotVec3(tmpAC, tmpAB); - final float dotAB_AB = dotVec3(tmpAB, tmpAB); + final float dotAC_AC = ac.dot(ac); + final float dotAC_AB = ac.dot(ab); + final float dotAB_AB = ab.dot(ab); // Compute barycentric coordinates final float invDenom = 1 / (dotAC_AC * dotAB_AB - dotAC_AB * dotAC_AB); { - subVec3(tmpAP, p1, a); //v2 - final float dotAC_AP1 = dotVec3(tmpAC, tmpAP); - final float dotAB_AP1 = dotVec3(tmpAB, tmpAP); + ap.minus(p1, a); // v2 + final float dotAC_AP1 = ac.dot(ap); + final float dotAB_AP1 = ab.dot(ap); final float u = (dotAB_AB * dotAC_AP1 - dotAC_AB * dotAB_AP1) * invDenom; final float v = (dotAC_AC * dotAB_AP1 - dotAC_AB * dotAC_AP1) * invDenom; @@ -775,9 +378,9 @@ public final class VectorUtil { } { - subVec3(tmpAP, p1, a); //v2 - final float dotAC_AP2 = dotVec3(tmpAC, tmpAP); - final float dotAB_AP2 = dotVec3(tmpAB, tmpAP); + ap.minus(p2, a); // v2 + final float dotAC_AP2 = ac.dot(ap); + final float dotAB_AP2 = ab.dot(ap); final float u = (dotAB_AB * dotAC_AP2 - dotAC_AB * dotAB_AP2) * invDenom; final float v = (dotAC_AC * dotAB_AP2 - dotAC_AB * dotAC_AP2) * invDenom; @@ -788,9 +391,9 @@ public final class VectorUtil { } { - subVec3(tmpAP, p2, a); //v2 - final float dotAC_AP3 = dotVec3(tmpAC, tmpAP); - final float dotAB_AP3 = dotVec3(tmpAB, tmpAP); + ap.minus(p3, a); // v3 + final float dotAC_AP3 = ac.dot(ap); + final float dotAB_AP3 = ab.dot(ap); final float u = (dotAB_AB * dotAC_AP3 - dotAC_AB * dotAB_AP3) * invDenom; final float v = (dotAC_AC * dotAB_AP3 - dotAC_AB * dotAC_AP3) * invDenom; @@ -815,25 +418,25 @@ public final class VectorUtil { * @param tmpAP * @return true if p1 or p2 or p3 is in triangle (a, b, c), false otherwise. */ - public static boolean isVec3InTriangle3(final float[] a, final float[] b, final float[] c, - final float[] p1, final float[] p2, final float[] p3, - final float[] tmpAC, final float[] tmpAB, final float[] tmpAP, - final float epsilon){ + public static boolean isVec3InTriangle3(final Vec3f a, final Vec3f b, final Vec3f c, + final Vec3f p1, final Vec3f p2, final Vec3f p3, + final Vec3f ac, final Vec3f ab, final Vec3f ap, + final float epsilon) { // Compute vectors - subVec3(tmpAC, c, a); //v0 - subVec3(tmpAB, b, a); //v1 + ac.minus(c, a); // v0 + ab.minus(b, a); // v1 // Compute dot products - final float dotAC_AC = dotVec3(tmpAC, tmpAC); - final float dotAC_AB = dotVec3(tmpAC, tmpAB); - final float dotAB_AB = dotVec3(tmpAB, tmpAB); + final float dotAC_AC = ac.dot(ac); + final float dotAC_AB = ac.dot(ab); + final float dotAB_AB = ab.dot(ab); // Compute barycentric coordinates final float invDenom = 1 / (dotAC_AC * dotAB_AB - dotAC_AB * dotAC_AB); { - subVec3(tmpAP, p1, a); //v2 - final float dotAC_AP1 = dotVec3(tmpAC, tmpAP); - final float dotAB_AP1 = dotVec3(tmpAB, tmpAP); + ap.minus(p1, a); // v2 + final float dotAC_AP1 = ac.dot(ap); + final float dotAB_AP1 = ab.dot(ap); final float u = (dotAB_AB * dotAC_AP1 - dotAC_AB * dotAB_AP1) * invDenom; final float v = (dotAC_AC * dotAB_AP1 - dotAC_AB * dotAC_AP1) * invDenom; @@ -846,9 +449,9 @@ public final class VectorUtil { } { - subVec3(tmpAP, p1, a); //v2 - final float dotAC_AP2 = dotVec3(tmpAC, tmpAP); - final float dotAB_AP2 = dotVec3(tmpAB, tmpAP); + ap.minus(p2, a); // v3 + final float dotAC_AP2 = ac.dot(ap); + final float dotAB_AP2 = ab.dot(ap); final float u = (dotAB_AB * dotAC_AP2 - dotAC_AB * dotAB_AP2) * invDenom; final float v = (dotAC_AC * dotAB_AP2 - dotAC_AB * dotAC_AP2) * invDenom; @@ -861,9 +464,9 @@ public final class VectorUtil { } { - subVec3(tmpAP, p2, a); //v2 - final float dotAC_AP3 = dotVec3(tmpAC, tmpAP); - final float dotAB_AP3 = dotVec3(tmpAB, tmpAP); + ap.minus(p3, a); // v4 + final float dotAC_AP3 = ac.dot(ap); + final float dotAB_AP3 = ab.dot(ap); final float u = (dotAB_AB * dotAC_AP3 - dotAC_AB * dotAB_AP3) * invDenom; final float v = (dotAC_AC * dotAB_AP3 - dotAC_AB * dotAC_AP3) * invDenom; @@ -874,7 +477,6 @@ public final class VectorUtil { return true; } } - return false; } @@ -918,9 +520,9 @@ public final class VectorUtil { final int n = vertices.size(); float area = 0.0f; for (int p = n - 1, q = 0; q < n; p = q++) { - final float[] pCoord = vertices.get(p).getCoord(); - final float[] qCoord = vertices.get(q).getCoord(); - area += pCoord[0] * qCoord[1] - qCoord[0] * pCoord[1]; + final Vert2fImmutable pCoord = vertices.get(p); + final Vert2fImmutable qCoord = vertices.get(q); + area += pCoord.x() * qCoord.y() - qCoord.x() * pCoord.y(); } return area; } @@ -940,36 +542,6 @@ public final class VectorUtil { } /** - * @param result vec2 result for normal - * @param v1 vec2 - * @param v2 vec2 - * @return result for chaining - */ - public static float[] getNormalVec2(final float[] result, final float[] v1, final float[] v2 ) { - subVec2(result, v2, v1); - final float tmp = result [ 0 ] ; result [ 0 ] = -result [ 1 ] ; result [ 1 ] = tmp ; - return normalizeVec2 ( result ) ; - } - - /** - * Returns the 3d surface normal of a triangle given three vertices. - * - * @param result vec3 result for normal - * @param v1 vec3 - * @param v2 vec3 - * @param v3 vec3 - * @param tmp1Vec3 temp vec3 - * @param tmp2Vec3 temp vec3 - * @return result for chaining - */ - public static float[] getNormalVec3(final float[] result, final float[] v1, final float[] v2, final float[] v3, - final float[] tmp1Vec3, final float[] tmp2Vec3) { - subVec3 ( tmp1Vec3, v2, v1 ); - subVec3 ( tmp2Vec3, v3, v1 ) ; - return normalizeVec3 ( crossVec3(result, tmp1Vec3, tmp2Vec3) ) ; - } - - /** * Finds the plane equation of a plane given its normal and a point on the plane. * * @param resultV4 vec4 plane equation @@ -977,15 +549,14 @@ public final class VectorUtil { * @param pVec3 * @return result for chaining */ - public static float[] getPlaneVec3(final float[/*4*/] resultV4, final float[] normalVec3, final float[] pVec3) { + public static Vec4f getPlaneVec3(final Vec4f resultV4, final Vec3f normalVec3, final Vec3f pVec3) { /** Ax + By + Cz + D == 0 ; D = - ( Ax + By + Cz ) = - ( A*a[0] + B*a[1] + C*a[2] ) = - vec3Dot ( normal, a ) ; */ - System.arraycopy(normalVec3, 0, resultV4, 0, 3); - resultV4 [ 3 ] = -dotVec3(normalVec3, pVec3) ; + resultV4.set(normalVec3, -normalVec3.dot(pVec3)); return resultV4; } @@ -1000,16 +571,16 @@ public final class VectorUtil { * @param temp2V3 * @return result for chaining */ - public static float[] getPlaneVec3(final float[/*4*/] resultVec4, final float[] v1, final float[] v2, final float[] v3, - final float[] temp1V3, final float[] temp2V3) { + public static Vec4f getPlaneVec3(final Vec4f resultVec4, final Vec3f v1, final Vec3f v2, final Vec3f v3, + final Vec3f temp1V3, final Vec3f temp2V3, final Vec3f temp3V3) { /** Ax + By + Cz + D == 0 ; D = - ( Ax + By + Cz ) = - ( A*a[0] + B*a[1] + C*a[2] ) = - vec3Dot ( normal, a ) ; */ - getNormalVec3( resultVec4, v1, v2, v3, temp1V3, temp2V3 ) ; - resultVec4 [ 3 ] = -dotVec3 (resultVec4, v1) ; + temp3V3.cross(temp1V3.minus(v2, v1), temp2V3.minus(v3, v1)).normalize(); + resultVec4.set(temp3V3, -temp3V3.dot(v1)); return resultVec4; } @@ -1025,14 +596,15 @@ public final class VectorUtil { * @param epsilon * @return resulting intersecting if exists, otherwise null */ - public static float[] line2PlaneIntersection(final float[] result, final Ray ray, final float[/*4*/] plane, final float epsilon) { - final float tmp = dotVec3(ray.dir, plane) ; + public static Vec3f line2PlaneIntersection(final Vec3f result, final Ray ray, final Vec4f plane, final float epsilon) { + final Vec3f plane3 = new Vec3f(plane); + final float tmp = ray.dir.dot(plane3); if ( Math.abs(tmp) < epsilon ) { return null; // ray is parallel to plane } - scaleVec3 ( result, ray.dir, -( dotVec3(ray.orig, plane) + plane[3] ) / tmp ) ; - return addVec3(result, result, ray.orig); + result.set( ray.dir ); + return result.scale( -( ray.orig.dot(plane3) + plane.w() ) / tmp ).add(ray.orig); } /** Compute intersection between two segments @@ -1042,26 +614,23 @@ public final class VectorUtil { * @param d vertex 2 of second segment * @return the intersection coordinates if the segments intersect, otherwise returns null */ - public static float[] seg2SegIntersection(final float[] result, final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d) { - final float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); + public static Vec3f seg2SegIntersection(final Vec3f result, final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d) { + final float determinant = (a.x()-b.x())*(c.y()-d.y()) - (a.y()-b.y())*(c.x()-d.x()); if (determinant == 0) return null; - final float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); - final float beta = (c.getX()*d.getY()-c.getY()*d.getY()); - final float xi = ((c.getX()-d.getX())*alpha-(a.getX()-b.getX())*beta)/determinant; - final float yi = ((c.getY()-d.getY())*alpha-(a.getY()-b.getY())*beta)/determinant; + final float alpha = (a.x()*b.y()-a.y()*b.x()); + final float beta = (c.x()*d.y()-c.y()*d.y()); + final float xi = ((c.x()-d.x())*alpha-(a.x()-b.x())*beta)/determinant; + final float yi = ((c.y()-d.y())*alpha-(a.y()-b.y())*beta)/determinant; - final float gamma = (xi - a.getX())/(b.getX() - a.getX()); - final float gamma1 = (xi - c.getX())/(d.getX() - c.getX()); + final float gamma = (xi - a.x())/(b.x() - a.x()); + final float gamma1 = (xi - c.x())/(d.x() - c.x()); if(gamma <= 0 || gamma >= 1) return null; if(gamma1 <= 0 || gamma1 >= 1) return null; - result[0] = xi; - result[1] = yi; - result[2] = 0; - return result; + return result.set(xi, yi, 0); } /** @@ -1074,23 +643,18 @@ public final class VectorUtil { */ public static boolean testSeg2SegIntersection(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d) { - final float[] A = a.getCoord(); - final float[] B = b.getCoord(); - final float[] C = c.getCoord(); - final float[] D = d.getCoord(); - - final float determinant = (A[0]-B[0])*(C[1]-D[1]) - (A[1]-B[1])*(C[0]-D[0]); + final float determinant = (a.x()-b.x())*(c.y()-d.y()) - (a.y()-b.y())*(c.x()-d.x()); if (determinant == 0) { return false; } - final float alpha = (A[0]*B[1]-A[1]*B[0]); - final float beta = (C[0]*D[1]-C[1]*D[1]); - final float xi = ((C[0]-D[0])*alpha-(A[0]-B[0])*beta)/determinant; + final float alpha = (a.x()*b.y()-a.y()*b.x()); + final float beta = (c.x()*d.y()-c.y()*d.y()); + final float xi = ((c.x()-d.x())*alpha-(a.x()-b.x())*beta)/determinant; - final float gamma0 = (xi - A[0])/(B[0] - A[0]); - final float gamma1 = (xi - C[0])/(D[0] - C[0]); + final float gamma0 = (xi - a.x())/(b.x() - a.x()); + final float gamma1 = (xi - c.x())/(d.x() - c.x()); if(gamma0 <= 0 || gamma0 >= 1 || gamma1 <= 0 || gamma1 >= 1) { return false; } @@ -1108,23 +672,19 @@ public final class VectorUtil { public static boolean testSeg2SegIntersection(final Vert2fImmutable a, final Vert2fImmutable b, final Vert2fImmutable c, final Vert2fImmutable d, final float epsilon) { - final float[] A = a.getCoord(); - final float[] B = b.getCoord(); - final float[] C = c.getCoord(); - final float[] D = d.getCoord(); - - final float determinant = (A[0]-B[0])*(C[1]-D[1]) - (A[1]-B[1])*(C[0]-D[0]); + final float determinant = (a.x()-b.x())*(c.y()-d.y()) - (a.y()-b.y())*(c.x()-d.x()); if ( FloatUtil.isZero(determinant, epsilon) ) { return false; } - final float alpha = (A[0]*B[1]-A[1]*B[0]); - final float beta = (C[0]*D[1]-C[1]*D[1]); - final float xi = ((C[0]-D[0])*alpha-(A[0]-B[0])*beta)/determinant; + final float alpha = (a.x()*b.y()-a.y()*b.x()); + final float beta = (c.x()*d.y()-c.y()*d.y()); + final float xi = ((c.x()-d.x())*alpha-(a.x()-b.x())*beta)/determinant; + + final float gamma0 = (xi - a.x())/(b.x() - a.x()); + final float gamma1 = (xi - c.x())/(d.x() - c.x()); - final float gamma0 = (xi - A[0])/(B[0] - A[0]); - final float gamma1 = (xi - C[0])/(D[0] - C[0]); if( FloatUtil.compare(gamma0, 0.0f, epsilon) <= 0 || FloatUtil.compare(gamma0, 1.0f, epsilon) >= 0 || FloatUtil.compare(gamma1, 0.0f, epsilon) <= 0 || @@ -1148,23 +708,20 @@ public final class VectorUtil { * @return the intersection coordinates if the lines intersect, otherwise * returns null */ - public static float[] line2lineIntersection(final float[] result, - final Vert2fImmutable a, final Vert2fImmutable b, - final Vert2fImmutable c, final Vert2fImmutable d) { - final float determinant = (a.getX()-b.getX())*(c.getY()-d.getY()) - (a.getY()-b.getY())*(c.getX()-d.getX()); + public static Vec3f line2lineIntersection(final Vec3f result, + final Vert2fImmutable a, final Vert2fImmutable b, + final Vert2fImmutable c, final Vert2fImmutable d) { + final float determinant = (a.x()-b.x())*(c.y()-d.y()) - (a.y()-b.y())*(c.x()-d.x()); if (determinant == 0) return null; - final float alpha = (a.getX()*b.getY()-a.getY()*b.getX()); - final float beta = (c.getX()*d.getY()-c.getY()*d.getY()); - final float xi = ((c.getX()-d.getX())*alpha-(a.getX()-b.getX())*beta)/determinant; - final float yi = ((c.getY()-d.getY())*alpha-(a.getY()-b.getY())*beta)/determinant; + final float alpha = (a.x()*b.y()-a.y()*b.x()); + final float beta = (c.x()*d.y()-c.y()*d.y()); + final float xi = ((c.x()-d.x())*alpha-(a.x()-b.x())*beta)/determinant; + final float yi = ((c.y()-d.y())*alpha-(a.y()-b.y())*beta)/determinant; - result[0] = xi; - result[1] = yi; - result[2] = 0; - return result; + return result.set(xi, yi, 0); } /** diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java b/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java index ec90b401f..66bf078f3 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java +++ b/src/jogl/classes/com/jogamp/opengl/math/Vert2fImmutable.java @@ -28,12 +28,9 @@ package com.jogamp.opengl.math; public interface Vert2fImmutable { - float getX(); + float x(); - float getY(); + float y(); int getCoordCount(); - - float[] getCoord(); - } diff --git a/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java b/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java index 76bd02fbc..6f63a746c 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java +++ b/src/jogl/classes/com/jogamp/opengl/math/Vert3fImmutable.java @@ -28,5 +28,7 @@ package com.jogamp.opengl.math; public interface Vert3fImmutable extends Vert2fImmutable { - float getZ(); + float z(); + + Vec3f getCoord(); } diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java index 234b2121b..531ea99ec 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -29,9 +29,12 @@ package com.jogamp.opengl.math.geom; import com.jogamp.graph.geom.plane.AffineTransform; import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Matrix4f; import com.jogamp.opengl.math.Quaternion; import com.jogamp.opengl.math.Ray; -import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.math.Recti; +import com.jogamp.opengl.math.Vec3f; +import com.jogamp.opengl.util.PMVMatrix; /** @@ -51,13 +54,17 @@ import com.jogamp.opengl.math.VectorUtil; */ public class AABBox { private static final boolean DEBUG = FloatUtil.DEBUG; - private final float[] low = new float[3]; - private final float[] high = new float[3]; - private final float[] center = new float[3]; + private final Vec3f low = new Vec3f(); + private final Vec3f high = new Vec3f(); + private final Vec3f center = new Vec3f(); /** - * Create an Axis Aligned bounding box (AABBox) - * where the low and and high MAX float Values. + * Create an Axis Aligned bounding box (AABBox) with the + * inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. + * <p> + * The dimension, i.e. {@link #getWidth()} abd {@link #getHeight()} is {@link Float#isInfinite()} thereafter. + * </p> + * @see #reset() */ public AABBox() { reset(); @@ -96,48 +103,43 @@ public class AABBox { } /** - * resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. + * Resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. + * <p> + * The dimension, i.e. {@link #getWidth()} abd {@link #getHeight()} is {@link Float#isInfinite()} thereafter. + * </p> * @return this AABBox for chaining */ public final AABBox reset() { setLow(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE); setHigh(-1*Float.MAX_VALUE,-1*Float.MAX_VALUE,-1*Float.MAX_VALUE); - center[0] = 0f; - center[1] = 0f; - center[2] = 0f; + center.set( 0f, 0f, 0f); return this; } /** Get the max xyz-coordinates - * @return a float array containing the max xyz coordinates + * @return max xyz coordinates */ - public final float[] getHigh() { + public final Vec3f getHigh() { return high; } private final void setHigh(final float hx, final float hy, final float hz) { - this.high[0] = hx; - this.high[1] = hy; - this.high[2] = hz; + this.high.set(hx, hy, hz); } /** Get the min xyz-coordinates - * @return a float array containing the min xyz coordinates + * @return min xyz coordinates */ - public final float[] getLow() { + public final Vec3f getLow() { return low; } private final void setLow(final float lx, final float ly, final float lz) { - this.low[0] = lx; - this.low[1] = ly; - this.low[2] = lz; + this.low.set(lx, ly, lz); } private final void computeCenter() { - center[0] = (high[0] + low[0])/2f; - center[1] = (high[1] + low[1])/2f; - center[2] = (high[2] + low[2])/2f; + center.set(high).add(low).scale(1f/2f); } /** @@ -147,9 +149,9 @@ public class AABBox { * @return this AABBox for chaining */ public final AABBox copy(final AABBox src) { - System.arraycopy(src.low, 0, low, 0, 3); - System.arraycopy(src.high, 0, high, 0, 3); - System.arraycopy(src.center, 0, center, 0, 3); + low.set(src.low); + high.set(src.high); + center.set(src.center); return this; } @@ -179,12 +181,23 @@ public class AABBox { */ public final AABBox setSize(final float lx, final float ly, final float lz, final float hx, final float hy, final float hz) { - this.low[0] = lx; - this.low[1] = ly; - this.low[2] = lz; - this.high[0] = hx; - this.high[1] = hy; - this.high[2] = hz; + this.low.set(lx, ly, lz); + this.high.set(hx, hy, hz); + computeCenter(); + return this; + } + + /** + * Set size of the AABBox specifying the coordinates + * of the low and high. + * + * @param low min xyz-coordinates + * @param high max xyz-coordinates + * @return this AABBox for chaining + */ + public final AABBox setSize(final Vec3f low, final Vec3f high) { + this.low.set(low); + this.high.set(high); computeCenter(); return this; } @@ -195,25 +208,30 @@ public class AABBox { * @return this AABBox for chaining */ public final AABBox resize(final AABBox newBox) { - final float[] newLow = newBox.getLow(); - final float[] newHigh = newBox.getHigh(); + final Vec3f newLow = newBox.getLow(); + final Vec3f newHigh = newBox.getHigh(); /** test low */ - if (newLow[0] < low[0]) - low[0] = newLow[0]; - if (newLow[1] < low[1]) - low[1] = newLow[1]; - if (newLow[2] < low[2]) - low[2] = newLow[2]; + if (newLow.x() < low.x()) { + low.setX( newLow.x() ); + } + if (newLow.y() < low.y()) { + low.setY( newLow.y() ); + } + if (newLow.z() < low.z()) { + low.setZ( newLow.z() ); + } /** test high */ - if (newHigh[0] > high[0]) - high[0] = newHigh[0]; - if (newHigh[1] > high[1]) - high[1] = newHigh[1]; - if (newHigh[2] > high[2]) - high[2] = newHigh[2]; - + if (newHigh.x() > high.x()) { + high.setX( newHigh.x() ); + } + if (newHigh.y() > high.y()) { + high.setY( newHigh.y() ); + } + if (newHigh.z() > high.z()) { + high.setZ( newHigh.z() ); + } computeCenter(); return this; } @@ -222,34 +240,32 @@ public class AABBox { * Resize the AABBox to encapsulate another AABox, which will be <i>transformed</i> on the fly first. * @param newBox AABBox to be encapsulated in * @param t the {@link AffineTransform} applied on <i>newBox</i> on the fly - * @param tmpV3 temp float[3] storage + * @param tmpV3 temporary storage * @return this AABBox for chaining */ - public final AABBox resize(final AABBox newBox, final AffineTransform t, final float[] tmpV3) { + public final AABBox resize(final AABBox newBox, final AffineTransform t, final Vec3f tmpV3) { /** test low */ { - final float[] newBoxLow = newBox.getLow(); + final Vec3f newBoxLow = newBox.getLow(); t.transform(newBoxLow, tmpV3); - tmpV3[2] = newBoxLow[2]; - if (tmpV3[0] < low[0]) - low[0] = tmpV3[0]; - if (tmpV3[1] < low[1]) - low[1] = tmpV3[1]; - if (tmpV3[2] < low[2]) - low[2] = tmpV3[2]; + if (tmpV3.x() < low.x()) + low.setX( tmpV3.x() ); + if (tmpV3.y() < low.y()) + low.setY( tmpV3.y() ); + if (tmpV3.z() < low.z()) + low.setZ( tmpV3.z() ); } /** test high */ { - final float[] newBoxHigh = newBox.getHigh(); + final Vec3f newBoxHigh = newBox.getHigh(); t.transform(newBoxHigh, tmpV3); - tmpV3[2] = newBoxHigh[2]; - if (tmpV3[0] > high[0]) - high[0] = tmpV3[0]; - if (tmpV3[1] > high[1]) - high[1] = tmpV3[1]; - if (tmpV3[2] > high[2]) - high[2] = tmpV3[2]; + if (tmpV3.x() > high.x()) + high.setX( tmpV3.x() ); + if (tmpV3.y() > high.y()) + high.setY( tmpV3.y() ); + if (tmpV3.z() > high.z()) + high.setZ( tmpV3.z() ); } computeCenter(); @@ -266,25 +282,25 @@ public class AABBox { */ public final AABBox resize(final float x, final float y, final float z) { /** test low */ - if (x < low[0]) { - low[0] = x; + if (x < low.x()) { + low.setX( x ); } - if (y < low[1]) { - low[1] = y; + if (y < low.y()) { + low.setY( y ); } - if (z < low[2]) { - low[2] = z; + if (z < low.z()) { + low.setZ( z ); } /** test high */ - if (x > high[0]) { - high[0] = x; + if (x > high.x()) { + high.setX( x ); } - if (y > high[1]) { - high[1] = y; + if (y > high.y()) { + high.setY( y ); } - if (z > high[2]) { - high[2] = z; + if (z > high.z()) { + high.setZ( z ); } computeCenter(); @@ -313,6 +329,16 @@ public class AABBox { } /** + * Resize the AABBox to encapsulate the passed + * xyz-coordinates. + * @param xyz xyz-axis coordinate values + * @return this AABBox for chaining + */ + public final AABBox resize(final Vec3f xyz) { + return resize(xyz.x(), xyz.y(), xyz.z()); + } + + /** * Check if the x & y coordinates are bounded/contained * by this AABBox * @param x x-axis coordinate value @@ -321,10 +347,10 @@ public class AABBox { * y belong to (low.y, high.y) */ public final boolean contains(final float x, final float y) { - if(x<low[0] || x>high[0]){ + if(x<low.x() || x>high.x()){ return false; } - if(y<low[1]|| y>high[1]){ + if(y<low.y()|| y>high.y()){ return false; } return true; @@ -340,13 +366,13 @@ public class AABBox { * y belong to (low.y, high.y) and z belong to (low.z, high.z) */ public final boolean contains(final float x, final float y, final float z) { - if(x<low[0] || x>high[0]){ + if(x<low.x() || x>high.x()){ return false; } - if(y<low[1]|| y>high[1]){ + if(y<low.y()|| y>high.y()){ return false; } - if(z<low[2] || z>high[2]){ + if(z<low.z() || z>high.z()){ return false; } return true; @@ -398,19 +424,19 @@ public class AABBox { // diff[XYZ] -> VectorUtil.subVec3(diff, ray.orig, center); // ext[XYZ] -> extend VectorUtil.subVec3(ext, high, center); - final float dirX = ray.dir[0]; - final float diffX = ray.orig[0] - center[0]; - final float extX = high[0] - center[0]; + final float dirX = ray.dir.x(); + final float diffX = ray.orig.x() - center.x(); + final float extX = high.x() - center.x(); if( Math.abs(diffX) > extX && diffX*dirX >= 0f ) return false; - final float dirY = ray.dir[1]; - final float diffY = ray.orig[1] - center[1]; - final float extY = high[1] - center[1]; + final float dirY = ray.dir.y(); + final float diffY = ray.orig.y() - center.y(); + final float extY = high.y() - center.y(); if( Math.abs(diffY) > extY && diffY*dirY >= 0f ) return false; - final float dirZ = ray.dir[2]; - final float diffZ = ray.orig[2] - center[2]; - final float extZ = high[2] - center[2]; + final float dirZ = ray.dir.z(); + final float diffZ = ray.orig.z() - center.z(); + final float extZ = high.z() - center.z(); if( Math.abs(diffZ) > extZ && diffZ*dirZ >= 0f ) return false; final float absDirY = Math.abs(dirY); @@ -460,45 +486,45 @@ public class AABBox { * @param assumeIntersection if true, method assumes an intersection, i.e. by pre-checking via {@link #intersectsRay(Ray)}. * In this case method will not validate a possible non-intersection and just computes * coordinates. - * @param tmp1V3 temp vec3 - * @param tmp2V3 temp vec3 - * @param tmp3V3 temp vec3 * @return float[3] result of intersection coordinates, or null if none exists */ - public final float[] getRayIntersection(final float[] result, final Ray ray, final float epsilon, - final boolean assumeIntersection, - final float[] tmp1V3, final float[] tmp2V3, final float[] tmp3V3) { + public final Vec3f getRayIntersection(final Vec3f result, final Ray ray, final float epsilon, + final boolean assumeIntersection) { final float[] maxT = { -1f, -1f, -1f }; - final float[] origin = ray.orig; - final float[] dir = ray.dir; + final Vec3f origin = ray.orig; + final Vec3f dir = ray.dir; boolean inside = true; // Find candidate planes. for(int i=0; i<3; i++) { - if(origin[i] < low[i]) { - result[i] = low[i]; + final float origin_i = origin.get(i); + final float dir_i = dir.get(i); + final float low_i = low.get(i); + final float high_i = high.get(i); + if(origin_i < low_i) { + result.set(i, low_i); inside = false; // Calculate T distances to candidate planes - if( 0 != Float.floatToIntBits(dir[i]) ) { - maxT[i] = (low[i] - origin[i]) / dir[i]; + if( 0 != Float.floatToIntBits(dir_i) ) { + maxT[i] = (low_i - origin_i) / dir_i; } - } else if(origin[i] > high[i]) { - result[i] = high[i]; + } else if(origin_i > high_i) { + result.set(i, high_i); inside = false; // Calculate T distances to candidate planes - if( 0 != Float.floatToIntBits(dir[i]) ) { - maxT[i] = (high[i] - origin[i]) / dir[i]; + if( 0 != Float.floatToIntBits(dir_i) ) { + maxT[i] = (high_i - origin_i) / dir_i; } } } // Ray origin inside bounding box if(inside) { - System.arraycopy(origin, 0, result, 0, 3); + result.set(origin); return result; } @@ -523,22 +549,22 @@ public class AABBox { } */ switch( whichPlane ) { case 0: - result[1] = origin[1] + maxT[whichPlane] * dir[1]; - if(result[1] < low[1] - epsilon || result[1] > high[1] + epsilon) { return null; } - result[2] = origin[2] + maxT[whichPlane] * dir[2]; - if(result[2] < low[2] - epsilon || result[2] > high[2] + epsilon) { return null; } + result.setY( origin.y() + maxT[whichPlane] * dir.y() ); + if(result.y() < low.y() - epsilon || result.y() > high.y() + epsilon) { return null; } + result.setZ( origin.z() + maxT[whichPlane] * dir.z() ); + if(result.z() < low.z() - epsilon || result.z() > high.z() + epsilon) { return null; } break; case 1: - result[0] = origin[0] + maxT[whichPlane] * dir[0]; - if(result[0] < low[0] - epsilon || result[0] > high[0] + epsilon) { return null; } - result[2] = origin[2] + maxT[whichPlane] * dir[2]; - if(result[2] < low[2] - epsilon || result[2] > high[2] + epsilon) { return null; } + result.setX( origin.x() + maxT[whichPlane] * dir.x() ); + if(result.x() < low.x() - epsilon || result.x() > high.x() + epsilon) { return null; } + result.setZ( origin.z() + maxT[whichPlane] * dir.z() ); + if(result.z() < low.z() - epsilon || result.z() > high.z() + epsilon) { return null; } break; case 2: - result[0] = origin[0] + maxT[whichPlane] * dir[0]; - if(result[0] < low[0] - epsilon || result[0] > high[0] + epsilon) { return null; } - result[1] = origin[1] + maxT[whichPlane] * dir[1]; - if(result[1] < low[1] - epsilon || result[1] > high[1] + epsilon) { return null; } + result.setX( origin.x() + maxT[whichPlane] * dir.x() ); + if(result.x() < low.x() - epsilon || result.x() > high.x() + epsilon) { return null; } + result.setY( origin.y() + maxT[whichPlane] * dir.y() ); + if(result.y() < low.y() - epsilon || result.y() > high.y() + epsilon) { return null; } break; default: throw new InternalError("XXX"); @@ -546,16 +572,16 @@ public class AABBox { } else { switch( whichPlane ) { case 0: - result[1] = origin[1] + maxT[whichPlane] * dir[1]; - result[2] = origin[2] + maxT[whichPlane] * dir[2]; + result.setY( origin.y() + maxT[whichPlane] * dir.y() ); + result.setZ( origin.z() + maxT[whichPlane] * dir.z() ); break; case 1: - result[0] = origin[0] + maxT[whichPlane] * dir[0]; - result[2] = origin[2] + maxT[whichPlane] * dir[2]; + result.setX( origin.x() + maxT[whichPlane] * dir.x() ); + result.setZ( origin.z() + maxT[whichPlane] * dir.z() ); break; case 2: - result[0] = origin[0] + maxT[whichPlane] * dir[0]; - result[1] = origin[1] + maxT[whichPlane] * dir[1]; + result.setX( origin.x() + maxT[whichPlane] * dir.x() ); + result.setY( origin.y() + maxT[whichPlane] * dir.y() ); break; default: throw new InternalError("XXX"); @@ -570,14 +596,14 @@ public class AABBox { * @return a float representing the size of the AABBox */ public final float getSize() { - return VectorUtil.distVec3(low, high); + return low.dist(high); } /** * Get the Center of this AABBox * @return the xyz-coordinates of the center of the AABBox */ - public final float[] getCenter() { + public final Vec3f getCenter() { return center; } @@ -587,23 +613,17 @@ public class AABBox { * high and low is recomputed by scaling its distance to fixed center. * </p> * @param size a constant float value - * @param tmpV3 caller provided temporary 3-component vector * @return this AABBox for chaining + * @see #scale2(float, float[]) */ - public final AABBox scale(final float size, final float[] tmpV3) { - tmpV3[0] = high[0] - center[0]; - tmpV3[1] = high[1] - center[1]; - tmpV3[2] = high[2] - center[2]; - - VectorUtil.scaleVec3(tmpV3, tmpV3, size); // in-place scale - VectorUtil.addVec3(high, center, tmpV3); + public final AABBox scale(final float size) { + final Vec3f tmp = new Vec3f(); + tmp.set(high).sub(center).scale(size); + high.set(center).add(tmp); - tmpV3[0] = low[0] - center[0]; - tmpV3[1] = low[1] - center[1]; - tmpV3[2] = low[2] - center[2]; + tmp.set(low).sub(center).scale(size); + low.set(center).add(tmp); - VectorUtil.scaleVec3(tmpV3, tmpV3, size); // in-place scale - VectorUtil.addVec3(low, center, tmpV3); return this; } @@ -613,12 +633,27 @@ public class AABBox { * high and low is scaled and center recomputed. * </p> * @param size a constant float value - * @param tmpV3 caller provided temporary 3-component vector + * @return this AABBox for chaining + * @see #scale(float, float[]) + */ + public final AABBox scale2(final float size) { + high.scale(size); + low.scale(size); + computeCenter(); + return this; + } + + /** + * Translate this AABBox by a float[3] vector + * @param dx the translation x-component + * @param dy the translation y-component + * @param dz the translation z-component + * @param t the float[3] translation vector * @return this AABBox for chaining */ - public final AABBox scale2(final float size, final float[] tmpV3) { - VectorUtil.scaleVec3(high, high, size); // in-place scale - VectorUtil.scaleVec3(low, low, size); // in-place scale + public final AABBox translate(final float dx, final float dy, final float dz) { + low.add(dx, dy, dz); + high.add(dx, dy, dz); computeCenter(); return this; } @@ -628,9 +663,9 @@ public class AABBox { * @param t the float[3] translation vector * @return this AABBox for chaining */ - public final AABBox translate(final float[] t) { - VectorUtil.addVec3(low, low, t); // in-place translate - VectorUtil.addVec3(high, high, t); // in-place translate + public final AABBox translate(final Vec3f t) { + low.add(t); + high.add(t); computeCenter(); return this; } @@ -641,46 +676,46 @@ public class AABBox { * @return this AABBox for chaining */ public final AABBox rotate(final Quaternion quat) { - quat.rotateVector(low, 0, low, 0); - quat.rotateVector(high, 0, high, 0); + quat.rotateVector(low, low); + quat.rotateVector(high, high); computeCenter(); return this; } public final float getMinX() { - return low[0]; + return low.x(); } public final float getMinY() { - return low[1]; + return low.y(); } public final float getMinZ() { - return low[2]; + return low.z(); } public final float getMaxX() { - return high[0]; + return high.x(); } public final float getMaxY() { - return high[1]; + return high.y(); } public final float getMaxZ() { - return high[2]; + return high.z(); } public final float getWidth(){ - return high[0] - low[0]; + return high.x() - low.x(); } public final float getHeight() { - return high[1] - low[1]; + return high.y() - low.y(); } public final float getDepth() { - return high[2] - low[2]; + return high.z() - low.z(); } @Override @@ -692,8 +727,7 @@ public class AABBox { return false; } final AABBox other = (AABBox) obj; - return VectorUtil.isVec2Equal(low, 0, other.low, 0, FloatUtil.EPSILON) && - VectorUtil.isVec3Equal(high, 0, other.high, 0, FloatUtil.EPSILON) ; + return low.isEqual(other.low) && high.isEqual(other.high); } @Override public final int hashCode() { @@ -701,79 +735,103 @@ public class AABBox { } /** + * Transform this box using the given {@link Matrix4f} into {@code out} + * @param mat transformation {@link Matrix4f} + * @param out the resulting {@link AABBox} + * @return the resulting {@link AABBox} for chaining + */ + public AABBox transform(final Matrix4f mat, final AABBox out) { + final Vec3f tmp = new Vec3f(); + out.reset(); + out.resize( mat.mulVec3f(low, tmp) ); + out.resize( mat.mulVec3f(high, tmp) ); + out.computeCenter(); + return out; + } + + /** + * Transform this box using the {@link PMVMatrix#getMvMat() modelview} of the given {@link PMVMatrix} into {@code out} + * @param pmv transformation {@link PMVMatrix} + * @param out the resulting {@link AABBox} + * @return the resulting {@link AABBox} for chaining + */ + public AABBox transformMv(final PMVMatrix pmv, final AABBox out) { + final Vec3f tmp = new Vec3f(); + out.reset(); + out.resize( pmv.mulMvMatVec3f(low, tmp) ); + out.resize( pmv.mulMvMatVec3f(high, tmp) ); + out.computeCenter(); + return out; + } + + /** * Assume this bounding box as being in object space and * compute the window bounding box. * <p> * If <code>useCenterZ</code> is <code>true</code>, - * only 4 {@link FloatUtil#mapObjToWinCoords(float, float, float, float[], int[], int, float[], int, float[], float[]) mapObjToWinCoords} + * only 4 {@link FloatUtil#mapObjToWin(float, float, float, float[], int[], float[], float[], float[]) mapObjToWinCoords} * operations are made on points [1..4] using {@link #getCenter()}'s z-value. - * Otherwise 8 {@link FloatUtil#mapObjToWinCoords(float, float, float, float[], int[], int, float[], int, float[], float[]) mapObjToWinCoords} + * Otherwise 8 {@link FloatUtil#mapObjToWin(float, float, float, float[], int[], float[], float[], float[]) mapObjToWinCoords} * operation on all 8 points are performed. * </p> * <pre> - * [2] ------ [4] + * .z() ------ [4] * | | * | | - * [1] ------ [3] + * .y() ------ [3] * </pre> - * @param mat4PMv P x Mv matrix - * @param view + * @param mat4PMv [projection] x [modelview] matrix, i.e. P x Mv + * @param viewport viewport rectangle * @param useCenterZ * @param vec3Tmp0 3 component vector for temp storage * @param vec4Tmp1 4 component vector for temp storage * @param vec4Tmp2 4 component vector for temp storage * @return */ - public AABBox mapToWindow(final AABBox result, final float[/*16*/] mat4PMv, final int[] view, final boolean useCenterZ, - final float[] vec3Tmp0, final float[] vec4Tmp1, final float[] vec4Tmp2) { + public AABBox mapToWindow(final AABBox result, final Matrix4f mat4PMv, final Recti viewport, final boolean useCenterZ) { + final Vec3f tmp = new Vec3f(); + final Vec3f winPos = new Vec3f(); { - // System.err.printf("AABBox.mapToWindow.0: view[%d, %d, %d, %d], this %s%n", view[0], view[1], view[2], view[3], toString()); - final float objZ = useCenterZ ? center[2] : getMinZ(); - FloatUtil.mapObjToWinCoords(getMinX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - // System.err.printf("AABBox.mapToWindow.p1: %f, %f, %f -> %f, %f, %f%n", getMinX(), getMinY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); - // System.err.println("AABBox.mapToWindow.p1:"); - // System.err.println(FloatUtil.matrixToString(null, " mat4PMv", "%10.5f", mat4PMv, 0, 4, 4, false /* rowMajorOrder */)); - + final float objZ = useCenterZ ? center.z() : getMinZ(); result.reset(); - result.resize(vec3Tmp0, 0); - FloatUtil.mapObjToWinCoords(getMinX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - // System.err.printf("AABBox.mapToWindow.p2: %f, %f, %f -> %f, %f, %f%n", getMinX(), getMaxY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); - result.resize(vec3Tmp0, 0); + Matrix4f.mapObjToWin(tmp.set(getMinX(), getMinY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); + + Matrix4f.mapObjToWin(tmp.set(getMinX(), getMaxY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); - FloatUtil.mapObjToWinCoords(getMaxX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - // System.err.printf("AABBox.mapToWindow.p3: %f, %f, %f -> %f, %f, %f%n", getMaxX(), getMinY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); - result.resize(vec3Tmp0, 0); + Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMaxY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); - FloatUtil.mapObjToWinCoords(getMaxX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - // System.err.printf("AABBox.mapToWindow.p4: %f, %f, %f -> %f, %f, %f%n", getMaxX(), getMaxY(), objZ, vec3Tmp0[0], vec3Tmp0[1], vec3Tmp0[2]); - result.resize(vec3Tmp0, 0); + Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMinY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); } if( !useCenterZ ) { final float objZ = getMaxZ(); - FloatUtil.mapObjToWinCoords(getMinX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - result.resize(vec3Tmp0, 0); - FloatUtil.mapObjToWinCoords(getMinX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - result.resize(vec3Tmp0, 0); + Matrix4f.mapObjToWin(tmp.set(getMinX(), getMinY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); + + Matrix4f.mapObjToWin(tmp.set(getMinX(), getMaxY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); - FloatUtil.mapObjToWinCoords(getMaxX(), getMinY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - result.resize(vec3Tmp0, 0); + Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMaxY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); - FloatUtil.mapObjToWinCoords(getMaxX(), getMaxY(), objZ, mat4PMv, view, 0, vec3Tmp0, 0, vec4Tmp1, vec4Tmp2); - result.resize(vec3Tmp0, 0); + Matrix4f.mapObjToWin(tmp.set(getMaxX(), getMinY(), objZ), mat4PMv, viewport, winPos); + result.resize(winPos); } if( DEBUG ) { - System.err.printf("AABBox.mapToWindow: view[%d, %d], this %s -> %s%n", view[0], view[1], toString(), result.toString()); + System.err.printf("AABBox.mapToWindow: view[%s], this %s -> %s%n", viewport, toString(), result.toString()); } return result; } @Override public final String toString() { - return "[ dim "+getWidth()+" x "+getHeight()+" x "+getDepth()+ - ", box "+low[0]+" / "+low[1]+" / "+low[2]+" .. "+high[0]+" / "+high[1]+" / "+high[2]+ - ", ctr "+center[0]+" / "+center[1]+" / "+center[2]+" ]"; + return "[dim "+getWidth()+" x "+getHeight()+" x "+getDepth()+ + ", box "+low+" .. "+high+", ctr "+center+"]"; } } diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java index 8b0fa559e..73079959b 100644 --- a/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java +++ b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java @@ -1,5 +1,5 @@ /** - * Copyright 2010 JogAmp Community. All rights reserved. + * Copyright 2010-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -27,15 +27,13 @@ */ package com.jogamp.opengl.math.geom; -import jogamp.common.os.PlatformPropsImpl; - -import com.jogamp.common.os.Platform; -import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.math.Matrix4f; +import com.jogamp.opengl.math.Vec3f; /** * Providing frustum {@link #getPlanes() planes} derived by different inputs - * ({@link #updateByPMV(float[], int) P*MV}, ..) used to classify objects + * ({@link #updateFrustumPlanes(float[], int) P*MV}, ..) used to classify objects * <ul> * <li> {@link #classifyPoint(float[]) point} </li> * <li> {@link #classifySphere(float[], float) sphere} </li> @@ -103,6 +101,7 @@ public class Frustum { this.zNear = zNear; this.zFar = zFar; } + @Override public final String toString() { return "FrustumFovDesc["+fovhv.toStringInDegrees()+", Z["+zNear+" - "+zFar+"]]"; } @@ -117,7 +116,7 @@ public class Frustum { * Use one of the <code>update(..)</code> methods to set the {@link #getPlanes() planes}. * </p> * @see #updateByPlanes(Plane[]) - * @see #updateByPMV(float[], int) + * @see #updateFrustumPlanes(float[], int) */ public Frustum() { for (int i = 0; i < 6; ++i) { @@ -134,7 +133,7 @@ public class Frustum { */ public static class Plane { /** Normal of the plane */ - public final float[] n = new float[3]; + public final Vec3f n = new Vec3f(); /** Distance to origin */ public float d; @@ -155,17 +154,17 @@ public class Frustum { * </p> **/ public final float distanceTo(final float x, final float y, final float z) { - return n[0] * x + n[1] * y + n[2] * z + d; + return n.x() * x + n.y() * y + n.z() * z + d; } /** Return distance of plane to given point, see {@link #distanceTo(float, float, float)}. */ - public final float distanceTo(final float[] p) { - return n[0] * p[0] + n[1] * p[1] + n[2] * p[2] + d; + public final float distanceTo(final Vec3f p) { + return n.x() * p.x() + n.y() * p.y() + n.z() * p.z() + d; } @Override public String toString() { - return "Plane[ [ " + n[0] + ", " + n[1] + ", " + n[2] + " ], " + d + "]"; + return "Plane[ [ " + n + " ], " + d + "]"; } } @@ -221,9 +220,9 @@ public class Frustum { * Operation Details: * <ul> * <li>The given {@link FovDesc} will be transformed - * into the given float[16] as a perspective matrix (column major order) first, - * see {@link FloatUtil#makePerspective(float[], int, boolean, FovHVHalves, float, float)}.</li> - * <li>Then the float[16] perspective matrix is used to {@link #updateByPMV(float[], int)} this instance.</li> + * into the given perspective matrix (column major order) first, + * see {@link Matrix4f#setToPerspective(FovHVHalves, float, float)}.</li> + * <li>Then the perspective matrix is used to {@link Matrix4f#updateFrustumPlanes(Frustum)} this instance.</li> * </ul> * </p> * <p> @@ -232,120 +231,42 @@ public class Frustum { * </p> * * @param m 4x4 matrix in column-major order (also result) - * @param m_offset offset in given array <i>m</i>, i.e. start of the 4x4 matrix - * @param initM if true, given matrix will be initialized w/ identity matrix, - * otherwise only the frustum fields are set. * @param fovDesc {@link Frustum} {@link FovDesc} * @return given matrix for chaining - * @see FloatUtil#makePerspective(float[], int, boolean, FovHVHalves, float, float) + * @see Matrix4f#setToPerspective(FovHVHalves, float, float) + * @see Matrix4f#updateFrustumPlanes(Frustum) + * @see Matrix4f#getFrustum(Frustum, FovDesc) */ - public float[] updateByFovDesc(final float[] m, final int m_offset, final boolean initM, - final FovDesc fovDesc) { - FloatUtil.makePerspective(m, m_offset, initM, fovDesc.fovhv, fovDesc.zNear, fovDesc.zFar); - updateByPMV(m, 0); + public Matrix4f updateByFovDesc(final Matrix4f m, final FovDesc fovDesc) { + m.setToPerspective(fovDesc.fovhv, fovDesc.zNear, fovDesc.zFar); + m.updateFrustumPlanes(this); return m; } /** * Calculate the frustum planes in world coordinates - * using the passed float[16] as premultiplied P*MV (column major order). + * using the passed premultiplied P*MV (column major order) matrix. * <p> * Frustum plane's normals will point to the inside of the viewing frustum, * as required by this class. * </p> */ - public void updateByPMV(final float[] pmv, final int pmv_off) { - // Left: a = m41 + m11, b = m42 + m12, c = m43 + m13, d = m44 + m14 - [1..4] column-major - // Left: a = m30 + m00, b = m31 + m01, c = m32 + m02, d = m33 + m03 - [0..3] column-major - { - final Plane p = planes[LEFT]; - final float[] p_n = p.n; - p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] + pmv[ pmv_off + 0 + 0 * 4 ]; - p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] + pmv[ pmv_off + 0 + 1 * 4 ]; - p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] + pmv[ pmv_off + 0 + 2 * 4 ]; - p.d = pmv[ pmv_off + 3 + 3 * 4 ] + pmv[ pmv_off + 0 + 3 * 4 ]; - } - - // Right: a = m41 - m11, b = m42 - m12, c = m43 - m13, d = m44 - m14 - [1..4] column-major - // Right: a = m30 - m00, b = m31 - m01, c = m32 - m02, d = m33 - m03 - [0..3] column-major - { - final Plane p = planes[RIGHT]; - final float[] p_n = p.n; - p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] - pmv[ pmv_off + 0 + 0 * 4 ]; - p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] - pmv[ pmv_off + 0 + 1 * 4 ]; - p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] - pmv[ pmv_off + 0 + 2 * 4 ]; - p.d = pmv[ pmv_off + 3 + 3 * 4 ] - pmv[ pmv_off + 0 + 3 * 4 ]; - } - - // Bottom: a = m41 + m21, b = m42 + m22, c = m43 + m23, d = m44 + m24 - [1..4] column-major - // Bottom: a = m30 + m10, b = m31 + m11, c = m32 + m12, d = m33 + m13 - [0..3] column-major - { - final Plane p = planes[BOTTOM]; - final float[] p_n = p.n; - p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] + pmv[ pmv_off + 1 + 0 * 4 ]; - p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] + pmv[ pmv_off + 1 + 1 * 4 ]; - p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] + pmv[ pmv_off + 1 + 2 * 4 ]; - p.d = pmv[ pmv_off + 3 + 3 * 4 ] + pmv[ pmv_off + 1 + 3 * 4 ]; - } - - // Top: a = m41 - m21, b = m42 - m22, c = m43 - m23, d = m44 - m24 - [1..4] column-major - // Top: a = m30 - m10, b = m31 - m11, c = m32 - m12, d = m33 - m13 - [0..3] column-major - { - final Plane p = planes[TOP]; - final float[] p_n = p.n; - p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] - pmv[ pmv_off + 1 + 0 * 4 ]; - p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] - pmv[ pmv_off + 1 + 1 * 4 ]; - p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] - pmv[ pmv_off + 1 + 2 * 4 ]; - p.d = pmv[ pmv_off + 3 + 3 * 4 ] - pmv[ pmv_off + 1 + 3 * 4 ]; - } - - // Near: a = m41 + m31, b = m42 + m32, c = m43 + m33, d = m44 + m34 - [1..4] column-major - // Near: a = m30 + m20, b = m31 + m21, c = m32 + m22, d = m33 + m23 - [0..3] column-major - { - final Plane p = planes[NEAR]; - final float[] p_n = p.n; - p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] + pmv[ pmv_off + 2 + 0 * 4 ]; - p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] + pmv[ pmv_off + 2 + 1 * 4 ]; - p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] + pmv[ pmv_off + 2 + 2 * 4 ]; - p.d = pmv[ pmv_off + 3 + 3 * 4 ] + pmv[ pmv_off + 2 + 3 * 4 ]; - } - - // Far: a = m41 - m31, b = m42 - m32, c = m43 - m33, d = m44 - m34 - [1..4] column-major - // Far: a = m30 - m20, b = m31 - m21, c = m32 + m22, d = m33 + m23 - [0..3] column-major - { - final Plane p = planes[FAR]; - final float[] p_n = p.n; - p_n[0] = pmv[ pmv_off + 3 + 0 * 4 ] - pmv[ pmv_off + 2 + 0 * 4 ]; - p_n[1] = pmv[ pmv_off + 3 + 1 * 4 ] - pmv[ pmv_off + 2 + 1 * 4 ]; - p_n[2] = pmv[ pmv_off + 3 + 2 * 4 ] - pmv[ pmv_off + 2 + 2 * 4 ]; - p.d = pmv[ pmv_off + 3 + 3 * 4 ] - pmv[ pmv_off + 2 + 3 * 4 ]; - } - - // Normalize all planes - for (int i = 0; i < 6; ++i) { - final Plane p = planes[i]; - final float[] p_n = p.n; - final double invl = Math.sqrt(p_n[0] * p_n[0] + p_n[1] * p_n[1] + p_n[2] * p_n[2]); - - p_n[0] /= invl; - p_n[1] /= invl; - p_n[2] /= invl; - p.d /= invl; - } + public void updateFrustumPlanes(final Matrix4f pmv) { + pmv.updateFrustumPlanes(this); } private static final boolean isOutsideImpl(final Plane p, final AABBox box) { - final float[] low = box.getLow(); - final float[] high = box.getHigh(); - - if ( p.distanceTo(low[0], low[1], low[2]) > 0.0f || - p.distanceTo(high[0], low[1], low[2]) > 0.0f || - p.distanceTo(low[0], high[1], low[2]) > 0.0f || - p.distanceTo(high[0], high[1], low[2]) > 0.0f || - p.distanceTo(low[0], low[1], high[2]) > 0.0f || - p.distanceTo(high[0], low[1], high[2]) > 0.0f || - p.distanceTo(low[0], high[1], high[2]) > 0.0f || - p.distanceTo(high[0], high[1], high[2]) > 0.0f ) { + final Vec3f lo = box.getLow(); + final Vec3f hi = box.getHigh(); + + if ( p.distanceTo(lo.x(), lo.y(), lo.z()) > 0.0f || + p.distanceTo(hi.x(), lo.y(), lo.z()) > 0.0f || + p.distanceTo(lo.x(), hi.y(), lo.z()) > 0.0f || + p.distanceTo(hi.x(), hi.y(), lo.z()) > 0.0f || + p.distanceTo(lo.x(), lo.y(), hi.z()) > 0.0f || + p.distanceTo(hi.x(), lo.y(), hi.z()) > 0.0f || + p.distanceTo(lo.x(), hi.y(), hi.z()) > 0.0f || + p.distanceTo(hi.x(), hi.y(), hi.z()) > 0.0f ) { return false; } return true; @@ -377,7 +298,7 @@ public class Frustum { * @param p the point * @return {@link Location} of point related to frustum planes */ - public final Location classifyPoint(final float[] p) { + public final Location classifyPoint(final Vec3f p) { Location res = Location.INSIDE; for (int i = 0; i < 6; ++i) { @@ -397,7 +318,7 @@ public class Frustum { * @param p the point * @return true if outside of the frustum, otherwise inside or on a plane */ - public final boolean isPointOutside(final float[] p) { + public final boolean isPointOutside(final Vec3f p) { return Location.OUTSIDE == classifyPoint(p); } @@ -408,7 +329,7 @@ public class Frustum { * @param radius radius of the sphere * @return {@link Location} of point related to frustum planes */ - public final Location classifySphere(final float[] p, final float radius) { + public final Location classifySphere(final Vec3f p, final float radius) { Location res = Location.INSIDE; // fully inside for (int i = 0; i < 6; ++i) { @@ -431,7 +352,7 @@ public class Frustum { * @param radius radius of the sphere * @return true if outside of the frustum, otherwise inside or intersecting */ - public final boolean isSphereOutside(final float[] p, final float radius) { + public final boolean isSphereOutside(final Vec3f p, final float radius) { return Location.OUTSIDE == classifySphere(p, radius); } @@ -439,13 +360,13 @@ public class Frustum { if( null == sb ) { sb = new StringBuilder(); } - sb.append("Frustum[ Planes[ ").append(PlatformPropsImpl.NEWLINE) - .append(" L: ").append(planes[0]).append(", ").append(PlatformPropsImpl.NEWLINE) - .append(" R: ").append(planes[1]).append(", ").append(PlatformPropsImpl.NEWLINE) - .append(" B: ").append(planes[2]).append(", ").append(PlatformPropsImpl.NEWLINE) - .append(" T: ").append(planes[3]).append(", ").append(PlatformPropsImpl.NEWLINE) - .append(" N: ").append(planes[4]).append(", ").append(PlatformPropsImpl.NEWLINE) - .append(" F: ").append(planes[5]).append("], ").append(PlatformPropsImpl.NEWLINE) + sb.append("Frustum[Planes[").append(System.lineSeparator()) + .append(" L: ").append(planes[0]).append(", ").append(System.lineSeparator()) + .append(" R: ").append(planes[1]).append(", ").append(System.lineSeparator()) + .append(" B: ").append(planes[2]).append(", ").append(System.lineSeparator()) + .append(" T: ").append(planes[3]).append(", ").append(System.lineSeparator()) + .append(" N: ").append(planes[4]).append(", ").append(System.lineSeparator()) + .append(" F: ").append(planes[5]).append("], ").append(System.lineSeparator()) .append("]"); return sb; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java index e5f9e5336..61de9155a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataClient.java @@ -431,29 +431,13 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData /** * Increase the capacity of the buffer if necessary to add given spareComponents components. * <p> - * Buffer will not change if current capacity satisfies spareComponents components. + * Buffer will not change if remaining free slots, capacity less position, satisfy spareComponents components. * </p> * @param spareComponents number of components to add if necessary. * @return true if buffer size has changed, i.e. grown. Otherwise false. */ public final boolean growIfNeeded(final int spareComponents) { - if( buffer.remaining() < spareComponents ) { - if( 0 != mappedElemCount ) { - throw new GLException("Mapped buffer can't grow. Insufficient storage size: Needed "+spareComponents+" components, "+ - "mappedElementCount "+mappedElemCount+ - ", has mapped buffer "+buffer+"; "+this); - } - final int has_comps = buffer.capacity(); - final int required_elems = compsToElemCount(has_comps + spareComponents); - final int new_elems = compsToElemCount( (int)( has_comps * growthFactor + 0.5f ) ); - final int elementCount = Math.max( new_elems, required_elems ); - return reserve( elementCount ); - } - return false; - } - - public final boolean growIfNeeded0(final int spareComponents) { - if( buffer==null || buffer.remaining()<spareComponents ) { + if( null == buffer || buffer.remaining() < spareComponents ) { if( 0 != mappedElemCount ) { throw new GLException("Mapped buffer can't grow. Insufficient storage size: Needed "+spareComponents+" components, "+ "mappedElementCount "+mappedElemCount+ @@ -467,9 +451,6 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData final int required_elems = compsToElemCount(has_comps + spareComponents); final int new_elems = compsToElemCount( (int)( has_comps * growthFactor + 0.5f ) ); final int elementCount = Math.max( new_elems, required_elems ); - if( DEBUG ) { - System.err.println("*** Size: Grow: elems "+compsToElemCount(has_comps)+" -> max("+new_elems+", "+required_elems+") -> "+elementCount); - } return reserve( elementCount ); } } @@ -480,7 +461,7 @@ public class GLArrayDataClient extends GLArrayDataWrapper implements GLArrayData * Increase the capacity of the buffer to given elementCount element size, * i.e. elementCount * componentsPerElement components. * <p> - * Buffer will not change if given elementCount is lower than current size. + * Buffer will not change if given elementCount is lower or equal current capacity. * </p> * @param elementCount number of elements to hold. * @return true if buffer size has changed, i.e. grown. Otherwise false. diff --git a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java index e530ad627..433a25eb8 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/GLArrayDataServer.java @@ -412,7 +412,7 @@ public class GLArrayDataServer extends GLArrayDataClient implements GLArrayDataE * @param comps This interleaved array segment's component count per element * @param vboTarget {@link GL#GL_ARRAY_BUFFER} or {@link GL#GL_ELEMENT_ARRAY_BUFFER} */ - public GLArrayData addGLSLSubArray(final String name, final int comps, final int vboTarget) { + public GLArrayDataWrapper addGLSLSubArray(final String name, final int comps, final int vboTarget) { if(interleavedOffset >= getCompsPerElem() * getBytesPerComp()) { final int iOffC = interleavedOffset / getBytesPerComp(); throw new GLException("Interleaved offset > total components ("+iOffC+" > "+getCompsPerElem()+")"); diff --git a/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java b/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java index 5383f91be..b8b2925ae 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java +++ b/src/jogl/classes/com/jogamp/opengl/util/PMVMatrix.java @@ -1,64 +1,66 @@ -/* - * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (c) 2011 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: +/** + * Copyright 2009-2023 JogAmp Community. All rights reserved. * - * - Redistribution of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: * - * - Redistribution in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. * - * Neither the name of Sun Microsystems, Inc. or the names of - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. * - * This software is provided "AS IS," without a warranty of any kind. ALL - * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, - * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN - * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR - * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR - * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR - * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE - * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, - * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF - * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. */ - package com.jogamp.opengl.util; +import java.nio.Buffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import com.jogamp.opengl.GL; import com.jogamp.opengl.GLException; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; - -import jogamp.common.os.PlatformPropsImpl; +import com.jogamp.opengl.GLUniformData; import com.jogamp.common.nio.Buffers; -import com.jogamp.common.util.FloatStack; import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Matrix4f; import com.jogamp.opengl.math.Quaternion; import com.jogamp.opengl.math.Ray; +import com.jogamp.opengl.math.Recti; +import com.jogamp.opengl.math.Vec3f; +import com.jogamp.opengl.math.Vec4f; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.math.geom.Frustum; /** * PMVMatrix implements a subset of the fixed function pipeline - * regarding the projection (P), modelview (Mv) matrix operation + * regarding the projection (P), modelview (Mv) and texture (T) matrix operations, * which is specified in {@link GLMatrixFunc}. * <p> - * Further more, PMVMatrix provides the {@link #glGetMviMatrixf() inverse modelview matrix (Mvi)} and - * {@link #glGetMvitMatrixf() inverse transposed modelview matrix (Mvit)}. - * {@link Frustum} is also provided by {@link #glGetFrustum()}. + * This is the second implementation of `PMVMatrix` using + * direct {@link Matrix4f}, {@link Vec4f} and {@link Vec3f} math operations instead of `float[]` + * via {@link com.jogamp.opengl.math.FloatUtil FloatUtil}. + * </p> + * <p> + * PMVMatrix provides the {@link #getMviMat() inverse modelview matrix (Mvi)} and + * {@link #getMvitMat() inverse transposed modelview matrix (Mvit)}. + * {@link Frustum} is also provided by {@link #getFrustum()}. + * * To keep these derived values synchronized after mutable Mv operations like {@link #glRotatef(float, float, float, float) glRotatef(..)} * in {@link #glMatrixMode(int) glMatrixMode}({@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}), * users have to call {@link #update()} before using Mvi and Mvit. @@ -66,7 +68,7 @@ import com.jogamp.opengl.math.geom.Frustum; * <p> * All matrices are provided in column-major order, * as specified in the OpenGL fixed function pipeline, i.e. compatibility profile. - * See {@link FloatUtil}. + * See {@link Matrix4f}. * </p> * <p> * PMVMatrix can supplement {@link GL2ES2} applications w/ the @@ -74,6 +76,23 @@ import com.jogamp.opengl.math.geom.Frustum; * </p> * <a name="storageDetails"><h5>Matrix storage details</h5></a> * <p> + * The {@link SyncBuffer} abstraction is provided, e.g. {@link #getSyncPMvMviMat()}, + * to synchronize the respective {@link Matrix4f matrices} with the `float[]` backing store. + * The latter is represents the data to {@link GLUniformData} via its {@link FloatBuffer}s, see {@link SyncBuffer#getBuffer()}, + * and is pushed to the GPU eventually. + * + * {@link SyncBuffer}'s {@link SyncAction} is called by {@link GLUniformData#getBuffer()}, + * i.e. before the data is pushed to the GPU. + * + * The provided {@link SyncAction} ensures that the {@link Matrix4f matrices data} + * gets copied into the `float[]` backing store. + * + * PMVMatrix provides two specializations of {@link SyncBuffer}, {@link SyncMatrix4f} for single {@link Matrix4f} mappings + * and {@link SyncMatrices4f} for multiple {@link Matrix4f} mappings. + * + * They can be feed directly to instantiate a {@link GLUniformData} object via e.g. {@link GLUniformData#GLUniformData(String, int, int, SyncBuffer)}. + * </p> + * <p> * All matrices are backed up by a common primitive float-array for performance considerations * and are a {@link Buffers#slice2Float(float[], int, int) sliced} representation of it. * </p> @@ -88,23 +107,27 @@ import com.jogamp.opengl.math.geom.Frustum; */ public final class PMVMatrix implements GLMatrixFunc { - /** Bit value stating a modified {@link #glGetPMatrixf() projection matrix (P)}, since last {@link #update()} call. */ + /** Bit value stating a modified {@link #getPMat() projection matrix (P)}, since last {@link #update()} call. */ public static final int MODIFIED_PROJECTION = 1 << 0; - /** Bit value stating a modified {@link #glGetMvMatrixf() modelview matrix (Mv)}, since last {@link #update()} call. */ + /** Bit value stating a modified {@link #getMvMat() modelview matrix (Mv)}, since last {@link #update()} call. */ public static final int MODIFIED_MODELVIEW = 1 << 1; - /** Bit value stating a modified {@link #glGetTMatrixf() texture matrix (T)}, since last {@link #update()} call. */ + /** Bit value stating a modified {@link #getTMat() texture matrix (T)}, since last {@link #update()} call. */ public static final int MODIFIED_TEXTURE = 1 << 2; /** Bit value stating all is modified */ public static final int MODIFIED_ALL = MODIFIED_PROJECTION | MODIFIED_MODELVIEW | MODIFIED_TEXTURE ; - /** Bit value stating a dirty {@link #glGetMviMatrixf() inverse modelview matrix (Mvi)}. */ - public static final int DIRTY_INVERSE_MODELVIEW = 1 << 0; - /** Bit value stating a dirty {@link #glGetMvitMatrixf() inverse transposed modelview matrix (Mvit)}. */ - public static final int DIRTY_INVERSE_TRANSPOSED_MODELVIEW = 1 << 1; - /** Bit value stating a dirty {@link #glGetFrustum() frustum}. */ - public static final int DIRTY_FRUSTUM = 1 << 2; - /** Bit value stating all is dirty */ - public static final int DIRTY_ALL = DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM; + /** Bit value for {@link #getMviMat() inverse modelview matrix (Mvi)}, updated via {@link #update()}. */ + public static final int INVERSE_MODELVIEW = 1 << 1; + /** Bit value for {@link #getMvitMat() inverse transposed modelview matrix (Mvit)}, updated via {@link #update()}. */ + public static final int INVERSE_TRANSPOSED_MODELVIEW = 1 << 2; + /** Bit value for {@link #getFrustum() frustum} and updated by {@link #getFrustum()}. */ + public static final int FRUSTUM = 1 << 3; + /** Bit value for {@link #getPMvMat() pre-multiplied P * Mv}, updated by {@link #getPMvMat()}. */ + public static final int PREMUL_PMV = 1 << 4; + /** Bit value for {@link #getPMviMat() pre-multiplied invert(P * Mv)}, updated by {@link #getPMviMat()}. */ + public static final int PREMUL_PMVI = 1 << 5; + /** Manual bits not covered by {@link #update()} but {@link #getFrustum()}, {@link #FRUSTUM}, {@link #getPMvMat()}, {@link #PREMUL_PMV}, {@link #getPMviMat()}, {@link #PREMUL_PMVI}, etc. */ + public static final int MANUAL_BITS = FRUSTUM | PREMUL_PMV | PREMUL_PMVI; /** * @param matrixModeName One of {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} @@ -170,86 +193,147 @@ public final class PMVMatrix implements GLMatrixFunc { } /** - * @param sb optional passed StringBuilder instance to be used - * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} - * @param a 4x4 matrix in column major order (OpenGL) - * @return matrix string representation - */ - @SuppressWarnings("deprecation") - public static StringBuilder matrixToString(final StringBuilder sb, final String f, final FloatBuffer a) { - return FloatUtil.matrixToString(sb, null, f, a, 0, 4, 4, false); - } - - /** - * @param sb optional passed StringBuilder instance to be used - * @param f the format string of one floating point, i.e. "%10.5f", see {@link java.util.Formatter} - * @param a 4x4 matrix in column major order (OpenGL) - * @param b 4x4 matrix in column major order (OpenGL) - * @return side by side representation + * Creates an instance of PMVMatrix. + * <p> + * This constructor only sets up an instance w/o additional {@link #INVERSE_MODELVIEW} or {@link #INVERSE_TRANSPOSED_MODELVIEW}. + * </p> + * <p> + * Implementation uses non-direct non-NIO Buffers with guaranteed backing array, + * which are synchronized to the actual Matrix4f instances. + * This allows faster access in Java computation. + * </p> + * @see #PMVMatrix(int) */ - @SuppressWarnings("deprecation") - public static StringBuilder matrixToString(final StringBuilder sb, final String f, final FloatBuffer a, final FloatBuffer b) { - return FloatUtil.matrixToString(sb, null, f, a, 0, b, 0, 4, 4, false); + public PMVMatrix() { + this(0); } /** * Creates an instance of PMVMatrix. * <p> + * Additional derived matrices can be requested via `derivedMatrices`, i.e. + * - {@link #INVERSE_MODELVIEW} + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} + * </p> + * <p> * Implementation uses non-direct non-NIO Buffers with guaranteed backing array, - * which allows faster access in Java computation. + * which are synchronized to the actual Matrix4f instances. + * This allows faster access in Java computation. * </p> + * @param derivedMatrices additional matrices can be requested by passing bits {@link #INVERSE_MODELVIEW} and {@link #INVERSE_TRANSPOSED_MODELVIEW}. + * @see #getReqBits() + * @see #isReqDirty() + * @see #getDirtyBits() + * @see #update() */ - public PMVMatrix() { - // I Identity - // T Texture - // P Projection - // Mv ModelView - // Mvi Modelview-Inverse - // Mvit Modelview-Inverse-Transpose - matrixArray = new float[5*16]; - - mP_offset = 0*16; - mMv_offset = 1*16; - mTex_offset = 4*16; - - matrixPMvMvit = Buffers.slice2Float(matrixArray, 0*16, 4*16); // P + Mv + Mvi + Mvit - matrixPMvMvi = Buffers.slice2Float(matrixArray, 0*16, 3*16); // P + Mv + Mvi - matrixPMv = Buffers.slice2Float(matrixArray, 0*16, 2*16); // P + Mv - matrixP = Buffers.slice2Float(matrixArray, 0*16, 1*16); // P - matrixMv = Buffers.slice2Float(matrixArray, 1*16, 1*16); // Mv - matrixMvi = Buffers.slice2Float(matrixArray, 2*16, 1*16); // Mvi - matrixMvit = Buffers.slice2Float(matrixArray, 3*16, 1*16); // Mvit - matrixTex = Buffers.slice2Float(matrixArray, 4*16, 1*16); // T - - mat4Tmp1 = new float[16]; - mat4Tmp2 = new float[16]; - mat4Tmp3 = new float[16]; - matrixTxSx = new float[16]; - FloatUtil.makeIdentity(matrixTxSx); - - // Start w/ zero size to save memory - matrixTStack = new FloatStack( 0, 2*16); // growSize: GL-min size (2) - matrixPStack = new FloatStack( 0, 2*16); // growSize: GL-min size (2) - matrixMvStack= new FloatStack( 0, 16*16); // growSize: half GL-min size (32) - - reset(); - - frustum = null; + public PMVMatrix(final int derivedMatrices) { + // I Identity + // T Texture + // P Projection + // Mv ModelView + // Mvi Modelview-Inverse + // Mvit Modelview-Inverse-Transpose + { + int mask = 0; + if( 0 != ( derivedMatrices & ( INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW ) ) ) { + mask |= INVERSE_MODELVIEW; + } + if( 0 != ( derivedMatrices & INVERSE_TRANSPOSED_MODELVIEW ) ) { + mask |= INVERSE_TRANSPOSED_MODELVIEW; + } + requestBits = mask; + } + + // actual underlying Matrix4f count + int mcount = 3; + + // actual underlying Matrix4f data + matP = new Matrix4f(); + matMv = new Matrix4f(); + matTex = new Matrix4f(); + + if( 0 != ( requestBits & INVERSE_MODELVIEW ) ) { + matMvi = new Matrix4f(); + mMvi_offset = 2*16; + ++mcount; + } else { + matMvi = null; + mMvi_offset = -1; + } + if( 0 != ( requestBits & INVERSE_TRANSPOSED_MODELVIEW ) ) { + matMvit = new Matrix4f(); + mMvit_offset = 3*16; + ++mcount; + } else { + matMvit = null; + mMvit_offset = -1; + } + mTex_offset = (mcount-1)*16; // last one + + // float back buffer for GPU, Matrix4f -> matrixStore via SyncedBuffer + matrixStore = new float[mcount*16]; + + // FloatBuffer for single Matrix4f back-buffer + bufP = Buffers.slice2Float(matrixStore, mP_offset, 1*16); // P + syncP = new SyncBuffer0(matP, bufP); // mP_offset + + bufMv = Buffers.slice2Float(matrixStore, mMv_offset, 1*16); // Mv + syncMv = new SyncBuffer1(matMv, bufMv, mMv_offset); + + bufP_Mv = Buffers.slice2Float(matrixStore, mP_offset, 2*16); // P + Mv + syncP_Mv = new SyncBufferN(new Matrix4f[] { matP, matMv }, bufP_Mv, mP_offset); + + bufTex = Buffers.slice2Float(matrixStore, mTex_offset, 1*16); // T + syncT = new SyncBuffer1(matTex, bufTex, mTex_offset); + + if( null != matMvi ) { + bufMvi = Buffers.slice2Float(matrixStore, mMvi_offset, 1*16); // Mvi + bufP_Mv_Mvi = Buffers.slice2Float(matrixStore, mP_offset, 3*16); // P + Mv + Mvi + syncMvi = new SyncBuffer1U(matMvi, bufMvi, mMvi_offset); + syncP_Mv_Mvi = new SyncBufferNU(new Matrix4f[] { matP, matMv, matMvi }, bufP_Mv_Mvi, mP_offset); + } else { + bufMvi = null; + bufP_Mv_Mvi = null; + syncMvi = null; + syncP_Mv_Mvi = null; + } + if( null != matMvit ) { + bufMvit = Buffers.slice2Float(matrixStore, mMvit_offset, 1*16); // Mvit + bufP_Mv_Mvi_Mvit = Buffers.slice2Float(matrixStore, mP_offset, 4*16); // P + Mv + Mvi + Mvit + syncMvit = new SyncBuffer1U(matMvit, bufMvit, mMvit_offset); + syncP_Mv_Mvi_Mvit = new SyncBufferNU(new Matrix4f[] { matP, matMv, matMvi, matMvit }, bufP_Mv_Mvi_Mvit, mP_offset); + } else { + bufMvit = null; + bufP_Mv_Mvi_Mvit = null; + syncMvit = null; + syncP_Mv_Mvi_Mvit = null; + } + + mat4Tmp1 = new Matrix4f(); + + mat4Tmp2 = null; // on demand + matPMv = null; // on demand + matPMvi = null; // on demand + matPMviOK = false; + frustum = null; // on demand + + reset(); } /** * Issues {@link #glLoadIdentity()} on all matrices, * i.e. {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} * and resets all internal states. + * + * Leaves {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW} the active matrix mode. */ public final void reset() { - FloatUtil.makeIdentity(matrixArray, mMv_offset); - FloatUtil.makeIdentity(matrixArray, mP_offset); - FloatUtil.makeIdentity(matrixArray, mTex_offset); + matP.loadIdentity(); + matMv.loadIdentity(); + matTex.loadIdentity(); modifiedBits = MODIFIED_ALL; - dirtyBits = DIRTY_ALL; - requestMask = 0; + dirtyBits = requestBits | MANUAL_BITS; matrixMode = GL_MODELVIEW; } @@ -258,126 +342,189 @@ public final class PMVMatrix implements GLMatrixFunc { return matrixMode; } + // + // Temporary storage access for efficiency + // + + /** + * Return the second temporary Matrix4f exposed to be reused for efficiency. + * <p> + * Temporary storage is only used by this class within single method calls, + * hence has no side-effects. + * </p> + */ + private final Matrix4f getTmp2Mat() { + if( null == mat4Tmp2 ) { + mat4Tmp2 = new Matrix4f(); + } + return mat4Tmp2; + } + + // + // Regular Matrix4f access as well as their SyncedBuffer counterpart SyncedMatrix and SyncedMatrices + // + /** * Returns the {@link GLMatrixFunc#GL_TEXTURE_MATRIX texture matrix} (T). * <p> * See <a href="#storageDetails"> matrix storage details</a>. * </p> */ - public final FloatBuffer glGetTMatrixf() { - return matrixTex; + public final Matrix4f getTMat() { + return matTex; } /** - * Returns the {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). + * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_TEXTURE_MATRIX texture matrix} (T). * <p> * See <a href="#storageDetails"> matrix storage details</a>. * </p> */ - public final FloatBuffer glGetPMatrixf() { - return matrixP; + public final SyncMatrix4f getSyncTMat() { + return syncT; } /** - * Returns the {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). + * Returns the {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). * <p> * See <a href="#storageDetails"> matrix storage details</a>. * </p> */ - public final FloatBuffer glGetMvMatrixf() { - return matrixMv; + public final Matrix4f getPMat() { + return matP; } /** - * Returns the inverse {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvi). + * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). * <p> - * Method enables the Mvi matrix update, and performs it's update w/o clearing the modified bits. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> + */ + public final SyncMatrix4f getSyncPMat() { + return syncP; + } + + /** + * Returns the {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). * <p> - * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> - * @see #update() - * @see #clearAllUpdateRequests() */ - public final FloatBuffer glGetMviMatrixf() { - requestMask |= DIRTY_INVERSE_MODELVIEW ; - updateImpl(false); - return matrixMvi; + public final Matrix4f getMvMat() { + return matMv; } /** - * Returns the inverse transposed {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvit). + * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). * <p> - * Method enables the Mvit matrix update, and performs it's update w/o clearing the modified bits. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> + */ + public final SyncMatrix4f getSyncMvMat() { + return syncMv; + } + + /** + * Returns {@link SyncMatrices4f} of 2 matrices within one FloatBuffer: {@link #getPMat() P} and {@link #getMvMat() Mv}. * <p> - * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> - * @see #update() - * @see #clearAllUpdateRequests() */ - public final FloatBuffer glGetMvitMatrixf() { - requestMask |= DIRTY_INVERSE_TRANSPOSED_MODELVIEW ; - updateImpl(false); - return matrixMvit; + public final SyncMatrices4f getSyncPMvMat() { + return syncP_Mv; } /** - * Returns 2 matrices within one FloatBuffer: {@link #glGetPMatrixf() P} and {@link #glGetMvMatrixf() Mv}. + * Returns the inverse {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvi) if requested. * <p> * See <a href="#storageDetails"> matrix storage details</a>. * </p> + * @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. */ - public final FloatBuffer glGetPMvMatrixf() { - return matrixPMv; + public final Matrix4f getMviMat() { + if( 0 == ( INVERSE_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + updateImpl(false); + return matMvi; } /** - * Returns 3 matrices within one FloatBuffer: {@link #glGetPMatrixf() P}, {@link #glGetMvMatrixf() Mv} and {@link #glGetMviMatrixf() Mvi}. + * Returns the {@link SyncMatrix} of inverse {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvi) if requested. * <p> - * Method enables the Mvi matrix update, and performs it's update w/o clearing the modified bits. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> + * @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. + */ + public final SyncMatrix4f getSyncMviMat() { + if( 0 == ( INVERSE_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + return syncMvi; + } + + /** + * Returns the inverse transposed {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvit) if requested. * <p> - * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> - * @see #update() - * @see #clearAllUpdateRequests() + * @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. */ - public final FloatBuffer glGetPMvMviMatrixf() { - requestMask |= DIRTY_INVERSE_MODELVIEW ; + public final Matrix4f getMvitMat() { + if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } updateImpl(false); - return matrixPMvMvi; + return matMvit; } /** - * Returns 4 matrices within one FloatBuffer: {@link #glGetPMatrixf() P}, {@link #glGetMvMatrixf() Mv}, {@link #glGetMviMatrixf() Mvi} and {@link #glGetMvitMatrixf() Mvit}. + * Returns the {@link SyncMatrix} of inverse transposed {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvit) if requested. * <p> - * Method enables the Mvi and Mvit matrix update, and performs it's update w/o clearing the modified bits. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> + * @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. + */ + public final SyncMatrix4f getSyncMvitMat() { + if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + return syncMvit; + } + + /** + * Returns {@link SyncMatrices4f} of 3 matrices within one FloatBuffer: {@link #getPMat() P}, {@link #getMvMat() Mv} and {@link #getMviMat() Mvi} if requested. * <p> - * See {@link #update()} and <a href="#storageDetails"> matrix storage details</a>. + * See <a href="#storageDetails"> matrix storage details</a>. * </p> - * @see #update() - * @see #clearAllUpdateRequests() + * @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. */ - public final FloatBuffer glGetPMvMvitMatrixf() { - requestMask |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW ; - updateImpl(false); - return matrixPMvMvit; + public final SyncMatrices4f getSyncPMvMviMat() { + if( 0 == ( INVERSE_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + return syncP_Mv_Mvi; } - /** Returns the frustum, derived from projection * modelview */ - public final Frustum glGetFrustum() { - requestMask |= DIRTY_FRUSTUM; - updateImpl(false); - return frustum; + /** + * Returns {@link SyncMatrices4f} of 4 matrices within one FloatBuffer: {@link #getPMat() P}, {@link #getMvMat() Mv}, {@link #getMviMat() Mvi} and {@link #getMvitMat() Mvit} if requested. + * <p> + * See <a href="#storageDetails"> matrix storage details</a>. + * </p> + * @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. + */ + public final SyncMatrices4f getSyncPMvMviMvitMat() { + if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + return syncP_Mv_Mvi_Mvit; } - /* + /** * @return the matrix of the current matrix-mode */ - public final FloatBuffer glGetMatrixf() { - return glGetMatrixf(matrixMode); + public final Matrix4f getCurrentMat() { + return getMat(matrixMode); } /** @@ -387,49 +534,182 @@ public final class PMVMatrix implements GLMatrixFunc { * {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} * @return the named matrix, not a copy! */ - public final FloatBuffer glGetMatrixf(final int matrixName) { + public final Matrix4f getMat(final int matrixName) { switch(matrixName) { case GL_MODELVIEW_MATRIX: case GL_MODELVIEW: - return matrixMv; + return matMv; case GL_PROJECTION_MATRIX: case GL_PROJECTION: - return matrixP; + return matP; case GL_TEXTURE_MATRIX: case GL.GL_TEXTURE: - return matrixTex; + return matTex; default: throw new GLException("unsupported matrixName: "+matrixName); } } + // + // Basic Matrix4f, Vec3f and Vec4f operations similar to GLMatrixFunc + // /** - * Multiplies the {@link #glGetPMatrixf() P} and {@link #glGetMvMatrixf() Mv} matrix, i.e. + * Multiplies the {@link #getPMat() P} and {@link #getMvMat() Mv} matrix, i.e. * <pre> - * mat4PMv = P x Mv + * result = P x Mv * </pre> - * @param mat4PMv 4x4 matrix storage for result - * @param mat4PMv_offset - * @return given matrix for chaining + * @param result 4x4 matrix storage for result + * @return given result matrix for chaining */ - public final float[] multPMvMatrixf(final float[/*16*/] mat4PMv, final int mat4PMv_offset) { - FloatUtil.multMatrix(matrixArray, mP_offset, matrixArray, mMv_offset, mat4PMv, mat4PMv_offset); - return mat4PMv; + public final Matrix4f mulPMvMat(final Matrix4f result) { + return result.mul(matP, matMv); } /** - * Multiplies the {@link #glGetMvMatrixf() Mv} and {@link #glGetPMatrixf() P} matrix, i.e. + * Multiplies the {@link #getMvMat() Mv} and {@link #getPMat() P} matrix, i.e. * <pre> - * mat4MvP = Mv x P + * result = Mv x P * </pre> - * @param mat4MvP 4x4 matrix storage for result - * @param mat4MvP_offset - * @return given matrix for chaining + * @param result 4x4 matrix storage for result + * @return given result matrix for chaining + */ + public final Matrix4f mulMvPMat(final Matrix4f result) { + return result.mul(matMv, matP); + } + + /** + * v_out = Mv * v_in + * @param v_in input vector, can be v_out for in-place transformation + * @param v_out output vector + * @returns v_out for chaining + */ + public final Vec4f mulMvMatVec4f(final Vec4f v_in, final Vec4f v_out) { + return matMv.mulVec4f(v_in, v_out); + } + + /** + * v_inout = Mv * v_inout + * @param v_inout input and output vector, i.e. in-place transformation + * @returns v_inout for chaining + */ + public final Vec4f mulMvMatVec4f(final Vec4f v_inout) { + return matMv.mulVec4f(v_inout); + } + + /** + * v_out = Mv * v_in + * + * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. + * + * @param v_in input vector, can be v_out for in-place transformation + * @param v_out output vector + * @returns v_out for chaining + */ + public final Vec3f mulMvMatVec3f(final Vec3f v_in, final Vec3f v_out) { + return matMv.mulVec3f(v_in, v_out); + } + + /** + * v_inout = Mv * v_inout + * + * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. + * + * @param v_inout input and output vector, i.e. in-place transformation + * @returns v_inout for chaining + */ + public final Vec3f mulMvMatVec3f(final Vec3f v_inout) { + return matMv.mulVec3f(v_inout); + } + + /** + * v_out = P * v_in + * @param v_in input vector, can be v_out for in-place transformation + * @param v_out output vector + * @return given result vector for chaining + * @returns v_out for chaining + */ + public final Vec4f mulPMatVec4f(final Vec4f v_in, final Vec4f v_out) { + return matP.mulVec4f(v_in, v_out); + } + + /** + * v_inout = P * v_inout + * @param v_inout input and output vector, i.e. in-place transformation + * @return given result vector for chaining + * @returns v_inout for chaining + */ + public final Vec4f mulPMatVec4f(final Vec4f v_inout) { + return matP.mulVec4f(v_inout); + } + + /** + * v_out = P * v_in + * + * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. + * + * @param v_in float[3] input vector, can be v_out for in-place transformation + * @param v_out float[3] output vector + * @returns v_out for chaining + */ + public final Vec3f mulPMatVec3f(final Vec3f v_in, final Vec3f v_out) { + return matP.mulVec3f(v_in, v_out); + } + + /** + * v_inout = P * v_inout + * + * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. + * + * @param v_inout input and output vector, i.e. in-place transformation + * @returns v_inout for chaining + */ + public final Vec3f mulPMatVec3f(final Vec3f v_inout) { + return matP.mulVec3f(v_inout); + } + + /** + * v_out = P * Mv * v_in + * @param v_in float[4] input vector, can be v_out for in-place transformation + * @param v_out float[4] output vector + * @returns v_out for chaining + */ + public final Vec4f mulPMvMatVec4f(final Vec4f v_in, final Vec4f v_out) { + return matP.mulVec4f( matMv.mulVec4f( v_in, v_out ) ); + } + + /** + * v_inout = P * Mv * v_inout + * @param v_inout input and output vector, i.e. in-place transformation + * @returns v_inout for chaining + */ + public final Vec4f mulPMvMatVec4f(final Vec4f v_inout) { + return matP.mulVec4f( matMv.mulVec4f( v_inout ) ); + } + + /** + * v_out = P * Mv * v_in + * + * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. + * + * @param v_in float[3] input vector, can be v_out for in-place transformation + * @param v_out float[3] output vector + * @returns v_out for chaining + */ + public final Vec3f mulPMvMatVec3f(final Vec3f v_in, final Vec3f v_out) { + return matP.mulVec3f( matMv.mulVec3f( v_in, v_out ) ); + } + + /** + * v_inout = P * Mv * v_inout + * + * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. + * + * @param v_inout float[3] input and output vector, i.e. in-place transformation + * @returns v_inout for chaining */ - public final float[] multMvPMatrixf(final float[/*16*/] mat4MvP, final int mat4MvP_offset) { - FloatUtil.multMatrix(matrixArray, mMv_offset, matrixArray, mP_offset, mat4MvP, mat4MvP_offset); - return mat4MvP; + public final Vec3f mulPMvMatVec3f(final Vec3f v_inout) { + return matP.mulVec3f( matMv.mulVec3f( v_inout ) ); } // @@ -455,9 +735,7 @@ public final class PMVMatrix implements GLMatrixFunc { if(matrixGetName==GL_MATRIX_MODE) { params.put(matrixMode); } else { - final FloatBuffer matrix = glGetMatrixf(matrixGetName); - params.put(matrix); // matrix -> params - matrix.reset(); + getMat(matrixGetName).get(params); // matrix -> params } params.position(pos); } @@ -467,9 +745,7 @@ public final class PMVMatrix implements GLMatrixFunc { if(matrixGetName==GL_MATRIX_MODE) { params[params_offset]=matrixMode; } else { - final FloatBuffer matrix = glGetMatrixf(matrixGetName); - matrix.get(params, params_offset, 16); // matrix -> params - matrix.reset(); + getMat(matrixGetName).get(params, params_offset); // matrix -> params } } @@ -496,18 +772,15 @@ public final class PMVMatrix implements GLMatrixFunc { @Override public final void glLoadMatrixf(final float[] values, final int offset) { if(matrixMode==GL_MODELVIEW) { - matrixMv.put(values, offset, 16); - matrixMv.reset(); - dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + matMv.load(values, offset); + dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - matrixP.put(values, offset, 16); - matrixP.reset(); - dirtyBits |= DIRTY_FRUSTUM ; + matP.load(values, offset); + dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - matrixTex.put(values, offset, 16); - matrixTex.reset(); + matTex.load(values, offset); modifiedBits |= MODIFIED_TEXTURE; } } @@ -516,148 +789,234 @@ public final class PMVMatrix implements GLMatrixFunc { public final void glLoadMatrixf(final java.nio.FloatBuffer m) { final int spos = m.position(); if(matrixMode==GL_MODELVIEW) { - matrixMv.put(m); - matrixMv.reset(); - dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + matMv.load(m); + dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - matrixP.put(m); - matrixP.reset(); - dirtyBits |= DIRTY_FRUSTUM ; + matP.load(m); + dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - matrixTex.put(m); - matrixTex.reset(); + matTex.load(m); modifiedBits |= MODIFIED_TEXTURE; } m.position(spos); } /** - * Load the current matrix with the values of the given {@link Quaternion}'s rotation {@link Quaternion#toMatrix(float[], int) matrix representation}. + * Load the current matrix with the values of the given {@link Matrix4f}. + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> + */ + public final void glLoadMatrixf(final Matrix4f m) { + if(matrixMode==GL_MODELVIEW) { + matMv.load(m); + dirtyBits |= requestBits | MANUAL_BITS ; + modifiedBits |= MODIFIED_MODELVIEW; + } else if(matrixMode==GL_PROJECTION) { + matP.load(m); + dirtyBits |= MANUAL_BITS ; + modifiedBits |= MODIFIED_PROJECTION; + } else if(matrixMode==GL.GL_TEXTURE) { + matTex.load(m); + modifiedBits |= MODIFIED_TEXTURE; + } + } + + /** + * Load the current matrix with the values of the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> */ public final void glLoadMatrix(final Quaternion quat) { if(matrixMode==GL_MODELVIEW) { - quat.toMatrix(matrixArray, mMv_offset); - matrixMv.reset(); - dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + matMv.setToRotation(quat); + dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - quat.toMatrix(matrixArray, mP_offset); - matrixP.reset(); - dirtyBits |= DIRTY_FRUSTUM ; + matP.setToRotation(quat); + dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - quat.toMatrix(matrixArray, mTex_offset); - matrixTex.reset(); + matTex.setToRotation(quat); modifiedBits |= MODIFIED_TEXTURE; } } @Override public final void glPopMatrix() { - final FloatStack stack; if(matrixMode==GL_MODELVIEW) { - stack = matrixMvStack; + matMv.pop(); + dirtyBits |= requestBits | MANUAL_BITS ; + modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - stack = matrixPStack; + matP.pop(); + dirtyBits |= MANUAL_BITS ; + modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - stack = matrixTStack; - } else { - throw new InternalError("XXX: mode "+matrixMode); + matTex.pop(); + modifiedBits |= MODIFIED_TEXTURE; } - stack.position(stack.position() - 16); - glLoadMatrixf(stack.buffer(), stack.position()); } @Override public final void glPushMatrix() { if(matrixMode==GL_MODELVIEW) { - matrixMvStack.putOnTop(matrixMv, 16); - matrixMv.reset(); + matMv.push(); } else if(matrixMode==GL_PROJECTION) { - matrixPStack.putOnTop(matrixP, 16); - matrixP.reset(); + matP.push(); } else if(matrixMode==GL.GL_TEXTURE) { - matrixTStack.putOnTop(matrixTex, 16); - matrixTex.reset(); + matTex.push(); } } @Override public final void glLoadIdentity() { if(matrixMode==GL_MODELVIEW) { - FloatUtil.makeIdentity(matrixArray, mMv_offset); - dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + matMv.loadIdentity(); + dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - FloatUtil.makeIdentity(matrixArray, mP_offset); - dirtyBits |= DIRTY_FRUSTUM ; + matP.loadIdentity(); + dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - FloatUtil.makeIdentity(matrixArray, mTex_offset); + matTex.loadIdentity(); modifiedBits |= MODIFIED_TEXTURE; } } - @SuppressWarnings("deprecation") @Override public final void glMultMatrixf(final FloatBuffer m) { + final int spos = m.position(); if(matrixMode==GL_MODELVIEW) { - FloatUtil.multMatrix(matrixMv, m); - dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + matMv.mul( mat4Tmp1.load( m ) ); + dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - FloatUtil.multMatrix(matrixP, m); - dirtyBits |= DIRTY_FRUSTUM ; + matP.mul( mat4Tmp1.load( m ) ); + dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - FloatUtil.multMatrix(matrixTex, m); + matTex.mul( mat4Tmp1.load( m ) ); modifiedBits |= MODIFIED_TEXTURE; } + m.position(spos); } @Override public final void glMultMatrixf(final float[] m, final int m_offset) { if(matrixMode==GL_MODELVIEW) { - FloatUtil.multMatrix(matrixArray, mMv_offset, m, m_offset); - dirtyBits |= DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW | DIRTY_FRUSTUM ; + matMv.mul( mat4Tmp1.load( m, m_offset ) ); + dirtyBits |= requestBits | MANUAL_BITS ; + modifiedBits |= MODIFIED_MODELVIEW; + } else if(matrixMode==GL_PROJECTION) { + matP.mul( mat4Tmp1.load( m, m_offset ) ); + dirtyBits |= MANUAL_BITS ; + modifiedBits |= MODIFIED_PROJECTION; + } else if(matrixMode==GL.GL_TEXTURE) { + matTex.mul( mat4Tmp1.load( m, m_offset ) ); + modifiedBits |= MODIFIED_TEXTURE; + } + } + + /** + * Multiply the current matrix: [c] = [c] x [m] + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> + * @param m the right hand Matrix4f + * @return this instance of chaining + */ + public final PMVMatrix glMultMatrixf(final Matrix4f m) { + if(matrixMode==GL_MODELVIEW) { + matMv.mul( m ); + dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { - FloatUtil.multMatrix(matrixArray, mP_offset, m, m_offset); - dirtyBits |= DIRTY_FRUSTUM ; + matP.mul( m ); + dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { - FloatUtil.multMatrix(matrixArray, mTex_offset, m, m_offset); + matTex.mul( m ); modifiedBits |= MODIFIED_TEXTURE; } + return this; } @Override public final void glTranslatef(final float x, final float y, final float z) { - glMultMatrixf(FloatUtil.makeTranslation(matrixTxSx, false, x, y, z), 0); + glMultMatrixf( mat4Tmp1.setToTranslation(x, y, z) ); + } + + /** + * Translate the current matrix. + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> + * @param t translation vec3 + * @return this instance of chaining + */ + public final PMVMatrix glTranslatef(final Vec3f t) { + return glMultMatrixf( mat4Tmp1.setToTranslation(t) ); } @Override public final void glScalef(final float x, final float y, final float z) { - glMultMatrixf(FloatUtil.makeScale(matrixTxSx, false, x, y, z), 0); + glMultMatrixf( mat4Tmp1.setToScale(x, y, z) ); + } + + /** + * Scale the current matrix. + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> + * @param s scale vec4f + * @return this instance of chaining + */ + public final PMVMatrix glScalef(final Vec3f s) { + return glMultMatrixf( mat4Tmp1.setToScale(s) ); } @Override public final void glRotatef(final float ang_deg, final float x, final float y, final float z) { - glMultMatrixf(FloatUtil.makeRotationAxis(mat4Tmp1, 0, ang_deg * FloatUtil.PI / 180.0f, x, y, z, mat4Tmp2), 0); + glMultMatrixf( mat4Tmp1.setToRotationAxis(FloatUtil.adegToRad(ang_deg), x, y, z) ); + } + + /** + * Rotate the current matrix by the given axis and angle in radians. + * <p> + * Consider using {@link #glRotate(Quaternion)} + * </p> + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> + * @param ang_rad angle in radians + * @param axis rotation axis + * @return this instance of chaining + * @see #glRotate(Quaternion) + */ + public final PMVMatrix glRotatef(final float ang_rad, final Vec3f axis) { + return glMultMatrixf( mat4Tmp1.setToRotationAxis(ang_rad, axis) ); } /** - * Rotate the current matrix with the given {@link Quaternion}'s rotation {@link Quaternion#toMatrix(float[], int) matrix representation}. + * Rotate the current matrix with the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + * <p> + * Extension to {@link GLMatrixFunc}. + * </p> + * @param quat the {@link Quaternion} + * @return this instance of chaining */ - public final void glRotate(final Quaternion quat) { - glMultMatrixf(quat.toMatrix(mat4Tmp1, 0), 0); + public final PMVMatrix glRotate(final Quaternion quat) { + return glMultMatrixf( mat4Tmp1.setToRotation(quat) ); } @Override public final void glOrthof(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { - glMultMatrixf( FloatUtil.makeOrtho(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar), 0 ); + glMultMatrixf( mat4Tmp1.setToOrtho(left, right, bottom, top, zNear, zFar) ); } /** @@ -665,11 +1024,11 @@ public final class PMVMatrix implements GLMatrixFunc { * * @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear} * or {@code left == right}, or {@code bottom == top}. - * @see FloatUtil#makeFrustum(float[], int, boolean, float, float, float, float, float, float) + * @see Matrix4f#setToFrustum(float, float, float, float, float, float) */ @Override public final void glFrustumf(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) throws GLException { - glMultMatrixf( FloatUtil.makeFrustum(mat4Tmp1, 0, true, left, right, bottom, top, zNear, zFar), 0 ); + glMultMatrixf( mat4Tmp1.setToFrustum(left, right, bottom, top, zNear, zFar) ); } // @@ -684,30 +1043,18 @@ public final class PMVMatrix implements GLMatrixFunc { * @param zNear * @param zFar * @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear} - * @see FloatUtil#makePerspective(float[], int, boolean, float, float, float, float) + * @see Matrix4f#setToPerspective(float, float, float, float) */ public final void gluPerspective(final float fovy_deg, final float aspect, final float zNear, final float zFar) throws GLException { - glMultMatrixf( FloatUtil.makePerspective(mat4Tmp1, 0, true, fovy_deg * FloatUtil.PI / 180.0f, aspect, zNear, zFar), 0 ); + glMultMatrixf( mat4Tmp1.setToPerspective(FloatUtil.adegToRad(fovy_deg), aspect, zNear, zFar) ); } /** * {@link #glMultMatrixf(FloatBuffer) Multiply} and {@link #glTranslatef(float, float, float) translate} the {@link #glGetMatrixMode() current matrix} * with the eye, object and orientation. */ - public final void gluLookAt(final float eyex, final float eyey, final float eyez, - final float centerx, final float centery, final float centerz, - final float upx, final float upy, final float upz) { - mat4Tmp2[0+0] = eyex; - mat4Tmp2[1+0] = eyey; - mat4Tmp2[2+0] = eyez; - mat4Tmp2[0+4] = centerx; - mat4Tmp2[1+4] = centery; - mat4Tmp2[2+4] = centerz; - mat4Tmp2[0+8] = upx; - mat4Tmp2[1+8] = upy; - mat4Tmp2[2+8] = upz; - glMultMatrixf( - FloatUtil.makeLookAt(mat4Tmp1, 0, mat4Tmp2 /* eye */, 0, mat4Tmp2 /* center */, 4, mat4Tmp2 /* up */, 8, mat4Tmp3), 0); + public final void gluLookAt(final Vec3f eye, final Vec3f center, final Vec3f up) { + glMultMatrixf( mat4Tmp1.setToLookAt(eye, center, up, getTmp2Mat()) ); } /** @@ -716,24 +1063,13 @@ public final class PMVMatrix implements GLMatrixFunc { * Traditional <code>gluProject</code> implementation. * </p> * - * @param objx - * @param objy - * @param objz - * @param viewport 4 component viewport vector - * @param viewport_offset - * @param win_pos 3 component window coordinate, the result - * @param win_pos_offset + * @param objPos 3 component object coordinate + * @param viewport Rect4i viewport + * @param winPos 3 component window coordinate, the result * @return true if successful, otherwise false (z is 1) */ - public final boolean gluProject(final float objx, final float objy, final float objz, - final int[] viewport, final int viewport_offset, - final float[] win_pos, final int win_pos_offset ) { - return FloatUtil.mapObjToWinCoords(objx, objy, objz, - matrixArray, mMv_offset, - matrixArray, mP_offset, - viewport, viewport_offset, - win_pos, win_pos_offset, - mat4Tmp1, mat4Tmp2); + public final boolean gluProject(final Vec3f objPos, final Recti viewport, final Vec3f winPos ) { + return Matrix4f.mapObjToWin(objPos, matMv, matP, viewport, winPos); } /** @@ -745,21 +1081,17 @@ public final class PMVMatrix implements GLMatrixFunc { * @param winx * @param winy * @param winz - * @param viewport 4 component viewport vector - * @param viewport_offset - * @param obj_pos 3 component object coordinate, the result - * @param obj_pos_offset + * @param viewport Rect4i viewport + * @param objPos 3 component object coordinate, the result * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) */ public final boolean gluUnProject(final float winx, final float winy, final float winz, - final int[] viewport, final int viewport_offset, - final float[] obj_pos, final int obj_pos_offset) { - return FloatUtil.mapWinToObjCoords(winx, winy, winz, - matrixArray, mMv_offset, - matrixArray, mP_offset, - viewport, viewport_offset, - obj_pos, obj_pos_offset, - mat4Tmp1, mat4Tmp2); + final Recti viewport, final Vec3f objPos) { + if( Matrix4f.mapWinToObj(winx, winy, winz, getPMviMat(), viewport, objPos) ) { + return true; + } else { + return false; + } } /** @@ -772,58 +1104,27 @@ public final class PMVMatrix implements GLMatrixFunc { * @param winy * @param winz * @param clipw - * @param modelMatrix 4x4 modelview matrix - * @param modelMatrix_offset - * @param projMatrix 4x4 projection matrix - * @param projMatrix_offset - * @param viewport 4 component viewport vector - * @param viewport_offset + * @param viewport Rect4i viewport * @param near * @param far - * @param obj_pos 4 component object coordinate, the result - * @param obj_pos_offset + * @param objPos 4 component object coordinate, the result * @return true if successful, otherwise false (failed to invert matrix, or becomes infinity due to zero z) */ public boolean gluUnProject4(final float winx, final float winy, final float winz, final float clipw, - final int[] viewport, final int viewport_offset, + final Recti viewport, final float near, final float far, - final float[] obj_pos, final int obj_pos_offset ) { - return FloatUtil.mapWinToObjCoords(winx, winy, winz, clipw, - matrixArray, mMv_offset, - matrixArray, mP_offset, - viewport, viewport_offset, - near, far, - obj_pos, obj_pos_offset, - mat4Tmp1, mat4Tmp2); - } - - /** - * Make given matrix the <i>pick</i> matrix based on given parameters. - * <p> - * Traditional <code>gluPickMatrix</code> implementation. - * </p> - * <p> - * See {@link FloatUtil#makePick(float[], int, float, float, float, float, int[], int, float[]) FloatUtil.makePick(..)} for details. - * </p> - * @param x the center x-component of a picking region in window coordinates - * @param y the center y-component of a picking region in window coordinates - * @param deltaX the width of the picking region in window coordinates. - * @param deltaY the height of the picking region in window coordinates. - * @param viewport 4 component viewport vector - * @param viewport_offset - */ - public final void gluPickMatrix(final float x, final float y, - final float deltaX, final float deltaY, - final int[] viewport, final int viewport_offset) { - if( null != FloatUtil.makePick(mat4Tmp1, 0, x, y, deltaX, deltaY, viewport, viewport_offset, mat4Tmp2) ) { - glMultMatrixf(mat4Tmp1, 0); + final Vec4f objPos) { + if( Matrix4f.mapWinToObj4(winx, winy, winz, clipw, getPMviMat(), viewport, near, far, objPos) ) { + return true; + } else { + return false; } } /** * Map two window coordinates w/ shared X/Y and distinctive Z * to a {@link Ray}. The resulting {@link Ray} maybe used for <i>picking</i> - * using a {@link AABBox#getRayIntersection(Ray, float[]) bounding box}. + * using a {@link AABBox#getRayIntersection(Vec3f, Ray, float, boolean) bounding box}. * <p> * Notes for picking <i>winz0</i> and <i>winz1</i>: * <ul> @@ -837,52 +1138,93 @@ public final class PMVMatrix implements GLMatrixFunc { * @param winz0 * @param winz1 * @param viewport - * @param viewport_offset * @param ray storage for the resulting {@link Ray} * @return true if successful, otherwise false (failed to invert matrix, or becomes z is infinity) */ public final boolean gluUnProjectRay(final float winx, final float winy, final float winz0, final float winz1, - final int[] viewport, final int viewport_offset, - final Ray ray) { - return FloatUtil.mapWinToRay(winx, winy, winz0, winz1, - matrixArray, mMv_offset, - matrixArray, mP_offset, - viewport, viewport_offset, - ray, - mat4Tmp1, mat4Tmp2, mat4Tmp3); + final Recti viewport, final Ray ray) { + return Matrix4f.mapWinToRay(winx, winy, winz0, winz1, getPMviMat(), viewport, ray); + } + + /** + * Make given matrix the <i>pick</i> matrix based on given parameters. + * <p> + * Traditional <code>gluPickMatrix</code> implementation. + * </p> + * <p> + * See {@link Matrix4f#setToPick(float, float, float, float, Recti, int, Matrix4f) for details. + * </p> + * @param x the center x-component of a picking region in window coordinates + * @param y the center y-component of a picking region in window coordinates + * @param deltaX the width of the picking region in window coordinates. + * @param deltaY the height of the picking region in window coordinates. + * @param viewport Rect4i viewport vector + */ + public final void gluPickMatrix(final float x, final float y, + final float deltaX, final float deltaY, final Recti viewport) { + if( null != mat4Tmp1.setToPick(x, y, deltaX, deltaY, viewport, getTmp2Mat()) ) { + glMultMatrixf( mat4Tmp1 ); + } } public StringBuilder toString(StringBuilder sb, final String f) { if(null == sb) { sb = new StringBuilder(); } - final boolean mviDirty = 0 != (DIRTY_INVERSE_MODELVIEW & dirtyBits); - final boolean mvitDirty = 0 != (DIRTY_INVERSE_TRANSPOSED_MODELVIEW & dirtyBits); - final boolean frustumDirty = 0 != (DIRTY_FRUSTUM & dirtyBits); - final boolean mviReq = 0 != (DIRTY_INVERSE_MODELVIEW & requestMask); - final boolean mvitReq = 0 != (DIRTY_INVERSE_TRANSPOSED_MODELVIEW & requestMask); - final boolean frustumReq = 0 != (DIRTY_FRUSTUM & requestMask); + final boolean pmvDirty = 0 != (PREMUL_PMV & dirtyBits); + final boolean pmvUsed = null != matPMv; + + final boolean pmviDirty = 0 != (PREMUL_PMVI & dirtyBits); + final boolean pmviUsed = null != matPMvi; + + final boolean frustumDirty = 0 != (FRUSTUM & dirtyBits); + final boolean frustumUsed = null != frustum; + + final boolean mviDirty = 0 != (INVERSE_MODELVIEW & dirtyBits); + final boolean mviReq = 0 != (INVERSE_MODELVIEW & requestBits); + + final boolean mvitDirty = 0 != (INVERSE_TRANSPOSED_MODELVIEW & dirtyBits); + final boolean mvitReq = 0 != (INVERSE_TRANSPOSED_MODELVIEW & requestBits); + final boolean modP = 0 != ( MODIFIED_PROJECTION & modifiedBits ); final boolean modMv = 0 != ( MODIFIED_MODELVIEW & modifiedBits ); final boolean modT = 0 != ( MODIFIED_TEXTURE & modifiedBits ); + int count = 3; // P, Mv, T sb.append("PMVMatrix[modified[P ").append(modP).append(", Mv ").append(modMv).append(", T ").append(modT); - sb.append("], dirty/req[Mvi ").append(mviDirty).append("/").append(mviReq).append(", Mvit ").append(mvitDirty).append("/").append(mvitReq).append(", Frustum ").append(frustumDirty).append("/").append(frustumReq).append("]").append(PlatformPropsImpl.NEWLINE); - sb.append(", Projection").append(PlatformPropsImpl.NEWLINE); - matrixToString(sb, f, matrixP); - sb.append(", Modelview").append(PlatformPropsImpl.NEWLINE); - matrixToString(sb, f, matrixMv); - sb.append(", Texture").append(PlatformPropsImpl.NEWLINE); - matrixToString(sb, f, matrixTex); - if( 0 != ( requestMask & DIRTY_INVERSE_MODELVIEW ) ) { - sb.append(", Inverse Modelview").append(PlatformPropsImpl.NEWLINE); - matrixToString(sb, f, matrixMvi); + sb.append("], dirty/used[PMv ").append(pmvDirty).append("/").append(pmvUsed).append(", Pmvi ").append(pmviDirty).append("/").append(pmviUsed).append(", Frustum ").append(frustumDirty).append("/").append(frustumUsed); + sb.append("], dirty/req[Mvi ").append(mviDirty).append("/").append(mviReq).append(", Mvit ").append(mvitDirty).append("/").append(mvitReq).append("]").append(System.lineSeparator()); + sb.append(", Projection").append(System.lineSeparator()); + matP.toString(sb, null, f); + sb.append(", Modelview").append(System.lineSeparator()); + matMv.toString(sb, null, f); + sb.append(", Texture").append(System.lineSeparator()); + matTex.toString(sb, null, f); + if( null != matPMv ) { + sb.append(", P * Mv").append(System.lineSeparator()); + matPMv.toString(sb, null, f); + ++count; + } + if( null != matPMvi ) { + sb.append(", P * Mv").append(System.lineSeparator()); + matPMvi.toString(sb, null, f); + ++count; } - if( 0 != ( requestMask & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) { - sb.append(", Inverse Transposed Modelview").append(PlatformPropsImpl.NEWLINE); - matrixToString(sb, f, matrixMvit); + if( mviReq ) { + sb.append(", Inverse Modelview").append(System.lineSeparator()); + matMvi.toString(sb, null, f); + ++count; } - sb.append("]"); + if( mvitReq ) { + sb.append(", Inverse Transposed Modelview").append(System.lineSeparator()); + matMvit.toString(sb, null, f); + ++count; + } + int tmpCount = 1; + if( null != mat4Tmp2 ) { + ++tmpCount; + } + sb.append(", matrices "+count+" + "+tmpCount+" temp = "+(count+tmpCount)+"]"); return sb; } @@ -902,6 +1244,8 @@ public final class PMVMatrix implements GLMatrixFunc { * @see #MODIFIED_PROJECTION * @see #MODIFIED_MODELVIEW * @see #MODIFIED_TEXTURE + * @see #getDirtyBits() + * @see #isReqDirty() */ public final int getModifiedBits(final boolean clear) { final int r = modifiedBits; @@ -912,169 +1256,399 @@ public final class PMVMatrix implements GLMatrixFunc { } /** - * Returns the dirty bits due to mutable operations. + * Returns the dirty bits due to mutable operations, + * i.e. + * - {@link #INVERSE_MODELVIEW} (if requested) + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} (if requested) + * - {@link #FRUSTUM} (always, cleared via {@link #getFrustum()} + * <p> + * A dirty bit is set, if the corresponding matrix had been modified by a mutable operation + * since last {@link #update()} call and requested in the constructor {@link #PMVMatrix(int)}. + * </p> * <p> - * A dirty bit is set , if the corresponding matrix had been modified by a mutable operation - * since last {@link #update()} call. The latter clears the dirty state only if the dirty matrix (Mvi or Mvit) or {@link Frustum} - * has been requested by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} - * or {@link #glGetFrustum() Frustum get} methods. + * {@link #update()} clears the dirty state for the matrices and {@link #getFrustum()} for {@link #FRUSTUM}. * </p> * - * @see #DIRTY_INVERSE_MODELVIEW - * @see #DIRTY_INVERSE_TRANSPOSED_MODELVIEW - * @see #DIRTY_FRUSTUM - * @see #glGetMviMatrixf() - * @see #glGetMvitMatrixf() - * @see #glGetPMvMviMatrixf() - * @see #glGetPMvMvitMatrixf() - * @see #glGetFrustum() + * @see #isReqDirty() + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #FRUSTUM + * @see #PMVMatrix(int) + * @see #getMviMat() + * @see #getMvitMat() + * @see #getSyncPMvMviMat() + * @see #getSyncPMvMviMvitMat() + * @see #getFrustum() */ public final int getDirtyBits() { return dirtyBits; } /** - * Returns the request bit mask, which uses bit values equal to the dirty mask. + * Returns true if the one of the {@link #getReqBits() requested bits} are are set dirty due to mutable operations, + * i.e. at least one of + * - {@link #INVERSE_MODELVIEW} + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} + * <p> + * A dirty bit is set, if the corresponding matrix had been modified by a mutable operation + * since last {@link #update()} call and requested in the constructor {@link #PMVMatrix(int)}. + * </p> * <p> - * The request bit mask is set by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} - * or {@link #glGetFrustum() Frustum get} methods. + * {@link #update()} clears the dirty state for the matrices and {@link #getFrustum()} for {@link #FRUSTUM}. * </p> * - * @see #clearAllUpdateRequests() - * @see #DIRTY_INVERSE_MODELVIEW - * @see #DIRTY_INVERSE_TRANSPOSED_MODELVIEW - * @see #DIRTY_FRUSTUM - * @see #glGetMviMatrixf() - * @see #glGetMvitMatrixf() - * @see #glGetPMvMviMatrixf() - * @see #glGetPMvMvitMatrixf() - * @see #glGetFrustum() + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #PMVMatrix(int) + * @see #getMviMat() + * @see #getMvitMat() + * @see #getSyncPMvMviMat() + * @see #getSyncPMvMviMvitMat() */ - public final int getRequestMask() { - return requestMask; + public final boolean isReqDirty() { + return 0 != ( requestBits & dirtyBits ); } - /** - * Clears all {@link #update()} requests of the Mvi and Mvit matrix and Frustum - * after it has been enabled by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} - * or {@link #glGetFrustum() Frustum get} methods. + * Returns the request bit mask, which uses bit values equal to the dirty mask + * and may contain + * - {@link #INVERSE_MODELVIEW} + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} * <p> - * Allows user to disable subsequent Mvi, Mvit and {@link Frustum} updates if no more required. + * The request bit mask is set by in the constructor {@link #PMVMatrix(int)}. * </p> * - * @see #glGetMviMatrixf() - * @see #glGetMvitMatrixf() - * @see #glGetPMvMviMatrixf() - * @see #glGetPMvMvitMatrixf() - * @see #glGetFrustum() - * @see #getRequestMask() + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #PMVMatrix(int) + * @see #getMviMat() + * @see #getMvitMat() + * @see #getSyncPMvMviMat() + * @see #getSyncPMvMviMvitMat() + * @see #getFrustum() + */ + public final int getReqBits() { + return requestBits; + } + + /** + * Returns the pre-multiplied projection x modelview, P x Mv. + * <p> + * This {@link Matrix4f} instance should be re-fetched via this method and not locally stored + * to have it updated from a potential modification of underlying projection and/or modelview matrix. + * {@link #update()} has no effect on this {@link Matrix4f}. + * </p> + * <p> + * This pre-multipled P x Mv is considered dirty, if its corresponding + * {@link #getPMat() P matrix} or {@link #getMvMat() Mv matrix} has been modified since its last update. + * </p> + * @see #update() + */ + public final Matrix4f getPMvMat() { + if( 0 != ( dirtyBits & PREMUL_PMV ) ) { + if( null == matPMv ) { + matPMv = new Matrix4f(); + } + matPMv.mul(matP, matMv); + dirtyBits &= ~PREMUL_PMV; + } + return matPMv; + } + + /** + * Returns the pre-multiplied inverse projection x modelview, + * if {@link Matrix4f#invert(Matrix4f)} succeeded, otherwise `null`. + * <p> + * This {@link Matrix4f} instance should be re-fetched via this method and not locally stored + * to have it updated from a potential modification of underlying projection and/or modelview matrix. + * {@link #update()} has no effect on this {@link Matrix4f}. + * </p> + * <p> + * This pre-multipled invert(P x Mv) is considered dirty, if its corresponding + * {@link #getPMat() P matrix} or {@link #getMvMat() Mv matrix} has been modified since its last update. + * </p> + * @see #update() */ - public final void clearAllUpdateRequests() { - requestMask &= ~DIRTY_ALL; + public final Matrix4f getPMviMat() { + if( 0 != ( dirtyBits & PREMUL_PMVI ) ) { + if( null == matPMvi ) { + matPMvi = new Matrix4f(); + } + final Matrix4f mPMv = getPMvMat(); + matPMviOK = matPMvi.invert(mPMv); + dirtyBits &= ~PREMUL_PMVI; + } + return matPMviOK ? matPMvi : null; + } + + /** + * Returns the frustum, derived from projection x modelview. + * <p> + * This {@link Frustum} instance should be re-fetched via this method and not locally stored + * to have it updated from a potential modification of underlying projection and/or modelview matrix. + * {@link #update()} has no effect on this {@link Frustum}. + * </p> + * <p> + * The {@link Frustum} is considered dirty, if its corresponding + * {@link #getPMat() P matrix} or {@link #getMvMat() Mv matrix} has been modified since its last update. + * </p> + * @see #update() + */ + public final Frustum getFrustum() { + if( 0 != ( dirtyBits & FRUSTUM ) ) { + if( null == frustum ) { + frustum = new Frustum(); + } + final Matrix4f mPMv = getPMvMat(); + frustum.updateFrustumPlanes(mPMv); + dirtyBits &= ~FRUSTUM; + } + return frustum; } /** - * Update the derived {@link #glGetMviMatrixf() inverse modelview (Mvi)}, - * {@link #glGetMvitMatrixf() inverse transposed modelview (Mvit)} matrices and {@link Frustum} - * <b>if</b> they are dirty <b>and</b> they were requested - * by one of the {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} - * or {@link #glGetFrustum() Frustum get} methods. + * Update the derived {@link #getMviMat() inverse modelview (Mvi)}, + * {@link #getMvitMat() inverse transposed modelview (Mvit)} matrices + * <b>if</b> they {@link #isReqDirty() are dirty} <b>and</b> + * requested via the constructor {@link #PMVMatrix(int)}.<br/> + * Hence updates the following dirty bits. + * - {@link #INVERSE_MODELVIEW} + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} + * <p> + * The {@link Frustum} is updated only via {@link #getFrustum()} separately. + * </p> * <p> - * The Mvi and Mvit matrices and {@link Frustum} are considered dirty, if their corresponding - * {@link #glGetMvMatrixf() Mv matrix} has been modified since their last update. + * The Mvi and Mvit matrices are considered dirty, if their corresponding + * {@link #getMvMat() Mv matrix} has been modified since their last update. * </p> * <p> - * Method should be called manually in case mutable operations has been called + * Method is automatically called by {@link SyncMatrix4f} and {@link SyncMatrices4f} + * instances {@link SyncAction} as retrieved by e.g. {@link #getSyncMvitMat()}. + * This ensures an automatic update cycle if used with {@link GLUniformData}. + * </p> + * <p> + * Method may be called manually in case mutable operations has been called * and caller operates on already fetched references, i.e. not calling - * {@link #glGetMviMatrixf() Mvi get}, {@link #glGetMvitMatrixf() Mvit get} - * or {@link #glGetFrustum() Frustum get} etc anymore. + * {@link #getMviMat()}, {@link #getMvitMat()} anymore. * </p> * <p> - * This method clears the modified bits like {@link #getModifiedBits(boolean) getModifiedBits(true)}, + * Method clears the modified bits like {@link #getModifiedBits(boolean) getModifiedBits(true)}, * which are set by any mutable operation. The modified bits have no impact * on this method, but the return value. * </p> * * @return true if any matrix has been modified since last update call or - * if the derived matrices Mvi and Mvit or {@link Frustum} were updated, otherwise false. + * if the derived matrices Mvi and Mvit were updated, otherwise false. * In other words, method returns true if any matrix used by the caller must be updated, * e.g. uniforms in a shader program. * * @see #getModifiedBits(boolean) - * @see #MODIFIED_PROJECTION - * @see #MODIFIED_MODELVIEW - * @see #MODIFIED_TEXTURE - * @see #DIRTY_INVERSE_MODELVIEW - * @see #DIRTY_INVERSE_TRANSPOSED_MODELVIEW - * @see #DIRTY_FRUSTUM - * @see #glGetMviMatrixf() - * @see #glGetMvitMatrixf() - * @see #glGetPMvMviMatrixf() - * @see #glGetPMvMvitMatrixf() - * @see #glGetFrustum() - * @see #clearAllUpdateRequests() + * @see #isReqDirty() + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #PMVMatrix(int) + * @see #getMviMat() + * @see #getMvitMat() + * @see #getSyncPMvMviMat() + * @see #getSyncPMvMviMvitMat() */ public final boolean update() { return updateImpl(true); } + + // + // private + // + private final boolean updateImpl(final boolean clearModBits) { boolean mod = 0 != modifiedBits; - if(clearModBits) { + if( clearModBits ) { modifiedBits = 0; } - - if( 0 != ( dirtyBits & ( DIRTY_FRUSTUM & requestMask ) ) ) { - if( null == frustum ) { - frustum = new Frustum(); + if( 0 != ( requestBits & ( ( dirtyBits & ( INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW ) ) ) ) ) { // only if dirt requested & dirty + if( !matMvi.invert(matMv) ) { + throw new GLException(msgCantComputeInverse); } - FloatUtil.multMatrix(matrixArray, mP_offset, matrixArray, mMv_offset, mat4Tmp1, 0); - // FloatUtil.multMatrix(matrixP, matrixMv, mat4Tmp1, 0); - frustum.updateByPMV(mat4Tmp1, 0); - dirtyBits &= ~DIRTY_FRUSTUM; + dirtyBits &= ~INVERSE_MODELVIEW; + mod = true; + } + if( 0 != ( requestBits & ( dirtyBits & INVERSE_TRANSPOSED_MODELVIEW ) ) ) { // only if requested & dirty + matMvit.transpose(matMvi); + dirtyBits &= ~INVERSE_TRANSPOSED_MODELVIEW; mod = true; } + return mod; + } + private static final String msgCantComputeInverse = "Invalid source Mv matrix, can't compute inverse"; - if( 0 == ( dirtyBits & requestMask ) ) { - return mod; // nothing more requested which may have been dirty + private final Matrix4f matP; + private final Matrix4f matMv; + private final Matrix4f matTex; + + private final Matrix4f matMvi; + private final Matrix4f matMvit; + + private static final int mP_offset = 0*16; + private static final int mMv_offset = 1*16; + private final int mMvi_offset; + private final int mMvit_offset; + private final int mTex_offset; + + private final float[] matrixStore; + + private final FloatBuffer bufP, bufMv, bufTex; + private final FloatBuffer bufMvi, bufMvit; + private final FloatBuffer bufP_Mv, bufP_Mv_Mvi, bufP_Mv_Mvi_Mvit; + + private final SyncMatrix4f syncP, syncMv, syncT; + private final SyncMatrix4f syncMvi, syncMvit; + private final SyncMatrices4f syncP_Mv, syncP_Mv_Mvi, syncP_Mv_Mvi_Mvit; + + private final Matrix4f mat4Tmp1; + + private int matrixMode = GL_MODELVIEW; + private int modifiedBits = MODIFIED_ALL; + private int dirtyBits = 0; // contains the dirty bits, i.e. hinting for update operation + private final int requestBits; // may contain the requested bits: INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW + private Matrix4f mat4Tmp2; + private Matrix4f matPMv; + private Matrix4f matPMvi; + private boolean matPMviOK; + private Frustum frustum; + + private abstract class PMVSyncBuffer implements SyncMatrix4f { + protected final Matrix4f mat; + private final FloatBuffer fbuf; + + public PMVSyncBuffer(final Matrix4f m, final FloatBuffer fbuf) { + this.mat = m; + this.fbuf = fbuf; } - return setMviMvit() || mod; + @Override + public final Buffer getBuffer() { return fbuf; } + + @Override + public final SyncBuffer sync() { getAction().sync(); return this; } + + @Override + public final Buffer getSyncBuffer() { getAction().sync(); return fbuf; } + + @Override + public final Matrix4f getMatrix() { return mat; } + + @Override + public final FloatBuffer getSyncFloats() { getAction().sync(); return fbuf; } } + private final class SyncBuffer0 extends PMVSyncBuffer { + private final SyncAction action = new SyncAction() { + @Override + public void sync() { mat.get(matrixStore); } + }; - // - // private - // - private static final String msgCantComputeInverse = "Invalid source Mv matrix, can't compute inverse"; + public SyncBuffer0(final Matrix4f m, final FloatBuffer fbuf) { super(m, fbuf); } - private final boolean setMviMvit() { - final float[] _matrixMvi = matrixMvi.array(); - final int _matrixMviOffset = matrixMvi.position(); - boolean res = false; - if( 0 != ( dirtyBits & DIRTY_INVERSE_MODELVIEW ) ) { // only if dirt; always requested at this point, see update() - if( null == FloatUtil.invertMatrix(matrixArray, mMv_offset, _matrixMvi, _matrixMviOffset) ) { - throw new GLException(msgCantComputeInverse); + @Override + public SyncAction getAction() { return action; } + + } + private final class SyncBuffer1 extends PMVSyncBuffer { + private final int offset; + private final SyncAction action = new SyncAction() { + @Override + public void sync() { mat.get(matrixStore, offset); } + }; + + public SyncBuffer1(final Matrix4f m, final FloatBuffer fbuf, final int offset) { + super(m, fbuf); + this.offset = offset; + } + + @Override + public SyncAction getAction() { return action; } + } + private final class SyncBuffer1U extends PMVSyncBuffer { + private final int offset; + private final SyncAction action = new SyncAction() { + @Override + public void sync() { + updateImpl(true); + mat.get(matrixStore, offset); } - dirtyBits &= ~DIRTY_INVERSE_MODELVIEW; - res = true; + }; + + public SyncBuffer1U(final Matrix4f m, final FloatBuffer fbuf, final int offset) { + super(m, fbuf); + this.offset = offset; + } + + @Override + public SyncAction getAction() { return action; } + } + + private abstract class PMVSyncBufferN implements SyncMatrices4f { + protected final Matrix4f[] mats; + private final FloatBuffer fbuf; + + public PMVSyncBufferN(final Matrix4f[] ms, final FloatBuffer fbuf) { + this.mats = ms; + this.fbuf = fbuf; } - if( 0 != ( requestMask & ( dirtyBits & DIRTY_INVERSE_TRANSPOSED_MODELVIEW ) ) ) { // only if requested & dirty - FloatUtil.transposeMatrix(_matrixMvi, _matrixMviOffset, matrixMvit.array(), matrixMvit.position()); - dirtyBits &= ~DIRTY_INVERSE_TRANSPOSED_MODELVIEW; - res = true; + + @Override + public final Buffer getBuffer() { return fbuf; } + + @Override + public final SyncBuffer sync() { getAction().sync(); return this; } + + @Override + public final Buffer getSyncBuffer() { getAction().sync(); return fbuf; } + + @Override + public Matrix4f[] getMatrices() { return mats; } + + @Override + public final FloatBuffer getSyncFloats() { getAction().sync(); return fbuf; } + } + private final class SyncBufferN extends PMVSyncBufferN { + private final int offset; + private final SyncAction action = new SyncAction() { + @Override + public void sync() { + int ioff = offset; + for(int i=0; i<mats.length; ++i, ioff+=16) { + mats[i].get(matrixStore, ioff); + } + } + }; + + public SyncBufferN(final Matrix4f[] ms, final FloatBuffer fbuf, final int offset) { + super(ms, fbuf); + this.offset = offset; } - return res; + + @Override + public SyncAction getAction() { return action; } } + private final class SyncBufferNU extends PMVSyncBufferN { + private final int offset; + private final SyncAction action = new SyncAction() { + @Override + public void sync() { + updateImpl(true); + int ioff = offset; + for(int i=0; i<mats.length; ++i, ioff+=16) { + mats[i].get(matrixStore, ioff); + } + } + }; - private final float[] matrixArray; - private final int mP_offset, mMv_offset, mTex_offset; - private final FloatBuffer matrixPMvMvit, matrixPMvMvi, matrixPMv, matrixP, matrixTex, matrixMv, matrixMvi, matrixMvit; - private final float[] matrixTxSx; - private final float[] mat4Tmp1, mat4Tmp2, mat4Tmp3; - private final FloatStack matrixTStack, matrixPStack, matrixMvStack; - private int matrixMode = GL_MODELVIEW; - private int modifiedBits = MODIFIED_ALL; - private int dirtyBits = DIRTY_ALL; // contains the dirty bits, i.e. hinting for update operation - private int requestMask = 0; // may contain the requested dirty bits: DIRTY_INVERSE_MODELVIEW | DIRTY_INVERSE_TRANSPOSED_MODELVIEW - private Frustum frustum; + public SyncBufferNU(final Matrix4f[] ms, final FloatBuffer fbuf, final int offset) { + super(ms, fbuf); + this.offset = offset; + } + + @Override + public SyncAction getAction() { return action; } + } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/SyncAction.java b/src/jogl/classes/com/jogamp/opengl/util/SyncAction.java new file mode 100644 index 000000000..fbf55e683 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/SyncAction.java @@ -0,0 +1,47 @@ +/** + * Copyright 2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +/** + * Specific data synchronization action implemented by the data provider + * to update the buffer with the underlying data before usage, e.g. uploading the {@link com.jogamp.opengl.GLUniformData GLUniformData} data to the GPU. + * <p> + * Example: Invoked before delivering {@link com.jogamp.opengl.GLUniformData GLUniformData}'s data via {@link com.jogamp.opengl.GLUniformData#getObject() getObject()} + * or {@link com.jogamp.opengl.GLUniformData#getBuffer() getBuffer()}. + * </p> + */ +public interface SyncAction { + /** + * Synchronizes the buffer with the underlying data before usage. + * <p> + * Example: {@link com.jogamp.opengl.GLUniformData GLUniformData} issues this method before delivering data via {@link com.jogamp.opengl.GLUniformData#getObject() getObject()} + * or {@link com.jogamp.opengl.GLUniformData#getBuffer() getBuffer()}. + * </p> + */ + void sync(); +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/SyncBuffer.java b/src/jogl/classes/com/jogamp/opengl/util/SyncBuffer.java new file mode 100644 index 000000000..27bdb5dfb --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/SyncBuffer.java @@ -0,0 +1,61 @@ +/** + * Copyright 2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.nio.Buffer; + +/** + * Convenient tuple of a {@link SyncAction} and {@link Buffer}. + * <p> + * {@link SyncAction#sync()} is used to update the {@link Buffer} with the underlying data + * known to the data provider. + * </p> + * @see SyncAction + */ +public interface SyncBuffer { + /** + * Return the {@link SyncAction}. + * @see SyncAction + */ + SyncAction getAction(); + + /** Return the {@link Buffer}, i.e. underlying data. */ + Buffer getBuffer(); + + /** + * Synchronizes the underlying data before usage. + * <p> + * Convenient shortcut for {@link #getAction()}.{@link SyncAction#sync() sync()} plus chaining. + * </p> + */ + SyncBuffer sync(); + + /** Return the {@link Buffer} after {@link SyncAction#sync() synchronizing} it. */ + Buffer getSyncBuffer(); + +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/SyncMatrices4f.java b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrices4f.java new file mode 100644 index 000000000..055345d65 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrices4f.java @@ -0,0 +1,41 @@ +/** + * Copyright 2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.nio.FloatBuffer; + +import com.jogamp.opengl.math.Matrix4f; + +/** {@link SyncBuffer} with a multiple underlying {@link Matrix4f}, used in {@link SyncMatrices4f16} and {@link PMVMatrix} */ +public interface SyncMatrices4f extends SyncBuffer { + /** Return the underlying multiple {@link Matrix4f}, used to {@link SyncAction#sync() synchronize} to the {@link #getBuffer()}. */ + Matrix4f[] getMatrices(); + + /** Return the {@link FloatBuffer} after {@link SyncAction#sync() synchronizing} it w/ the underlying {@link #getMatrices()}. */ + FloatBuffer getSyncFloats(); +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrices4f16.java index a2b29b4eb..bca827b9f 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSinkFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrices4f16.java @@ -1,5 +1,5 @@ /** - * Copyright 2013 JogAmp Community. All rights reserved. + * Copyright 2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -25,44 +25,50 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ -package com.jogamp.opengl.util.av; +package com.jogamp.opengl.util; -import jogamp.opengl.util.av.NullAudioSink; +import java.nio.Buffer; +import java.nio.FloatBuffer; -import com.jogamp.common.util.ReflectionUtil; +import com.jogamp.opengl.math.Matrix4f; -public class AudioSinkFactory { - private static final String ALAudioSinkClazzName = "jogamp.opengl.openal.av.ALAudioSink"; - private static final String JavaAudioSinkClazzName = "jogamp.opengl.util.av.JavaSoundAudioSink"; - - public static AudioSink createDefault() { - final ClassLoader cl = GLMediaPlayerFactory.class.getClassLoader(); - AudioSink sink = create(cl, ALAudioSinkClazzName); - if( null == sink ) { - sink = create(cl, JavaAudioSinkClazzName); - } - if( null == sink ) { - sink = createNull(); - } - return sink; - } - public static AudioSink createNull() { - return new NullAudioSink(); - } - - public static AudioSink create(final ClassLoader cl, final String implName) { - final AudioSink audioSink; - if(ReflectionUtil.isClassAvailable(implName, cl)){ - try { - audioSink = (AudioSink) ReflectionUtil.createInstance(implName, cl); - if( audioSink.isInitialized() ) { - return audioSink; - } - } catch (final Throwable t) { - if(AudioSink.DEBUG) { System.err.println("Caught "+t.getClass().getName()+": "+t.getMessage()); t.printStackTrace(); } +/** {@link SyncBuffer} {@link SyncMatrices4f16} implementation for multiple underlying {@link Matrix4f} instances using one {@code float[16*n]} backing array. */ +public final class SyncMatrices4f16 implements SyncMatrices4f { + private final Matrix4f[] mats; + private final float[] f16s; + private final FloatBuffer fbuf; + private final SyncAction action = new SyncAction() { + @Override + public void sync() { + int ioff = 0; + for(int i=0; i<mats.length; ++i, ioff+=16) { + mats[i].get(f16s, ioff); } } - return null; + }; + + public SyncMatrices4f16(final Matrix4f[] mats) { + this.mats = mats; + this.f16s = new float[16*mats.length]; + this.fbuf = FloatBuffer.wrap(f16s); } -} + @Override + public SyncAction getAction() { return action; } + + @Override + public Buffer getBuffer() { return fbuf; } + + @Override + public SyncBuffer sync() { getAction().sync(); return this; } + + @Override + public Buffer getSyncBuffer() { getAction().sync(); return fbuf; } + + @Override + public Matrix4f[] getMatrices() { return mats; } + + @Override + public FloatBuffer getSyncFloats() { getAction().sync(); return fbuf; } + +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/SyncMatrix4f.java b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrix4f.java new file mode 100644 index 000000000..6ab473771 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrix4f.java @@ -0,0 +1,42 @@ +/** + * Copyright 2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.nio.FloatBuffer; + +import com.jogamp.opengl.math.Matrix4f; + +/** {@link SyncBuffer} interface with a single underlying {@link Matrix4f}, used in {@link SyncMatrix4f16} and {@link PMVMatrix}. */ +public interface SyncMatrix4f extends SyncBuffer { + /** Return the underlying {@link Matrix4f}, used to {@link SyncAction#sync() synchronize} to the {@link #getBuffer()}. */ + Matrix4f getMatrix(); + + /** Return the {@link FloatBuffer} after {@link SyncAction#sync() synchronizing} it w/ the underlying {@link #getMatrix()}. */ + FloatBuffer getSyncFloats(); + +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/SyncMatrix4f16.java b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrix4f16.java new file mode 100644 index 000000000..03a4b64fa --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/SyncMatrix4f16.java @@ -0,0 +1,74 @@ +/** + * Copyright 2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package com.jogamp.opengl.util; + +import java.nio.Buffer; +import java.nio.FloatBuffer; + +import com.jogamp.opengl.math.Matrix4f; + +/** {@link SyncBuffer} {@link SyncMatrix4f} implementation for a single underlying {@link Matrix4f} using one {@code float[16]} backing array. */ +public final class SyncMatrix4f16 implements SyncMatrix4f { + private final Matrix4f mat; + private final float[] f16; + private final FloatBuffer fbuf; + private final SyncAction action = new SyncAction() { + @Override + public void sync() { mat.get(f16); } + }; + + public SyncMatrix4f16() { + this.mat = new Matrix4f(); + this.f16 = new float[16]; + this.fbuf = FloatBuffer.wrap(f16); + } + + public SyncMatrix4f16(final Matrix4f m) { + this.mat = m; + this.f16 = new float[16]; + this.fbuf = FloatBuffer.wrap(f16); + } + + @Override + public SyncAction getAction() { return action; } + + @Override + public Buffer getBuffer() { return fbuf; } + + @Override + public SyncBuffer sync() { getAction().sync(); return this; } + + @Override + public Buffer getSyncBuffer() { getAction().sync(); return fbuf; } + + @Override + public Matrix4f getMatrix() { return mat; } + + @Override + public FloatBuffer getSyncFloats() { getAction().sync(); return fbuf; } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/TimeFrameI.java b/src/jogl/classes/com/jogamp/opengl/util/TimeFrameI.java deleted file mode 100644 index 99def6f5c..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/TimeFrameI.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2013 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.opengl.util; - -/** - * Integer time frame in milliseconds, maybe specialized for texture/video, audio, .. animated content. - * <p> - * Type and value range has been chosen to suit embedded CPUs - * and characteristics of audio / video streaming and animations. - * Milliseconds of type integer with a maximum value of {@link Integer#MAX_VALUE} - * will allow tracking time up 2,147,483.647 seconds or - * 24 days 20 hours 31 minutes and 23 seconds. - * </p> - * <p> - * Milliseconds granularity is also more than enough to deal with A-V synchronization, - * where the threshold usually lies within 22ms. - * </p> - * <p> - * Milliseconds granularity for displaying video frames might seem inaccurate - * for each single frame, i.e. 60Hz != 16ms, however, accumulated values diminish - * this error and vertical sync is achieved by build-in V-Sync of the video drivers. - * </p> - */ -public class TimeFrameI { - /** Constant marking an invalid PTS, i.e. Integer.MIN_VALUE == 0x80000000 == {@value}. Sync w/ native code. */ - public static final int INVALID_PTS = 0x80000000; - - /** Constant marking the end of the stream PTS, i.e. Integer.MIN_VALUE - 1 == 0x7FFFFFFF == {@value}. Sync w/ native code. */ - public static final int END_OF_STREAM_PTS = 0x7FFFFFFF; - - protected int pts; - protected int duration; - - public TimeFrameI() { - pts = INVALID_PTS; - duration = 0; - } - public TimeFrameI(final int pts, final int duration) { - this.pts = pts; - this.duration = duration; - } - - /** Get this frame's presentation timestamp (PTS) in milliseconds. */ - public final int getPTS() { return pts; } - /** Set this frame's presentation timestamp (PTS) in milliseconds. */ - public final void setPTS(final int pts) { this.pts = pts; } - /** Get this frame's duration in milliseconds. */ - public final int getDuration() { return duration; } - /** Set this frame's duration in milliseconds. */ - public final void setDuration(final int duration) { this.duration = duration; } - - @Override - public String toString() { - return "TimeFrame[pts " + pts + " ms, l " + duration + " ms]"; - } -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java b/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java deleted file mode 100644 index fd6172e13..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/av/AudioSink.java +++ /dev/null @@ -1,436 +0,0 @@ -/** - * Copyright 2013 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package com.jogamp.opengl.util.av; - -import java.nio.ByteBuffer; - -import com.jogamp.opengl.util.TimeFrameI; - -import jogamp.opengl.Debug; - -public interface AudioSink { - public static final boolean DEBUG = Debug.debug("AudioSink"); - - /** Default frame duration in millisecond, i.e. 1 frame per {@value} ms. */ - public static final int DefaultFrameDuration = 32; - - /** Initial audio queue size in milliseconds. {@value} ms, i.e. 16 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ - public static final int DefaultInitialQueueSize = 16 * 32; // 512 ms - /** Audio queue grow size in milliseconds. {@value} ms, i.e. 16 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ - public static final int DefaultQueueGrowAmount = 16 * 32; // 512 ms - /** Audio queue limit w/ video in milliseconds. {@value} ms, i.e. 96 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ - public static final int DefaultQueueLimitWithVideo = 96 * 32; // 3072 ms - /** Audio queue limit w/o video in milliseconds. {@value} ms, i.e. 32 frames per 32 ms. See {@link #init(AudioFormat, float, int, int, int)}.*/ - public static final int DefaultQueueLimitAudioOnly = 32 * 32; // 1024 ms - - /** - * Specifies the linear audio PCM format. - */ - public static class AudioFormat { - /** - * @param sampleRate sample rate in Hz (1/s) - * @param sampleSize sample size in bits - * @param channelCount number of channels - * @param signed true if signed number, false for unsigned - * @param fixedP true for fixed point value, false for unsigned floating point value with a sampleSize of 32 (float) or 64 (double) - * @param planar true for planar data package (each channel in own data buffer), false for packed data channels interleaved in one buffer. - * @param littleEndian true for little-endian, false for big endian - */ - public AudioFormat(final int sampleRate, final int sampleSize, final int channelCount, final boolean signed, final boolean fixedP, final boolean planar, final boolean littleEndian) { - this.sampleRate = sampleRate; - this.sampleSize = sampleSize; - this.channelCount = channelCount; - this.signed = signed; - this.fixedP = fixedP; - this.planar = planar; - this.littleEndian = littleEndian; - if( !fixedP ) { - if( sampleSize != 32 && sampleSize != 64 ) { - throw new IllegalArgumentException("Floating point: sampleSize "+sampleSize+" bits"); - } - if( !signed ) { - throw new IllegalArgumentException("Floating point: unsigned"); - } - } - } - - /** Sample rate in Hz (1/s). */ - public final int sampleRate; - /** Sample size in bits. */ - public final int sampleSize; - /** Number of channels. */ - public final int channelCount; - public final boolean signed; - /** Fixed or floating point values. Floating point 'float' has {@link #sampleSize} 32, 'double' has {@link #sampleSize} 64. */ - public final boolean fixedP; - /** Planar or packed samples. If planar, each channel has their own data buffer. If packed, channel data is interleaved in one buffer. */ - public final boolean planar; - public final boolean littleEndian; - - - // - // Time <-> Bytes - // - - /** - * Returns the byte size of the given milliseconds - * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}. - * <p> - * Time -> Byte Count - * </p> - */ - public final int getDurationsByteSize(final int millisecs) { - final int bytesPerSample = sampleSize >>> 3; // /8 - return millisecs * ( channelCount * bytesPerSample * ( sampleRate / 1000 ) ); - } - - /** - * Returns the duration in milliseconds of the given byte count - * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}. - * <p> - * Byte Count -> Time - * </p> - */ - public final int getBytesDuration(final int byteCount) { - final int bytesPerSample = sampleSize >>> 3; // /8 - return byteCount / ( channelCount * bytesPerSample * ( sampleRate / 1000 ) ); - } - - /** - * Returns the duration in milliseconds of the given sample count per frame and channel - * according to the {@link #sampleRate}, i.e. - * <pre> - * ( 1000f * sampleCount ) / sampleRate - * </pre> - * <p> - * Sample Count -> Time - * </p> - * @param sampleCount sample count per frame and channel - */ - public final float getSamplesDuration(final int sampleCount) { - return ( 1000f * sampleCount ) / sampleRate; - } - - /** - * Returns the rounded frame count of the given milliseconds and frame duration. - * <pre> - * Math.max( 1, millisecs / frameDuration + 0.5f ) - * </pre> - * <p> - * Note: <code>frameDuration</code> can be derived by <i>sample count per frame and channel</i> - * via {@link #getSamplesDuration(int)}. - * </p> - * <p> - * Frame Time -> Frame Count - * </p> - * @param millisecs time in milliseconds - * @param frameDuration duration per frame in milliseconds. - */ - public final int getFrameCount(final int millisecs, final float frameDuration) { - return Math.max(1, (int) ( millisecs / frameDuration + 0.5f )); - } - - /** - * Returns the byte size of given sample count - * according to the {@link #sampleSize}, i.e.: - * <pre> - * sampleCount * ( sampleSize / 8 ) - * </pre> - * <p> - * Note: To retrieve the byte size for all channels, - * you need to pre-multiply <code>sampleCount</code> with {@link #channelCount}. - * </p> - * <p> - * Sample Count -> Byte Count - * </p> - * @param sampleCount sample count - */ - public final int getSamplesByteCount(final int sampleCount) { - return sampleCount * ( sampleSize >>> 3 ); - } - - /** - * Returns the sample count of given byte count - * according to the {@link #sampleSize}, i.e.: - * <pre> - * ( byteCount * 8 ) / sampleSize - * </pre> - * <p> - * Note: If <code>byteCount</code> covers all channels and you request the sample size per channel, - * you need to divide the result by <code>sampleCount</code> by {@link #channelCount}. - * </p> - * <p> - * Byte Count -> Sample Count - * </p> - * @param byteCount number of bytes - */ - public final int getBytesSampleCount(final int byteCount) { - return ( byteCount << 3 ) / sampleSize; - } - - @Override - public String toString() { - return "AudioDataFormat[sampleRate "+sampleRate+", sampleSize "+sampleSize+", channelCount "+channelCount+ - ", signed "+signed+", fixedP "+fixedP+", "+(planar?"planar":"packed")+", "+(littleEndian?"little":"big")+"-endian]"; } - } - /** Default {@link AudioFormat}, [type PCM, sampleRate 44100, sampleSize 16, channelCount 2, signed, fixedP, !planar, littleEndian]. */ - public static final AudioFormat DefaultFormat = new AudioFormat(44100, 16, 2, true /* signed */, - true /* fixed point */, false /* planar */, true /* littleEndian */); - - public static abstract class AudioFrame extends TimeFrameI { - protected int byteSize; - - public AudioFrame() { - this.byteSize = 0; - } - public AudioFrame(final int pts, final int duration, final int byteCount) { - super(pts, duration); - this.byteSize=byteCount; - } - - /** Get this frame's size in bytes. */ - public final int getByteSize() { return byteSize; } - /** Set this frame's size in bytes. */ - public final void setByteSize(final int size) { this.byteSize=size; } - - @Override - public String toString() { - return "AudioFrame[pts " + pts + " ms, l " + duration + " ms, "+byteSize + " bytes]"; - } - } - public static class AudioDataFrame extends AudioFrame { - protected final ByteBuffer data; - - public AudioDataFrame(final int pts, final int duration, final ByteBuffer bytes, final int byteCount) { - super(pts, duration, byteCount); - if( byteCount > bytes.remaining() ) { - throw new IllegalArgumentException("Give size "+byteCount+" exceeds remaining bytes in ls "+bytes+". "+this); - } - this.data=bytes; - } - - /** Get this frame's data. */ - public final ByteBuffer getData() { return data; } - - @Override - public String toString() { - return "AudioDataFrame[pts " + pts + " ms, l " + duration + " ms, "+byteSize + " bytes, " + data + "]"; - } - } - - /** - * Returns the <code>initialized state</code> of this instance. - * <p> - * The <code>initialized state</code> is affected by this instance - * overall availability, i.e. after instantiation, - * as well as by {@link #destroy()}. - * </p> - */ - public boolean isInitialized(); - - /** Returns the playback speed. */ - public float getPlaySpeed(); - - /** - * Sets the playback speed. - * <p> - * To simplify test, play speed is <i>normalized</i>, i.e. - * <ul> - * <li><code>1.0f</code>: if <code> Math.abs(1.0f - rate) < 0.01f </code></li> - * </ul> - * </p> - * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. - */ - public boolean setPlaySpeed(float s); - - /** Returns the volume. */ - public float getVolume(); - - /** - * Sets the volume [0f..1f]. - * <p> - * To simplify test, volume is <i>normalized</i>, i.e. - * <ul> - * <li><code>0.0f</code>: if <code> Math.abs(v) < 0.01f </code></li> - * <li><code>1.0f</code>: if <code> Math.abs(1.0f - v) < 0.01f </code></li> - * </ul> - * </p> - * @return true if successful, otherwise false, i.e. due to unsupported value range of implementation. - */ - public boolean setVolume(float v); - - /** - * Returns the preferred {@link AudioFormat} by this sink. - * <p> - * The preferred format is guaranteed to be supported - * and shall reflect this sinks most native format, - * i.e. best performance w/o data conversion. - * </p> - * <p> - * Known {@link #AudioFormat} attributes considered by implementations: - * <ul> - * <li>ALAudioSink: {@link AudioFormat#sampleRate}. - * </ul> - * </p> - * @see #initSink(AudioFormat) - * @see #isSupported(AudioFormat) - */ - public AudioFormat getPreferredFormat(); - - /** Return the maximum number of supported channels. */ - public int getMaxSupportedChannels(); - - /** - * Returns true if the given format is supported by the sink, otherwise false. - * @see #initSink(AudioFormat) - * @see #getPreferredFormat() - */ - public boolean isSupported(AudioFormat format); - - /** - * Initializes the sink. - * <p> - * Implementation must match the given <code>requestedFormat</code> {@link AudioFormat}. - * </p> - * <p> - * Caller shall validate <code>requestedFormat</code> via {@link #isSupported(AudioFormat)} - * beforehand and try to find a suitable supported one. - * {@link #getPreferredFormat()} and {@link #getMaxSupportedChannels()} may help. - * </p> - * @param requestedFormat the requested {@link AudioFormat}. - * @param frameDuration average or fixed frame duration in milliseconds - * helping a caching {@link AudioFrame} based implementation to determine the frame count in the queue. - * See {@link #DefaultFrameDuration}. - * @param initialQueueSize initial time in milliseconds to queue in this sink, see {@link #DefaultInitialQueueSize}. - * @param queueGrowAmount time in milliseconds to grow queue if full, see {@link #DefaultQueueGrowAmount}. - * @param queueLimit maximum time in milliseconds the queue can hold (and grow), see {@link #DefaultQueueLimitWithVideo} and {@link #DefaultQueueLimitAudioOnly}. - * @return true if successful, otherwise false - */ - public boolean init(AudioFormat requestedFormat, float frameDuration, - int initialQueueSize, int queueGrowAmount, int queueLimit); - - /** - * Returns the {@link AudioFormat} as chosen by {@link #init(AudioFormat, float, int, int, int)}, - * i.e. it shall match the <i>requestedFormat</i>. - */ - public AudioFormat getChosenFormat(); - - /** - * Returns true, if {@link #play()} has been requested <i>and</i> the sink is still playing, - * otherwise false. - */ - public boolean isPlaying(); - - /** - * Play buffers queued via {@link #enqueueData(AudioFrame)} from current internal position. - * If no buffers are yet queued or the queue runs empty, playback is being continued when buffers are enqueued later on. - * @see #enqueueData(AudioFrame) - * @see #pause() - */ - public void play(); - - /** - * Pause playing buffers while keeping enqueued data incl. it's internal position. - * @see #play() - * @see #flush() - * @see #enqueueData(AudioFrame) - */ - public void pause(); - - /** - * Flush all queued buffers, implies {@link #pause()}. - * <p> - * {@link #init(AudioFormat, float, int, int, int)} must be called first. - * </p> - * @see #play() - * @see #pause() - * @see #enqueueData(AudioFrame) - */ - public void flush(); - - /** Destroys this instance, i.e. closes all streams and devices allocated. */ - public void destroy(); - - /** - * Returns the number of allocated buffers as requested by - * {@link #init(AudioFormat, float, int, int, int)}. - */ - public int getFrameCount(); - - /** @return the current enqueued frames count since {@link #init(AudioFormat, float, int, int, int)}. */ - public int getEnqueuedFrameCount(); - - /** - * Returns the current number of frames queued for playing. - * <p> - * {@link #init(AudioFormat, float, int, int, int)} must be called first. - * </p> - */ - public int getQueuedFrameCount(); - - /** - * Returns the current number of bytes queued for playing. - * <p> - * {@link #init(AudioFormat, float, int, int, int)} must be called first. - * </p> - */ - public int getQueuedByteCount(); - - /** - * Returns the current queued frame time in milliseconds for playing. - * <p> - * {@link #init(AudioFormat, float, int, int, int)} must be called first. - * </p> - */ - public int getQueuedTime(); - - /** - * Return the current audio presentation timestamp (PTS) in milliseconds. - */ - public int getPTS(); - - /** - * Returns the current number of frames in the sink available for writing. - * <p> - * {@link #init(AudioFormat, float, int, int, int)} must be called first. - * </p> - */ - public int getFreeFrameCount(); - - /** - * Enqueue <code>byteCount</code> bytes of the remaining bytes of the given NIO {@link ByteBuffer} to this sink. - * <p> - * The data must comply with the chosen {@link AudioFormat} as returned by {@link #initSink(AudioFormat)}. - * </p> - * <p> - * {@link #init(AudioFormat, float, int, int, int)} must be called first. - * </p> - * @returns the enqueued internal {@link AudioFrame}. - */ - public AudioFrame enqueueData(int pts, ByteBuffer bytes, int byteCount); -} diff --git a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java index c2de32372..51724d240 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java +++ b/src/jogl/classes/com/jogamp/opengl/util/av/GLMediaPlayer.java @@ -33,10 +33,14 @@ import com.jogamp.opengl.GLException; import jogamp.opengl.Debug; +import java.util.List; + +import com.jogamp.common.av.AudioFormat; +import com.jogamp.common.av.AudioSink; +import com.jogamp.common.av.TimeFrameI; import com.jogamp.common.net.Uri; import com.jogamp.opengl.util.texture.Texture; import com.jogamp.opengl.util.texture.TextureSequence; -import com.jogamp.opengl.util.TimeFrameI; /** * GLMediaPlayer interface specifies a {@link TextureSequence} state machine @@ -45,7 +49,7 @@ import com.jogamp.opengl.util.TimeFrameI; * Audio maybe supported and played back internally or via an {@link AudioSink} implementation. * </p> * <p> - * Audio and video streams can be selected or muted via {@link #initStream(Uri, int, int, int)} + * Audio and video streams can be selected or muted via {@link #playStream(Uri, int, int, int)} * using the appropriate <a href="#streamIDs">stream id</a>'s. * </p> * <p> @@ -56,7 +60,7 @@ import com.jogamp.opengl.util.TimeFrameI; * <p> * Most of the stream processing is performed on the decoding thread, a.k.a. <i>StreamWorker</i>: * <ul> - * <li>Stream initialization triggered by {@link #initStream(Uri, int, int, int) initStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> + * <li>Stream initialization triggered by {@link #playStream(Uri, int, int, int) playStream(..)} - User gets notified whether the stream has been initialized or not via {@link GLMediaEventListener#attributesChanged(GLMediaPlayer, int, long) attributesChanges(..)}.</li> * <li>Stream decoding - User gets notified of a new frame via {@link GLMediaEventListener#newFrameAvailable(GLMediaPlayer, com.jogamp.opengl.util.texture.TextureSequence.TextureFrame, long) newFrameAvailable(...)}.</li> * <li>Caught <a href="#streamerror">exceptions on the decoding thread</a> are delivered as {@link StreamException}s.</li> * </ul> @@ -81,17 +85,18 @@ import com.jogamp.opengl.util.TimeFrameI; * <a name="lifecycle"><h5>GLMediaPlayer Lifecycle</h5></a> * <p> * <table border="1"> - * <tr><th>Action</th> <th>{@link State} Before</th> <th>{@link State} After</th> <th>{@link GLMediaEventListener Event}</th></tr> - * <tr><td>{@link #initStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_INIT EVENT_CHANGE_INIT} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> - * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}</td> <td>{@link State#Paused Paused}, , {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or ( {@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> - * <tr><td>{@link #play()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PLAY EVENT_CHANGE_PLAY}</td></tr> - * <tr><td>{@link #pause(boolean)}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr> + * <tr><th>Action</th> <th>{@link State} Before</th> <th>{@link State} After</th> <th>{@link EventMask#Bit Event}</th></tr> + * <tr><td>{@link #playStream(Uri, int, int, int)}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link State#Initialized Initialized}<sup><a href="#streamworker">1</a></sup>, {@link State#Uninitialized Uninitialized}</td> <td>{@link EventMask.Bit#Init Init} or ( {@link EventMask.Bit#Error Error} + {@link EventMask.Bit#Uninit Uninit} )</td></tr> + * <tr><td>{@link #initGL(GL)}</td> <td>{@link State#Initialized Initialized}, {@link State#Uninitialized Uninitialized} </td> <td>{@link State#Playing Playing}, {@link State#Uninitialized Uninitialized}</td> <td>{@link EventMask.Bit#Play Play} or ( {@link EventMask.Bit#Error Error} + {@link EventMask.Bit#Uninit Uninit} )</td></tr> + * <tr><td>{@link #pause(boolean)}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link EventMask.Bit#Pause Pause}</td></tr> + * <tr><td>{@link #resume()}</td> <td>{@link State#Paused Paused}</td> <td>{@link State#Playing Playing}</td> <td>{@link EventMask.Bit#Play Play}</td></tr> + * <tr><td>{@link #stop()}</td> <td>{@link State#Playing Playing}, {@link State#Paused Paused}</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link EventMask.Bit#Pause Pause}</td></tr> * <tr><td>{@link #seek(int)}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr> - * <tr><td>{@link #getNextTexture(GL)}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr> - * <tr><td>{@link #getLastTexture()}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>{@link State#Paused Paused}, {@link State#Playing Playing}</td> <td>none</td></tr> - * <tr><td>{@link TextureFrame#END_OF_STREAM_PTS END_OF_STREAM}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_EOS EVENT_CHANGE_EOS} + {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE}</td></tr> - * <tr><td>{@link StreamException}</td> <td>ANY</td> <td>{@link State#Paused Paused}, {@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_ERR EVENT_CHANGE_ERR} + ( {@link GLMediaEventListener#EVENT_CHANGE_PAUSE EVENT_CHANGE_PAUSE} or {@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT} )</td></tr> - * <tr><td>{@link #destroy(GL)}</td> <td>ANY</td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link GLMediaEventListener#EVENT_CHANGE_UNINIT EVENT_CHANGE_UNINIT}</td></tr> + * <tr><td>{@link #getNextTexture(GL)}</td> <td><i>any</i></td> <td><i>same</i></td> <td>none</td></tr> + * <tr><td>{@link #getLastTexture()}</td> <td><i>any</i></td> <td><i>same</i></td> <td>none</td></tr> + * <tr><td>{@link TextureFrame#END_OF_STREAM_PTS END_OF_STREAM}</td> <td>{@link State#Playing Playing}</td> <td>{@link State#Paused Paused}</td> <td>{@link EventMask.Bit#EOS EOS} + {@link EventMask.Bit#Pause Pause}</td></tr> + * <tr><td>{@link StreamException}</td> <td><i>any</i></td> <td>{@link State#Paused Paused}, {@link State#Uninitialized Uninitialized}</td> <td>{@link EventMask.Bit#Error Error} + ( {@link EventMask.Bit#Pause Pause} or {@link EventMask.Bit#Uninit Uninit} )</td></tr> + * <tr><td>{@link #destroy(GL)}</td> <td><i>any</i></td> <td>{@link State#Uninitialized Uninitialized}</td> <td>{@link EventMask.Bit#Uninit Uninit}</td></tr> * </table> * </p> * @@ -188,6 +193,7 @@ import com.jogamp.opengl.util.TimeFrameI; */ public interface GLMediaPlayer extends TextureSequence { public static final boolean DEBUG = Debug.debug("GLMediaPlayer"); + public static final boolean DEBUG_AVSYNC = Debug.debug("GLMediaPlayer.AVSync"); public static final boolean DEBUG_NATIVE = Debug.debug("GLMediaPlayer.Native"); /** Default texture count, value {@value}. */ @@ -275,41 +281,112 @@ public interface GLMediaPlayer extends TextureSequence { * </p> */ public interface GLMediaEventListener extends TexSeqEventListener<GLMediaPlayer> { - - /** State changed to {@link State#Initialized}. See <a href="#lifecycle">Lifecycle</a>.*/ - static final int EVENT_CHANGE_INIT = 1<<0; - /** State changed to {@link State#Uninitialized}. See <a href="#lifecycle">Lifecycle</a>.*/ - static final int EVENT_CHANGE_UNINIT = 1<<1; - /** State changed to {@link State#Playing}. See <a href="#lifecycle">Lifecycle</a>.*/ - static final int EVENT_CHANGE_PLAY = 1<<2; - /** State changed to {@link State#Paused}. See <a href="#lifecycle">Lifecycle</a>.*/ - static final int EVENT_CHANGE_PAUSE = 1<<3; - /** End of stream reached. See <a href="#lifecycle">Lifecycle</a>.*/ - static final int EVENT_CHANGE_EOS = 1<<4; - /** An error occurred, e.g. during off-thread initialization. See {@link StreamException} and <a href="#lifecycle">Lifecycle</a>. */ - static final int EVENT_CHANGE_ERR = 1<<5; - - /** Stream video id change. */ - static final int EVENT_CHANGE_VID = 1<<16; - /** Stream audio id change. */ - static final int EVENT_CHANGE_AID = 1<<17; - /** TextureFrame size or vertical flip change. */ - static final int EVENT_CHANGE_SIZE = 1<<18; - /** Stream fps change. */ - static final int EVENT_CHANGE_FPS = 1<<19; - /** Stream bps change. */ - static final int EVENT_CHANGE_BPS = 1<<20; - /** Stream length change. */ - static final int EVENT_CHANGE_LENGTH = 1<<21; - /** Stream codec change. */ - static final int EVENT_CHANGE_CODEC = 1<<22; - /** * @param mp the event source * @param event_mask the changes attributes * @param when system time in msec. */ - public void attributesChanged(GLMediaPlayer mp, int event_mask, long when); + public void attributesChanged(GLMediaPlayer mp, EventMask event_mask, long when); + } + + /** Changes attributes event mask */ + public static final class EventMask { + + /** Attribute change bits */ + public static enum Bit { + /** State changed to {@link State#Initialized}. See <a href="#lifecycle">Lifecycle</a>.*/ + Init ( 1<<0 ), + /** State changed to {@link State#Uninitialized}. See <a href="#lifecycle">Lifecycle</a>.*/ + Uninit ( 1<<1 ), + /** State changed to {@link State#Playing}. See <a href="#lifecycle">Lifecycle</a>.*/ + Play ( 1<<2 ), + /** State changed to {@link State#Paused}. See <a href="#lifecycle">Lifecycle</a>.*/ + Pause ( 1<<3 ), + /** End of stream reached. See <a href("#lifecycle">Lifecycle</a>.*/ + EOS ( 1<<4 ), + /** An error occurred, e.g. during off-thread initialization. See {@link StreamException} and <a href("#lifecycle">Lifecycle</a>. */ + Error ( 1<<5 ), + + /** Stream video id change. */ + VID ( 1<<16 ), + /** Stream audio id change. */ + AID ( 1<<17 ), + /** TextureFrame size or vertical flip change. */ + Size ( 1<<18 ), + /** Stream fps change. */ + FPS ( 1<<19 ), + /** Stream bps change. */ + BPS ( 1<<20 ), + /** Stream length change. */ + Length ( 1<<21 ), + /** Stream codec change. */ + Codec ( 1<<22 ); + + Bit(final int v) { value = v; } + public final int value; + } + public int mask; + + public static int getBits(final List<Bit> v) { + int res = 0; + for(final Bit b : v) { + res |= b.value; + } + return res; + } + public EventMask(final List<Bit> v) { + mask = getBits(v); + } + public EventMask(final Bit v) { + mask = v.value; + } + public EventMask(final int v) { + mask = v; + } + public EventMask() { + mask = 0; + } + + public boolean isSet(final Bit bit) { return bit.value == ( mask & bit.value ); } + public boolean isSet(final List<Bit> bits) { final int bits_i = getBits(bits); return bits_i == ( mask & bits_i ); } + public boolean isSet(final int bits) { return bits == ( mask & bits ); } + public boolean isZero() { return 0 == mask; } + + public EventMask setBit(final Bit v) { mask |= v.value; return this; } + public EventMask setBits(final List<Bit> v) { + for(final Bit b : v) { + mask |= b.value; + } + return this; + } + + @Override + public String toString() { + int count = 0; + final StringBuilder out = new StringBuilder(); + for (final Bit dt : Bit.values()) { + if( isSet(dt) ) { + if( 0 < count ) { out.append(", "); } + out.append(dt.name()); count++; + } + } + if( 0 == count ) { + out.append("None"); + } else if( 1 < count ) { + out.insert(0, "["); + out.append("]"); + } + return out.toString(); + } + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + return (other instanceof EventMask) && + this.mask == ((EventMask)other).mask; + } } /** @@ -343,6 +420,20 @@ public interface GLMediaPlayer extends TextureSequence { public void setTextureWrapST(int[] wrapST); /** + * Limit maximum supported audio channels by user. + * <p> + * Must be set before {@link #playStream(Uri, int, int, int)} + * </p> + * <p> + * May be utilized to enforce 1 channel (mono) downsampling + * in combination with JOAL/OpenAL to experience spatial 3D position effects. + * </p> + * @param cc maximum supported audio channels, will be clipped [1..x], with x being the underlying audio subsystem's maximum + * @see #playStream(Uri, int, int, int) + */ + public void setAudioChannelLimit(final int cc); + + /** * Issues asynchronous stream initialization. * <p> * <a href="#lifecycle">Lifecycle</a>: {@link State#Uninitialized} -> {@link State#Initialized}<sup><a href="#streamworker">1</a></sup> or {@link State#Uninitialized} @@ -372,7 +463,7 @@ public interface GLMediaPlayer extends TextureSequence { * @throws IllegalArgumentException if arguments are invalid * @since 2.3.0 */ - public void initStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; + public void playStream(Uri streamLoc, int vid, int aid, int textureCount) throws IllegalStateException, IllegalArgumentException; /** * Returns the {@link StreamException} caught in the decoder thread, or <code>null</code> if none occured. @@ -389,7 +480,7 @@ public interface GLMediaPlayer extends TextureSequence { * <p> * <a href="#lifecycle">Lifecycle</a>: {@link State#Initialized} -> {@link State#Paused} or {@link State#Initialized} * </p> - * Argument <code>gl</code> is ignored if video is muted, see {@link #initStream(Uri, int, int, int)}. + * Argument <code>gl</code> is ignored if video is muted, see {@link #playStream(Uri, int, int, int)}. * * @param gl current GL object. Maybe <code>null</code>, for audio only. * @throws IllegalStateException if not invoked in {@link State#Initialized}. @@ -401,7 +492,7 @@ public interface GLMediaPlayer extends TextureSequence { /** * If implementation uses a {@link AudioSink}, it's instance will be returned. * <p> - * The {@link AudioSink} instance is available after {@link #initStream(Uri, int, int, int)}, + * The {@link AudioSink} instance is available after {@link #playStream(Uri, int, int, int)}, * if used by implementation. * </p> */ @@ -416,6 +507,14 @@ public interface GLMediaPlayer extends TextureSequence { public State destroy(GL gl); /** + * Stops streaming and releases the GL, stream and other resources, but keeps {@link #attachObject(String, Object) attached user objects}. + * <p> + * <a href="#lifecycle">Lifecycle</a>: <code>ANY</code> -> {@link State#Uninitialized} + * </p> + */ + public State stop(); + + /** * Sets the playback speed. * <p> * To simplify test, play speed is <i>normalized</i>, i.e. @@ -452,7 +551,7 @@ public interface GLMediaPlayer extends TextureSequence { * <a href="#lifecycle">Lifecycle</a>: {@link State#Paused} -> {@link State#Playing} * </p> */ - public State play(); + public State resume(); /** * Pauses the <i>StreamWorker</i> decoding thread. @@ -460,7 +559,7 @@ public interface GLMediaPlayer extends TextureSequence { * <a href="#lifecycle">Lifecycle</a>: {@link State#Playing} -> {@link State#Paused} * </p> * <p> - * If a <i>new</i> frame is desired after the next {@link #play()} call, + * If a <i>new</i> frame is desired after the next {@link #resume()} call, * e.g. to make a snapshot of a camera input stream, * <code>flush</code> shall be set to <code>true</code>. * </p> @@ -498,13 +597,13 @@ public interface GLMediaPlayer extends TextureSequence { public int getAID(); /** - * @return the current decoded frame count since {@link #play()} and {@link #seek(int)} + * @return the current decoded frame count since {@link #resume()} and {@link #seek(int)} * as increased by {@link #getNextTexture(GL)} or the decoding thread. */ public int getDecodedFrameCount(); /** - * @return the current presented frame count since {@link #play()} and {@link #seek(int)} + * @return the current presented frame count since {@link #resume()} and {@link #seek(int)} * as increased by {@link #getNextTexture(GL)} for new frames. */ public int getPresentedFrameCount(); @@ -547,7 +646,7 @@ public interface GLMediaPlayer extends TextureSequence { public TextureSequence.TextureFrame getNextTexture(GL gl) throws IllegalStateException; /** - * Return the stream location, as set by {@link #initStream(Uri, int, int, int)}. + * Return the stream location, as set by {@link #playStream(Uri, int, int, int)}. * @since 2.3.0 */ public Uri getUri(); diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java index 532db3a7a..163e00c7a 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderCode.java @@ -72,7 +72,7 @@ import com.jogamp.common.util.VersionNumber; * was added since 2.2.1. * </p> */ -public class ShaderCode { +public final class ShaderCode { public static final boolean DEBUG_CODE = Debug.isPropertyDefined("jogl.debug.GLSLCode", true); /** Unique resource suffix for {@link GL2ES2#GL_VERTEX_SHADER} in source code: <code>{@value}</code> */ @@ -815,7 +815,7 @@ public class ShaderCode { if(null!=shaderSource) { if(DEBUG_CODE) { System.err.println("ShaderCode.compile:"); - dumpShaderSource(System.err); + dumpSource(System.err); } valid=ShaderUtil.createAndCompileShader(gl, shader, shaderType, shaderSource, verboseOut); @@ -859,7 +859,7 @@ public class ShaderCode { } @Override public String toString() { - final StringBuilder buf = new StringBuilder("ShaderCode[id="+id+", type="+shaderTypeStr()+", valid="+valid+", shader: "); + final StringBuilder buf = new StringBuilder("ShaderCode[id="+id()+", type="+shaderTypeStr()+", valid="+valid+", "+shader.remaining()+"/"+shader.capacity()+" shader: "); for(int i=0; i<shader.remaining(); i++) { buf.append(" "+shader.get(i)); } @@ -871,15 +871,20 @@ public class ShaderCode { return buf.toString(); } - public void dumpShaderSource(final PrintStream out) { + public void dumpSource(final PrintStream out) { if(null==shaderSource) { out.println("<no shader source>"); return; } final int sourceCount = shaderSource.length; - final int shaderCount = (null!=shader)?shader.capacity():0; + final int shaderCount = shader.capacity(); + out.println(); + out.print("ShaderCode[id="+id()+", type="+shaderTypeStr()+", valid="+valid+", "+shader.remaining()+"/"+shaderCount+" shader: "); + if( 0 == shaderCount ) { + out.println("none]"); + } for(int i=0; i<shaderCount; i++) { - out.println(""); + out.println(); out.println("Shader #"+i+"/"+shaderCount+" name "+shader.get(i)); out.println("--------------------------------------------------------------"); if(i>=sourceCount) { @@ -902,6 +907,7 @@ public class ShaderCode { } out.println("--------------------------------------------------------------"); } + out.println("]"); } /** @@ -922,7 +928,7 @@ public class ShaderCode { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } - final int shaderCount = (null!=shader)?shader.capacity():0; + final int shaderCount = shader.capacity(); if(0>shaderIdx || shaderIdx>=shaderCount) { throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); } @@ -1027,7 +1033,7 @@ public class ShaderCode { if(null==shaderSource) { throw new IllegalStateException("no shader source"); } - final int shaderCount = (null!=shader)?shader.capacity():0; + final int shaderCount = shader.capacity(); if(0>shaderIdx || shaderIdx>=shaderCount) { throw new IndexOutOfBoundsException("shaderIdx not within shader bounds [0.."+(shaderCount-1)+"]: "+shaderIdx); } @@ -1437,18 +1443,18 @@ public class ShaderCode { // Internals only below this point // - protected CharSequence[][] shaderSource = null; - protected Buffer shaderBinary = null; - protected int shaderBinaryFormat = -1; - protected IntBuffer shader = null; - protected int shaderType = -1; - protected int id = -1; + private CharSequence[][] shaderSource; + private Buffer shaderBinary; + private int shaderBinaryFormat = -1; + private final IntBuffer shader; + private int shaderType = -1; + private int id = -1; - protected boolean valid=false; + private boolean valid=false; private static synchronized int getNextID() { return nextID++; } - protected static int nextID = 1; + private static int nextID = 1; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java index 63455ba51..fcc1cadd0 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderProgram.java @@ -36,7 +36,7 @@ import java.util.HashSet; import java.util.Iterator; import java.io.PrintStream; -public class ShaderProgram { +public final class ShaderProgram { public ShaderProgram() { id = getNextID(); @@ -103,6 +103,7 @@ public class ShaderProgram { gl.glDeleteProgram(shaderProgram); shaderProgram=0; } + programLinked=false; } // @@ -282,9 +283,13 @@ public class ShaderProgram { sb = new StringBuilder(); } sb.append("ShaderProgram[id=").append(id); - sb.append(", linked="+programLinked+", inUse="+programInUse+", program: "+shaderProgram+","); - for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { - sb.append(Platform.getNewline()).append(" ").append(iter.next()); + sb.append(", linked="+programLinked+", inUse="+programInUse+", program: "+shaderProgram+", "+allShaderCode.size()+" code: "); + if( 0 < allShaderCode.size() ) { + for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { + sb.append(Platform.getNewline()).append(" ").append(iter.next()); + } + } else { + sb.append("none"); } sb.append("]"); return sb; @@ -304,7 +309,10 @@ public class ShaderProgram { } public synchronized void useProgram(final GL2ES2 gl, boolean on) { - if(!programLinked) { throw new GLException("Program is not linked"); } + if(on && !programLinked) { + System.err.println("Error: ShaderProgram.useProgram(on "+on+") not linked: "+this); + throw new GLException("Program is not linked"); + } if(programInUse==on) { return; } if( 0 == shaderProgram ) { on = false; @@ -316,6 +324,15 @@ public class ShaderProgram { programInUse = false; } + public void dumpSource(final PrintStream out) { + out.println(); + out.println(toString()); + for(final Iterator<ShaderCode> iter=allShaderCode.iterator(); iter.hasNext(); ) { + iter.next().dumpSource(out); + } + out.println(); + } + private boolean programLinked = false; private boolean programInUse = false; private int shaderProgram = 0; // non zero is valid! diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java index e06f7be00..f13c2f743 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderState.java @@ -57,7 +57,7 @@ import com.jogamp.opengl.util.GLArrayDataEditable; * and can be retrieved via {@link #getShaderState(GL)}. * </p> */ -public class ShaderState { +public final class ShaderState { public static final boolean DEBUG; static { diff --git a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java index 165a5167a..5ad1d49d9 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/glsl/ShaderUtil.java @@ -42,7 +42,7 @@ import com.jogamp.opengl.*; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.GLExtensions; -public class ShaderUtil { +public final class ShaderUtil { public static String getShaderInfoLog(final GL _gl, final int shaderObj) { final GL2ES2 gl = _gl.getGL2ES2(); final int[] infoLogLength=new int[1]; diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java index 43a6cfc58..e0f465da7 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/EyeParameter.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 JogAmp Community. All rights reserved. + * Copyright 2014-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -28,6 +28,7 @@ package com.jogamp.opengl.util.stereo; import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.math.Vec3f; /** * Constant single eye parameter of the viewer, relative to its {@link ViewerPose}. @@ -36,8 +37,8 @@ public final class EyeParameter { /** Eye number, <code>0</code> for the left eye and <code>1</code> for the right eye. */ public final int number; - /** float[3] eye position vector used to define eye height in meter relative to <i>actor</i>. */ - public final float[] positionOffset; + /** eye position vector used to define eye height in meter relative to <i>actor</i>. */ + public final Vec3f positionOffset; /** Field of view in both directions, may not be centered, either {@link FovHVHalves#inTangents} or radians. */ public final FovHVHalves fovhv; @@ -51,18 +52,18 @@ public final class EyeParameter { /** Z-axis eye relief in meter. */ public final float eyeReliefZ; - public EyeParameter(final int number, final float[] positionOffset, final FovHVHalves fovhv, + public EyeParameter(final int number, final Vec3f positionOffset, final FovHVHalves fovhv, final float distNoseToPupil, final float verticalDelta, final float eyeRelief) { this.number = number; - this.positionOffset = new float[3]; - System.arraycopy(positionOffset, 0, this.positionOffset, 0, 3); + this.positionOffset = new Vec3f(positionOffset); this.fovhv = fovhv; this.distNoseToPupilX = distNoseToPupil; this.distMiddleToPupilY = verticalDelta; this.eyeReliefZ = eyeRelief; } + @Override public final String toString() { - return "EyeParam[num "+number+", posOff["+positionOffset[0]+", "+positionOffset[1]+", "+positionOffset[2]+"], "+fovhv+ - ", distPupil[noseX "+distNoseToPupilX+", middleY "+distMiddleToPupilY+", reliefZ "+eyeReliefZ+"]]"; + return "EyeParam[num "+number+", posOff["+positionOffset+"], "+fovhv+ + ", distPupil[noseX "+distNoseToPupilX+", middleY "+distMiddleToPupilY+", reliefZ "+eyeReliefZ+"]]"; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/LocationSensorParameter.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/LocationSensorParameter.java index b795927cd..6294adee1 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/LocationSensorParameter.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/LocationSensorParameter.java @@ -1,5 +1,5 @@ /** - * Copyright 2015 JogAmp Community. All rights reserved. + * Copyright 2015-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -27,6 +27,7 @@ */ package com.jogamp.opengl.util.stereo; +import com.jogamp.opengl.math.Matrix4f; import com.jogamp.opengl.math.geom.Frustum; /** @@ -38,13 +39,15 @@ public final class LocationSensorParameter { /** The {@link Frustum}'s {@link Frustum.FovDesc} description of the location sensor. */ public final Frustum.FovDesc frustumDesc; /** The {@link Frustum}'s float[16] projection matrix of the location sensor. */ - public final float[] frustumProjMat; + public final Matrix4f frustumProjMat; public LocationSensorParameter(final Frustum.FovDesc fovDesc) { this.frustumDesc = fovDesc; this.frustum = new Frustum(); - this.frustumProjMat = frustum.updateByFovDesc(new float[16], 0, true, fovDesc); + this.frustumProjMat = frustum.updateByFovDesc(new Matrix4f(), fovDesc); } + + @Override public final String toString() { return "LocationSensor["+frustumDesc+"]"; } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java index b6112650a..85e752302 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoDevice.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 JogAmp Community. All rights reserved. + * Copyright 2014-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -33,6 +33,7 @@ import com.jogamp.nativewindow.util.PointImmutable; import jogamp.opengl.Debug; import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.math.Vec3f; /** * Interface describing a native stereoscopic device @@ -94,7 +95,7 @@ public interface StereoDevice { public int getRequiredRotation(); /** - * Return the device default eye position offset for {@link #createRenderer(int, int, float[], FovHVHalves[], float)}. + * Return the device default eye position offset for {@link #createRenderer(int, int, Vec3f, FovHVHalves[], float)}. * <p> * Result is an array of float values for * <ul> @@ -105,7 +106,7 @@ public interface StereoDevice { * </p> * @return */ - public float[] getDefaultEyePositionOffset(); + public Vec3f getDefaultEyePositionOffset(); /** * Returns the device default {@link FovHVHalves} for all supported eyes @@ -198,7 +199,7 @@ public interface StereoDevice { * Returns the supported distortion compensation of the {@link StereoDeviceRenderer}, * e.g. {@link StereoDeviceRenderer#DISTORTION_BARREL}, {@link StereoDeviceRenderer#DISTORTION_CHROMATIC}, etc. * @see StereoDeviceRenderer#getDistortionBits() - * @see #createRenderer(int, int, float[], FovHVHalves[], float, int) + * @see #createRenderer(int, int, Vec3f, FovHVHalves[], float, int) * @see #getRecommendedDistortionBits() * @see #getMinimumDistortionBits() */ @@ -212,7 +213,7 @@ public interface StereoDevice { * User shall use the recommended distortion compensation to achieve a distortion free view. * </p> * @see StereoDeviceRenderer#getDistortionBits() - * @see #createRenderer(int, int, float[], FovHVHalves[], float, int) + * @see #createRenderer(int, int, Vec3f, FovHVHalves[], float, int) * @see #getSupportedDistortionBits() * @see #getMinimumDistortionBits() */ @@ -227,7 +228,7 @@ public interface StereoDevice { * @see #getSupportedDistortionBits() * @see #getRecommendedDistortionBits() * @see StereoDeviceRenderer#getDistortionBits() - * @see #createRenderer(int, int, float[], FovHVHalves[], float, int) + * @see #createRenderer(int, int, Vec3f, FovHVHalves[], float, int) */ public int getMinimumDistortionBits(); @@ -245,6 +246,6 @@ public interface StereoDevice { * @return */ public StereoDeviceRenderer createRenderer(final int distortionBits, - final int textureCount, final float[] eyePositionOffset, + final int textureCount, final Vec3f eyePositionOffset, final FovHVHalves[] eyeFov, final float pixelsPerDisplayPixel, final int textureUnit); } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java index b6f76a343..63cb3e1e7 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/StereoUtil.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 JogAmp Community. All rights reserved. + * Copyright 2014-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -27,9 +27,9 @@ */ package com.jogamp.opengl.util.stereo; -import com.jogamp.opengl.math.FloatUtil; +import com.jogamp.opengl.math.Matrix4f; import com.jogamp.opengl.math.Quaternion; -import com.jogamp.opengl.math.VectorUtil; +import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.util.CustomGLEventListener; import com.jogamp.opengl.util.stereo.StereoDeviceRenderer.Eye; @@ -137,24 +137,24 @@ public class StereoUtil { * @param eye * @param zNear frustum near value * @param zFar frustum far value - * @param mat4Projection float[16] projection matrix result - * @param mat4Modelview float[16] modelview matrix result + * @param mat4Projection projection matrix result + * @param mat4Modelview modelview matrix result */ public static void getSBSUpstreamPMV(final ViewerPose viewerPose, final Eye eye, final float zNear, final float zFar, - final float[] mat4Projection, final float[] mat4Modelview) { - final float[] mat4Tmp1 = new float[16]; - final float[] mat4Tmp2 = new float[16]; - final float[] vec3Tmp1 = new float[3]; - final float[] vec3Tmp2 = new float[3]; - final float[] vec3Tmp3 = new float[3]; + final Matrix4f mat4Projection, final Matrix4f mat4Modelview) { + final Matrix4f mat4Tmp1 = new Matrix4f(); + final Matrix4f mat4Tmp2 = new Matrix4f(); + final Vec3f vec3Tmp1 = new Vec3f(); + final Vec3f vec3Tmp2 = new Vec3f(); + final Vec3f vec3Tmp3 = new Vec3f(); final EyeParameter eyeParam = eye.getEyeParameter(); // // Projection // - FloatUtil.makePerspective(mat4Projection, 0, true, eyeParam.fovhv, zNear, zFar); + mat4Projection.setToPerspective(eyeParam.fovhv, zNear, zFar); // // Modelview @@ -162,21 +162,17 @@ public class StereoUtil { final Quaternion rollPitchYaw = new Quaternion(); // private final float eyeYaw = FloatUtil.PI; // 180 degrees in radians // rollPitchYaw.rotateByAngleY(eyeYaw); - final float[] shiftedEyePos = rollPitchYaw.rotateVector(vec3Tmp1, 0, viewerPose.position, 0); - VectorUtil.addVec3(shiftedEyePos, shiftedEyePos, eyeParam.positionOffset); + final Vec3f shiftedEyePos = rollPitchYaw.rotateVector(viewerPose.position, vec3Tmp1).add(eyeParam.positionOffset); rollPitchYaw.mult(viewerPose.orientation); - final float[] up = rollPitchYaw.rotateVector(vec3Tmp2, 0, VectorUtil.VEC3_UNIT_Y, 0); - final float[] forward = rollPitchYaw.rotateVector(vec3Tmp3, 0, VectorUtil.VEC3_UNIT_Z_NEG, 0); - final float[] center = VectorUtil.addVec3(forward, shiftedEyePos, forward); + final Vec3f up = rollPitchYaw.rotateVector(Vec3f.UNIT_Y, vec3Tmp2); + final Vec3f forward = rollPitchYaw.rotateVector(Vec3f.UNIT_Z_NEG, vec3Tmp3); // -> center + final Vec3f center = forward.add(shiftedEyePos); - final float[] mLookAt = FloatUtil.makeLookAt(mat4Tmp2, 0, shiftedEyePos, 0, center, 0, up, 0, mat4Tmp1); - final float[] mViewAdjust = FloatUtil.makeTranslation(mat4Modelview, true, - eyeParam.distNoseToPupilX, - eyeParam.distMiddleToPupilY, - eyeParam.eyeReliefZ); - - /* mat4Modelview = */ FloatUtil.multMatrix(mViewAdjust, mLookAt); + final Matrix4f mLookAt = mat4Tmp2.setToLookAt(shiftedEyePos, center, up, mat4Tmp1); + mat4Modelview.mul( mat4Tmp1.setToTranslation( eyeParam.distNoseToPupilX, + eyeParam.distMiddleToPupilY, + eyeParam.eyeReliefZ ), mLookAt); } } diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/ViewerPose.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/ViewerPose.java index 10ee4c994..5d2cf925c 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/ViewerPose.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/ViewerPose.java @@ -1,5 +1,5 @@ /** - * Copyright 2014 JogAmp Community. All rights reserved. + * Copyright 2014-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: @@ -28,13 +28,14 @@ package com.jogamp.opengl.util.stereo; import com.jogamp.opengl.math.Quaternion; +import com.jogamp.opengl.math.Vec3f; /** * {@link #position} and {@link #orientation} of viewer. */ public final class ViewerPose { /** - * float[3] position of viewer in meter. + * position of viewer in meter. * <p> * Apply the following to resolve the actual eye position: * <ul> @@ -43,13 +44,13 @@ public final class ViewerPose { * </ul> * </p> */ - public final float[] position; + public final Vec3f position; /** Orientation of viewer. */ public final Quaternion orientation; public ViewerPose() { - this.position = new float[3]; + this.position = new Vec3f(); this.orientation = new Quaternion(); } public ViewerPose(final float[] position, final Quaternion orientation) { @@ -64,11 +65,14 @@ public final class ViewerPose { } /** Set position and orientation of this instance. */ public final void setPosition(final float posX, final float posY, final float posZ) { - position[0] = posX; - position[1] = posY; - position[2] = posZ; + position.set( posX, posY, posZ ); } + /** Set position and orientation of this instance. */ + public final void setPosition(final Vec3f pos) { + position.set( pos ); + } + @Override public final String toString() { - return "ViewerPose[pos["+position[0]+", "+position[1]+", "+position[2]+"], "+orientation+"]"; + return "ViewerPose[pos["+position+"], "+orientation+"]"; } }
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java b/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java index 957758e78..0cdef8770 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java +++ b/src/jogl/classes/com/jogamp/opengl/util/stereo/generic/GenericStereoDeviceFactory.java @@ -35,6 +35,7 @@ import com.jogamp.nativewindow.util.Dimension; import com.jogamp.nativewindow.util.DimensionImmutable; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.FovHVHalves; +import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.util.stereo.EyeParameter; import com.jogamp.opengl.util.stereo.StereoDevice; import com.jogamp.opengl.util.stereo.StereoDeviceConfig; @@ -53,7 +54,7 @@ public class GenericStereoDeviceFactory extends StereoDeviceFactory { */ public static GenericStereoDeviceConfig createMono(final String name, final DimensionImmutable surfaceSizeInPixel, final float[] screenSizeInMeters, - final float[] defaultEyePositionOffset) { + final Vec3f defaultEyePositionOffset) { final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; final float d2r = FloatUtil.PI / 180.0f; return new GenericStereoDeviceConfig( @@ -90,7 +91,7 @@ public class GenericStereoDeviceFactory extends StereoDeviceFactory { public static GenericStereoDeviceConfig createStereoSBS(final String name, final DimensionImmutable surfaceSizeInPixel, final float[] screenSizeInMeters, final float interpupillaryDistanceInMeters, final float fovy, - final float[] defaultEyePositionOffset) { + final Vec3f defaultEyePositionOffset) { final float pupilCenterFromScreenTopInMeters = screenSizeInMeters[1] / 2f; final float d2r = FloatUtil.PI / 180.0f; @@ -138,7 +139,7 @@ public class GenericStereoDeviceFactory extends StereoDeviceFactory { final DimensionImmutable surfaceSizeInPixel, final float[] screenSizeInMeters, final float interpupillaryDistanceInMeters, final float fovy, final DimensionImmutable eyeTextureSize, - final float[] defaultEyePositionOffset) { + final Vec3f defaultEyePositionOffset) { DistortionMesh.Producer lenseDistMeshProduce = null; try { lenseDistMeshProduce = diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java b/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java index e485e5452..2da2dcbfc 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/ImageSequence.java @@ -153,17 +153,21 @@ public class ImageSequence implements TextureSequence { private String textureLookupFunctionName = "myTexture2D"; @Override - public String getTextureLookupFunctionName(final String desiredFuncName) throws IllegalStateException { + public String setTextureLookupFunctionName(final String texLookupFuncName) throws IllegalStateException { if(useBuildInTexLookup) { - return "texture2D"; - } - if(null != desiredFuncName && desiredFuncName.length()>0) { - textureLookupFunctionName = desiredFuncName; + textureLookupFunctionName = "texture2D"; + } else if(null != texLookupFuncName && texLookupFuncName.length()>0) { + textureLookupFunctionName = texLookupFuncName; } return textureLookupFunctionName; } @Override + public String getTextureLookupFunctionName() throws IllegalStateException { + return textureLookupFunctionName; + } + + @Override public String getTextureLookupFragmentShaderImpl() throws IllegalStateException { if(useBuildInTexLookup) { return ""; diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java index 7147fd61b..1e308b215 100644 --- a/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/TextureSequence.java @@ -32,7 +32,7 @@ import com.jogamp.opengl.GLAutoDrawable; import com.jogamp.opengl.GLRunnable; import com.jogamp.opengl.GLEventListener; -import com.jogamp.opengl.util.TimeFrameI; +import com.jogamp.common.av.TimeFrameI; /** * Protocol for texture sequences, like animations, movies, etc. @@ -226,15 +226,28 @@ public interface TextureSequence { public String getTextureSampler2DType() throws IllegalStateException ; /** - * @param desiredFuncName desired lookup function name. If <code>null</code> or ignored by the implementation, - * a build-in name is returned. - * @return the final lookup function name + * Set the desired shader code's texture lookup function name. * - * @see {@link #getTextureLookupFragmentShaderImpl()} + * @param texLookupFuncName desired lookup function name. If <code>null</code> or ignored by the implementation, + * a build-in name is returned. + * @return the chosen lookup function name * * @throws IllegalStateException if instance is not initialized + * @see #getTextureLookupFunctionName() + * @see #getTextureFragmentShaderHashCode() + * @see #getTextureLookupFragmentShaderImpl() */ - public String getTextureLookupFunctionName(String desiredFuncName) throws IllegalStateException ; + public String setTextureLookupFunctionName(String texLookupFuncName) throws IllegalStateException ; + + /** + * Returns the chosen lookup function name, which can be set via {@link #setTextureLookupFunctionName(String)}. + * + * @throws IllegalStateException if instance is not initialized + * @see #setTextureLookupFunctionName(String) + * @see #getTextureFragmentShaderHashCode() + * @see #getTextureLookupFragmentShaderImpl() + */ + public String getTextureLookupFunctionName() throws IllegalStateException ; /** * Returns the complete texture2D lookup function code of type @@ -245,36 +258,49 @@ public interface TextureSequence { * } * </pre> * <p> - * <i>funcName</i> can be negotiated and queried via {@link #getTextureLookupFunctionName(String)}. + * <i>funcName</i> is set via {@link #setTextureLookupFunctionName(String)} + * and queried via {@link #getTextureLookupFunctionName()}. + * </p> + * <p> + * User shall call {@link #setTextureLookupFunctionName(String)} first if intended. * </p> + * <p> * Note: This function may return an empty string in case a build-in lookup * function is being chosen. If the implementation desires so, - * {@link #getTextureLookupFunctionName(String)} will ignore the desired function name + * {@link #getTextureLookupFunctionName()} will ignore the desired function name * and returns the build-in lookup function name. * </p> - * @see #getTextureLookupFunctionName(String) - * @see #getTextureSampler2DType() - * * @throws IllegalStateException if instance is not initialized + * @see #getTextureLookupFunctionName() + * @see #setTextureLookupFunctionName(String) + * @see #getTextureFragmentShaderHashCode() + * @see #getTextureSampler2DType() */ public String getTextureLookupFragmentShaderImpl() throws IllegalStateException; /** * Returns the hash code of the strings: * <ul> + * <li>{@link #getTextureLookupFunctionName()}</li> * <li>{@link #getTextureLookupFragmentShaderImpl()}</li> * <li>{@link #getTextureSampler2DType()}</li> * </ul> * <p> + * User shall call {@link #setTextureLookupFunctionName(String)} first if intended. + * </p> + * <p> * Returns zero if {@link #isTextureAvailable() texture is not available}. * </p> * The returned hash code allows selection of a matching shader program for this {@link TextureSequence} instance. * <p> * </p> * <p> - * Implementation shall cache the resulting hash code, + * Implementation caches the resulting hash code, * which must be reset to zero if {@link #isTextureAvailable() texture is not available}. * </p> + * @see #setTextureLookupFunctionName(String) + * @see #getTextureLookupFunctionName() + * @see #getTextureLookupFragmentShaderImpl() */ public int getTextureFragmentShaderHashCode(); } |