From 5d6e8a367c03644740187e500c6de5d3ac039d5e Mon Sep 17 00:00:00 2001
From: Sven Gothel
+ * Unlike {@link com.jogamp.opengl.util.PMVMatrix PMVMatrix}, this class doesn't implement
+ * {@link com.jogamp.opengl.fixedfunc.GLMatrixFunc GLMatrixFunc} and is OpenGL agnostic.
+ *
+ * This is the second implementation of `PMVMatrix4f` using
+ * direct {@link Matrix4f}, {@link Vec4f} and {@link Vec3f} math operations instead of `float[]`
+ * via {@link com.jogamp.math.FloatUtil FloatUtil}.
+ *
+ * PMVMatrix4f provides the {@link #getMvi() inverse modelview matrix (Mvi)} and
+ * {@link #getMvit() 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 #rotateMv(Quaternion)}
+ * users have to call {@link #update()} before using Mvi and Mvit.
+ *
+ * All matrices are provided in column-major order,
+ * as specified in the OpenGL fixed function pipeline, i.e. compatibility profile.
+ * See {@link Matrix4f}.
+ *
+ * PMVMatrix4f can supplement {@link com.jogamp.opengl.GL2ES2 GL2ES2} applications w/ the
+ * lack of the described matrix functionality.
+ *
+ * The {@link SyncBuffer} abstraction is provided, e.g. {@link #getSyncPMvMvi()},
+ * to synchronize the respective {@link Matrix4f matrices} with the `float[]` backing store.
+ * The latter is represents the data to {@link com.jogamp.opengl.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 com.jogamp.opengl.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.
+ *
+ * PMVMatrix4f 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 com.jogamp.opengl.GLUniformData} object via e.g. {@link com.jogamp.opengl.GLUniformData#GLUniformData(String, int, int, SyncBuffer)}.
+ *
+ * All {@link Matrix4f matrix} {@link SyncBuffer}'s backing store 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.
+ *
+ * {@link Matrix4f} {@link SyncBuffer}'s Backing-Store Notes:
+ * Matrix storage details
+ *
+ *
+ *
+ * This constructor only sets up an instance w/o additional {@link #INVERSE_MODELVIEW} or {@link #INVERSE_TRANSPOSED_MODELVIEW}. + *
+ *+ * 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. + *
+ * @see #PMVMatrix4f(int) + */ + public PMVMatrix4f() { + this(0); + } + + /** + * Creates an instance of PMVMatrix4f. + *+ * Additional derived matrices can be requested via `derivedMatrices`, i.e. + * - {@link #INVERSE_MODELVIEW} + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} + *
+ *+ * 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. + *
+ * @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 PMVMatrix4f(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 + final FloatBuffer bufP = Buffers.slice2Float(matrixStore, mP_offset, 1*16); // P + syncP = new SyncBuffer0(matP, bufP); // mP_offset + + final FloatBuffer bufMv = Buffers.slice2Float(matrixStore, mMv_offset, 1*16); // Mv + syncMv = new SyncBuffer1(matMv, bufMv, mMv_offset); + + final FloatBuffer bufP_Mv = Buffers.slice2Float(matrixStore, mP_offset, 2*16); // P + Mv + syncP_Mv = new SyncBufferN(new Matrix4f[] { matP, matMv }, bufP_Mv, mP_offset); + + final FloatBuffer bufTex = Buffers.slice2Float(matrixStore, mTex_offset, 1*16); // T + syncT = new SyncBuffer1(matTex, bufTex, mTex_offset); + + if( null != matMvi ) { + final FloatBuffer bufMvi = Buffers.slice2Float(matrixStore, mMvi_offset, 1*16); // Mvi + final FloatBuffer 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 { + syncMvi = null; + syncP_Mv_Mvi = null; + } + if( null != matMvit ) { + final FloatBuffer bufMvit = Buffers.slice2Float(matrixStore, mMvit_offset, 1*16); // Mvit + final FloatBuffer 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 { + 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 Matrix4f#loadIdentity()} on all matrices and resets all internal states. + */ + public void reset() { + matP.loadIdentity(); + matMv.loadIdentity(); + matTex.loadIdentity(); + + modifiedBits = MODIFIED_ALL; + dirtyBits = requestBits | MANUAL_BITS; + } + + // + // Temporary storage access for efficiency + // + + + /** + * Return the second temporary Matrix4f exposed to be reused for efficiency. + *+ * Temporary storage is only used by this class within single method calls, + * hence has no side-effects. + *
+ */ + protected 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). + *+ * Consider using {@link #setTextureDirty()} if modifying the returned {@link Matrix4f}. + *
+ *+ * See matrix storage details. + *
+ */ + public final Matrix4f getT() { + return matTex; + } + + /** + * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_TEXTURE_MATRIX texture matrix} (T). + *+ * See matrix storage details. + *
+ */ + public final SyncMatrix4f getSyncT() { + return syncT; + } + + /** + * Returns the {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). + *+ * Consider using {@link #setProjectionDirty()} if modifying the returned {@link Matrix4f}. + *
+ *+ * See matrix storage details. + *
+ */ + public final Matrix4f getP() { + return matP; + } + + /** + * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). + *+ * See matrix storage details. + *
+ */ + public final SyncMatrix4f getSyncP() { + return syncP; + } + + /** + * Returns the {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). + *+ * Consider using {@link #setModelviewDirty()} if modifying the returned {@link Matrix4f}. + *
+ *+ * See matrix storage details. + *
+ */ + public final Matrix4f getMv() { + return matMv; + } + + /** + * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). + *+ * See matrix storage details. + *
+ */ + public final SyncMatrix4f getSyncMv() { + return syncMv; + } + + /** + * Returns {@link SyncMatrices4f} of 2 matrices within one FloatBuffer: {@link #getP() P} and {@link #getMv() Mv}. + *+ * See matrix storage details. + *
+ */ + public final SyncMatrices4f getSyncPMv() { + return syncP_Mv; + } + + /** + * Returns the inverse {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvi) if requested. + *+ * See matrix storage details. + *
+ * @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix4f(int)}. + */ + public final Matrix4f getMvi() { + if( 0 == ( INVERSE_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + updateImpl(false); + return matMvi; + } + + /** + * Returns the {@link SyncMatrix} of inverse {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvi) if requested. + *+ * See matrix storage details. + *
+ * @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix4f(int)}. + */ + public final SyncMatrix4f getSyncMvi() { + 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. + *+ * See matrix storage details. + *
+ * @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix4f(int)}. + */ + public final Matrix4f getMvit() { + if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + updateImpl(false); + return matMvit; + } + + /** + * Returns the {@link SyncMatrix} of inverse transposed {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvit) if requested. + *+ * See matrix storage details. + *
+ * @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix4f(int)}. + */ + public final SyncMatrix4f getSyncMvit() { + 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 #getP() P}, {@link #getMv() Mv} and {@link #getMvi() Mvi} if requested. + *+ * See matrix storage details. + *
+ * @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix4f(int)}. + */ + public final SyncMatrices4f getSyncPMvMvi() { + if( 0 == ( INVERSE_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + return syncP_Mv_Mvi; + } + + /** + * Returns {@link SyncMatrices4f} of 4 matrices within one FloatBuffer: {@link #getP() P}, {@link #getMv() Mv}, {@link #getMvi() Mvi} and {@link #getMvit() Mvit} if requested. + *+ * See matrix storage details. + *
+ * @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix4f(int)}. + */ + public final SyncMatrices4f getSyncPMvMviMvit() { + if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { + throw new IllegalArgumentException("Not requested in ctor"); + } + return syncP_Mv_Mvi_Mvit; + } + + // + // Basic Matrix4f, Vec3f and Vec4f operations similar to GLMatrixFunc + // + + /** + * Returns multiplication result of {@link #getP() P} and {@link #getMv() Mv} matrix, i.e. + *+ * result = P x Mv + *+ * @param result 4x4 matrix storage for result + * @return given result matrix for chaining + */ + public final Matrix4f getMulPMv(final Matrix4f result) { + return result.mul(matP, matMv); + } + + /** + * Returns multiplication result of {@link #getMv() Mv} and {@link #getP() P} matrix, i.e. + *
+ * result = Mv x P + *+ * @param result 4x4 matrix storage for result + * @return given result matrix for chaining + */ + public final Matrix4f getMulMvP(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 mulWithMv(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 mulWithMv(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 mulWithMv(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 mulWithMv(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 mulWithP(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 mulWithP(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 mulWithP(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 mulWithP(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 mulWithPMv(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 mulWithPMv(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 mulWithPMv(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 Vec3f mulWithPMv(final Vec3f v_inout) { + return matP.mulVec3f( matMv.mulVec3f( v_inout ) ); + } + + // + // GLMatrixFunc alike functionality + // + + /** + * Load the {@link #getMv() modelview matrix} with the provided values. + */ + public final PMVMatrix4f loadMv(final float[] values, final int offset) { + matMv.load(values, offset); + setModelviewDirty(); + return this; + } + /** + * Load the {@link #getMv() modelview matrix} with the provided values. + */ + public final PMVMatrix4f loadMv(final java.nio.FloatBuffer m) { + final int spos = m.position(); + matMv.load(m); + setModelviewDirty(); + m.position(spos); + return this; + } + /** + * Load the {@link #getMv() modelview matrix} with the values of the given {@link Matrix4f}. + */ + public final PMVMatrix4f loadMv(final Matrix4f m) { + matMv.load(m); + setModelviewDirty(); + return this; + } + /** + * Load the {@link #getMv() modelview matrix} with the values of the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + */ + public final PMVMatrix4f loadMv(final Quaternion quat) { + matMv.setToRotation(quat); + setModelviewDirty(); + return this; + } + + /** + * Load the {@link #getP() projection matrix} with the provided values. + */ + public final PMVMatrix4f loadP(final float[] values, final int offset) { + matP.load(values, offset); + setProjectionDirty(); + return this; + } + /** + * Load the {@link #getP() projection matrix} with the provided values. + */ + public final PMVMatrix4f loadP(final java.nio.FloatBuffer m) { + final int spos = m.position(); + matP.load(m); + setProjectionDirty(); + m.position(spos); + return this; + } + /** + * Load the {@link #getP() projection matrix} with the values of the given {@link Matrix4f}. + */ + public final PMVMatrix4f loadP(final Matrix4f m) { + matP.load(m); + setProjectionDirty(); + return this; + } + /** + * Load the {@link #getP() projection matrix} with the values of the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + */ + public final PMVMatrix4f loadP(final Quaternion quat) { + matP.setToRotation(quat); + setProjectionDirty(); + return this; + } + + /** + * Load the {@link #getT() texture matrix} with the provided values. + */ + public final PMVMatrix4f loadT(final float[] values, final int offset) { + matTex.load(values, offset); + setTextureDirty(); + return this; + } + /** + * Load the {@link #getT() texture matrix} with the provided values. + */ + public final PMVMatrix4f loadT(final java.nio.FloatBuffer m) { + final int spos = m.position(); + matTex.load(m); + setTextureDirty(); + m.position(spos); + return this; + } + /** + * Load the {@link #getT() texture matrix} with the values of the given {@link Matrix4f}. + */ + public final PMVMatrix4f loadT(final Matrix4f m) { + matTex.load(m); + setTextureDirty(); + return this; + } + /** + * Load the {@link #getT() texture matrix} with the values of the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + */ + public final PMVMatrix4f loadT(final Quaternion quat) { + matTex.setToRotation(quat); + setTextureDirty(); + return this; + } + + /** + * Load the {@link #getMv() modelview matrix} with the values of the given {@link Matrix4f}. + */ + public final PMVMatrix4f loadMvIdentity() { + matMv.loadIdentity(); + setModelviewDirty(); + return this; + } + + /** + * Load the {@link #getP() projection matrix} with the values of the given {@link Matrix4f}. + */ + public final PMVMatrix4f loadPIdentity() { + matP.loadIdentity(); + setProjectionDirty(); + return this; + } + + /** + * Load the {@link #getT() texture matrix} with the values of the given {@link Matrix4f}. + */ + public final PMVMatrix4f loadTIdentity() { + matTex.loadIdentity(); + setTextureDirty(); + return this; + } + + /** + * Multiply the {@link #getMv() modelview matrix}: [c] = [c] x [m] + * @param m the right hand Matrix4f + * @return this instance of chaining + */ + public final PMVMatrix4f mulMv(final Matrix4f m) { + matMv.mul( m ); + setModelviewDirty(); + return this; + } + + /** + * Multiply the {@link #getP() projection matrix}: [c] = [c] x [m] + * @param m the right hand Matrix4f + * @return this instance of chaining + */ + public final PMVMatrix4f mulP(final Matrix4f m) { + matP.mul( m ); + setProjectionDirty(); + return this; + } + + /** + * Multiply the {@link #getT() texture matrix}: [c] = [c] x [m] + * @param m the right hand Matrix4f + * @return this instance of chaining + */ + public final PMVMatrix4f mulT(final Matrix4f m) { + matTex.mul( m ); + setTextureDirty(); + return this; + } + + /** + * Translate the {@link #getMv() modelview matrix}. + * @param x + * @param y + * @param z + * @return this instance of chaining + */ + public final PMVMatrix4f translateMv(final float x, final float y, final float z) { + return mulMv( mat4Tmp1.setToTranslation(x, y, z) ); + } + /** + * Translate the {@link #getMv() modelview matrix}. + * @param t translation vec3 + * @return this instance of chaining + */ + public final PMVMatrix4f translateMv(final Vec3f t) { + return mulMv( mat4Tmp1.setToTranslation(t) ); + } + + /** + * Translate the {@link #getP() projection matrix}. + * @param x + * @param y + * @param z + * @return this instance of chaining + */ + public final PMVMatrix4f translateP(final float x, final float y, final float z) { + return mulP( mat4Tmp1.setToTranslation(x, y, z) ); + } + /** + * Translate the {@link #getP() projection matrix}. + * @param t translation vec3 + * @return this instance of chaining + */ + public final PMVMatrix4f translateP(final Vec3f t) { + return mulP( mat4Tmp1.setToTranslation(t) ); + } + + /** + * Scale the {@link #getMv() modelview matrix}. + * @param x + * @param y + * @param z + * @return this instance of chaining + */ + public final PMVMatrix4f scaleMv(final float x, final float y, final float z) { + return mulMv( mat4Tmp1.setToScale(x, y, z) ); + } + /** + * Scale the {@link #getMv() modelview matrix}. + * @param s scale vec4f + * @return this instance of chaining + */ + public final PMVMatrix4f scaleMv(final Vec3f s) { + return mulMv( mat4Tmp1.setToScale(s) ); + } + + /** + * Scale the {@link #getP() projection matrix}. + * @param x + * @param y + * @param z + * @return this instance of chaining + */ + public final PMVMatrix4f scaleP(final float x, final float y, final float z) { + return mulP( mat4Tmp1.setToScale(x, y, z) ); + } + /** + * Scale the {@link #getP() projection matrix}. + * @param s scale vec4f + * @return this instance of chaining + */ + public final PMVMatrix4f scaleP(final Vec3f s) { + return mulP( mat4Tmp1.setToScale(s) ); + } + + /** + * Rotate the {@link #getMv() modelview matrix} by the given axis and angle in radians. + *
+ * Consider using {@link #rotateMv(Quaternion)} + *
+ * @param ang_rad angle in radians + * @param axis rotation axis + * @return this instance of chaining + * @see #rotateMv(Quaternion) + */ + public final PMVMatrix4f rotateMv(final float ang_rad, final float x, final float y, final float z) { + return mulMv( mat4Tmp1.setToRotationAxis(ang_rad, x, y, z) ); + } + /** + * Rotate the {@link #getMv() modelview matrix} by the given axis and angle in radians. + *+ * Consider using {@link #rotateMv(Quaternion)} + *
+ * @param ang_rad angle in radians + * @param axis rotation axis + * @return this instance of chaining + * @see #rotateMv(Quaternion) + */ + public final PMVMatrix4f rotateMv(final float ang_rad, final Vec3f axis) { + return mulMv( mat4Tmp1.setToRotationAxis(ang_rad, axis) ); + } + /** + * Rotate the {@link #getMv() modelview matrix} with the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + * @param quat the {@link Quaternion} + * @return this instance of chaining + */ + public final PMVMatrix4f rotateMv(final Quaternion quat) { + return mulMv( mat4Tmp1.setToRotation(quat) ); + } + + /** + * Rotate the {@link #getP() projection matrix} by the given axis and angle in radians. + *+ * Consider using {@link #rotateP(Quaternion)} + *
+ * @param ang_rad angle in radians + * @param axis rotation axis + * @return this instance of chaining + * @see #rotateP(Quaternion) + */ + public final PMVMatrix4f rotateP(final float ang_rad, final float x, final float y, final float z) { + return mulP( mat4Tmp1.setToRotationAxis(ang_rad, x, y, z) ); + } + /** + * Rotate the {@link #getP() projection matrix} by the given axis and angle in radians. + *+ * Consider using {@link #rotateP(Quaternion)} + *
+ * @param ang_rad angle in radians + * @param axis rotation axis + * @return this instance of chaining + * @see #rotateP(Quaternion) + */ + public final PMVMatrix4f rotateP(final float ang_rad, final Vec3f axis) { + return mulP( mat4Tmp1.setToRotationAxis(ang_rad, axis) ); + } + /** + * Rotate the {@link #getP() projection matrix} with the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. + * @param quat the {@link Quaternion} + * @return this instance of chaining + */ + public final PMVMatrix4f rotateP(final Quaternion quat) { + return mulP( mat4Tmp1.setToRotation(quat) ); + } + + /** Pop the {@link #getMv() modelview matrix} from its stack. */ + public final PMVMatrix4f popMv() { + matMv.pop(); + setModelviewDirty(); + return this; + } + /** Pop the {@link #getP() projection matrix} from its stack. */ + public final PMVMatrix4f popP() { + matP.pop(); + setProjectionDirty(); + return this; + } + /** Pop the {@link #getT() texture matrix} from its stack. */ + public final PMVMatrix4f popT() { + matTex.pop(); + setTextureDirty(); + return this; + } + /** Push the {@link #getMv() modelview matrix} to its stack, while preserving its values. */ + public final PMVMatrix4f pushMv() { + matMv.push(); + return this; + } + /** Push the {@link #getP() projection matrix} to its stack, while preserving its values. */ + public final PMVMatrix4f pushP() { + matP.push(); + return this; + } + /** Push the {@link #getT() texture matrix} to its stack, while preserving its values. */ + public final PMVMatrix4f pushT() { + matTex.push(); + return this; + } + + /** + * {@link #mulP(Matrix4f) Multiply} the {@link #getP() projection matrix} with the orthogonal matrix. + * @param left + * @param right + * @param bottom + * @param top + * @param zNear + * @param zFar + * @see Matrix4f#setToOrtho(float, float, float, float, float, float) + */ + public final void orthoP(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { + mulP( mat4Tmp1.setToOrtho(left, right, bottom, top, zNear, zFar) ); + } + + /** + * {@link #mulP(Matrix4f) Multiply} the {@link #getP() projection matrix} with the frustum matrix. + * + * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear} + * or {@code left == right}, or {@code bottom == top}. + * @see Matrix4f#setToFrustum(float, float, float, float, float, float) + */ + public final void frustumP(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) throws IllegalArgumentException { + mulP( mat4Tmp1.setToFrustum(left, right, bottom, top, zNear, zFar) ); + } + + // + // Extra functionality + // + + /** + * {@link #mulP(Matrix4f) Multiply} the {@link #getP() projection matrix} with the perspective/frustum matrix. + * + * @param fovy_rad fov angle in radians + * @param aspect aspect ratio width / height + * @param zNear + * @param zFar + * @throws IllegalArgumentException if {@code zNear <= 0} or {@code zFar <= zNear} + * @see Matrix4f#setToPerspective(float, float, float, float) + */ + public final PMVMatrix4f perspectiveP(final float fovy_rad, final float aspect, final float zNear, final float zFar) throws IllegalArgumentException { + mulP( mat4Tmp1.setToPerspective(fovy_rad, aspect, zNear, zFar) ); + return this; + } + + /** + * {@link #mulP(Matrix4f) Multiply} the {@link #getP() projection matrix} + * with the eye, object and orientation, i.e. {@link Matrix4f#setToLookAt(Vec3f, Vec3f, Vec3f, Matrix4f)}. + */ + public final PMVMatrix4f lookAtP(final Vec3f eye, final Vec3f center, final Vec3f up) { + mulP( mat4Tmp1.setToLookAt(eye, center, up, getTmp2Mat()) ); + return this; + } + + /** + * Map object coordinates to window coordinates. + *
+ * Traditional gluProject
implementation.
+ *
+ * Traditional gluUnProject
implementation.
+ *
+ * Traditional gluUnProject4
implementation.
+ *
+ * Notes for picking winz0 and winz1: + *
+ * A modified bit is set, if the corresponding matrix had been modified by a mutable operation + * since last {@link #update()} or {@link #getModifiedBits(boolean) getModifiedBits(true)} call. + *
+ * @param clear if true, clears the modified bits, otherwise leaves them untouched. + * + * @see #MODIFIED_PROJECTION + * @see #MODIFIED_MODELVIEW + * @see #MODIFIED_TEXTURE + * @see #getDirtyBits() + * @see #isReqDirty() + */ + public final int getModifiedBits(final boolean clear) { + final int r = modifiedBits; + if(clear) { + modifiedBits = 0; + } + return r; + } + + /** + * 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()} + *+ * 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 #PMVMatrix4f(int)}. + *
+ *+ * {@link #update()} clears the dirty state for the matrices and {@link #getFrustum()} for {@link #FRUSTUM}. + *
+ * + * @see #isReqDirty() + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #FRUSTUM + * @see #PMVMatrix4f(int) + * @see #getMvi() + * @see #getMvit() + * @see #getSyncPMvMvi() + * @see #getSyncPMvMviMvit() + * @see #getFrustum() + */ + public final int getDirtyBits() { + return dirtyBits; + } + + /** + * 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} + *+ * 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 #PMVMatrix4f(int)}. + *
+ *+ * {@link #update()} clears the dirty state for the matrices and {@link #getFrustum()} for {@link #FRUSTUM}. + *
+ * + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #PMVMatrix4f(int) + * @see #getMvi() + * @see #getMvit() + * @see #getSyncPMvMvi() + * @see #getSyncPMvMviMvit() + */ + public final boolean isReqDirty() { + return 0 != ( requestBits & dirtyBits ); + } + + /** + * Sets the {@link #getMv() Modelview (Mv)} matrix dirty and modified, + * i.e. adds {@link #getReqBits() requested bits} and {@link #MANUAL_BITS} to {@link #getDirtyBits() dirty bits}. + * @see #isReqDirty() + */ + public final void setModelviewDirty() { + dirtyBits |= requestBits | MANUAL_BITS ; + modifiedBits |= MODIFIED_MODELVIEW; + } + + /** + * Sets the {@link #getP() Projection (P)} matrix dirty and modified, + * i.e. adds {@link #MANUAL_BITS} to {@link #getDirtyBits() dirty bits}. + */ + public final void setProjectionDirty() { + dirtyBits |= MANUAL_BITS ; + modifiedBits |= MODIFIED_PROJECTION; + } + + /** + * Sets the {@link #getT() Texture (T)} matrix modified. + */ + public final void setTextureDirty() { + modifiedBits |= MODIFIED_TEXTURE; + } + + /** + * Returns the request bit mask, which uses bit values equal to the dirty mask + * and may contain + * - {@link #INVERSE_MODELVIEW} + * - {@link #INVERSE_TRANSPOSED_MODELVIEW} + *+ * The request bit mask is set by in the constructor {@link #PMVMatrix4f(int)}. + *
+ * + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #PMVMatrix4f(int) + * @see #getMvi() + * @see #getMvit() + * @see #getSyncPMvMvi() + * @see #getSyncPMvMviMvit() + * @see #getFrustum() + */ + public final int getReqBits() { + return requestBits; + } + + /** + * Returns the pre-multiplied projection x modelview, P x Mv. + *+ * 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}. + *
+ *+ * This pre-multipled P x Mv is considered dirty, if its corresponding + * {@link #getP() P matrix} or {@link #getMv() Mv matrix} has been modified since its last update. + *
+ * @see #update() + */ + public final Matrix4f getPMv() { + 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`. + *+ * 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}. + *
+ *+ * This pre-multipled invert(P x Mv) is considered dirty, if its corresponding + * {@link #getP() P matrix} or {@link #getMv() Mv matrix} has been modified since its last update. + *
+ * @see #update() + */ + public final Matrix4f getPMvi() { + if( 0 != ( dirtyBits & PREMUL_PMVI ) ) { + if( null == matPMvi ) { + matPMvi = new Matrix4f(); + } + final Matrix4f mPMv = getPMv(); + matPMviOK = matPMvi.invert(mPMv); + dirtyBits &= ~PREMUL_PMVI; + } + return matPMviOK ? matPMvi : null; + } + + /** + * Returns the frustum, derived from projection x modelview. + *+ * 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}. + *
+ *+ * The {@link Frustum} is considered dirty, if its corresponding + * {@link #getP() P matrix} or {@link #getMv() Mv matrix} has been modified since its last update. + *
+ * @see #update() + */ + public final Frustum getFrustum() { + if( 0 != ( dirtyBits & FRUSTUM ) ) { + if( null == frustum ) { + frustum = new Frustum(); + } + final Matrix4f mPMv = getPMv(); + frustum.updateFrustumPlanes(mPMv); + dirtyBits &= ~FRUSTUM; + } + return frustum; + } + + /** + * Update the derived {@link #getMvi() inverse modelview (Mvi)}, + * {@link #getMvit() inverse transposed modelview (Mvit)} matrices + * if they {@link #isReqDirty() are dirty} and + * requested via the constructor {@link #PMVMatrix4f(int)}.+ * The {@link Frustum} is updated only via {@link #getFrustum()} separately. + *
+ *+ * The Mvi and Mvit matrices are considered dirty, if their corresponding + * {@link #getMv() Mv matrix} has been modified since their last update. + *
+ *+ * Method is automatically called by {@link SyncMatrix4f} and {@link SyncMatrices4f} + * instances {@link SyncAction} as retrieved by e.g. {@link #getSyncMvit()}. + * This ensures an automatic update cycle if used with {@link com.jogamp.opengl.GLUniformData}. + *
+ *+ * Method may be called manually in case mutable operations has been called + * and caller operates on already fetched references, i.e. not calling + * {@link #getMvi()}, {@link #getMvit()} anymore. + *
+ *+ * 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. + *
+ * + * @return true if any matrix has been modified since last update call or + * 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 #isReqDirty() + * @see #INVERSE_MODELVIEW + * @see #INVERSE_TRANSPOSED_MODELVIEW + * @see #PMVMatrix4f(int) + * @see #getMvi() + * @see #getMvit() + * @see #getSyncPMvMvi() + * @see #getSyncPMvMviMvit() + */ + public final boolean update() { + return updateImpl(true); + } + + // + // private + // + + private final boolean updateImpl(final boolean clearModBits) { + boolean mod = 0 != modifiedBits; + if( clearModBits ) { + modifiedBits = 0; + } + if( 0 != ( requestBits & ( ( dirtyBits & ( INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW ) ) ) ) ) { // only if dirt requested & dirty + if( !matMvi.invert(matMv) ) { + throw new RuntimeException("Invalid source Mv matrix, can't compute inverse"); + } + 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; + } + + protected final Matrix4f matP; + protected final Matrix4f matMv; + protected 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 SyncMatrix4f syncP, syncMv, syncT; + private final SyncMatrix4f syncMvi, syncMvit; + private final SyncMatrices4f syncP_Mv, syncP_Mv_Mvi, syncP_Mv_Mvi_Mvit; + + protected final Matrix4f mat4Tmp1; + private Matrix4f mat4Tmp2; + + 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 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; + } + + @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); } + }; + + public SyncBuffer0(final Matrix4f m, final FloatBuffer fbuf) { super(m, fbuf); } + + @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); + } + }; + + 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; + } + + @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