/**
* 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.math;
import java.nio.FloatBuffer;
import java.util.Locale;
import com.jogamp.opengl.GLException;
import jogamp.opengl.Debug;
import com.jogamp.common.os.Platform;
/**
* Basic Float math utility functions.
*
* Implementation assumes linear matrix layout in column-major order
* matching OpenGL's implementation, illustration:
*
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;
*
*
*
*
*
*
* Implementation utilizes unrolling of small vertices and matrices wherever possible
* while trying to access memory in a linear fashion for performance reasons, see:
*
*
*/
public final class FloatUtil {
public static final boolean DEBUG = Debug.debugExplicit("Math");
//
// 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)
* @return given matrix for chaining
*/
public static float[] makeIdentity(final float[] m) {
m[0+4*0] = 1f;
m[1+4*0] = 0f;
m[2+4*0] = 0f;
m[3+4*0] = 0f;
m[0+4*1] = 0f;
m[1+4*1] = 1f;
m[2+4*1] = 0f;
m[3+4*1] = 0f;
m[0+4*2] = 0f;
m[1+4*2] = 0f;
m[2+4*2] = 1f;
m[3+4*2] = 0f;
m[0+4*3] = 0f;
m[1+4*3] = 0f;
m[2+4*3] = 0f;
m[3+4*3] = 1f;
return m;
}
/**
* Make a translation matrix in column-major order from the given axis deltas
*
Translation matrix (Column Order):
1 0 0 0
0 1 0 0
0 0 1 0
x y z 1
*
*
* All matrix fields are only set if initM
is true
.
*
* @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
* 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 boolean initM, final float tx, final float ty, final float tz) {
if( initM ) {
makeIdentity(m);
} else {
m[0+4*0] = 1;
m[1+4*1] = 1;
m[2+4*2] = 1;
m[3+4*3] = 1;
}
m[0+4*3] = tx;
m[1+4*3] = ty;
m[2+4*3] = tz;
return m;
}
/**
* Make a scale matrix in column-major order from the given axis factors
*
Scale matrix (Any Order):
x 0 0 0
0 y 0 0
0 0 z 0
0 0 0 1
*
*
* All matrix fields are only set if initM
is true
.
*
* @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
* 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 boolean initM, final float sx, final float sy, final float sz) {
if( initM ) {
makeIdentity(m);
} else {
m[0+4*3] = 0;
m[1+4*3] = 0;
m[2+4*3] = 0;
m[3+4*3] = 1;
}
m[0+4*0] = sx;
m[1+4*1] = sy;
m[2+4*2] = sz;
return m;
}
/**
* Make given matrix the frustum matrix based on given parameters.
*
Frustum matrix (Column Order):
2*zNear/dx 0 0 0
0 2*zNear/dy 0 0
A B C -1
0 0 D 0
*
*
* All matrix fields are only set if initM
is true
.
*
*
* @param m 4x4 matrix in column-major order (also result)
* @param m_offset offset in given array m, 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 left
* @param right
* @param bottom
* @param top
* @param zNear
* @param zFar
* @return given matrix for chaining
* @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear}
* or {@code left == right}, or {@code bottom == top}.
*/
public static float[] makeFrustum(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) throws GLException {
if( zNear <= 0.0f || zFar <= zNear ) {
throw new GLException("Requirements zNear > 0 and zFar > zNear, but zNear "+zNear+", zFar "+zFar);
}
if( left == right || top == bottom) {
throw new GLException("GL_INVALID_VALUE: top,bottom and left,right must not be equal");
}
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 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;
m[m_offset+0+4*0] = zNear2/dx;
m[m_offset+1+4*1] = zNear2/dy;
m[m_offset+0+4*2] = A;
m[m_offset+1+4*2] = B;
m[m_offset+2+4*2] = C;
m[m_offset+3+4*2] = -1.0f;
m[m_offset+2+4*3] = D;
m[m_offset+3+4*3] = 0f;
return m;
}
/**
* Make given matrix the perspective {@link #makeFrustum(float[], int, boolean, float, float, float, float, float, float) frustum}
* matrix based on given parameters.
*
* All matrix fields are only set if initM
is true
.
*
*
* @param m 4x4 matrix in column-major order (also result)
* @param m_offset offset in given array m, 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 fovy_rad angle in radians
* @param aspect aspect ratio width / height
* @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)
*/
public static float[] makePerspective(final float[] m, final int m_off, final boolean initM,
final float fovy_rad, final float aspect, final float zNear, final float zFar) throws GLException {
final float top = 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 makeFrustum(m, m_off, initM, left, right, bottom, top, zNear, zFar);
}
/**
* Make given matrix the look-at matrix based on given parameters.
*
* Consist out of two matrix multiplications:
*
* R = L x T,
* with L for look-at matrix and
* T for eye translation.
*
* Result R can be utilized for modelview multiplication, i.e.
* M = M x R,
* with M being the modelview matrix.
*
*
*
* All matrix fields are set.
*
* @param m 4x4 matrix in column-major order, result only
* @param m_offset offset in given array m, i.e. start of the 4x4 matrix
* @param eye 3 component eye vector
* @param eye_offset
* @param center 3 component center vector
* @param center_offset
* @param up 3 component up vector
* @param up_offset
* @param mat4Tmp temp float[16] storage
* @return given matrix m
for chaining
*/
public static float[] makeLookAt(final float[] m, final int m_offset,
final float[] eye, final int eye_offset,
final float[] center, final int center_offset,
final float[] up, final int up_offset,
final float[] mat4Tmp) {
final int forward_off = 0;
final int side_off = 3;
final int up2_off = 6;
// forward!
mat4Tmp[0] = center[0+center_offset] - eye[0+eye_offset];
mat4Tmp[1] = center[1+center_offset] - eye[1+eye_offset];
mat4Tmp[2] = center[2+center_offset] - eye[2+eye_offset];
VectorUtil.normalizeVec3(mat4Tmp); // normalize forward
/* Side = forward x up */
VectorUtil.crossVec3(mat4Tmp, side_off, mat4Tmp, forward_off, up, up_offset);
VectorUtil.normalizeVec3(mat4Tmp, side_off); // normalize side
/* Recompute up as: up = side x forward */
VectorUtil.crossVec3(mat4Tmp, up2_off, mat4Tmp, side_off, mat4Tmp, forward_off);
m[m_offset + 0 * 4 + 0] = mat4Tmp[0+side_off]; // side
m[m_offset + 0 * 4 + 1] = mat4Tmp[0+up2_off]; // up2
m[m_offset + 0 * 4 + 2] = -mat4Tmp[0]; // forward
m[m_offset + 0 * 4 + 3] = 0;
m[m_offset + 1 * 4 + 0] = mat4Tmp[1+side_off]; // side
m[m_offset + 1 * 4 + 1] = mat4Tmp[1+up2_off]; // up2
m[m_offset + 1 * 4 + 2] = -mat4Tmp[1]; // forward
m[m_offset + 1 * 4 + 3] = 0;
m[m_offset + 2 * 4 + 0] = mat4Tmp[2+side_off]; // side
m[m_offset + 2 * 4 + 1] = mat4Tmp[2+up2_off]; // up2
m[m_offset + 2 * 4 + 2] = -mat4Tmp[2]; // forward
m[m_offset + 2 * 4 + 3] = 0;
m[m_offset + 3 * 4 + 0] = 0;
m[m_offset + 3 * 4 + 1] = 0;
m[m_offset + 3 * 4 + 2] = 0;
m[m_offset + 3 * 4 + 3] = 1;
makeTranslation(mat4Tmp, true, -eye[0+eye_offset], -eye[1+eye_offset], -eye[2+eye_offset]);
multMatrix(m, m_offset, mat4Tmp, 0);
return m;
}
/**
* Make given matrix the pick matrix based on given parameters.
*
* Traditional gluPickMatrix
implementation.
*
*
* Consist out of two matrix multiplications:
*
* R = T x S,
* with T for viewport translation matrix and
* S for viewport scale matrix.
*
* Result R can be utilized for projection multiplication, i.e.
* P = P x R,
* with P being the projection matrix.
*
*
*
* To effectively use the generated pick matrix for picking,
* call {@link #makePick(float[], int, float, float, float, float, int[], int, float[]) makePick}
* and multiply a {@link #makePerspective(float[], int, boolean, float, float, float, float) custom perspective matrix}
* by this pick matrix. Then you may load the result onto the perspective matrix stack.
*
*
* All matrix fields are set.
*
* @param m 4x4 matrix in column-major order, result only
* @param m_offset offset in given array m, i.e. start of the 4x4 matrix
* @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
* @param mat4Tmp temp float[16] storage
* @return given matrix m
for chaining or null
if either delta value is <= zero.
*/
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,
final float[] mat4Tmp) {
if (deltaX <= 0 || deltaY <= 0) {
return null;
}
/* Translate and scale the picked region to the entire window */
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, mat4Tmp);
return m;
}
/**
* 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 mres for chaining
*/
public static float[] transposeMatrix(final float[] msrc, final float[] mres) {
mres[0] = msrc[0*4];
mres[1] = msrc[1*4];
mres[2] = msrc[2*4];
mres[3] = msrc[3*4];
final int i4_1 = 1*4;
mres[0+i4_1] = msrc[1+0*4];
mres[1+i4_1] = msrc[1+1*4];
mres[2+i4_1] = msrc[1+2*4];
mres[3+i4_1] = msrc[1+3*4];
final int i4_2 = 2*4;
mres[0+i4_2] = msrc[2+0*4];
mres[1+i4_2] = msrc[2+1*4];
mres[2+i4_2] = msrc[2+2*4];
mres[3+i4_2] = msrc[2+3*4];
final int i4_3 = 3*4;
mres[0+i4_3] = msrc[3+0*4];
mres[1+i4_3] = msrc[3+1*4];
mres[2+i4_3] = msrc[3+2*4];
mres[3+i4_3] = msrc[3+3*4];
return mres;
}
/**
* 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) {
float a11 = m[ 1+4*1 ];
float a21 = m[ 2+4*1 ];
float a31 = m[ 3+4*1 ];
float a12 = m[ 1+4*2 ];
float a22 = m[ 2+4*2 ];
float a32 = m[ 3+4*2 ];
float a13 = m[ 1+4*3 ];
float a23 = m[ 2+4*3 ];
float a33 = m[ 3+4*3 ];
float ret = 0;
ret += m[ 0 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
a11 = m[ 1+4*0 ];
a21 = m[ 2+4*0 ];
a31 = m[ 3+4*0 ];
ret -= m[ 0+4*1 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
a12 = m[ 1+4*1 ];
a22 = m[ 2+4*1 ];
a32 = m[ 3+4*1 ];
ret += m[ 0+4*2 ] * ( + a11*(a22*a33 - a23*a32) - a12*(a21*a33 - a23*a31) + a13*(a21*a32 - a22*a31));
a13 = m[ 1+4*2 ];
a23 = m[ 2+4*2 ];
a33 = m[ 3+4*2 ];
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.
*
* Returns null
if inversion is not possible,
* e.g. matrix is singular due to a bad matrix.
*
*
* @param msrc 4x4 matrix in column-major order, the source
* @param mres 4x4 matrix in column-major order, the result - may be msrc
(in-place)
* @return given result matrix mres for chaining if successful, otherwise null
. See above.
*/
public static float[] invertMatrix(final float[] msrc, final float[] mres) {
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]*scale;
final float a21 = msrc[1+4*0]*scale;
final float a31 = msrc[2+4*0]*scale;
final float a41 = msrc[3+4*0]*scale;
final float a12 = msrc[0+4*1]*scale;
final float a22 = msrc[1+4*1]*scale;
final float a32 = msrc[2+4*1]*scale;
final float a42 = msrc[3+4*1]*scale;
final float a13 = msrc[0+4*2]*scale;
final float a23 = msrc[1+4*2]*scale;
final float a33 = msrc[2+4*2]*scale;
final float a43 = msrc[3+4*2]*scale;
final float a14 = msrc[0+4*3]*scale;
final float a24 = msrc[1+4*3]*scale;
final float a34 = msrc[2+4*3]*scale;
final float a44 = msrc[3+4*3]*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;
}
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;
}
/**
* Map object coordinates to window coordinates.
*
* Traditional gluProject
implementation.
*
*
* @param objx
* @param objy
* @param objz
* @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 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 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;
vec4Tmp1[3] = 1.0f;
// vec4Tmp2 = Mv * o
// vec4Tmp1 = P * vec4Tmp2
// vec4Tmp1 = P * ( Mv * o )
// vec4Tmp1 = P * Mv * o
multMatrixVec(modelMatrix, modelMatrix_offset, vec4Tmp1, 0, vec4Tmp2, 0);
multMatrixVec(projMatrix, projMatrix_offset, vec4Tmp2, 0, vec4Tmp1, 0);
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.
*
* Traditional gluUnProject
implementation.
*
*
* @param winx
* @param winy
* @param winz
* @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 obj_pos 3 component object coordinate, the result
* @param obj_pos_offset
* @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 infinity due to zero z)
*/
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);
// mat4Tmp1 = Inv(P x Mv)
if ( null == invertMatrix(mat4Tmp1, mat4Tmp1) ) {
return false;
}
mat4Tmp2[0] = winx;
mat4Tmp2[1] = winy;
mat4Tmp2[2] = winz;
mat4Tmp2[3] = 1.0f;
// Map x and y from window coordinates
mat4Tmp2[0] = (mat4Tmp2[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset];
mat4Tmp2[1] = (mat4Tmp2[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset];
// Map to range -1 to 1
mat4Tmp2[0] = mat4Tmp2[0] * 2 - 1;
mat4Tmp2[1] = mat4Tmp2[1] * 2 - 1;
mat4Tmp2[2] = mat4Tmp2[2] * 2 - 1;
final int raw_off = 4;
// object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2
multMatrixVec(mat4Tmp1, 0, mat4Tmp2, 0, mat4Tmp2, raw_off);
if (mat4Tmp2[3+raw_off] == 0.0) {
return false;
}
mat4Tmp2[3+raw_off] = 1.0f / mat4Tmp2[3+raw_off];
obj_pos[0+obj_pos_offset] = mat4Tmp2[0+raw_off] * mat4Tmp2[3+raw_off];
obj_pos[1+obj_pos_offset] = mat4Tmp2[1+raw_off] * mat4Tmp2[3+raw_off];
obj_pos[2+obj_pos_offset] = mat4Tmp2[2+raw_off] * mat4Tmp2[3+raw_off];
return true;
}
/**
* Map window coordinates to object coordinates.
*
* Traditional gluUnProject4
implementation.
*
*
* @param winx
* @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 near
* @param far
* @param obj_pos 4 component object coordinate, the result
* @param obj_pos_offset
* @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 infinity due to zero z)
*/
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);
// mat4Tmp1 = Inv(P x Mv)
if ( null == invertMatrix(mat4Tmp1, mat4Tmp1) ) {
return false;
}
mat4Tmp2[0] = winx;
mat4Tmp2[1] = winy;
mat4Tmp2[2] = winz;
mat4Tmp2[3] = clipw;
// Map x and y from window coordinates
mat4Tmp2[0] = (mat4Tmp2[0] - viewport[0+viewport_offset]) / viewport[2+viewport_offset];
mat4Tmp2[1] = (mat4Tmp2[1] - viewport[1+viewport_offset]) / viewport[3+viewport_offset];
mat4Tmp2[2] = (mat4Tmp2[2] - near) / (far - near);
// Map to range -1 to 1
mat4Tmp2[0] = mat4Tmp2[0] * 2 - 1;
mat4Tmp2[1] = mat4Tmp2[1] * 2 - 1;
mat4Tmp2[2] = mat4Tmp2[2] * 2 - 1;
final int raw_off = 4;
// object raw coords = Inv(P x Mv) * winPos -> mat4Tmp2
multMatrixVec(mat4Tmp1, 0, mat4Tmp2, 0, mat4Tmp2, raw_off);
if (mat4Tmp2[3+raw_off] == 0.0) {
return false;
}
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];
obj_pos[3+obj_pos_offset] = mat4Tmp2[3+raw_off];
return true;
}
/**
* 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
*/
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];
final float b30 = b[b_off+3+0*4];
final float b01 = b[b_off+0+1*4];
final float b11 = b[b_off+1+1*4];
final float b21 = b[b_off+2+1*4];
final float b31 = b[b_off+3+1*4];
final float b02 = b[b_off+0+2*4];
final float b12 = b[b_off+1+2*4];
final float b22 = b[b_off+2+2*4];
final float b32 = b[b_off+3+2*4];
final float b03 = b[b_off+0+3*4];
final float b13 = b[b_off+1+3*4];
final float b23 = b[b_off+2+3*4];
final float b33 = b[b_off+3+3*4];
float ai0=a[a_off+ 0*4]; // row-0 of a
float ai1=a[a_off+ 1*4];
float ai2=a[a_off+ 2*4];
float ai3=a[a_off+ 3*4];
d[d_off+ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[d_off+ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[d_off+ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[d_off+ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[a_off+1+0*4]; // row-1 of a
ai1=a[a_off+1+1*4];
ai2=a[a_off+1+2*4];
ai3=a[a_off+1+3*4];
d[d_off+1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[d_off+1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[d_off+1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[d_off+1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[a_off+2+0*4]; // row-2 of a
ai1=a[a_off+2+1*4];
ai2=a[a_off+2+2*4];
ai3=a[a_off+2+3*4];
d[d_off+2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[d_off+2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[d_off+2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[d_off+2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[a_off+3+0*4]; // row-3 of a
ai1=a[a_off+3+1*4];
ai2=a[a_off+3+2*4];
ai3=a[a_off+3+3*4];
d[d_off+3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
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 ;
}
/**
* 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 d for chaining
*/
public static float[] multMatrix(final float[] a, final float[] b, final float[] d) {
final float b00 = b[0+0*4];
final float b10 = b[1+0*4];
final float b20 = b[2+0*4];
final float b30 = b[3+0*4];
final float b01 = b[0+1*4];
final float b11 = b[1+1*4];
final float b21 = b[2+1*4];
final float b31 = b[3+1*4];
final float b02 = b[0+2*4];
final float b12 = b[1+2*4];
final float b22 = b[2+2*4];
final float b32 = b[3+2*4];
final float b03 = b[0+3*4];
final float b13 = b[1+3*4];
final float b23 = b[2+3*4];
final float b33 = b[3+3*4];
float ai0=a[ 0*4]; // row-0 of a
float ai1=a[ 1*4];
float ai2=a[ 2*4];
float ai3=a[ 3*4];
d[ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[1+0*4]; // row-1 of a
ai1=a[1+1*4];
ai2=a[1+2*4];
ai3=a[1+3*4];
d[1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[2+0*4]; // row-2 of a
ai1=a[2+1*4];
ai2=a[2+2*4];
ai3=a[2+3*4];
d[2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[3+0*4]; // row-3 of a
ai1=a[3+1*4];
ai2=a[3+2*4];
ai3=a[3+3*4];
d[3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
d[3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
d[3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
d[3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
return d;
}
/**
* Multiply matrix: [a] = [a] x [b]
* @param a 4x4 matrix in column-major order (also result)
* @param b 4x4 matrix in column-major order
*/
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];
final float b30 = b[b_off+3+0*4];
final float b01 = b[b_off+0+1*4];
final float b11 = b[b_off+1+1*4];
final float b21 = b[b_off+2+1*4];
final float b31 = b[b_off+3+1*4];
final float b02 = b[b_off+0+2*4];
final float b12 = b[b_off+1+2*4];
final float b22 = b[b_off+2+2*4];
final float b32 = b[b_off+3+2*4];
final float b03 = b[b_off+0+3*4];
final float b13 = b[b_off+1+3*4];
final float b23 = b[b_off+2+3*4];
final float b33 = b[b_off+3+3*4];
float ai0=a[a_off+ 0*4]; // row-0 of a
float ai1=a[a_off+ 1*4];
float ai2=a[a_off+ 2*4];
float ai3=a[a_off+ 3*4];
a[a_off+ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[a_off+ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[a_off+ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[a_off+ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[a_off+1+0*4]; // row-1 of a
ai1=a[a_off+1+1*4];
ai2=a[a_off+1+2*4];
ai3=a[a_off+1+3*4];
a[a_off+1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[a_off+1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[a_off+1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[a_off+1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[a_off+2+0*4]; // row-2 of a
ai1=a[a_off+2+1*4];
ai2=a[a_off+2+2*4];
ai3=a[a_off+2+3*4];
a[a_off+2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[a_off+2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[a_off+2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[a_off+2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[a_off+3+0*4]; // row-3 of a
ai1=a[a_off+3+1*4];
ai2=a[a_off+3+2*4];
ai3=a[a_off+3+3*4];
a[a_off+3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
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 ;
}
/**
* 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 a for chaining
*/
public static float[] multMatrix(final float[] a, final float[] b) {
final float b00 = b[0+0*4];
final float b10 = b[1+0*4];
final float b20 = b[2+0*4];
final float b30 = b[3+0*4];
final float b01 = b[0+1*4];
final float b11 = b[1+1*4];
final float b21 = b[2+1*4];
final float b31 = b[3+1*4];
final float b02 = b[0+2*4];
final float b12 = b[1+2*4];
final float b22 = b[2+2*4];
final float b32 = b[3+2*4];
final float b03 = b[0+3*4];
final float b13 = b[1+3*4];
final float b23 = b[2+3*4];
final float b33 = b[3+3*4];
float ai0=a[ 0*4]; // row-0 of a
float ai1=a[ 1*4];
float ai2=a[ 2*4];
float ai3=a[ 3*4];
a[ 0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[ 1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[ 2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[ 3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[1+0*4]; // row-1 of a
ai1=a[1+1*4];
ai2=a[1+2*4];
ai3=a[1+3*4];
a[1+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[1+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[1+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[1+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[2+0*4]; // row-2 of a
ai1=a[2+1*4];
ai2=a[2+2*4];
ai3=a[2+3*4];
a[2+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[2+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[2+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[2+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
ai0=a[3+0*4]; // row-3 of a
ai1=a[3+1*4];
ai2=a[3+2*4];
ai3=a[3+3*4];
a[3+0*4] = ai0 * b00 + ai1 * b10 + ai2 * b20 + ai3 * b30 ;
a[3+1*4] = ai0 * b01 + ai1 * b11 + ai2 * b21 + ai3 * b31 ;
a[3+2*4] = ai0 * b02 + ai1 * b12 + ai2 * b22 + ai3 * b32 ;
a[3+3*4] = ai0 * b03 + ai1 * b13 + ai2 * b23 + ai3 * b33 ;
return a;
}
/**
* 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
*/
public static void multMatrix(final FloatBuffer a, final FloatBuffer b, final float[] d) {
final int a_off = a.position();
final int b_off = b.position();
for (int i = 0; i < 4; i++) {
// one row in column-major order
final int a_off_i = a_off+i;
final float ai0=a.get(a_off_i+0*4), ai1=a.get(a_off_i+1*4), ai2=a.get(a_off_i+2*4), ai3=a.get(a_off_i+3*4); // row-i of a
d[i+0*4] = ai0 * b.get(b_off+0+0*4) + ai1 * b.get(b_off+1+0*4) + ai2 * b.get(b_off+2+0*4) + ai3 * b.get(b_off+3+0*4) ;
d[i+1*4] = ai0 * b.get(b_off+0+1*4) + ai1 * b.get(b_off+1+1*4) + ai2 * b.get(b_off+2+1*4) + ai3 * b.get(b_off+3+1*4) ;
d[i+2*4] = ai0 * b.get(b_off+0+2*4) + ai1 * b.get(b_off+1+2*4) + ai2 * b.get(b_off+2+2*4) + ai3 * b.get(b_off+3+2*4) ;
d[i+3*4] = ai0 * b.get(b_off+0+3*4) + ai1 * b.get(b_off+1+3*4) + ai2 * b.get(b_off+2+3*4) + ai3 * b.get(b_off+3+3*4) ;
}
}
/**
* Multiply matrix: [a] = [a] x [b]
* @param a 4x4 matrix in column-major order (also result)
* @param b 4x4 matrix in column-major order
*/
public static void multMatrix(final FloatBuffer a, final FloatBuffer b) {
final int a_off = a.position();
final int b_off = b.position();
for (int i = 0; i < 4; i++) {
// one row in column-major order
final int a_off_i = a_off+i;
final float ai0=a.get(a_off_i+0*4), ai1=a.get(a_off_i+1*4), ai2=a.get(a_off_i+2*4), ai3=a.get(a_off_i+3*4); // row-i of a
a.put(a_off_i+0*4 , ai0 * b.get(b_off+0+0*4) + ai1 * b.get(b_off+1+0*4) + ai2 * b.get(b_off+2+0*4) + ai3 * b.get(b_off+3+0*4) );
a.put(a_off_i+1*4 , ai0 * b.get(b_off+0+1*4) + ai1 * b.get(b_off+1+1*4) + ai2 * b.get(b_off+2+1*4) + ai3 * b.get(b_off+3+1*4) );
a.put(a_off_i+2*4 , ai0 * b.get(b_off+0+2*4) + ai1 * b.get(b_off+1+2*4) + ai2 * b.get(b_off+2+2*4) + ai3 * b.get(b_off+3+2*4) );
a.put(a_off_i+3*4 , ai0 * b.get(b_off+0+3*4) + ai1 * b.get(b_off+1+3*4) + ai2 * b.get(b_off+2+3*4) + ai3 * b.get(b_off+3+3*4) );
}
}
/**
* @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 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 ];
final int m_in_off_1 = 1+m_in_off;
v_out[1 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_1] + v_in[1+v_in_off] * m_in[1*4+m_in_off_1] +
v_in[2+v_in_off] * m_in[2*4+m_in_off_1] + v_in[3+v_in_off] * m_in[3*4+m_in_off_1];
final int m_in_off_2 = 2+m_in_off;
v_out[2 + v_out_off] = v_in[0+v_in_off] * m_in[0*4+m_in_off_2] + v_in[1+v_in_off] * m_in[1*4+m_in_off_2] +
v_in[2+v_in_off] * m_in[2*4+m_in_off_2] + v_in[3+v_in_off] * m_in[3*4+m_in_off_2];
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];
}
/**
* @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];
}
/**
* @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
* @return given result vector v_out for chaining
*/
public static float[] multMatrixVec(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 ] + v_in[3] * 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] + v_in[3] * 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] + v_in[3] * m_in[3*4+2];
v_out[3] = v_in[0] * m_in[0*4+3] + v_in[1] * m_in[1*4+3] +
v_in[2] * m_in[2*4+3] + v_in[3] * m_in[3*4+3];
return v_out;
}
/**
* @param m_in 4x4 matrix in column-major order
* @param v_in 4-component column-vector
* @param v_out m_in * v_in
*/
public static void multMatrixVec(final FloatBuffer m_in, final float[] v_in, final float[] v_out) {
final int m_in_off = m_in.position();
for (int i = 0; i < 4; i++) {
// (one matrix row in column-major order) X (column vector)
final int i_m_in_off = i+m_in_off;
v_out[i] =
v_in[0] * m_in.get(0*4+i_m_in_off) +
v_in[1] * m_in.get(1*4+i_m_in_off) +
v_in[2] * m_in.get(2*4+i_m_in_off) +
v_in[3] * m_in.get(3*4+i_m_in_off);
}
}
/**
* 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 v_out for chaining
*/
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];
return v_out;
}
/**
* @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 mxn matrix (rows x columns)
* @param aOffset offset to a
's current position
* @param rows
* @param columns
* @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL)
* @param row row number to print
* @return matrix row string representation
*/
public static StringBuilder matrixRowToString(StringBuilder sb, final String f,
final FloatBuffer a, final int aOffset,
final int rows, final int columns, final boolean rowMajorOrder, final int row) {
if(null == sb) {
sb = new StringBuilder();
}
final int a0 = aOffset + a.position();
if(rowMajorOrder) {
for(int c=0; ca's current position
* @param rows
* @param columns
* @param rowMajorOrder if true floats are layed out in row-major-order, otherwise column-major-order (OpenGL)
* @param row row number to print
* @return matrix row string representation
*/
public static StringBuilder matrixRowToString(StringBuilder sb, final String f,
final float[] a, final int aOffset, final int rows, final int columns, final boolean rowMajorOrder, final int row) {
if(null == sb) {
sb = new StringBuilder();
}
if(rowMajorOrder) {
for(int c=0; ca'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 matrix string representation
*/
public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f,
final FloatBuffer a, final int aOffset, final int rows, final int columns, final boolean rowMajorOrder) {
if(null == sb) {
sb = new StringBuilder();
}
final String prefix = ( null == rowPrefix ) ? "" : rowPrefix;
sb.append(prefix).append("{ ");
for(int i=0; ia'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 matrix string representation
*/
public static StringBuilder matrixToString(StringBuilder sb, final String rowPrefix, final String f,
final float[] a, final int aOffset, final int rows, final int columns, final boolean rowMajorOrder) {
if(null == sb) {
sb = new StringBuilder();
}
final String prefix = ( null == rowPrefix ) ? "" : rowPrefix;
sb.append(prefix).append("{ ");
for(int i=0; i
* The machine Epsilon value is computed once.
*
*
* On a reference machine the result was {@link #EPSILON} in 23 iterations.
*
* @see #EPSILON
*/
public static float getMachineEpsilon() {
if( !machEpsilonAvail ) {
synchronized(FloatUtil.class) {
if( !machEpsilonAvail ) {
machEpsilonAvail = true;
calculateMachineEpsilonFloat();
}
}
}
return machEpsilon;
}
public static final float E = 2.7182818284590452354f;
/** The value PI, i.e. 180 degrees in radians. */
public static final float PI = 3.14159265358979323846f;
/** The value 2PI, i.e. 360 degrees in radians. */
public static final float TWO_PI = 2f * PI;
/** The value PI/2, i.e. 90 degrees in radians. */
public static final float HALF_PI = PI / 2f;
/** The value PI/4, i.e. 45 degrees in radians. */
public static final float QUARTER_PI = PI / 4f;
/** 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.
*
* Definition of machine epsilon guarantees that:
*
* 1.0f + EPSILON != 1.0f
*
* In other words: machEps is the maximum relative error of the chosen rounding procedure.
*
*
* A number can be considered zero if it is in the range (or in the set):
*
* MaybeZeroSet e ]-machEps .. machEps[ (exclusive)
*
* While comparing floating point values, machEps allows to clip the relative error:
*
* boolean isZero = afloat < EPSILON;
* boolean isNotZero = afloat >= EPSILON;
*
* boolean isEqual = abs(bfloat - afloat) < EPSILON;
* boolean isNotEqual = abs(bfloat - afloat) >= EPSILON;
*
*
* @see #isEqual(float, float, float)
* @see #isZero(float, float)
*/
public static final float EPSILON = 1.1920929E-7f; // Float.MIN_VALUE == 1.4e-45f ; double EPSILON 2.220446049250313E-16d
/**
* Inversion Epsilon, used with equals method to determine if two inverted matrices are close enough to be considered equal.
*
* Using {@value}, which is ~100 times {@link FloatUtil#EPSILON}.
*
*/
public static final float INV_DEVIANCE = 1.0E-5f; // FloatUtil.EPSILON == 1.1920929E-7f; double ALLOWED_DEVIANCE: 1.0E-8f
/**
* Return true if both values are equal w/o regarding an epsilon.
*
* Implementation considers following corner cases:
*
* - NaN == NaN
* - +Inf == +Inf
* - -Inf == -Inf
*
*
* @see #isEqual(float, float, float)
*/
public static boolean isEqualRaw(final float a, final float b) {
// Values are equal (Inf, Nan .. )
return Float.floatToIntBits(a) == Float.floatToIntBits(b);
}
/**
* Return true if both values are equal, i.e. their absolute delta < epsilon
.
*
* Implementation considers following corner cases:
*
* - NaN == NaN
* - +Inf == +Inf
* - -Inf == -Inf
*
*
* @see #EPSILON
*/
public static boolean isEqual(final float a, final float b, final float epsilon) {
if ( Math.abs(a - b) < epsilon ) {
return true;
} else {
// Values are equal (Inf, Nan .. )
return Float.floatToIntBits(a) == Float.floatToIntBits(b);
}
}
/**
* Return true if both values are equal, i.e. their absolute delta < {@link #EPSILON}.
*
* Implementation considers following corner cases:
*
* - NaN == NaN
* - +Inf == +Inf
* - -Inf == -Inf
*
*
* @see #EPSILON
*/
public static boolean isEqual(final float a, final float b) {
if ( Math.abs(a - b) < EPSILON ) {
return true;
} else {
// Values are equal (Inf, Nan .. )
return Float.floatToIntBits(a) == Float.floatToIntBits(b);
}
}
/**
* Return true if both values are equal, i.e. their absolute delta < {@link #EPSILON}.
*
* Implementation does not consider corner cases like {@link #isEqual(float, float, float)}.
*
* @see #EPSILON
*/
public static boolean isEqual2(final float a, final float b) {
return Math.abs(a - b) < EPSILON;
}
/**
* Return true if both values are equal w/o regarding an epsilon.
*
* Implementation considers following corner cases:
*
* - NaN == NaN
* - +Inf == +Inf
* - -Inf == -Inf
* - NaN > 0
* - +Inf > -Inf
*
*
* @see #compare(float, float, float)
*/
public static int compare(final float a, final float b) {
if (a < b) {
return -1; // Neither is NaN, a is smaller
}
if (a > b) {
return 1; // Neither is NaN, a is larger
}
final int aBits = Float.floatToIntBits(a);
final int bBits = Float.floatToIntBits(b);
if( aBits == bBits ) {
return 0; // Values are equal (Inf, Nan .. )
} else if( aBits < bBits ) {
return -1; // (-0.0, 0.0) or (!NaN, NaN)
} else {
return 1; // ( 0.0, -0.0) or ( NaN, !NaN)
}
}
/**
* Return true if both values are equal, i.e. their absolute delta < epsilon
.
*
* Implementation considers following corner cases:
*
* - NaN == NaN
* - +Inf == +Inf
* - -Inf == -Inf
* - NaN > 0
* - +Inf > -Inf
*
*
* @see #EPSILON
*/
public static int compare(final float a, final float b, final float epsilon) {
if ( Math.abs(a - b) < epsilon ) {
return 0;
} else {
return compare(a, b);
}
}
/**
* Return true if value is zero, i.e. it's absolute value < epsilon
.
* @see #EPSILON
*/
public static boolean isZero(final float a, final float epsilon) {
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;
}
/**
* Invokes {@link Math#abs(float)}
* @param a float to process
* @return absolute value of {@code a}
* @deprecated use {@link Math#abs(float)} directly
*/
@Deprecated
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); }
public static float sin(final float a) { return (float) java.lang.Math.sin(a); }
public static float asin(final float a) { return (float) java.lang.Math.asin(a); }
public static float cos(final float a) { return (float) java.lang.Math.cos(a); }
public static float acos(final float a) { return (float) java.lang.Math.acos(a); }
public static float tan(final float a) { return (float) java.lang.Math.tan(a); }
public static float atan(final float a) { return (float) java.lang.Math.atan(a); }
public static float atan2(final float y, final float x) { return (float) java.lang.Math.atan2(y, x); }
public static float sqrt(final float a) { return (float) java.lang.Math.sqrt(a); }
/**
* Returns resolution of Z buffer of given parameter,
* see Love Your Z-Buffer.
*
* return z * z / ( zNear * (1<<zBits) - z )
*
* Examples:
*
* 1.5256461E-4 = 16 zBits, -0.2 zDist, 0.1 zNear
* 6.1033297E-6 = 16 zBits, -1.0 zDist, 0.1 zNear
*
* @param zBits number of bits of Z precision, i.e. z-buffer depth
* @param z distance from the eye to the object
* @param zNear distance from eye to near clip plane
* @return smallest resolvable Z separation at this range.
*/
public static float getZBufferEpsilon(final int zBits, final float z, final float zNear) {
return z * z / ( zNear * ( 1 << zBits ) - z );
}
/**
* Returns Z buffer value of given parameter,
* see Love Your Z-Buffer.
*
* float a = zFar / ( zFar - zNear )
* float b = zFar * zNear / ( zNear - zFar )
* return (int) ( (1<<zBits) * ( a + b / z ) )
*
* @param zBits number of bits of Z precision, i.e. z-buffer depth
* @param z distance from the eye to the object
* @param zNear distance from eye to near clip plane
* @param zFar distance from eye to far clip plane
* @return z buffer value
*/
public static int getZBufferValue(final int zBits, final float z, final float zNear, final float zFar) {
final float a = zFar / ( zFar - zNear );
final float b = zFar * zNear / ( zNear - zFar );
return (int) ( (1<