aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/com/jogamp/opengl/math/geom
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2014-02-23 14:51:06 +0100
committerSven Gothel <[email protected]>2014-02-23 14:51:06 +0100
commit3352601e0860584509adf2b76f993d03893ded4b (patch)
tree974fccc8c0eb2f5ad9d4ffd741dfc35869ed67b5 /src/jogl/classes/com/jogamp/opengl/math/geom
parentf51933f0ebe9ae030c26c066e59a728ce08b8559 (diff)
parentc67de337a8aaf52e36104c3f13e273aa19d21f1f (diff)
Merge branch 'master' into stash_glyphcache
Conflicts: make/scripts/tests.sh src/jogl/classes/com/jogamp/graph/curve/OutlineShape.java src/jogl/classes/com/jogamp/graph/curve/Region.java src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java src/jogl/classes/com/jogamp/graph/curve/opengl/RegionRenderer.java src/jogl/classes/com/jogamp/graph/curve/opengl/Renderer.java src/jogl/classes/com/jogamp/graph/curve/opengl/TextRenderer.java src/jogl/classes/com/jogamp/graph/font/Font.java src/jogl/classes/com/jogamp/opengl/math/VectorUtil.java src/jogl/classes/jogamp/graph/curve/text/GlyphShape.java src/jogl/classes/jogamp/graph/curve/text/GlyphString.java src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
Diffstat (limited to 'src/jogl/classes/com/jogamp/opengl/math/geom')
-rw-r--r--src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java360
-rw-r--r--src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java388
2 files changed, 748 insertions, 0 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java
new file mode 100644
index 000000000..5fbc28c60
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/opengl/math/geom/AABBox.java
@@ -0,0 +1,360 @@
+/**
+ * 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.opengl.math.geom;
+
+import com.jogamp.opengl.math.VectorUtil;
+
+
+/**
+ * Axis Aligned Bounding Box. Defined by two 3D coordinates (low and high)
+ * The low being the the lower left corner of the box, and the high being the upper
+ * right corner of the box.
+ *
+ */
+public class AABBox implements Cloneable {
+ private float[] low = new float[3];
+ private float[] high = new float[3];
+ private float[] center = new float[3];
+
+ /** Create a Axis Aligned bounding box (AABBox)
+ * where the low and and high MAX float Values.
+ */
+ public AABBox() {
+ reset();
+ }
+
+ /** Create an AABBox specifying the coordinates
+ * of the low and high
+ * @param lx min x-coordinate
+ * @param ly min y-coordnate
+ * @param lz min z-coordinate
+ * @param hx max x-coordinate
+ * @param hy max y-coordinate
+ * @param hz max z-coordinate
+ */
+ public AABBox(float lx, float ly, float lz,
+ float hx, float hy, float hz) {
+ setSize(lx, ly, lz, hx, hy, hz);
+ }
+
+ /** Create a AABBox defining the low and high
+ * @param low min xyz-coordinates
+ * @param high max xyz-coordinates
+ */
+ public AABBox(float[] low, float[] high) {
+ setSize(low[0],low[1],low[2], high[0],high[1],high[2]);
+ }
+
+ /** resets this box to the inverse low/high, allowing the next {@link #resize(float, float, float)} command to hit. */
+ public final void 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;
+ }
+
+ /** Get the max xyz-coordinates
+ * @return a float array containing the max xyz coordinates
+ */
+ public final float[] getHigh() {
+ return high;
+ }
+
+ private final void setHigh(float hx, float hy, float hz) {
+ this.high[0] = hx;
+ this.high[1] = hy;
+ this.high[2] = hz;
+ }
+
+ /** Get the min xyz-coordinates
+ * @return a float array containing the min xyz coordinates
+ */
+ public final float[] getLow() {
+ return low;
+ }
+
+ private final void setLow(float lx, float ly, float lz) {
+ this.low[0] = lx;
+ this.low[1] = ly;
+ this.low[2] = lz;
+ }
+
+ private final void computeCenter() {
+ center[0] = (high[0] + low[0])/2;
+ center[1] = (high[1] + low[1])/2;
+ center[2] = (high[2] + low[2])/2;
+ }
+
+ /**
+ * Set size of the AABBox specifying the coordinates
+ * of the low and high.
+ *
+ * @param lx min x-coordinate
+ * @param ly min y-coordnate
+ * @param lz min z-coordinate
+ * @param hx max x-coordinate
+ * @param hy max y-coordinate
+ * @param hz max z-coordinate
+ */
+ public final void setSize(float lx, float ly, float lz,
+ float hx, float hy, 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;
+ computeCenter();
+ }
+
+ /** Resize the AABBox to encapsulate another AABox
+ * @param newBox AABBox to be encapsulated in
+ */
+ public final void resize(AABBox newBox) {
+ float[] newLow = newBox.getLow();
+ float[] 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];
+
+ /** 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];
+
+ computeCenter();
+ }
+
+ /** Resize the AABBox to encapsulate the passed
+ * xyz-coordinates.
+ * @param x x-axis coordinate value
+ * @param y y-axis coordinate value
+ * @param z z-axis coordinate value
+ */
+ public final void resize(float x, float y, float z) {
+ /** test low */
+ if (x < low[0])
+ low[0] = x;
+ if (y < low[1])
+ low[1] = y;
+ if (z < low[2])
+ low[2] = z;
+
+ /** test high */
+ if (x > high[0])
+ high[0] = x;
+ if (y > high[1])
+ high[1] = y;
+ if (z > high[2])
+ high[2] = z;
+
+ computeCenter();
+ }
+
+ /** Resize the AABBox to encapsulate the passed
+ * xyz-coordinates.
+ * @param xyz xyz-axis coordinate values
+ * @param offset of the array
+ */
+ public final void resize(float[] xyz, int offset) {
+ resize(xyz[0+offset], xyz[1+offset], xyz[2+offset]);
+ }
+
+ /** Check if the x & y coordinates are bounded/contained
+ * by this AABBox
+ * @param x x-axis coordinate value
+ * @param y y-axis coordinate value
+ * @return true if x belong to (low.x, high.x) and
+ * y belong to (low.y, high.y)
+ */
+ public final boolean contains(float x, float y) {
+ if(x<low[0] || x>high[0]){
+ return false;
+ }
+ if(y<low[1]|| y>high[1]){
+ return false;
+ }
+ return true;
+ }
+
+ /** Check if the xyz coordinates are bounded/contained
+ * by this AABBox.
+ * @param x x-axis coordinate value
+ * @param y y-axis coordinate value
+ * @param z z-axis coordinate value
+ * @return true if x belong to (low.x, high.x) and
+ * y belong to (low.y, high.y) and z belong to (low.z, high.z)
+ */
+ public final boolean contains(float x, float y, float z) {
+ if(x<low[0] || x>high[0]){
+ return false;
+ }
+ if(y<low[1]|| y>high[1]){
+ return false;
+ }
+ if(z<low[2] || z>high[2]){
+ return false;
+ }
+ return true;
+ }
+
+ /** Check if there is a common region between this AABBox and the passed
+ * 2D region irrespective of z range
+ * @param x lower left x-coord
+ * @param y lower left y-coord
+ * @param w width
+ * @param h hight
+ * @return true if this AABBox might have a common region with this 2D region
+ */
+ public final boolean intersects(float x, float y, float w, float h) {
+ if (w <= 0 || h <= 0) {
+ return false;
+ }
+
+ final float _w = getWidth();
+ final float _h = getHeight();
+ if (_w <= 0 || _h <= 0) {
+ return false;
+ }
+
+ final float x0 = getMinX();
+ final float y0 = getMinY();
+ return (x + w > x0 &&
+ y + h > y0 &&
+ x < x0 + _w &&
+ y < y0 + _h);
+ }
+
+
+ /** Get the size of the Box where the size is represented by the
+ * length of the vector between low and high.
+ * @return a float representing the size of the AABBox
+ */
+ public final float getSize() {
+ return VectorUtil.computeLength(low, high);
+ }
+
+ /**Get the Center of the AABBox
+ * @return the xyz-coordinates of the center of the AABBox
+ */
+ public final float[] getCenter() {
+ return center;
+ }
+
+ /** Scale the AABBox by a constant
+ * @param size a constant float value
+ */
+ public final void scale(float size) {
+ float[] diffH = new float[3];
+ diffH[0] = high[0] - center[0];
+ diffH[1] = high[1] - center[1];
+ diffH[2] = high[2] - center[2];
+
+ diffH = VectorUtil.scale(diffH, size);
+
+ float[] diffL = new float[3];
+ diffL[0] = low[0] - center[0];
+ diffL[1] = low[1] - center[1];
+ diffL[2] = low[2] - center[2];
+
+ diffL = VectorUtil.scale(diffL, size);
+
+ high = VectorUtil.vectorAdd(center, diffH);
+ low = VectorUtil.vectorAdd(center, diffL);
+ }
+
+ public final float getMinX() {
+ return low[0];
+ }
+
+ public final float getMinY() {
+ return low[1];
+ }
+
+ public final float getMinZ() {
+ return low[2];
+ }
+
+ public final float getMaxX() {
+ return high[0];
+ }
+
+ public final float getMaxY() {
+ return high[1];
+ }
+
+ public final float getMaxZ() {
+ return high[2];
+ }
+
+ public final float getWidth(){
+ return high[0] - low[0];
+ }
+
+ public final float getHeight() {
+ return high[1] - low[1];
+ }
+
+ public final float getDepth() {
+ return high[2] - low[2];
+ }
+
+ @Override
+ public final AABBox clone() {
+ return new AABBox(this.low, this.high);
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if( obj == this ) {
+ return true;
+ }
+ if( null == obj || !(obj instanceof AABBox) ) {
+ return false;
+ }
+ final AABBox other = (AABBox) obj;
+ return VectorUtil.checkEquality(low, other.low) &&
+ VectorUtil.checkEquality(high, other.high) ;
+ }
+
+ @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]+" ]";
+ }
+}
diff --git a/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java
new file mode 100644
index 000000000..fb311083f
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/opengl/math/geom/Frustum.java
@@ -0,0 +1,388 @@
+/**
+ * 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.opengl.math.geom;
+
+import com.jogamp.common.os.Platform;
+
+/**
+ * Providing frustum {@link #getPlanes() planes} derived by different inputs
+ * ({@link #updateByPMV(float[], int) P*MV}, ..)
+ * used to {@link #classifySphere(float[], float) classify objects} and to test
+ * whether they are {@link #isOutside(AABBox) outside}.
+ *
+ * <p>
+ * Extracting the world-frustum planes from the P*Mv:
+ * <pre>
+ * Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix
+ * Gil Gribb <[email protected]>
+ * Klaus Hartmann <[email protected]>
+ * http://graphics.cs.ucf.edu/cap4720/fall2008/plane_extraction.pdf
+ * </pre>
+ * Classifying Point, Sphere and AABBox:
+ * <pre>
+ * Efficient View Frustum Culling
+ * Daniel Sýkora <[email protected]>
+ * Josef Jelínek <[email protected]>
+ * http://www.cg.tuwien.ac.at/hostings/cescg/CESCG-2002/DSykoraJJelinek/index.html
+ * </pre>
+ * <pre>
+ * Lighthouse3d.com
+ * http://www.lighthouse3d.com/tutorials/view-frustum-culling/
+ * </pre>
+ *
+ * Fundamentals about Planes, Half-Spaces and Frustum-Culling:<br/>
+ * <pre>
+ * Planes and Half-Spaces, Max Wagner <[email protected]>
+ * http://www.emeyex.com/site/tuts/PlanesHalfSpaces.pdf
+ * </pre>
+ * <pre>
+ * Frustum Culling, Max Wagner <[email protected]>
+ * http://www.emeyex.com/site/tuts/FrustumCulling.pdf
+ * </pre>
+ * </p>
+ */
+public class Frustum {
+ /** Normalized planes[l, r, b, t, n, f] */
+ protected Plane[] planes = new Plane[6];
+
+ /**
+ * Creates an undefined instance w/o calculating the frustum.
+ * <p>
+ * Use one of the <code>update(..)</code> methods to set the {@link #getPlanes() planes}.
+ * </p>
+ * @see #updateByPlanes(Plane[])
+ * @see #updateByPMV(float[], int)
+ */
+ public Frustum() {
+ for (int i = 0; i < 6; ++i) {
+ planes[i] = new Plane();
+ }
+ }
+
+ /**
+ * Plane equation := dot(n, x - p) = 0 -> ax + bc + cx + d == 0
+ * <p>
+ * In order to work w/ {@link Frustum#isOutside(AABBox) isOutside(..)} methods,
+ * the normals have to point to the inside of the frustum.
+ * </p>
+ */
+ public static class Plane {
+ /** Normal of the plane */
+ public final float[] n = new float[3];
+
+ /** Distance to origin */
+ public float d;
+
+ /**
+ * Return signed distance of plane to given point.
+ * <ul>
+ * <li>If dist &lt; 0 , then the point p lies in the negative halfspace.</li>
+ * <li>If dist = 0 , then the point p lies in the plane.</li>
+ * <li>If dist &gt; 0 , then the point p lies in the positive halfspace.</li>
+ * </ul>
+ * A plane cuts 3D space into 2 half spaces.
+ * <p>
+ * Positive halfspace is where the plane’s normals vector points into.
+ * </p>
+ * <p>
+ * Negative halfspace is the <i>other side</i> of the plane, i.e. *-1
+ * </p>
+ **/
+ public final float distanceTo(float x, float y, float z) {
+ return n[0] * x + n[1] * y + n[2] * z + d;
+ }
+
+ /** Return distance of plane to given point, see {@link #distanceTo(float, float, float)}. */
+ public final float distanceTo(float[] p) {
+ return n[0] * p[0] + n[1] * p[1] + n[2] * p[2] + d;
+ }
+
+ @Override
+ public String toString() {
+ return "Plane[ [ " + n[0] + ", " + n[1] + ", " + n[2] + " ], " + d + "]";
+ }
+ }
+
+ /** Index for left plane: {@value} */
+ public static final int LEFT = 0;
+ /** Index for right plane: {@value} */
+ public static final int RIGHT = 1;
+ /** Index for bottom plane: {@value} */
+ public static final int BOTTOM = 2;
+ /** Index for top plane: {@value} */
+ public static final int TOP = 3;
+ /** Index for near plane: {@value} */
+ public static final int NEAR = 4;
+ /** Index for far plane: {@value} */
+ public static final int FAR = 5;
+
+ /**
+ * {@link Plane}s are ordered in the returned array as follows:
+ * <ul>
+ * <li>{@link #LEFT}</li>
+ * <li>{@link #RIGHT}</li>
+ * <li>{@link #BOTTOM}</li>
+ * <li>{@link #TOP}</li>
+ * <li>{@link #NEAR}</li>
+ * <li>{@link #FAR}</li>
+ * </ul>
+ * <p>
+ * {@link Plane}'s normals are pointing to the inside of the frustum
+ * in order to work w/ {@link #isOutside(AABBox) isOutside(..)} methods.
+ * </p>
+ *
+ * @return array of normalized {@link Plane}s, order see above.
+ */
+ public final Plane[] getPlanes() { return planes; }
+
+ /**
+ * Copy the given <code>src</code> planes into this this instance's planes.
+ * @param src the 6 source planes
+ */
+ public final void updateByPlanes(Plane[] src) {
+ for (int i = 0; i < 6; ++i) {
+ final Plane p0 = planes[i];
+ final float[] p0_n = p0.n;
+ final Plane p1 = src[i];
+ final float[] p1_n = p1.n;
+ p0_n[0] = p1_n[0];
+ p0_n[1] = p1_n[1];
+ p0_n[2] = p1_n[2];
+ p0.d = p1.d;
+ }
+ }
+
+ /**
+ * Calculate the frustum planes in world coordinates
+ * using the passed float[16] as premultiplied P*MV (column major order).
+ * <p>
+ * Frustum plane's normals will point to the inside of the viewing frustum,
+ * as required by this class.
+ * </p>
+ */
+ public void updateByPMV(float[] pmv, int pmv_off) {
+ // Left: a = m41 + m11, b = m42 + m12, c = m43 + m13, d = m44 + m14 - [1..4] row-major
+ // Left: a = m30 + m00, b = m31 + m01, c = m32 + m02, d = m33 + m03 - [0..3] row-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] row-major
+ // Right: a = m30 - m00, b = m31 - m01, c = m32 - m02, d = m33 - m03 - [0..3] row-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] row-major
+ // Bottom: a = m30 + m10, b = m31 + m11, c = m32 + m12, d = m33 + m13 - [0..3] row-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] row-major
+ // Top: a = m30 - m10, b = m31 - m11, c = m32 - m12, d = m33 - m13 - [0..3] row-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] row-major
+ // Near: a = m30 + m20, b = m31 + m21, c = m32 + m22, d = m33 + m23 - [0..3] row-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] row-major
+ // Far: a = m30 - m20, b = m31 - m21, c = m32 + m22, d = m33 + m23 - [0..3] row-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;
+ }
+ }
+
+ private static final boolean isOutsideImpl(Plane p, 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 ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Check to see if an axis aligned bounding box is completely outside of the frustum.
+ * <p>
+ * Note: If method returns false, the box may only be partially inside.
+ * </p>
+ */
+ public final boolean isAABBoxOutside(AABBox box) {
+ for (int i = 0; i < 6; ++i) {
+ if ( isOutsideImpl(planes[i], box) ) {
+ // fully outside
+ return true;
+ }
+ }
+ // We make no attempt to determine whether it's fully inside or not.
+ return false;
+ }
+
+
+ public static enum Location { OUTSIDE, INSIDE, INTERSECT };
+
+ /**
+ * Check to see if a point is outside, inside or on a plane of the frustum.
+ *
+ * @param p the point
+ * @return {@link Location} of point related to frustum planes
+ */
+ public final Location classifyPoint(float[] p) {
+ Location res = Location.INSIDE;
+
+ for (int i = 0; i < 6; ++i) {
+ final float d = planes[i].distanceTo(p);
+ if ( d < 0.0f ) {
+ return Location.OUTSIDE;
+ } else if ( d == 0.0f ) {
+ res = Location.INTERSECT;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Check to see if a point is outside of the frustum.
+ *
+ * @param p the point
+ * @return true if outside of the frustum, otherwise inside or on a plane
+ */
+ public final boolean isPointOutside(float[] p) {
+ return Location.OUTSIDE == classifyPoint(p);
+ }
+
+ /**
+ * Check to see if a sphere is outside, intersecting or inside of the frustum.
+ *
+ * @param p center of the sphere
+ * @param radius radius of the sphere
+ * @return {@link Location} of point related to frustum planes
+ */
+ public final Location classifySphere(float[] p, float radius) {
+ Location res = Location.INSIDE; // fully inside
+
+ for (int i = 0; i < 6; ++i) {
+ final float d = planes[i].distanceTo(p);
+ if ( d < -radius ) {
+ // fully outside
+ return Location.OUTSIDE;
+ } else if (d < radius ) {
+ // intersecting
+ res = Location.INTERSECT;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Check to see if a sphere is outside of the frustum.
+ *
+ * @param p center of the sphere
+ * @param radius radius of the sphere
+ * @return true if outside of the frustum, otherwise inside or intersecting
+ */
+ public final boolean isSphereOutside(float[] p, float radius) {
+ return Location.OUTSIDE == classifySphere(p, radius);
+ }
+
+ public StringBuilder toString(StringBuilder sb) {
+ if( null == sb ) {
+ sb = new StringBuilder();
+ }
+ sb.append("Frustum[ Planes[ ").append(Platform.NEWLINE)
+ .append(" L: ").append(planes[0]).append(", ").append(Platform.NEWLINE)
+ .append(" R: ").append(planes[1]).append(", ").append(Platform.NEWLINE)
+ .append(" B: ").append(planes[2]).append(", ").append(Platform.NEWLINE)
+ .append(" T: ").append(planes[3]).append(", ").append(Platform.NEWLINE)
+ .append(" N: ").append(planes[4]).append(", ").append(Platform.NEWLINE)
+ .append(" F: ").append(planes[5]).append("], ").append(Platform.NEWLINE)
+ .append("]");
+ return sb;
+ }
+
+ @Override
+ public String toString() {
+ return toString(null).toString();
+ }
+}