/** * Copyright 2009-2023 JogAmp Community. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of JogAmp Community. */ package com.jogamp.opengl.util; import java.nio.Buffer; import java.nio.FloatBuffer; import java.nio.IntBuffer; import com.jogamp.opengl.GL; import com.jogamp.opengl.GLException; import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.opengl.GLUniformData; import com.jogamp.common.nio.Buffers; import com.jogamp.opengl.math.FloatUtil; import com.jogamp.opengl.math.Matrix4f; import com.jogamp.opengl.math.Quaternion; import com.jogamp.opengl.math.Ray; import com.jogamp.opengl.math.Recti; import com.jogamp.opengl.math.Vec3f; import com.jogamp.opengl.math.Vec4f; import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.math.geom.Frustum; /** * PMVMatrix implements a subset of the fixed function pipeline * regarding the projection (P), modelview (Mv) and texture (T) matrix operations, * which is specified in {@link GLMatrixFunc}. *
* This is the second implementation of `PMVMatrix` using * direct {@link Matrix4f}, {@link Vec4f} and {@link Vec3f} math operations instead of `float[]` * via {@link com.jogamp.opengl.math.FloatUtil FloatUtil}. *
** PMVMatrix provides the {@link #getMviMat() inverse modelview matrix (Mvi)} and * {@link #getMvitMat() inverse transposed modelview matrix (Mvit)}. * {@link Frustum} is also provided by {@link #getFrustum()}. * * To keep these derived values synchronized after mutable Mv operations like {@link #glRotatef(float, float, float, float) glRotatef(..)} * in {@link #glMatrixMode(int) glMatrixMode}({@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}), * users have to call {@link #update()} before using Mvi and Mvit. *
** All matrices are provided in column-major order, * as specified in the OpenGL fixed function pipeline, i.e. compatibility profile. * See {@link Matrix4f}. *
** PMVMatrix can supplement {@link GL2ES2} applications w/ the * lack of the described matrix functionality. *
** The {@link SyncBuffer} abstraction is provided, e.g. {@link #getSyncPMvMviMat()}, * to synchronize the respective {@link Matrix4f matrices} with the `float[]` backing store. * The latter is represents the data to {@link GLUniformData} via its {@link FloatBuffer}s, see {@link SyncBuffer#getBuffer()}, * and is pushed to the GPU eventually. * * {@link SyncBuffer}'s {@link SyncAction} is called by {@link GLUniformData#getBuffer()}, * i.e. before the data is pushed to the GPU. * * The provided {@link SyncAction} ensures that the {@link Matrix4f matrices data} * gets copied into the `float[]` backing store. * * PMVMatrix provides two specializations of {@link SyncBuffer}, {@link SyncMatrix4f} for single {@link Matrix4f} mappings * and {@link SyncMatrices4f} for multiple {@link Matrix4f} mappings. * * They can be feed directly to instantiate a {@link GLUniformData} object via e.g. {@link GLUniformData#GLUniformData(String, int, int, SyncBuffer)}. *
** All matrices are backed up by a common primitive float-array for performance considerations * and are a {@link Buffers#slice2Float(float[], int, int) sliced} representation of it. *
** Note: *
* 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 #PMVMatrix(int) */ public PMVMatrix() { this(0); } /** * Creates an instance of PMVMatrix. ** 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 PMVMatrix(final int derivedMatrices) { // I Identity // T Texture // P Projection // Mv ModelView // Mvi Modelview-Inverse // Mvit Modelview-Inverse-Transpose { int mask = 0; if( 0 != ( derivedMatrices & ( INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW ) ) ) { mask |= INVERSE_MODELVIEW; } if( 0 != ( derivedMatrices & INVERSE_TRANSPOSED_MODELVIEW ) ) { mask |= INVERSE_TRANSPOSED_MODELVIEW; } requestBits = mask; } // actual underlying Matrix4f count int mcount = 3; // actual underlying Matrix4f data matP = new Matrix4f(); matMv = new Matrix4f(); matTex = new Matrix4f(); if( 0 != ( requestBits & INVERSE_MODELVIEW ) ) { matMvi = new Matrix4f(); mMvi_offset = 2*16; ++mcount; } else { matMvi = null; mMvi_offset = -1; } if( 0 != ( requestBits & INVERSE_TRANSPOSED_MODELVIEW ) ) { matMvit = new Matrix4f(); mMvit_offset = 3*16; ++mcount; } else { matMvit = null; mMvit_offset = -1; } mTex_offset = (mcount-1)*16; // last one // float back buffer for GPU, Matrix4f -> matrixStore via SyncedBuffer matrixStore = new float[mcount*16]; // FloatBuffer for single Matrix4f back-buffer bufP = Buffers.slice2Float(matrixStore, mP_offset, 1*16); // P syncP = new SyncBuffer0(matP, bufP); // mP_offset bufMv = Buffers.slice2Float(matrixStore, mMv_offset, 1*16); // Mv syncMv = new SyncBuffer1(matMv, bufMv, mMv_offset); bufP_Mv = Buffers.slice2Float(matrixStore, mP_offset, 2*16); // P + Mv syncP_Mv = new SyncBufferN(new Matrix4f[] { matP, matMv }, bufP_Mv, mP_offset); bufTex = Buffers.slice2Float(matrixStore, mTex_offset, 1*16); // T syncT = new SyncBuffer1(matTex, bufTex, mTex_offset); if( null != matMvi ) { bufMvi = Buffers.slice2Float(matrixStore, mMvi_offset, 1*16); // Mvi bufP_Mv_Mvi = Buffers.slice2Float(matrixStore, mP_offset, 3*16); // P + Mv + Mvi syncMvi = new SyncBuffer1U(matMvi, bufMvi, mMvi_offset); syncP_Mv_Mvi = new SyncBufferNU(new Matrix4f[] { matP, matMv, matMvi }, bufP_Mv_Mvi, mP_offset); } else { bufMvi = null; bufP_Mv_Mvi = null; syncMvi = null; syncP_Mv_Mvi = null; } if( null != matMvit ) { bufMvit = Buffers.slice2Float(matrixStore, mMvit_offset, 1*16); // Mvit bufP_Mv_Mvi_Mvit = Buffers.slice2Float(matrixStore, mP_offset, 4*16); // P + Mv + Mvi + Mvit syncMvit = new SyncBuffer1U(matMvit, bufMvit, mMvit_offset); syncP_Mv_Mvi_Mvit = new SyncBufferNU(new Matrix4f[] { matP, matMv, matMvi, matMvit }, bufP_Mv_Mvi_Mvit, mP_offset); } else { bufMvit = null; bufP_Mv_Mvi_Mvit = null; syncMvit = null; syncP_Mv_Mvi_Mvit = null; } mat4Tmp1 = new Matrix4f(); mat4Tmp2 = null; // on demand matPMv = null; // on demand matPMvi = null; // on demand matPMviOK = false; frustum = null; // on demand reset(); } /** * Issues {@link #glLoadIdentity()} on all matrices, * i.e. {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} * and resets all internal states. * * Leaves {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW} the active matrix mode. */ public final void reset() { matP.loadIdentity(); matMv.loadIdentity(); matTex.loadIdentity(); modifiedBits = MODIFIED_ALL; dirtyBits = requestBits | MANUAL_BITS; matrixMode = GL_MODELVIEW; } /** Returns the current matrix-mode, one of {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE}. */ public final int glGetMatrixMode() { return matrixMode; } // // 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. *
*/ private final Matrix4f getTmp2Mat() { if( null == mat4Tmp2 ) { mat4Tmp2 = new Matrix4f(); } return mat4Tmp2; } // // Regular Matrix4f access as well as their SyncedBuffer counterpart SyncedMatrix and SyncedMatrices // /** * Returns the {@link GLMatrixFunc#GL_TEXTURE_MATRIX texture matrix} (T). ** See matrix storage details. *
*/ public final Matrix4f getTMat() { return matTex; } /** * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_TEXTURE_MATRIX texture matrix} (T). ** See matrix storage details. *
*/ public final SyncMatrix4f getSyncTMat() { return syncT; } /** * Returns the {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). ** See matrix storage details. *
*/ public final Matrix4f getPMat() { return matP; } /** * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_PROJECTION_MATRIX projection matrix} (P). ** See matrix storage details. *
*/ public final SyncMatrix4f getSyncPMat() { return syncP; } /** * Returns the {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). ** See matrix storage details. *
*/ public final Matrix4f getMvMat() { return matMv; } /** * Returns the {@link SyncMatrix} of {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mv). ** See matrix storage details. *
*/ public final SyncMatrix4f getSyncMvMat() { return syncMv; } /** * Returns {@link SyncMatrices4f} of 2 matrices within one FloatBuffer: {@link #getPMat() P} and {@link #getMvMat() Mv}. ** See matrix storage details. *
*/ public final SyncMatrices4f getSyncPMvMat() { 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 #PMVMatrix(int)}. */ public final Matrix4f getMviMat() { 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 #PMVMatrix(int)}. */ public final SyncMatrix4f getSyncMviMat() { if( 0 == ( INVERSE_MODELVIEW & requestBits ) ) { throw new IllegalArgumentException("Not requested in ctor"); } return syncMvi; } /** * Returns the inverse transposed {@link GLMatrixFunc#GL_MODELVIEW_MATRIX modelview matrix} (Mvit) if requested. ** See matrix storage details. *
* @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. */ public final Matrix4f getMvitMat() { 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 #PMVMatrix(int)}. */ public final SyncMatrix4f getSyncMvitMat() { if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { throw new IllegalArgumentException("Not requested in ctor"); } return syncMvit; } /** * Returns {@link SyncMatrices4f} of 3 matrices within one FloatBuffer: {@link #getPMat() P}, {@link #getMvMat() Mv} and {@link #getMviMat() Mvi} if requested. ** See matrix storage details. *
* @throws IllegalArgumentException if {@link #INVERSE_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. */ public final SyncMatrices4f getSyncPMvMviMat() { 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 #getPMat() P}, {@link #getMvMat() Mv}, {@link #getMviMat() Mvi} and {@link #getMvitMat() Mvit} if requested. ** See matrix storage details. *
* @throws IllegalArgumentException if {@link #INVERSE_TRANSPOSED_MODELVIEW} has not been requested in ctor {@link #PMVMatrix(int)}. */ public final SyncMatrices4f getSyncPMvMviMvitMat() { if( 0 == ( INVERSE_TRANSPOSED_MODELVIEW & requestBits ) ) { throw new IllegalArgumentException("Not requested in ctor"); } return syncP_Mv_Mvi_Mvit; } /** * @return the matrix of the current matrix-mode */ public final Matrix4f getCurrentMat() { return getMat(matrixMode); } /** * @param matrixName Either a matrix-get-name, i.e. * {@link GLMatrixFunc#GL_MODELVIEW_MATRIX GL_MODELVIEW_MATRIX}, {@link GLMatrixFunc#GL_PROJECTION_MATRIX GL_PROJECTION_MATRIX} or {@link GLMatrixFunc#GL_TEXTURE_MATRIX GL_TEXTURE_MATRIX}, * or a matrix-mode-name, i.e. * {@link GLMatrixFunc#GL_MODELVIEW GL_MODELVIEW}, {@link GLMatrixFunc#GL_PROJECTION GL_PROJECTION} or {@link GL#GL_TEXTURE GL_TEXTURE} * @return the named matrix, not a copy! */ public final Matrix4f getMat(final int matrixName) { switch(matrixName) { case GL_MODELVIEW_MATRIX: case GL_MODELVIEW: return matMv; case GL_PROJECTION_MATRIX: case GL_PROJECTION: return matP; case GL_TEXTURE_MATRIX: case GL.GL_TEXTURE: return matTex; default: throw new GLException("unsupported matrixName: "+matrixName); } } // // Basic Matrix4f, Vec3f and Vec4f operations similar to GLMatrixFunc // /** * Multiplies the {@link #getPMat() P} and {@link #getMvMat() Mv} matrix, i.e. ** result = P x Mv ** @param result 4x4 matrix storage for result * @return given result matrix for chaining */ public final Matrix4f mulPMvMat(final Matrix4f result) { return result.mul(matP, matMv); } /** * Multiplies the {@link #getMvMat() Mv} and {@link #getPMat() P} matrix, i.e. *
* result = Mv x P ** @param result 4x4 matrix storage for result * @return given result matrix for chaining */ public final Matrix4f mulMvPMat(final Matrix4f result) { return result.mul(matMv, matP); } /** * v_out = Mv * v_in * @param v_in input vector, can be v_out for in-place transformation * @param v_out output vector * @returns v_out for chaining */ public final Vec4f mulMvMatVec4f(final Vec4f v_in, final Vec4f v_out) { return matMv.mulVec4f(v_in, v_out); } /** * v_inout = Mv * v_inout * @param v_inout input and output vector, i.e. in-place transformation * @returns v_inout for chaining */ public final Vec4f mulMvMatVec4f(final Vec4f v_inout) { return matMv.mulVec4f(v_inout); } /** * v_out = Mv * v_in * * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. * * @param v_in input vector, can be v_out for in-place transformation * @param v_out output vector * @returns v_out for chaining */ public final Vec3f mulMvMatVec3f(final Vec3f v_in, final Vec3f v_out) { return matMv.mulVec3f(v_in, v_out); } /** * v_inout = Mv * v_inout * * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. * * @param v_inout input and output vector, i.e. in-place transformation * @returns v_inout for chaining */ public final Vec3f mulMvMatVec3f(final Vec3f v_inout) { return matMv.mulVec3f(v_inout); } /** * v_out = P * v_in * @param v_in input vector, can be v_out for in-place transformation * @param v_out output vector * @return given result vector for chaining * @returns v_out for chaining */ public final Vec4f mulPMatVec4f(final Vec4f v_in, final Vec4f v_out) { return matP.mulVec4f(v_in, v_out); } /** * v_inout = P * v_inout * @param v_inout input and output vector, i.e. in-place transformation * @return given result vector for chaining * @returns v_inout for chaining */ public final Vec4f mulPMatVec4f(final Vec4f v_inout) { return matP.mulVec4f(v_inout); } /** * v_out = P * v_in * * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. * * @param v_in float[3] input vector, can be v_out for in-place transformation * @param v_out float[3] output vector * @returns v_out for chaining */ public final Vec3f mulPMatVec3f(final Vec3f v_in, final Vec3f v_out) { return matP.mulVec3f(v_in, v_out); } /** * v_inout = P * v_inout * * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. * * @param v_inout input and output vector, i.e. in-place transformation * @returns v_inout for chaining */ public final Vec3f mulPMatVec3f(final Vec3f v_inout) { return matP.mulVec3f(v_inout); } /** * v_out = P * Mv * v_in * @param v_in float[4] input vector, can be v_out for in-place transformation * @param v_out float[4] output vector * @returns v_out for chaining */ public final Vec4f mulPMvMatVec4f(final Vec4f v_in, final Vec4f v_out) { return matP.mulVec4f( matMv.mulVec4f( v_in, v_out ) ); } /** * v_inout = P * Mv * v_inout * @param v_inout input and output vector, i.e. in-place transformation * @returns v_inout for chaining */ public final Vec4f mulPMvMatVec4f(final Vec4f v_inout) { return matP.mulVec4f( matMv.mulVec4f( v_inout ) ); } /** * v_out = P * Mv * v_in * * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. * * @param v_in float[3] input vector, can be v_out for in-place transformation * @param v_out float[3] output vector * @returns v_out for chaining */ public final Vec3f mulPMvMatVec3f(final Vec3f v_in, final Vec3f v_out) { return matP.mulVec3f( matMv.mulVec3f( v_in, v_out ) ); } /** * v_inout = P * Mv * v_inout * * Affine 3f-vector transformation by 4x4 matrix, see {@link Matrix4f#mulVec3f(Vec3f, Vec3f)}. * * @param v_inout float[3] input and output vector, i.e. in-place transformation * @returns v_inout for chaining */ public final Vec3f mulPMvMatVec3f(final Vec3f v_inout) { return matP.mulVec3f( matMv.mulVec3f( v_inout ) ); } // // GLMatrixFunc implementation // @Override public final void glMatrixMode(final int matrixName) { switch(matrixName) { case GL_MODELVIEW: case GL_PROJECTION: case GL.GL_TEXTURE: break; default: throw new GLException("unsupported matrixName: "+matrixName); } matrixMode = matrixName; } @Override public final void glGetFloatv(final int matrixGetName, final FloatBuffer params) { final int pos = params.position(); if(matrixGetName==GL_MATRIX_MODE) { params.put(matrixMode); } else { getMat(matrixGetName).get(params); // matrix -> params } params.position(pos); } @Override public final void glGetFloatv(final int matrixGetName, final float[] params, final int params_offset) { if(matrixGetName==GL_MATRIX_MODE) { params[params_offset]=matrixMode; } else { getMat(matrixGetName).get(params, params_offset); // matrix -> params } } @Override public final void glGetIntegerv(final int pname, final IntBuffer params) { final int pos = params.position(); if(pname==GL_MATRIX_MODE) { params.put(matrixMode); } else { throw new GLException("unsupported pname: "+pname); } params.position(pos); } @Override public final void glGetIntegerv(final int pname, final int[] params, final int params_offset) { if(pname==GL_MATRIX_MODE) { params[params_offset]=matrixMode; } else { throw new GLException("unsupported pname: "+pname); } } @Override public final void glLoadMatrixf(final float[] values, final int offset) { if(matrixMode==GL_MODELVIEW) { matMv.load(values, offset); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.load(values, offset); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.load(values, offset); modifiedBits |= MODIFIED_TEXTURE; } } @Override public final void glLoadMatrixf(final java.nio.FloatBuffer m) { final int spos = m.position(); if(matrixMode==GL_MODELVIEW) { matMv.load(m); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.load(m); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.load(m); modifiedBits |= MODIFIED_TEXTURE; } m.position(spos); } /** * Load the current matrix with the values of the given {@link Matrix4f}. *
* Extension to {@link GLMatrixFunc}. *
*/ public final void glLoadMatrixf(final Matrix4f m) { if(matrixMode==GL_MODELVIEW) { matMv.load(m); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.load(m); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.load(m); modifiedBits |= MODIFIED_TEXTURE; } } /** * Load the current matrix with the values of the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. ** Extension to {@link GLMatrixFunc}. *
*/ public final void glLoadMatrix(final Quaternion quat) { if(matrixMode==GL_MODELVIEW) { matMv.setToRotation(quat); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.setToRotation(quat); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.setToRotation(quat); modifiedBits |= MODIFIED_TEXTURE; } } @Override public final void glPopMatrix() { if(matrixMode==GL_MODELVIEW) { matMv.pop(); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.pop(); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.pop(); modifiedBits |= MODIFIED_TEXTURE; } } @Override public final void glPushMatrix() { if(matrixMode==GL_MODELVIEW) { matMv.push(); } else if(matrixMode==GL_PROJECTION) { matP.push(); } else if(matrixMode==GL.GL_TEXTURE) { matTex.push(); } } @Override public final void glLoadIdentity() { if(matrixMode==GL_MODELVIEW) { matMv.loadIdentity(); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.loadIdentity(); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.loadIdentity(); modifiedBits |= MODIFIED_TEXTURE; } } @Override public final void glMultMatrixf(final FloatBuffer m) { final int spos = m.position(); if(matrixMode==GL_MODELVIEW) { matMv.mul( mat4Tmp1.load( m ) ); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.mul( mat4Tmp1.load( m ) ); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.mul( mat4Tmp1.load( m ) ); modifiedBits |= MODIFIED_TEXTURE; } m.position(spos); } @Override public final void glMultMatrixf(final float[] m, final int m_offset) { if(matrixMode==GL_MODELVIEW) { matMv.mul( mat4Tmp1.load( m, m_offset ) ); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.mul( mat4Tmp1.load( m, m_offset ) ); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.mul( mat4Tmp1.load( m, m_offset ) ); modifiedBits |= MODIFIED_TEXTURE; } } /** * Multiply the current matrix: [c] = [c] x [m] ** Extension to {@link GLMatrixFunc}. *
* @param m the right hand Matrix4f * @return this instance of chaining */ public final PMVMatrix glMultMatrixf(final Matrix4f m) { if(matrixMode==GL_MODELVIEW) { matMv.mul( m ); dirtyBits |= requestBits | MANUAL_BITS ; modifiedBits |= MODIFIED_MODELVIEW; } else if(matrixMode==GL_PROJECTION) { matP.mul( m ); dirtyBits |= MANUAL_BITS ; modifiedBits |= MODIFIED_PROJECTION; } else if(matrixMode==GL.GL_TEXTURE) { matTex.mul( m ); modifiedBits |= MODIFIED_TEXTURE; } return this; } @Override public final void glTranslatef(final float x, final float y, final float z) { glMultMatrixf( mat4Tmp1.setToTranslation(x, y, z) ); } /** * Translate the current matrix. ** Extension to {@link GLMatrixFunc}. *
* @param t translation vec3 * @return this instance of chaining */ public final PMVMatrix glTranslatef(final Vec3f t) { return glMultMatrixf( mat4Tmp1.setToTranslation(t) ); } @Override public final void glScalef(final float x, final float y, final float z) { glMultMatrixf( mat4Tmp1.setToScale(x, y, z) ); } /** * Scale the current matrix. ** Extension to {@link GLMatrixFunc}. *
* @param s scale vec4f * @return this instance of chaining */ public final PMVMatrix glScalef(final Vec3f s) { return glMultMatrixf( mat4Tmp1.setToScale(s) ); } @Override public final void glRotatef(final float ang_deg, final float x, final float y, final float z) { glMultMatrixf( mat4Tmp1.setToRotationAxis(FloatUtil.adegToRad(ang_deg), x, y, z) ); } /** * Rotate the current matrix by the given axis and angle in radians. ** Consider using {@link #glRotate(Quaternion)} *
** Extension to {@link GLMatrixFunc}. *
* @param ang_rad angle in radians * @param axis rotation axis * @return this instance of chaining * @see #glRotate(Quaternion) */ public final PMVMatrix glRotatef(final float ang_rad, final Vec3f axis) { return glMultMatrixf( mat4Tmp1.setToRotationAxis(ang_rad, axis) ); } /** * Rotate the current matrix with the given {@link Quaternion}'s rotation {@link Matrix4f#setToRotation(Quaternion) matrix representation}. ** Extension to {@link GLMatrixFunc}. *
* @param quat the {@link Quaternion} * @return this instance of chaining */ public final PMVMatrix glRotate(final Quaternion quat) { return glMultMatrixf( mat4Tmp1.setToRotation(quat) ); } @Override public final void glOrthof(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) { glMultMatrixf( mat4Tmp1.setToOrtho(left, right, bottom, top, zNear, zFar) ); } /** * {@inheritDoc} * * @throws GLException 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) */ @Override public final void glFrustumf(final float left, final float right, final float bottom, final float top, final float zNear, final float zFar) throws GLException { glMultMatrixf( mat4Tmp1.setToFrustum(left, right, bottom, top, zNear, zFar) ); } // // Extra functionality // /** * {@link #glMultMatrixf(FloatBuffer) Multiply} the {@link #glGetMatrixMode() current matrix} with the perspective/frustum matrix. * * @param fovy_deg fov angle in degrees * @param aspect aspect ratio width / height * @param zNear * @param zFar * @throws GLException if {@code zNear <= 0} or {@code zFar <= zNear} * @see Matrix4f#setToPerspective(float, float, float, float) */ public final void gluPerspective(final float fovy_deg, final float aspect, final float zNear, final float zFar) throws GLException { glMultMatrixf( mat4Tmp1.setToPerspective(FloatUtil.adegToRad(fovy_deg), aspect, zNear, zFar) ); } /** * {@link #glMultMatrixf(FloatBuffer) Multiply} and {@link #glTranslatef(float, float, float) translate} the {@link #glGetMatrixMode() current matrix} * with the eye, object and orientation. */ public final void gluLookAt(final Vec3f eye, final Vec3f center, final Vec3f up) { glMultMatrixf( mat4Tmp1.setToLookAt(eye, center, up, getTmp2Mat()) ); } /** * Map object coordinates to window coordinates. *
* Traditional gluProject
implementation.
*
* Traditional gluUnProject
implementation.
*
* Traditional gluUnProject4
implementation.
*
* Notes for picking winz0 and winz1: *
* Traditional gluPickMatrix
implementation.
*
* See {@link Matrix4f#setToPick(float, float, float, float, Recti, int, Matrix4f) for details. *
* @param x the center x-component of a picking region in window coordinates * @param y the center y-component of a picking region in window coordinates * @param deltaX the width of the picking region in window coordinates. * @param deltaY the height of the picking region in window coordinates. * @param viewport Rect4i viewport vector */ public final void gluPickMatrix(final float x, final float y, final float deltaX, final float deltaY, final Recti viewport) { if( null != mat4Tmp1.setToPick(x, y, deltaX, deltaY, viewport, getTmp2Mat()) ) { glMultMatrixf( mat4Tmp1 ); } } public StringBuilder toString(StringBuilder sb, final String f) { if(null == sb) { sb = new StringBuilder(); } final boolean pmvDirty = 0 != (PREMUL_PMV & dirtyBits); final boolean pmvUsed = null != matPMv; final boolean pmviDirty = 0 != (PREMUL_PMVI & dirtyBits); final boolean pmviUsed = null != matPMvi; final boolean frustumDirty = 0 != (FRUSTUM & dirtyBits); final boolean frustumUsed = null != frustum; final boolean mviDirty = 0 != (INVERSE_MODELVIEW & dirtyBits); final boolean mviReq = 0 != (INVERSE_MODELVIEW & requestBits); final boolean mvitDirty = 0 != (INVERSE_TRANSPOSED_MODELVIEW & dirtyBits); final boolean mvitReq = 0 != (INVERSE_TRANSPOSED_MODELVIEW & requestBits); final boolean modP = 0 != ( MODIFIED_PROJECTION & modifiedBits ); final boolean modMv = 0 != ( MODIFIED_MODELVIEW & modifiedBits ); final boolean modT = 0 != ( MODIFIED_TEXTURE & modifiedBits ); int count = 3; // P, Mv, T sb.append("PMVMatrix[modified[P ").append(modP).append(", Mv ").append(modMv).append(", T ").append(modT); sb.append("], dirty/used[PMv ").append(pmvDirty).append("/").append(pmvUsed).append(", Pmvi ").append(pmviDirty).append("/").append(pmviUsed).append(", Frustum ").append(frustumDirty).append("/").append(frustumUsed); sb.append("], dirty/req[Mvi ").append(mviDirty).append("/").append(mviReq).append(", Mvit ").append(mvitDirty).append("/").append(mvitReq).append("]").append(System.lineSeparator()); sb.append(", Projection").append(System.lineSeparator()); matP.toString(sb, null, f); sb.append(", Modelview").append(System.lineSeparator()); matMv.toString(sb, null, f); sb.append(", Texture").append(System.lineSeparator()); matTex.toString(sb, null, f); if( null != matPMv ) { sb.append(", P * Mv").append(System.lineSeparator()); matPMv.toString(sb, null, f); ++count; } if( null != matPMvi ) { sb.append(", P * Mv").append(System.lineSeparator()); matPMvi.toString(sb, null, f); ++count; } if( mviReq ) { sb.append(", Inverse Modelview").append(System.lineSeparator()); matMvi.toString(sb, null, f); ++count; } if( mvitReq ) { sb.append(", Inverse Transposed Modelview").append(System.lineSeparator()); matMvit.toString(sb, null, f); ++count; } int tmpCount = 1; if( null != mat4Tmp2 ) { ++tmpCount; } sb.append(", matrices "+count+" + "+tmpCount+" temp = "+(count+tmpCount)+"]"); return sb; } @Override public String toString() { return toString(null, "%10.5f").toString(); } /** * Returns the modified bits due to mutable operations.. ** 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 #PMVMatrix(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 #PMVMatrix(int) * @see #getMviMat() * @see #getMvitMat() * @see #getSyncPMvMviMat() * @see #getSyncPMvMviMvitMat() * @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 #PMVMatrix(int)}. *
** {@link #update()} clears the dirty state for the matrices and {@link #getFrustum()} for {@link #FRUSTUM}. *
* * @see #INVERSE_MODELVIEW * @see #INVERSE_TRANSPOSED_MODELVIEW * @see #PMVMatrix(int) * @see #getMviMat() * @see #getMvitMat() * @see #getSyncPMvMviMat() * @see #getSyncPMvMviMvitMat() */ public final boolean isReqDirty() { return 0 != ( requestBits & dirtyBits ); } /** * 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 #PMVMatrix(int)}. *
* * @see #INVERSE_MODELVIEW * @see #INVERSE_TRANSPOSED_MODELVIEW * @see #PMVMatrix(int) * @see #getMviMat() * @see #getMvitMat() * @see #getSyncPMvMviMat() * @see #getSyncPMvMviMvitMat() * @see #getFrustum() */ public final int getReqBits() { return requestBits; } /** * Returns the pre-multiplied projection x modelview, P x Mv. ** 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 #getPMat() P matrix} or {@link #getMvMat() Mv matrix} has been modified since its last update. *
* @see #update() */ public final Matrix4f getPMvMat() { if( 0 != ( dirtyBits & PREMUL_PMV ) ) { if( null == matPMv ) { matPMv = new Matrix4f(); } matPMv.mul(matP, matMv); dirtyBits &= ~PREMUL_PMV; } return matPMv; } /** * Returns the pre-multiplied inverse projection x modelview, * if {@link Matrix4f#invert(Matrix4f)} succeeded, otherwise `null`. ** 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 #getPMat() P matrix} or {@link #getMvMat() Mv matrix} has been modified since its last update. *
* @see #update() */ public final Matrix4f getPMviMat() { if( 0 != ( dirtyBits & PREMUL_PMVI ) ) { if( null == matPMvi ) { matPMvi = new Matrix4f(); } final Matrix4f mPMv = getPMvMat(); matPMviOK = matPMvi.invert(mPMv); dirtyBits &= ~PREMUL_PMVI; } return matPMviOK ? matPMvi : null; } /** * Returns the frustum, derived from projection x modelview. ** 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 #getPMat() P matrix} or {@link #getMvMat() 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 = getPMvMat(); frustum.updateFrustumPlanes(mPMv); dirtyBits &= ~FRUSTUM; } return frustum; } /** * Update the derived {@link #getMviMat() inverse modelview (Mvi)}, * {@link #getMvitMat() inverse transposed modelview (Mvit)} matrices * if they {@link #isReqDirty() are dirty} and * requested via the constructor {@link #PMVMatrix(int)}.* The {@link Frustum} is updated only via {@link #getFrustum()} separately. *
** The Mvi and Mvit matrices are considered dirty, if their corresponding * {@link #getMvMat() 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 #getSyncMvitMat()}. * This ensures an automatic update cycle if used with {@link 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 #getMviMat()}, {@link #getMvitMat()} 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 #PMVMatrix(int) * @see #getMviMat() * @see #getMvitMat() * @see #getSyncPMvMviMat() * @see #getSyncPMvMviMvitMat() */ public final boolean update() { return updateImpl(true); } // // private // private final boolean updateImpl(final boolean clearModBits) { boolean mod = 0 != modifiedBits; if( clearModBits ) { modifiedBits = 0; } if( 0 != ( requestBits & ( ( dirtyBits & ( INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW ) ) ) ) ) { // only if dirt requested & dirty if( !matMvi.invert(matMv) ) { throw new GLException(msgCantComputeInverse); } dirtyBits &= ~INVERSE_MODELVIEW; mod = true; } if( 0 != ( requestBits & ( dirtyBits & INVERSE_TRANSPOSED_MODELVIEW ) ) ) { // only if requested & dirty matMvit.transpose(matMvi); dirtyBits &= ~INVERSE_TRANSPOSED_MODELVIEW; mod = true; } return mod; } private static final String msgCantComputeInverse = "Invalid source Mv matrix, can't compute inverse"; private final Matrix4f matP; private final Matrix4f matMv; private final Matrix4f matTex; private final Matrix4f matMvi; private final Matrix4f matMvit; private static final int mP_offset = 0*16; private static final int mMv_offset = 1*16; private final int mMvi_offset; private final int mMvit_offset; private final int mTex_offset; private final float[] matrixStore; private final FloatBuffer bufP, bufMv, bufTex; private final FloatBuffer bufMvi, bufMvit; private final FloatBuffer bufP_Mv, bufP_Mv_Mvi, bufP_Mv_Mvi_Mvit; private final SyncMatrix4f syncP, syncMv, syncT; private final SyncMatrix4f syncMvi, syncMvit; private final SyncMatrices4f syncP_Mv, syncP_Mv_Mvi, syncP_Mv_Mvi_Mvit; private final Matrix4f mat4Tmp1; private int matrixMode = GL_MODELVIEW; private int modifiedBits = MODIFIED_ALL; private int dirtyBits = 0; // contains the dirty bits, i.e. hinting for update operation private final int requestBits; // may contain the requested bits: INVERSE_MODELVIEW | INVERSE_TRANSPOSED_MODELVIEW private Matrix4f mat4Tmp2; private Matrix4f matPMv; private Matrix4f matPMvi; private boolean matPMviOK; private Frustum frustum; private abstract class PMVSyncBuffer implements SyncMatrix4f { protected final Matrix4f mat; private final FloatBuffer fbuf; public PMVSyncBuffer(final Matrix4f m, final FloatBuffer fbuf) { this.mat = m; this.fbuf = fbuf; } @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