| /* |
| * 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; |
| |
| /** |
| * Matrix math utilities. These methods operate on OpenGL ES format |
| * matrices and vectors stored in float arrays. |
| * |
| * 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 row x 1 column column-vectors stored in order: |
| * <pre> |
| * v[offset + 0] |
| * v[offset + 1] |
| * v[offset + 2] |
| * v[offset + 3] |
| * </pre> |
| * |
| */ |
| public class Matrix { |
| /** |
| * Multiply two 4x4 matrices together and store 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. |
| * |
| * The same float array may be passed for result, lhs, and/or rhs. However, |
| * the result element values are undefined if the result elements overlap |
| * either 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 if result, lhs, or rhs are null, or if |
| * resultOffset + 16 > result.length or lhsOffset + 16 > lhs.length or |
| * rhsOffset + 16 > rhs.length. |
| */ |
| public static native void multiplyMM(float[] result, int resultOffset, |
| float[] lhs, int lhsOffset, float[] rhs, int rhsOffset); |
| |
| /** |
| * Multiply a 4 element vector by a 4x4 matrix and store the result in a 4 |
| * element column vector. In matrix notation: result = lhs x rhs |
| * |
| * The same float array may be passed for resultVec, lhsMat, and/or rhsVec. |
| * However, the resultVec element values are undefined if the resultVec |
| * elements overlap either the lhsMat or rhsVec 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 if resultVec, lhsMat, |
| * or rhsVec are null, or if resultVecOffset + 4 > resultVec.length |
| * or lhsMatOffset + 16 > lhsMat.length or |
| * rhsVecOffset + 4 > rhsVec.length. |
| */ |
| public static native void multiplyMV(float[] resultVec, |
| int resultVecOffset, float[] lhsMat, int lhsMatOffset, |
| float[] rhsVec, int rhsVecOffset); |
| |
| /** |
| * Transposes a 4 x 4 matrix. |
| * |
| * @param mTrans the array that holds the output inverted matrix |
| * @param mTransOffset an offset into mInv where the inverted matrix is |
| * stored. |
| * @param m the input array |
| * @param mOffset an offset into m where the 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. |
| * |
| * @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 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 |
| |
| // array of transpose source matrix |
| float[] src = new float[16]; |
| |
| // transpose matrix |
| transposeM(src, 0, m, mOffset); |
| |
| // temp array for pairs |
| float[] tmp = new float[12]; |
| |
| // calculate pairs for first 8 elements (cofactors) |
| tmp[0] = src[10] * src[15]; |
| tmp[1] = src[11] * src[14]; |
| tmp[2] = src[9] * src[15]; |
| tmp[3] = src[11] * src[13]; |
| tmp[4] = src[9] * src[14]; |
| tmp[5] = src[10] * src[13]; |
| tmp[6] = src[8] * src[15]; |
| tmp[7] = src[11] * src[12]; |
| tmp[8] = src[8] * src[14]; |
| tmp[9] = src[10] * src[12]; |
| tmp[10] = src[8] * src[13]; |
| tmp[11] = src[9] * src[12]; |
| |
| // Holds the destination matrix while we're building it up. |
| float[] dst = new float[16]; |
| |
| // calculate first 8 elements (cofactors) |
| dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7]; |
| dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7]; |
| dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7]; |
| dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7]; |
| dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7]; |
| dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7]; |
| dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6]; |
| dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6]; |
| dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3]; |
| dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3]; |
| dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3]; |
| dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3]; |
| dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3]; |
| dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3]; |
| dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2]; |
| dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2]; |
| |
| // calculate pairs for second 8 elements (cofactors) |
| tmp[0] = src[2] * src[7]; |
| tmp[1] = src[3] * src[6]; |
| tmp[2] = src[1] * src[7]; |
| tmp[3] = src[3] * src[5]; |
| tmp[4] = src[1] * src[6]; |
| tmp[5] = src[2] * src[5]; |
| tmp[6] = src[0] * src[7]; |
| tmp[7] = src[3] * src[4]; |
| tmp[8] = src[0] * src[6]; |
| tmp[9] = src[2] * src[4]; |
| tmp[10] = src[0] * src[5]; |
| tmp[11] = src[1] * src[4]; |
| |
| // calculate second 8 elements (cofactors) |
| dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15]; |
| dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15]; |
| dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15]; |
| dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15]; |
| dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15]; |
| dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15]; |
| dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14]; |
| dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14]; |
| dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9]; |
| dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10]; |
| dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10]; |
| dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8]; |
| dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8]; |
| dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9]; |
| dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9]; |
| dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8]; |
| |
| // calculate determinant |
| float det = |
| src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] |
| * dst[3]; |
| |
| if (det == 0.0f) { |
| |
| } |
| |
| // calculate matrix inverse |
| det = 1 / det; |
| for (int j = 0; j < 16; j++) |
| mInv[j + mInvOffset] = dst[j] * det; |
| |
| 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; |
| } |
| |
| |
| /** |
| * Define a projection matrix in terms of six 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 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 = 2.0f * ((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; |
| } |
| |
| /** |
| * 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 |
| * @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 |
| * @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) |
| * @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 scale factor x |
| * @param y scale factor y |
| * @param z scale factor z |
| */ |
| public static void rotateM(float[] rm, int rmOffset, |
| float[] m, int mOffset, |
| float a, float x, float y, float z) { |
| float[] r = new float[16]; |
| setRotateM(r, 0, a, x, y, z); |
| multiplyMM(rm, rmOffset, m, mOffset, r, 0); |
| } |
| |
| /** |
| * 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 scale factor x |
| * @param y scale factor y |
| * @param z scale factor z |
| */ |
| public static void rotateM(float[] m, int mOffset, |
| float a, float x, float y, float z) { |
| float[] temp = new float[32]; |
| setRotateM(temp, 0, a, x, y, z); |
| multiplyMM(temp, 16, m, mOffset, temp, 0); |
| System.arraycopy(temp, 16, m, mOffset, 16); |
| } |
| |
| /** |
| * Rotates matrix m by angle a (in degrees) around the axis (x, y, z) |
| * @param rm returns the result |
| * @param rmOffset index into rm where the result matrix starts |
| * @param a angle to rotate in degrees |
| * @param x scale factor x |
| * @param y scale factor y |
| * @param z scale factor z |
| */ |
| 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 angle of rotation, in degrees |
| * @param z angle of rotation, in degrees |
| */ |
| 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; |
| } |
| } |