diff options
author | Sven Göthel <sgothel@jausoft.com> | 2024-01-20 05:01:38 +0100 |
---|---|---|
committer | Sven Göthel <sgothel@jausoft.com> | 2024-01-20 05:01:38 +0100 |
commit | c1531c3d99b19032040018b9414263b0d3000147 (patch) | |
tree | 93ad05df0398d430884350166a88371f82143947 /src/graphui | |
parent | 5cca51e32999a882e2a5f00cb45ecafc824ffd86 (diff) |
Graph Clipping: Use Frustum Clipping using AABBox -> Mv transformed Cube -> Frustum mapping + GraphUI Support
AABBox clipping naturally couldn't be transformed into 3D Model-View (Mv) Space,
as it is axis aligned and only provided 2 points (min/max).
Therefor we map the Group's AABBox to a 8-point Cube,
perform the Mv-transformation and then produce the 6-plane Frustum.
As before, we cull fully outside shapes within the Group's draw method
and perform fragment clipping with same Frustum planes in the shader.
With clipping enabled, the 3D z-axis getBounds() depth
will be slightly increased for functional Frustum operation.
This is also done for setFixedSize(Vec2f).
The Frustum planes are copied to the Graph shader
via float[4*6] -> uniform vec4 gcu_ClipFrustum[6]; // L, R, B, T, N, F each {n.x, n.y, n.z, d}
+++
Concludes related work of below commits
- 1040bed4ecc6f4598ea459f1073a9240583fc3c3
- 5cca51e32999a882e2a5f00cb45ecafc824ffd86
Diffstat (limited to 'src/graphui')
3 files changed, 97 insertions, 38 deletions
diff --git a/src/graphui/classes/com/jogamp/graph/ui/Group.java b/src/graphui/classes/com/jogamp/graph/ui/Group.java index 1202752b1..d1156120a 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/Group.java +++ b/src/graphui/classes/com/jogamp/graph/ui/Group.java @@ -42,6 +42,8 @@ import com.jogamp.math.Vec2f; import com.jogamp.math.Vec3f; import com.jogamp.math.Vec4f; import com.jogamp.math.geom.AABBox; +import com.jogamp.math.geom.Cube; +import com.jogamp.math.geom.Frustum; import com.jogamp.math.util.PMVMatrix4f; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLProfile; @@ -77,14 +79,15 @@ public class Group extends Shape implements Container { } private final List<Shape> shapes = new CopyOnWriteArrayList<Shape>(); - private final Vec2f fixedSize = new Vec2f(); + /** Enforced fixed size. In case z-axis is NaN, its 3D z-axis will be adjusted. */ + private final Vec3f fixedSize = new Vec3f(); private Layout layouter; private Rectangle border = null; private boolean relayoutOnDirtyShapes = true; private boolean widgetMode = false; private boolean clipOnBounds = false; - private AABBox clipBBox = null; + private Frustum clipFrustum = null; /** * Create a group of {@link Shape}s w/o {@link Group.Layout}. @@ -118,43 +121,65 @@ public class Group extends Shape implements Container { /** Set {@link Group.Layout}. */ public Group setLayout(final Layout l) { layouter = l; return this; } - /** Enforce size of this group to given dimension. */ - public Group setFixedSize(final Vec2f v) { fixedSize.set(v); return this; } - public Vec2f getFixedSize() { return fixedSize; } + /** Enforce size of this group for all given 3 dimensions {@link #getBounds()} without adjusting 3D z-axis like {@link #setFixedSize(Vec2f)}. */ + public Group setFixedSize(final Vec3f v) { fixedSize.set(v); return this; } + /** + * Enforce size of this group to given 2 dimensions, + * adjusting the 3D z-axis {@link #getBounds()} giving room for potential clipping via {@link #setClipOnBounds(boolean)} or {@link #setClipFrustum(Frustum)}. + * @see #setFixedSize(Vec3f) + */ + public Group setFixedSize(final Vec2f v) { fixedSize.set(v.x(), v.y(), Float.NaN); return this; } + /** Returns borrowed fixed size instance, see {@link #setFixedSize(Vec3f)} and {@link #setFixedSize(Vec2f)}. */ + public Vec3f getFixedSize() { return fixedSize; } + /** Returns given {@link Vec2f} instance set with 2 dimensions, see {@link #setFixedSize(Vec2f)}. */ + public Vec2f getFixedSize(final Vec2f out) { out.set(fixedSize.x(), fixedSize.y()); return out; } /** - * Enable {@link AABBox} clipping on {@link #getBounds()} for this group and its shapes as follows + * Enable {@link Frustum} clipping on {@link #getBounds()} for this group and its shapes as follows * <ul> * <li>Discard {@link Shape} {@link #draw(GL2ES2, RegionRenderer) rendering} if not intersecting {@code clip-box}.</li> * <li>Otherwise perform pixel-accurate clipping inside the shader to {@code clip-box}.</li> * </ul> * <p> - * {@link #setClipBBox(AABBox)} takes precedence over {@link #setClipOnBounds(boolean)}. + * {@link #setClipFrustum(Frustum)} takes precedence over {@link #setClipOnBounds(boolean)}. + * </p> + * <p> + * With clipping enabled, the 3D z-axis {@link #getBounds()} depth + * will be slightly increased for functional {@link Frustum} operation. * </p> * @param v boolean to toggle clipping * @return this instance for chaining - * @see #setClipBBox(AABBox) + * @see #setClipFrustum(Frustum) + * @see #setFixedSize(Vec2f) + * @see #setFixedSize(Vec3f) */ public Group setClipOnBounds(final boolean v) { clipOnBounds = v; return this; } /** Returns {@link #setClipOnBounds(boolean)} value */ public boolean getClipOnBounds() { return clipOnBounds; } /** - * Enable {@link AABBox} clipping on explicit given pre-multiplied Mv-matrix {@code clip-box} as follows + * Enable {@link Frustum} clipping on explicit given pre-multiplied w/ Mv-matrix {@code clip-box} + * for this group and its shapes as follows * <ul> * <li>Discard {@link Shape} {@link #draw(GL2ES2, RegionRenderer) rendering} if not intersecting {@code clip-box}.</li> * <li>Otherwise perform pixel-accurate clipping inside the shader to {@code clip-box}.</li> * </ul> * <p> - * {@link #setClipBBox(AABBox)} takes precedence over {@link #setClipOnBounds(boolean)}. + * {@link #setClipFrustum(Frustum)} takes precedence over {@link #setClipOnBounds(boolean)}. + * </p> + * <p> + * With clipping enabled, the 3D z-axis {@link #getBounds()} depth + * will be slightly increased for functional {@link Frustum} operation. * </p> - * @param v {@link AABBox} pre-multiplied Mv-matrix + * @param v {@link Frustum} pre-multiplied w/ Mv-matrix * @return this instance for chaining * @see #setClipOnBounds(boolean) + * @see #setFixedSize(Vec2f) + * @see #setFixedSize(Vec3f) */ - public Group setClipBBox(final AABBox v) { clipBBox = v; return this; } - /** Returns {@link #setClipBBox(AABBox)} value */ - public AABBox getClipBBox() { return clipBBox; } + public Group setClipFrustum(final Frustum v) { clipFrustum = v; return this; } + /** Returns {@link #setClipFrustum(Frustum)} value */ + public Frustum getClipFrustum() { return clipFrustum; } @Override public int getShapeCount() { return shapes.size(); } @@ -281,12 +306,12 @@ public class Group extends Shape implements Container { final Object[] shapesS = shapes.toArray(); Arrays.sort(shapesS, (Comparator)Shape.ZAscendingComparator); - final boolean useClipBBox = null != clipBBox; - if( useClipBBox || clipOnBounds ) { - final AABBox origClipBox = renderer.getClipBBox(); + final boolean useClipFrustum = null != clipFrustum; + if( useClipFrustum || clipOnBounds ) { + final Frustum origClipFrustum = renderer.getClipFrustum(); - final AABBox clipBox = useClipBBox ? clipBBox : box.transform(pmv.getMv(), tempBB0); - renderer.setClipBBox( tempBB1.set(clipBox) ); // Mv pre-multiplied AABBox + final Frustum frustumMv = useClipFrustum ? clipFrustum : tempC00.set( box ).transform( pmv.getMv() ).updateFrustumPlanes(tempF00); + renderer.setClipFrustum( frustumMv ); final int shapeCount = shapesS.length; for(int i=0; i<shapeCount; i++) { @@ -295,16 +320,18 @@ public class Group extends Shape implements Container { pmv.pushMv(); shape.setTransformMv(pmv); - final AABBox childBox = shape.getBounds(); - if( clipBox.intersects( childBox.transform(pmv.getMv(), tempBB0) ) && - ( !doFrustumCulling || !pmv.getFrustum().isAABBoxOutside( childBox ) ) ) + final AABBox shapeBox = shape.getBounds(); + final Cube shapeMv = tempC01.set( shapeBox ).transform( pmv.getMv() ); + + if( ( !frustumMv.isOutside( shapeMv ) ) && + ( !doFrustumCulling || !pmv.getFrustum().isOutside( shapeBox ) ) ) { shape.draw(gl, renderer); } pmv.popMv(); } } - renderer.setClipBBox(origClipBox); + renderer.setClipFrustum(origClipFrustum); } else { final int shapeCount = shapesS.length; for(int i=0; i<shapeCount; i++) { @@ -323,8 +350,9 @@ public class Group extends Shape implements Container { border.draw(gl, renderer); } } - private final AABBox tempBB0 = new AABBox(); // OK, synchronized - private final AABBox tempBB1 = new AABBox(); // OK, synchronized + private final Frustum tempF00 = new Frustum(); // OK, synchronized + private final Cube tempC00 = new Cube(); // OK, synchronized + private final Cube tempC01 = new Cube(); // OK, synchronized @SuppressWarnings({ "unchecked", "rawtypes" }) @Override @@ -489,12 +517,35 @@ public class Group extends Shape implements Container { box.resize(h.x() + p.right, h.y() + p.top, l.z()); setRotationPivot( box.getCenter() ); } - if( !FloatUtil.isZero(fixedSize.x()) && !FloatUtil.isZero(fixedSize.y()) ) { - final Vec3f low = box.getLow(); - final Vec3f high = new Vec3f(low); - high.add(fixedSize.x(), fixedSize.y(), 0); - box.setSize(low, high); + final boolean useFixedSize = !FloatUtil.isZero(fixedSize.x()) && !FloatUtil.isZero(fixedSize.y()); + final boolean useClipping = null != clipFrustum || clipOnBounds; + if( useFixedSize || useClipping ) { + // final AABBox old = new AABBox(box); + final boolean adjustZ = useClipping || ( useFixedSize && Float.isNaN(fixedSize.z()) ); + final Vec3f lo = box.getLow(); + if( adjustZ ) { + final float oldDepth = box.getDepth(); + final Vec3f hi; + final float zAdjustment = 10f*Scene.DEFAULT_ACTIVE_ZOFFSET_SCALE*Scene.DEFAULT_Z16_EPSILON; + lo.add( 0, 0, -(1f*zAdjustment)); + if( useFixedSize ) { + hi = new Vec3f(lo); + hi.add(fixedSize.x(), fixedSize.y(), oldDepth+(2f*zAdjustment)); + } else { + hi = box.getHigh(); + hi.add( 0, 0, oldDepth+(1f*zAdjustment)); + } + box.setSize(lo, hi); + } else if( useFixedSize ) { + final Vec3f hi = useFixedSize ? new Vec3f(lo) : box.getHigh(); + + hi.add(fixedSize.x(), fixedSize.y(), fixedSize.z()); + box.setSize(lo, hi); + } + // System.err.println("- was "+old); + // System.err.println("- has "+box); } + if( hasBorder() ) { if( null == border ) { final int firstRMs = null != firstGS ? firstGS.getRenderModes() : 0; diff --git a/src/graphui/classes/com/jogamp/graph/ui/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/Scene.java index 189c75c9d..03cc29097 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/Scene.java +++ b/src/graphui/classes/com/jogamp/graph/ui/Scene.java @@ -111,6 +111,8 @@ public final class Scene implements Container, GLEventListener { public static final float DEFAULT_ZFAR = 7000.0f; /** Default Z precision on 16-bit depth buffer using {@link #DEFAULT_SCENE_DIST} z-position and {@link #DEFAULT_ZNEAR}. Value is {@code 6.1033297E-6}. */ public static final float DEFAULT_Z16_EPSILON = FloatUtil.getZBufferEpsilon(16 /* zBits */, DEFAULT_SCENE_DIST, DEFAULT_ZNEAR); + /** Default Z precision scale, i.e. multiple of {@link #DEFAULT_Z16_EPSILON} for {@link #setActiveShapeZOffsetScale(float)}. Value is {@value}. */ + public static final float DEFAULT_ACTIVE_ZOFFSET_SCALE = 10f; /** Default Z precision on 16-bit depth buffer using {@code -1} z-position and {@link #DEFAULT_ZNEAR}. Value is {@code 1.5256461E-4}. */ // public static final float DIST1_Z16_EPSILON = FloatUtil.getZBufferEpsilon(16 /* zBits */, -1, DEFAULT_ZNEAR); @@ -1033,7 +1035,7 @@ public final class Scene implements Container, GLEventListener { activeShape = shape; } } - private float activeZOffsetScale = 10f; + private float activeZOffsetScale = DEFAULT_ACTIVE_ZOFFSET_SCALE; /** Returns the active {@link Shape} Z-Offset scale, defaults to {@code 10.0}. */ public float getActiveShapeZOffsetScale() { return activeZOffsetScale; } diff --git a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java index f84c3cf5a..838a39254 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java +++ b/src/graphui/classes/com/jogamp/graph/ui/widgets/RangedGroup.java @@ -39,6 +39,9 @@ import com.jogamp.math.Vec2f; import com.jogamp.math.Vec3f; import com.jogamp.math.Vec4f; import com.jogamp.math.geom.AABBox; +import com.jogamp.math.geom.Cube; +import com.jogamp.math.geom.Frustum; +import com.jogamp.math.util.PMVMatrix4f; import com.jogamp.opengl.GL2ES2; import com.jogamp.opengl.GLProfile; import com.jogamp.opengl.util.texture.TextureSequence; @@ -134,7 +137,7 @@ public class RangedGroup extends Widget { } public Group getContent() { return content; } - public Vec2f getContentSize() { return clippedContent.getFixedSize(); } + public Vec2f getContentSize(final Vec2f out) { return clippedContent.getFixedSize(out); } public Group getClippedContent() { return clippedContent; } public RangeSlider getHorizSlider() { return horizSlider; } public RangeSlider getVertSlider() { return vertSlider; } @@ -145,7 +148,7 @@ public class RangedGroup extends Widget { super.validateImpl(gl, glp); final AABBox b = content.getBounds(); - final Vec2f contentSize = getContentSize(); + final Vec3f contentSize = clippedContent.getFixedSize(); contentPosZero.set(0, 0); if( null != horizSlider ) { horizSlider.setMinMax(new Vec2f(0, content.getBounds().getWidth()), 0); @@ -164,12 +167,15 @@ public class RangedGroup extends Widget { @Override protected void drawImpl0(final GL2ES2 gl, final RegionRenderer renderer, final Vec4f rgba) { if( content.isVisible() ) { - // Mv pre-multiplied AABBox, clippedContent is on same PMV - final AABBox clipBBox = clippedContent.getBounds().transform(renderer.getMatrix().getMv(), tempBB); - content.setClipBBox(clipBBox); + final PMVMatrix4f pmv = renderer.getMatrix(); + + // Mv pre-multiplied Frustum, clippedContent is on same PMV + final Frustum clipFrustum = tempC00.set( clippedContent.getBounds() ).transform( pmv.getMv() ).updateFrustumPlanes(tempF00); + content.setClipFrustum(clipFrustum); super.drawImpl0(gl, renderer, rgba); - content.setClipBBox(null); + content.setClipFrustum(null); } } - private final AABBox tempBB = new AABBox(); // OK, synchronized + private final Frustum tempF00 = new Frustum(); // OK, synchronized + private final Cube tempC00 = new Cube(); // OK, synchronized } |