| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.opengl; |
| |
| import androidx.annotation.NonNull; |
| |
| /** |
| * Matrix math utilities. These methods operate on OpenGL ES format |
| * matrices and vectors stored in float arrays. |
| * <p> |
| * Matrices are 4 x 4 column-vector matrices stored in column-major |
| * order: |
| * <pre> |
| * m[offset + 0] m[offset + 4] m[offset + 8] m[offset + 12] |
| * m[offset + 1] m[offset + 5] m[offset + 9] m[offset + 13] |
| * m[offset + 2] m[offset + 6] m[offset + 10] m[offset + 14] |
| * m[offset + 3] m[offset + 7] m[offset + 11] m[offset + 15]</pre> |
| * |
| * Vectors are 4 x 1 column vectors stored in order: |
| * <pre> |
| * v[offset + 0] |
| * v[offset + 1] |
| * v[offset + 2] |
| * v[offset + 3]</pre> |
| */ |
| public class Matrix { |
| |
| /** Temporary memory for operations that need temporary matrix data. */ |
| private static final ThreadLocal<float[]> ThreadTmp = new ThreadLocal() { |
| @Override protected float[] initialValue() { |
| return new float[32]; |
| } |
| }; |
| |
| /** |
| * @deprecated All methods are static, do not instantiate this class. |
| */ |
| @Deprecated |
| public Matrix() {} |
| |
| private static boolean overlap( |
| float[] a, int aStart, int aLength, float[] b, int bStart, int bLength) { |
| if (a != b) { |
| return false; |
| } |
| |
| if (aStart == bStart) { |
| return true; |
| } |
| |
| int aEnd = aStart + aLength; |
| int bEnd = bStart + bLength; |
| |
| if (aEnd == bEnd) { |
| return true; |
| } |
| |
| if (aStart < bStart && bStart < aEnd) { |
| return true; |
| } |
| if (aStart < bEnd && bEnd < aEnd) { |
| return true; |
| } |
| |
| if (bStart < aStart && aStart < bEnd) { |
| return true; |
| } |
| if (bStart < aEnd && aEnd < bEnd) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Multiplies two 4x4 matrices together and stores the result in a third 4x4 |
| * matrix. In matrix notation: result = lhs x rhs. Due to the way |
| * matrix multiplication works, the result matrix will have the same |
| * effect as first multiplying by the rhs matrix, then multiplying by |
| * the lhs matrix. This is the opposite of what you might expect. |
| * <p> |
| * The same float array may be passed for result, lhs, and/or rhs. This |
| * operation is expected to do the correct thing if the result elements |
| * overlap with either of the lhs or rhs elements. |
| * |
| * @param result The float array that holds the result. |
| * @param resultOffset The offset into the result array where the result is |
| * stored. |
| * @param lhs The float array that holds the left-hand-side matrix. |
| * @param lhsOffset The offset into the lhs array where the lhs is stored |
| * @param rhs The float array that holds the right-hand-side matrix. |
| * @param rhsOffset The offset into the rhs array where the rhs is stored. |
| * |
| * @throws IllegalArgumentException under any of the following conditions: |
| * result, lhs, or rhs are null; |
| * resultOffset + 16 > result.length |
| * or lhsOffset + 16 > lhs.length |
| * or rhsOffset + 16 > rhs.length; |
| * resultOffset < 0 or lhsOffset < 0 or rhsOffset < 0 |
| */ |
| public static void multiplyMM(float[] result, int resultOffset, |
| float[] lhs, int lhsOffset, float[] rhs, int rhsOffset) { |
| // error checking |
| if (result == null) { |
| throw new IllegalArgumentException("result == null"); |
| } |
| if (lhs == null) { |
| throw new IllegalArgumentException("lhs == null"); |
| } |
| if (rhs == null) { |
| throw new IllegalArgumentException("rhs == null"); |
| } |
| if (resultOffset < 0) { |
| throw new IllegalArgumentException("resultOffset < 0"); |
| } |
| if (lhsOffset < 0) { |
| throw new IllegalArgumentException("lhsOffset < 0"); |
| } |
| if (rhsOffset < 0) { |
| throw new IllegalArgumentException("rhsOffset < 0"); |
| } |
| if (result.length < resultOffset + 16) { |
| throw new IllegalArgumentException("result.length < resultOffset + 16"); |
| } |
| if (lhs.length < lhsOffset + 16) { |
| throw new IllegalArgumentException("lhs.length < lhsOffset + 16"); |
| } |
| if (rhs.length < rhsOffset + 16) { |
| throw new IllegalArgumentException("rhs.length < rhsOffset + 16"); |
| } |
| |
| // Check for overlap between rhs and result or lhs and result |
| if ( overlap(result, resultOffset, 16, lhs, lhsOffset, 16) |
| || overlap(result, resultOffset, 16, rhs, rhsOffset, 16) ) { |
| float[] tmp = ThreadTmp.get(); |
| for (int i=0; i<4; i++) { |
| final float rhs_i0 = rhs[ 4*i + 0 + rhsOffset ]; |
| float ri0 = lhs[ 0 + lhsOffset ] * rhs_i0; |
| float ri1 = lhs[ 1 + lhsOffset ] * rhs_i0; |
| float ri2 = lhs[ 2 + lhsOffset ] * rhs_i0; |
| float ri3 = lhs[ 3 + lhsOffset ] * rhs_i0; |
| for (int j=1; j<4; j++) { |
| final float rhs_ij = rhs[ 4*i + j + rhsOffset]; |
| ri0 += lhs[ 4*j + 0 + lhsOffset ] * rhs_ij; |
| ri1 += lhs[ 4*j + 1 + lhsOffset ] * rhs_ij; |
| ri2 += lhs[ 4*j + 2 + lhsOffset ] * rhs_ij; |
| ri3 += lhs[ 4*j + 3 + lhsOffset ] * rhs_ij; |
| } |
| tmp[ 4*i + 0 ] = ri0; |
| tmp[ 4*i + 1 ] = ri1; |
| tmp[ 4*i + 2 ] = ri2; |
| tmp[ 4*i + 3 ] = ri3; |
| } |
| |
| // copy from tmp to result |
| for (int i=0; i < 16; i++) { |
| result[ i + resultOffset ] = tmp[ i ]; |
| } |
| |
| } else { |
| for (int i=0; i<4; i++) { |
| final float rhs_i0 = rhs[ 4*i + 0 + rhsOffset ]; |
| float ri0 = lhs[ 0 + lhsOffset ] * rhs_i0; |
| float ri1 = lhs[ 1 + lhsOffset ] * rhs_i0; |
| float ri2 = lhs[ 2 + lhsOffset ] * rhs_i0; |
| float ri3 = lhs[ 3 + lhsOffset ] * rhs_i0; |
| for (int j=1; j<4; j++) { |
| final float rhs_ij = rhs[ 4*i + j + rhsOffset]; |
| ri0 += lhs[ 4*j + 0 + lhsOffset ] * rhs_ij; |
| ri1 += lhs[ 4*j + 1 + lhsOffset ] * rhs_ij; |
| ri2 += lhs[ 4*j + 2 + lhsOffset ] * rhs_ij; |
| ri3 += lhs[ 4*j + 3 + lhsOffset ] * rhs_ij; |
| } |
| result[ 4*i + 0 + resultOffset ] = ri0; |
| result[ 4*i + 1 + resultOffset ] = ri1; |
| result[ 4*i + 2 + resultOffset ] = ri2; |
| result[ 4*i + 3 + resultOffset ] = ri3; |
| } |
| } |
| } |
| |
| /** |
| * Multiplies a 4 element vector by a 4x4 matrix and stores the result in a |
| * 4-element column vector. In matrix notation: result = lhs x rhs |
| * <p> |
| * The same float array may be passed for resultVec, lhsMat, and/or rhsVec. |
| * This operation is expected to do the correct thing if the result elements |
| * overlap with either of the lhs or rhs elements. |
| * |
| * @param resultVec The float array that holds the result vector. |
| * @param resultVecOffset The offset into the result array where the result |
| * vector is stored. |
| * @param lhsMat The float array that holds the left-hand-side matrix. |
| * @param lhsMatOffset The offset into the lhs array where the lhs is stored |
| * @param rhsVec The float array that holds the right-hand-side vector. |
| * @param rhsVecOffset The offset into the rhs vector where the rhs vector |
| * is stored. |
| * |
| * @throws IllegalArgumentException under any of the following conditions: |
| * resultVec, lhsMat, or rhsVec are null; |
| * resultVecOffset + 4 > resultVec.length |
| * or lhsMatOffset + 16 > lhsMat.length |
| * or rhsVecOffset + 4 > rhsVec.length; |
| * resultVecOffset < 0 or lhsMatOffset < 0 or rhsVecOffset < 0 |
| */ |
| public static void multiplyMV(float[] resultVec, |
| int resultVecOffset, float[] lhsMat, int lhsMatOffset, |
| float[] rhsVec, int rhsVecOffset) { |
| // error checking |
| if (resultVec == null) { |
| throw new IllegalArgumentException("resultVec == null"); |
| } |
| if (lhsMat == null) { |
| throw new IllegalArgumentException("lhsMat == null"); |
| } |
| if (rhsVec == null) { |
| throw new IllegalArgumentException("rhsVec == null"); |
| } |
| if (resultVecOffset < 0) { |
| throw new IllegalArgumentException("resultVecOffset < 0"); |
| } |
| if (lhsMatOffset < 0) { |
| throw new IllegalArgumentException("lhsMatOffset < 0"); |
| } |
| if (rhsVecOffset < 0) { |
| throw new IllegalArgumentException("rhsVecOffset < 0"); |
| } |
| if (resultVec.length < resultVecOffset + 4) { |
| throw new IllegalArgumentException("resultVec.length < resultVecOffset + 4"); |
| } |
| if (lhsMat.length < lhsMatOffset + 16) { |
| throw new IllegalArgumentException("lhsMat.length < lhsMatOffset + 16"); |
| } |
| if (rhsVec.length < rhsVecOffset + 4) { |
| throw new IllegalArgumentException("rhsVec.length < rhsVecOffset + 4"); |
| } |
| |
| float tmp0 = lhsMat[0 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] + |
| lhsMat[0 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] + |
| lhsMat[0 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] + |
| lhsMat[0 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset]; |
| float tmp1 = lhsMat[1 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] + |
| lhsMat[1 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] + |
| lhsMat[1 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] + |
| lhsMat[1 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset]; |
| float tmp2 = lhsMat[2 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] + |
| lhsMat[2 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] + |
| lhsMat[2 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] + |
| lhsMat[2 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset]; |
| float tmp3 = lhsMat[3 + 4 * 0 + lhsMatOffset] * rhsVec[0 + rhsVecOffset] + |
| lhsMat[3 + 4 * 1 + lhsMatOffset] * rhsVec[1 + rhsVecOffset] + |
| lhsMat[3 + 4 * 2 + lhsMatOffset] * rhsVec[2 + rhsVecOffset] + |
| lhsMat[3 + 4 * 3 + lhsMatOffset] * rhsVec[3 + rhsVecOffset]; |
| |
| resultVec[ 0 + resultVecOffset ] = tmp0; |
| resultVec[ 1 + resultVecOffset ] = tmp1; |
| resultVec[ 2 + resultVecOffset ] = tmp2; |
| resultVec[ 3 + resultVecOffset ] = tmp3; |
| } |
| |
| /** |
| * Transposes a 4 x 4 matrix. |
| * <p> |
| * mTrans and m must not overlap. |
| * |
| * @param mTrans the array that holds the output transposed matrix |
| * @param mTransOffset an offset into mTrans where the transposed matrix is |
| * stored. |
| * @param m the input array |
| * @param mOffset an offset into m where the input matrix is stored. |
| */ |
| public static void transposeM(float[] mTrans, int mTransOffset, float[] m, |
| int mOffset) { |
| for (int i = 0; i < 4; i++) { |
| int mBase = i * 4 + mOffset; |
| mTrans[i + mTransOffset] = m[mBase]; |
| mTrans[i + 4 + mTransOffset] = m[mBase + 1]; |
| mTrans[i + 8 + mTransOffset] = m[mBase + 2]; |
| mTrans[i + 12 + mTransOffset] = m[mBase + 3]; |
| } |
| } |
| |
| /** |
| * Inverts a 4 x 4 matrix. |
| * <p> |
| * mInv and m must not overlap. |
| * |
| * @param mInv the array that holds the output inverted matrix |
| * @param mInvOffset an offset into mInv where the inverted matrix is |
| * stored. |
| * @param m the input array |
| * @param mOffset an offset into m where the input matrix is stored. |
| * @return true if the matrix could be inverted, false if it could not. |
| */ |
| public static boolean invertM(float[] mInv, int mInvOffset, float[] m, |
| int mOffset) { |
| // Invert a 4 x 4 matrix using Cramer's Rule |
| |
| // transpose matrix |
| final float src0 = m[mOffset + 0]; |
| final float src4 = m[mOffset + 1]; |
| final float src8 = m[mOffset + 2]; |
| final float src12 = m[mOffset + 3]; |
| |
| final float src1 = m[mOffset + 4]; |
| final float src5 = m[mOffset + 5]; |
| final float src9 = m[mOffset + 6]; |
| final float src13 = m[mOffset + 7]; |
| |
| final float src2 = m[mOffset + 8]; |
| final float src6 = m[mOffset + 9]; |
| final float src10 = m[mOffset + 10]; |
| final float src14 = m[mOffset + 11]; |
| |
| final float src3 = m[mOffset + 12]; |
| final float src7 = m[mOffset + 13]; |
| final float src11 = m[mOffset + 14]; |
| final float src15 = m[mOffset + 15]; |
| |
| // calculate pairs for first 8 elements (cofactors) |
| final float atmp0 = src10 * src15; |
| final float atmp1 = src11 * src14; |
| final float atmp2 = src9 * src15; |
| final float atmp3 = src11 * src13; |
| final float atmp4 = src9 * src14; |
| final float atmp5 = src10 * src13; |
| final float atmp6 = src8 * src15; |
| final float atmp7 = src11 * src12; |
| final float atmp8 = src8 * src14; |
| final float atmp9 = src10 * src12; |
| final float atmp10 = src8 * src13; |
| final float atmp11 = src9 * src12; |
| |
| // calculate first 8 elements (cofactors) |
| final float dst0 = (atmp0 * src5 + atmp3 * src6 + atmp4 * src7) |
| - (atmp1 * src5 + atmp2 * src6 + atmp5 * src7); |
| final float dst1 = (atmp1 * src4 + atmp6 * src6 + atmp9 * src7) |
| - (atmp0 * src4 + atmp7 * src6 + atmp8 * src7); |
| final float dst2 = (atmp2 * src4 + atmp7 * src5 + atmp10 * src7) |
| - (atmp3 * src4 + atmp6 * src5 + atmp11 * src7); |
| final float dst3 = (atmp5 * src4 + atmp8 * src5 + atmp11 * src6) |
| - (atmp4 * src4 + atmp9 * src5 + atmp10 * src6); |
| final float dst4 = (atmp1 * src1 + atmp2 * src2 + atmp5 * src3) |
| - (atmp0 * src1 + atmp3 * src2 + atmp4 * src3); |
| final float dst5 = (atmp0 * src0 + atmp7 * src2 + atmp8 * src3) |
| - (atmp1 * src0 + atmp6 * src2 + atmp9 * src3); |
| final float dst6 = (atmp3 * src0 + atmp6 * src1 + atmp11 * src3) |
| - (atmp2 * src0 + atmp7 * src1 + atmp10 * src3); |
| final float dst7 = (atmp4 * src0 + atmp9 * src1 + atmp10 * src2) |
| - (atmp5 * src0 + atmp8 * src1 + atmp11 * src2); |
| |
| // calculate pairs for second 8 elements (cofactors) |
| final float btmp0 = src2 * src7; |
| final float btmp1 = src3 * src6; |
| final float btmp2 = src1 * src7; |
| final float btmp3 = src3 * src5; |
| final float btmp4 = src1 * src6; |
| final float btmp5 = src2 * src5; |
| final float btmp6 = src0 * src7; |
| final float btmp7 = src3 * src4; |
| final float btmp8 = src0 * src6; |
| final float btmp9 = src2 * src4; |
| final float btmp10 = src0 * src5; |
| final float btmp11 = src1 * src4; |
| |
| // calculate second 8 elements (cofactors) |
| final float dst8 = (btmp0 * src13 + btmp3 * src14 + btmp4 * src15) |
| - (btmp1 * src13 + btmp2 * src14 + btmp5 * src15); |
| final float dst9 = (btmp1 * src12 + btmp6 * src14 + btmp9 * src15) |
| - (btmp0 * src12 + btmp7 * src14 + btmp8 * src15); |
| final float dst10 = (btmp2 * src12 + btmp7 * src13 + btmp10 * src15) |
| - (btmp3 * src12 + btmp6 * src13 + btmp11 * src15); |
| final float dst11 = (btmp5 * src12 + btmp8 * src13 + btmp11 * src14) |
| - (btmp4 * src12 + btmp9 * src13 + btmp10 * src14); |
| final float dst12 = (btmp2 * src10 + btmp5 * src11 + btmp1 * src9 ) |
| - (btmp4 * src11 + btmp0 * src9 + btmp3 * src10); |
| final float dst13 = (btmp8 * src11 + btmp0 * src8 + btmp7 * src10) |
| - (btmp6 * src10 + btmp9 * src11 + btmp1 * src8 ); |
| final float dst14 = (btmp6 * src9 + btmp11 * src11 + btmp3 * src8 ) |
| - (btmp10 * src11 + btmp2 * src8 + btmp7 * src9 ); |
| final float dst15 = (btmp10 * src10 + btmp4 * src8 + btmp9 * src9 ) |
| - (btmp8 * src9 + btmp11 * src10 + btmp5 * src8 ); |
| |
| // calculate determinant |
| final float det = |
| src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3; |
| |
| if (det == 0.0f) { |
| return false; |
| } |
| |
| // calculate matrix inverse |
| final float invdet = 1.0f / det; |
| mInv[ mInvOffset] = dst0 * invdet; |
| mInv[ 1 + mInvOffset] = dst1 * invdet; |
| mInv[ 2 + mInvOffset] = dst2 * invdet; |
| mInv[ 3 + mInvOffset] = dst3 * invdet; |
| |
| mInv[ 4 + mInvOffset] = dst4 * invdet; |
| mInv[ 5 + mInvOffset] = dst5 * invdet; |
| mInv[ 6 + mInvOffset] = dst6 * invdet; |
| mInv[ 7 + mInvOffset] = dst7 * invdet; |
| |
| mInv[ 8 + mInvOffset] = dst8 * invdet; |
| mInv[ 9 + mInvOffset] = dst9 * invdet; |
| mInv[10 + mInvOffset] = dst10 * invdet; |
| mInv[11 + mInvOffset] = dst11 * invdet; |
| |
| mInv[12 + mInvOffset] = dst12 * invdet; |
| mInv[13 + mInvOffset] = dst13 * invdet; |
| mInv[14 + mInvOffset] = dst14 * invdet; |
| mInv[15 + mInvOffset] = dst15 * invdet; |
| |
| return true; |
| } |
| |
| /** |
| * Computes an orthographic projection matrix. |
| * |
| * @param m returns the result |
| * @param mOffset |
| * @param left |
| * @param right |
| * @param bottom |
| * @param top |
| * @param near |
| * @param far |
| */ |
| public static void orthoM(float[] m, int mOffset, |
| float left, float right, float bottom, float top, |
| float near, float far) { |
| if (left == right) { |
| throw new IllegalArgumentException("left == right"); |
| } |
| if (bottom == top) { |
| throw new IllegalArgumentException("bottom == top"); |
| } |
| if (near == far) { |
| throw new IllegalArgumentException("near == far"); |
| } |
| |
| final float r_width = 1.0f / (right - left); |
| final float r_height = 1.0f / (top - bottom); |
| final float r_depth = 1.0f / (far - near); |
| final float x = 2.0f * (r_width); |
| final float y = 2.0f * (r_height); |
| final float z = -2.0f * (r_depth); |
| final float tx = -(right + left) * r_width; |
| final float ty = -(top + bottom) * r_height; |
| final float tz = -(far + near) * r_depth; |
| m[mOffset + 0] = x; |
| m[mOffset + 5] = y; |
| m[mOffset +10] = z; |
| m[mOffset +12] = tx; |
| m[mOffset +13] = ty; |
| m[mOffset +14] = tz; |
| m[mOffset +15] = 1.0f; |
| m[mOffset + 1] = 0.0f; |
| m[mOffset + 2] = 0.0f; |
| m[mOffset + 3] = 0.0f; |
| m[mOffset + 4] = 0.0f; |
| m[mOffset + 6] = 0.0f; |
| m[mOffset + 7] = 0.0f; |
| m[mOffset + 8] = 0.0f; |
| m[mOffset + 9] = 0.0f; |
| m[mOffset + 11] = 0.0f; |
| } |
| |
| |
| /** |
| * Defines a projection matrix in terms of six clip planes. |
| * |
| * @param m the float array that holds the output perspective matrix |
| * @param offset the offset into float array m where the perspective |
| * matrix data is written |
| * @param left |
| * @param right |
| * @param bottom |
| * @param top |
| * @param near |
| * @param far |
| */ |
| public static void frustumM(float[] m, int offset, |
| float left, float right, float bottom, float top, |
| float near, float far) { |
| if (left == right) { |
| throw new IllegalArgumentException("left == right"); |
| } |
| if (top == bottom) { |
| throw new IllegalArgumentException("top == bottom"); |
| } |
| if (near == far) { |
| throw new IllegalArgumentException("near == far"); |
| } |
| if (near <= 0.0f) { |
| throw new IllegalArgumentException("near <= 0.0f"); |
| } |
| if (far <= 0.0f) { |
| throw new IllegalArgumentException("far <= 0.0f"); |
| } |
| final float r_width = 1.0f / (right - left); |
| final float r_height = 1.0f / (top - bottom); |
| final float r_depth = 1.0f / (near - far); |
| final float x = 2.0f * (near * r_width); |
| final float y = 2.0f * (near * r_height); |
| final float A = (right + left) * r_width; |
| final float B = (top + bottom) * r_height; |
| final float C = (far + near) * r_depth; |
| final float D = 2.0f * (far * near * r_depth); |
| m[offset + 0] = x; |
| m[offset + 5] = y; |
| m[offset + 8] = A; |
| m[offset + 9] = B; |
| m[offset + 10] = C; |
| m[offset + 14] = D; |
| m[offset + 11] = -1.0f; |
| m[offset + 1] = 0.0f; |
| m[offset + 2] = 0.0f; |
| m[offset + 3] = 0.0f; |
| m[offset + 4] = 0.0f; |
| m[offset + 6] = 0.0f; |
| m[offset + 7] = 0.0f; |
| m[offset + 12] = 0.0f; |
| m[offset + 13] = 0.0f; |
| m[offset + 15] = 0.0f; |
| } |
| |
| /** |
| * Defines a projection matrix in terms of a field of view angle, an |
| * aspect ratio, and z clip planes. |
| * |
| * @param m the float array that holds the perspective matrix |
| * @param offset the offset into float array m where the perspective |
| * matrix data is written |
| * @param fovy field of view in y direction, in degrees |
| * @param aspect width to height aspect ratio of the viewport |
| * @param zNear |
| * @param zFar |
| */ |
| public static void perspectiveM(float[] m, int offset, |
| float fovy, float aspect, float zNear, float zFar) { |
| float f = 1.0f / (float) Math.tan(fovy * (Math.PI / 360.0)); |
| float rangeReciprocal = 1.0f / (zNear - zFar); |
| |
| m[offset + 0] = f / aspect; |
| m[offset + 1] = 0.0f; |
| m[offset + 2] = 0.0f; |
| m[offset + 3] = 0.0f; |
| |
| m[offset + 4] = 0.0f; |
| m[offset + 5] = f; |
| m[offset + 6] = 0.0f; |
| m[offset + 7] = 0.0f; |
| |
| m[offset + 8] = 0.0f; |
| m[offset + 9] = 0.0f; |
| m[offset + 10] = (zFar + zNear) * rangeReciprocal; |
| m[offset + 11] = -1.0f; |
| |
| m[offset + 12] = 0.0f; |
| m[offset + 13] = 0.0f; |
| m[offset + 14] = 2.0f * zFar * zNear * rangeReciprocal; |
| m[offset + 15] = 0.0f; |
| } |
| |
| /** |
| * Computes the length of a vector. |
| * |
| * @param x x coordinate of a vector |
| * @param y y coordinate of a vector |
| * @param z z coordinate of a vector |
| * @return the length of a vector |
| */ |
| public static float length(float x, float y, float z) { |
| return (float) Math.sqrt(x * x + y * y + z * z); |
| } |
| |
| /** |
| * Sets matrix m to the identity matrix. |
| * |
| * @param sm returns the result |
| * @param smOffset index into sm where the result matrix starts |
| */ |
| public static void setIdentityM(float[] sm, int smOffset) { |
| for (int i=0 ; i<16 ; i++) { |
| sm[smOffset + i] = 0; |
| } |
| for(int i = 0; i < 16; i += 5) { |
| sm[smOffset + i] = 1.0f; |
| } |
| } |
| |
| /** |
| * Scales matrix m by x, y, and z, putting the result in sm. |
| * <p> |
| * m and sm must not overlap. |
| * |
| * @param sm returns the result |
| * @param smOffset index into sm where the result matrix starts |
| * @param m source matrix |
| * @param mOffset index into m where the source matrix starts |
| * @param x scale factor x |
| * @param y scale factor y |
| * @param z scale factor z |
| */ |
| public static void scaleM(float[] sm, int smOffset, |
| float[] m, int mOffset, |
| float x, float y, float z) { |
| for (int i=0 ; i<4 ; i++) { |
| int smi = smOffset + i; |
| int mi = mOffset + i; |
| sm[ smi] = m[ mi] * x; |
| sm[ 4 + smi] = m[ 4 + mi] * y; |
| sm[ 8 + smi] = m[ 8 + mi] * z; |
| sm[12 + smi] = m[12 + mi]; |
| } |
| } |
| |
| /** |
| * Scales matrix m in place by sx, sy, and sz. |
| * |
| * @param m matrix to scale |
| * @param mOffset index into m where the matrix starts |
| * @param x scale factor x |
| * @param y scale factor y |
| * @param z scale factor z |
| */ |
| public static void scaleM(float[] m, int mOffset, |
| float x, float y, float z) { |
| for (int i=0 ; i<4 ; i++) { |
| int mi = mOffset + i; |
| m[ mi] *= x; |
| m[ 4 + mi] *= y; |
| m[ 8 + mi] *= z; |
| } |
| } |
| |
| /** |
| * Translates matrix m by x, y, and z, putting the result in tm. |
| * <p> |
| * m and tm must not overlap. |
| * |
| * @param tm returns the result |
| * @param tmOffset index into sm where the result matrix starts |
| * @param m source matrix |
| * @param mOffset index into m where the source matrix starts |
| * @param x translation factor x |
| * @param y translation factor y |
| * @param z translation factor z |
| */ |
| public static void translateM(float[] tm, int tmOffset, |
| float[] m, int mOffset, |
| float x, float y, float z) { |
| for (int i=0 ; i<12 ; i++) { |
| tm[tmOffset + i] = m[mOffset + i]; |
| } |
| for (int i=0 ; i<4 ; i++) { |
| int tmi = tmOffset + i; |
| int mi = mOffset + i; |
| tm[12 + tmi] = m[mi] * x + m[4 + mi] * y + m[8 + mi] * z + |
| m[12 + mi]; |
| } |
| } |
| |
| /** |
| * Translates matrix m by x, y, and z in place. |
| * |
| * @param m matrix |
| * @param mOffset index into m where the matrix starts |
| * @param x translation factor x |
| * @param y translation factor y |
| * @param z translation factor z |
| */ |
| public static void translateM( |
| float[] m, int mOffset, |
| float x, float y, float z) { |
| for (int i=0 ; i<4 ; i++) { |
| int mi = mOffset + i; |
| m[12 + mi] += m[mi] * x + m[4 + mi] * y + m[8 + mi] * z; |
| } |
| } |
| |
| /** |
| * Rotates matrix m by angle a (in degrees) around the axis (x, y, z). |
| * <p> |
| * m and rm must not overlap. |
| * |
| * @param rm returns the result |
| * @param rmOffset index into rm where the result matrix starts |
| * @param m source matrix |
| * @param mOffset index into m where the source matrix starts |
| * @param a angle to rotate in degrees |
| * @param x X axis component |
| * @param y Y axis component |
| * @param z Z axis component |
| */ |
| public static void rotateM(float[] rm, int rmOffset, |
| float[] m, int mOffset, |
| float a, float x, float y, float z) { |
| float[] tmp = ThreadTmp.get(); |
| setRotateM(tmp, 16, a, x, y, z); |
| multiplyMM(rm, rmOffset, m, mOffset, tmp, 16); |
| } |
| |
| /** |
| * Rotates matrix m in place by angle a (in degrees) |
| * around the axis (x, y, z). |
| * |
| * @param m source matrix |
| * @param mOffset index into m where the matrix starts |
| * @param a angle to rotate in degrees |
| * @param x X axis component |
| * @param y Y axis component |
| * @param z Z axis component |
| */ |
| public static void rotateM(float[] m, int mOffset, |
| float a, float x, float y, float z) { |
| rotateM(m, mOffset, m, mOffset, a, x, y, z); |
| } |
| |
| /** |
| * Creates a matrix for rotation by angle a (in degrees) |
| * around the axis (x, y, z). |
| * <p> |
| * An optimized path will be used for rotation about a major axis |
| * (e.g. x=1.0f y=0.0f z=0.0f). |
| * |
| * @param rm returns the result |
| * @param rmOffset index into rm where the result matrix starts |
| * @param a angle to rotate in degrees |
| * @param x X axis component |
| * @param y Y axis component |
| * @param z Z axis component |
| */ |
| public static void setRotateM(float[] rm, int rmOffset, |
| float a, float x, float y, float z) { |
| rm[rmOffset + 3] = 0; |
| rm[rmOffset + 7] = 0; |
| rm[rmOffset + 11]= 0; |
| rm[rmOffset + 12]= 0; |
| rm[rmOffset + 13]= 0; |
| rm[rmOffset + 14]= 0; |
| rm[rmOffset + 15]= 1; |
| a *= (float) (Math.PI / 180.0f); |
| float s = (float) Math.sin(a); |
| float c = (float) Math.cos(a); |
| if (1.0f == x && 0.0f == y && 0.0f == z) { |
| rm[rmOffset + 5] = c; rm[rmOffset + 10]= c; |
| rm[rmOffset + 6] = s; rm[rmOffset + 9] = -s; |
| rm[rmOffset + 1] = 0; rm[rmOffset + 2] = 0; |
| rm[rmOffset + 4] = 0; rm[rmOffset + 8] = 0; |
| rm[rmOffset + 0] = 1; |
| } else if (0.0f == x && 1.0f == y && 0.0f == z) { |
| rm[rmOffset + 0] = c; rm[rmOffset + 10]= c; |
| rm[rmOffset + 8] = s; rm[rmOffset + 2] = -s; |
| rm[rmOffset + 1] = 0; rm[rmOffset + 4] = 0; |
| rm[rmOffset + 6] = 0; rm[rmOffset + 9] = 0; |
| rm[rmOffset + 5] = 1; |
| } else if (0.0f == x && 0.0f == y && 1.0f == z) { |
| rm[rmOffset + 0] = c; rm[rmOffset + 5] = c; |
| rm[rmOffset + 1] = s; rm[rmOffset + 4] = -s; |
| rm[rmOffset + 2] = 0; rm[rmOffset + 6] = 0; |
| rm[rmOffset + 8] = 0; rm[rmOffset + 9] = 0; |
| rm[rmOffset + 10]= 1; |
| } else { |
| float len = length(x, y, z); |
| if (1.0f != len) { |
| float recipLen = 1.0f / len; |
| x *= recipLen; |
| y *= recipLen; |
| z *= recipLen; |
| } |
| float nc = 1.0f - c; |
| float xy = x * y; |
| float yz = y * z; |
| float zx = z * x; |
| float xs = x * s; |
| float ys = y * s; |
| float zs = z * s; |
| rm[rmOffset + 0] = x*x*nc + c; |
| rm[rmOffset + 4] = xy*nc - zs; |
| rm[rmOffset + 8] = zx*nc + ys; |
| rm[rmOffset + 1] = xy*nc + zs; |
| rm[rmOffset + 5] = y*y*nc + c; |
| rm[rmOffset + 9] = yz*nc - xs; |
| rm[rmOffset + 2] = zx*nc - ys; |
| rm[rmOffset + 6] = yz*nc + xs; |
| rm[rmOffset + 10] = z*z*nc + c; |
| } |
| } |
| |
| /** |
| * Converts Euler angles to a rotation matrix. |
| * |
| * @param rm returns the result |
| * @param rmOffset index into rm where the result matrix starts |
| * @param x angle of rotation, in degrees |
| * @param y is broken, do not use |
| * @param z angle of rotation, in degrees |
| * |
| * @deprecated This method is incorrect around the y axis. This method is |
| * deprecated and replaced (below) by setRotateEulerM2 which |
| * behaves correctly |
| */ |
| @Deprecated |
| public static void setRotateEulerM(float[] rm, int rmOffset, |
| float x, float y, float z) { |
| x *= (float) (Math.PI / 180.0f); |
| y *= (float) (Math.PI / 180.0f); |
| z *= (float) (Math.PI / 180.0f); |
| float cx = (float) Math.cos(x); |
| float sx = (float) Math.sin(x); |
| float cy = (float) Math.cos(y); |
| float sy = (float) Math.sin(y); |
| float cz = (float) Math.cos(z); |
| float sz = (float) Math.sin(z); |
| float cxsy = cx * sy; |
| float sxsy = sx * sy; |
| |
| rm[rmOffset + 0] = cy * cz; |
| rm[rmOffset + 1] = -cy * sz; |
| rm[rmOffset + 2] = sy; |
| rm[rmOffset + 3] = 0.0f; |
| |
| rm[rmOffset + 4] = cxsy * cz + cx * sz; |
| rm[rmOffset + 5] = -cxsy * sz + cx * cz; |
| rm[rmOffset + 6] = -sx * cy; |
| rm[rmOffset + 7] = 0.0f; |
| |
| rm[rmOffset + 8] = -sxsy * cz + sx * sz; |
| rm[rmOffset + 9] = sxsy * sz + sx * cz; |
| rm[rmOffset + 10] = cx * cy; |
| rm[rmOffset + 11] = 0.0f; |
| |
| rm[rmOffset + 12] = 0.0f; |
| rm[rmOffset + 13] = 0.0f; |
| rm[rmOffset + 14] = 0.0f; |
| rm[rmOffset + 15] = 1.0f; |
| } |
| |
| /** |
| * Converts Euler angles to a rotation matrix. |
| * |
| * @param rm returns the result |
| * @param rmOffset index into rm where the result matrix starts |
| * @param x angle of rotation, in degrees |
| * @param y angle of rotation, in degrees |
| * @param z angle of rotation, in degrees |
| * |
| * @throws IllegalArgumentException if rm is null; |
| * or if rmOffset + 16 > rm.length; |
| * rmOffset < 0 |
| */ |
| public static void setRotateEulerM2(@NonNull float[] rm, int rmOffset, |
| float x, float y, float z) { |
| if (rm == null) { |
| throw new IllegalArgumentException("rm == null"); |
| } |
| if (rmOffset < 0) { |
| throw new IllegalArgumentException("rmOffset < 0"); |
| } |
| if (rm.length < rmOffset + 16) { |
| throw new IllegalArgumentException("rm.length < rmOffset + 16"); |
| } |
| |
| x *= (float) (Math.PI / 180.0f); |
| y *= (float) (Math.PI / 180.0f); |
| z *= (float) (Math.PI / 180.0f); |
| float cx = (float) Math.cos(x); |
| float sx = (float) Math.sin(x); |
| float cy = (float) Math.cos(y); |
| float sy = (float) Math.sin(y); |
| float cz = (float) Math.cos(z); |
| float sz = (float) Math.sin(z); |
| float cxsy = cx * sy; |
| float sxsy = sx * sy; |
| |
| rm[rmOffset + 0] = cy * cz; |
| rm[rmOffset + 1] = -cy * sz; |
| rm[rmOffset + 2] = sy; |
| rm[rmOffset + 3] = 0.0f; |
| |
| rm[rmOffset + 4] = sxsy * cz + cx * sz; |
| rm[rmOffset + 5] = -sxsy * sz + cx * cz; |
| rm[rmOffset + 6] = -sx * cy; |
| rm[rmOffset + 7] = 0.0f; |
| |
| rm[rmOffset + 8] = -cxsy * cz + sx * sz; |
| rm[rmOffset + 9] = cxsy * sz + sx * cz; |
| rm[rmOffset + 10] = cx * cy; |
| rm[rmOffset + 11] = 0.0f; |
| |
| rm[rmOffset + 12] = 0.0f; |
| rm[rmOffset + 13] = 0.0f; |
| rm[rmOffset + 14] = 0.0f; |
| rm[rmOffset + 15] = 1.0f; |
| } |
| |
| /** |
| * Defines a viewing transformation in terms of an eye point, a center of |
| * view, and an up vector. |
| * |
| * @param rm returns the result |
| * @param rmOffset index into rm where the result matrix starts |
| * @param eyeX eye point X |
| * @param eyeY eye point Y |
| * @param eyeZ eye point Z |
| * @param centerX center of view X |
| * @param centerY center of view Y |
| * @param centerZ center of view Z |
| * @param upX up vector X |
| * @param upY up vector Y |
| * @param upZ up vector Z |
| */ |
| public static void setLookAtM(float[] rm, int rmOffset, |
| float eyeX, float eyeY, float eyeZ, |
| float centerX, float centerY, float centerZ, float upX, float upY, |
| float upZ) { |
| |
| // See the OpenGL GLUT documentation for gluLookAt for a description |
| // of the algorithm. We implement it in a straightforward way: |
| |
| float fx = centerX - eyeX; |
| float fy = centerY - eyeY; |
| float fz = centerZ - eyeZ; |
| |
| // Normalize f |
| float rlf = 1.0f / Matrix.length(fx, fy, fz); |
| fx *= rlf; |
| fy *= rlf; |
| fz *= rlf; |
| |
| // compute s = f x up (x means "cross product") |
| float sx = fy * upZ - fz * upY; |
| float sy = fz * upX - fx * upZ; |
| float sz = fx * upY - fy * upX; |
| |
| // and normalize s |
| float rls = 1.0f / Matrix.length(sx, sy, sz); |
| sx *= rls; |
| sy *= rls; |
| sz *= rls; |
| |
| // compute u = s x f |
| float ux = sy * fz - sz * fy; |
| float uy = sz * fx - sx * fz; |
| float uz = sx * fy - sy * fx; |
| |
| rm[rmOffset + 0] = sx; |
| rm[rmOffset + 1] = ux; |
| rm[rmOffset + 2] = -fx; |
| rm[rmOffset + 3] = 0.0f; |
| |
| rm[rmOffset + 4] = sy; |
| rm[rmOffset + 5] = uy; |
| rm[rmOffset + 6] = -fy; |
| rm[rmOffset + 7] = 0.0f; |
| |
| rm[rmOffset + 8] = sz; |
| rm[rmOffset + 9] = uz; |
| rm[rmOffset + 10] = -fz; |
| rm[rmOffset + 11] = 0.0f; |
| |
| rm[rmOffset + 12] = 0.0f; |
| rm[rmOffset + 13] = 0.0f; |
| rm[rmOffset + 14] = 0.0f; |
| rm[rmOffset + 15] = 1.0f; |
| |
| translateM(rm, rmOffset, -eyeX, -eyeY, -eyeZ); |
| } |
| } |