com.codename1.impl.ios.Matrix Maven / Gradle / Ivy
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.impl.ios;
/**
* Encapsulates a 4x4 transformation matrix that can be used to apply 3D transformations
* to a {@link com.codename1.ui.Graphics} context. This can also be used for 2D transformations,
* by only using the upper left 3x3 grid of the matrix.
*
* Internal Representation
*
* Although matrix data can be set in several different formats (See {@link #setData}), the internal representation
* is always that of a 4x4 matrix stored in a 16-element {@literal float} array in row-major order.
* If you are working with 2D transformations only, then the upper left sub-matrix will contain
* your 3x3 affine transformation, and the 4th row and 4th columns will be zeroes, except in the lower-right most
* column, which will be a {@literal 1}.
*
* @author shannah
* @see com.codename1.ui.Graphics#setTransform
* @see com.codename1.ui.Graphics#getTransform
*/
public final class Matrix {
public static final int TYPE_UNKNOWN = -1;
public static final int TYPE_IDENTITY = 0;
public static final int TYPE_TRANSLATION = 1;
public static final int TYPE_ROTATION = 2;
public static final int TYPE_SCALE = 3;
public static final int M00=0;
public static final int M01=4;
public static final int M02=8;
public static final int M03=12;
public static final int M10=1;
public static final int M11=5;
public static final int M12=9;
public static final int M13=13;
public static final int M20=2;
public static final int M21=6;
public static final int M22=10;
public static final int M23=14;
public static final int M30=3;
public static final int M31=7;
public static final int M32=11;
public static final int M33=15;
public final float[] data;
private int type = TYPE_UNKNOWN;
private Factory factory;
public static class Factory {
private float[] sTemp = new float[32];
private static Factory defaultFactory = null;
public static Factory getDefault() {
if (defaultFactory == null) {
defaultFactory = new Factory();
}
return defaultFactory;
}
public Matrix makeMatrix(float[] data) {
Matrix m = new Matrix(data);
m.factory = this;
return m;
}
public Matrix makeIdentity() {
Matrix out = makeMatrix(null);
out.factory = this;
out.type = TYPE_IDENTITY;
return out;
}
public Matrix makeRotation(float angle, float x, float y, float z) {
float[] m = new float[16];
MatrixUtil.setRotateM(m, 0, (float) (angle * 180f / Math.PI), x, y, z);
Matrix out = makeMatrix(m);
out.factory = this;
out.type = TYPE_ROTATION;
return out;
}
public Matrix makeTranslation(float x, float y, float z) {
Matrix m = makeIdentity();
MatrixUtil.translateM(m.data, 0, x, y, z);
m.factory = this;
m.type = TYPE_TRANSLATION;
return m;
}
public Matrix makePerspective(float fovy, float aspect, float zNear, float zFar) {
float[] m = new float[16];
MatrixUtil.perspectiveM(m, 0, (float) (fovy * 180f / Math.PI), aspect, zNear, zFar);
Matrix out = new Matrix(m);
out.factory = this;
return out;
}
public Matrix makeOrtho(float left, float right, float bottom, float top,
float near, float far) {
float[] m = new float[16];
MatrixUtil.orthoM(m, 0, left, right, bottom, top, near, far);
Matrix out = new Matrix(m);
out.factory = this;
return out;
}
public Matrix makeCamera(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY,
float upZ) {
float[] m = new float[16];
MatrixUtil.setLookAtM(m, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
Matrix out = new Matrix(m);
out.factory = this;
return out;
}
}
public static Matrix make(float[] data) {
return Factory.getDefault().makeMatrix(data);
}
public static Matrix makeIdentity() {
return Factory.getDefault().makeMatrix(null);
}
public static Matrix makeTranslation(float x, float y, float z){
return Factory.getDefault().makeTranslation(x, y, z);
}
public void setTranslation(float x, float y, float z) {
reset();
MatrixUtil.translateM(data, 0, x, y, z);
type = TYPE_TRANSLATION;
}
public static Matrix makeRotation(float angle, float x, float y, float z) {
return Factory.getDefault().makeRotation(angle, x, y, z);
}
public static Matrix makeOrtho(float left, float right, float bottom, float top,
float near, float far) {
return Factory.getDefault().makeOrtho(left, right, bottom, top, near, far);
}
public static Matrix makePerspective(float fovy, float aspect, float zNear, float zFar) {
return Factory.getDefault().makePerspective(fovy, aspect, zNear, zFar);
}
public static Matrix makeCamera(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY,
float upZ) {
return Factory.getDefault().makeCamera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
}
public void rotate(float a, float x, float y, float z) {
MatrixUtil.setRotateM(factory.sTemp, 0, (float) (a * 180f / Math.PI), x, y, z);
MatrixUtil.multiplyMM(factory.sTemp, 16, data, 0, factory.sTemp, 0);
System.arraycopy(factory.sTemp, 16, data, 0, 16);
if ( type == TYPE_IDENTITY ){
type = TYPE_ROTATION;
} else {
type = TYPE_UNKNOWN;
}
}
public void translate(float x, float y, float z) {
MatrixUtil.translateM(data, 0, x, y, z);
if ( type == TYPE_IDENTITY || type == TYPE_TRANSLATION ){
type = TYPE_TRANSLATION;
} else {
type = TYPE_UNKNOWN;
}
}
public void scale(float x, float y, float z) {
MatrixUtil.scaleM(data, 0, x, y, z);
if ( type == TYPE_IDENTITY || type == TYPE_SCALE ){
type = TYPE_SCALE;
} else {
type = TYPE_UNKNOWN;
}
}
public void setPerspective(float fovy, float aspect, float zNear, float zFar) {
MatrixUtil.perspectiveM(data, 0, (float) (fovy * 180f / Math.PI), aspect, zNear, zFar);
type = TYPE_UNKNOWN;
}
public void setOrtho(float left, float right, float bottom, float top,
float near, float far) {
MatrixUtil.orthoM(data, 0, left, right, bottom, top, near, far);
type = TYPE_UNKNOWN;
}
public void setCamera(float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY,
float upZ) {
MatrixUtil.setLookAtM(data, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
type = TYPE_UNKNOWN;
}
public void setIdentity() {
reset();
}
public void transformCoord(float[] pIn, float[] pOut) {
MatrixUtil.transformPoints(data, Math.min(3, pIn.length), pIn, 0, pOut, 0, 1);
}
public String toString() {
//StringBuilder sb = new StringBuilder();
return "[[" + data[0] + "," + data[4] + "," + data[8] + "," + data[12] + "]\n"
+ "[" + data[1] + "," + data[5] + "," + data[9] + "," + data[13] + "]\n"
+ "[" + data[2] + "," + data[6] + "," + data[10] + "," + data[14] + "]\n"
+ "[" + data[3] + "," + data[7] + "," + data[11] + "," + data[15] + "]";
}
public boolean equals(Matrix m2){
if ( m2 == null ){
return false;
}
for ( int i=0; i<16; i++){
if ( Math.abs(this.data[i]-m2.data[i]) > 0.0001 ){
return false;
}
}
return true;
}
public boolean isIdentity() {
for (int i = 0; i < 16; i++) {
if (i % 5 == 0 && Math.abs(data[i] - 1f) > 0.0001) {
return false;
} else if (i % 5 != 0 && Math.abs(data[i]) > 0.0001) {
return false;
}
}
return true;
}
public boolean invert() {
boolean res = MatrixUtil.invertM(factory.sTemp, 0, data, 0);
if (!res) {
return res;
} else {
System.arraycopy(factory.sTemp, 0, data, 0, 16);
return res;
}
}
/**
* Constructor. Copies data from the provided data array. See
* {@link #setData} documentation for information acceptable formats for the
* {@literal m} array.
*
* @param m An array containing data for the matrix. This can be in several
* different formats. See {@link #setData} for a list of acceptable formats.
*
* @see #setData
*/
private Matrix(float[] m) {
if (m == null) {
m = new float[]{1f};
}
if (m.length == 16) {
data = m;
} else {
data = new float[16];
setData(m);
}
}
public void transformPoints(int pointSize, float[] in, int srcPos, float[] out, int destPos, int numPoints) {
MatrixUtil.transformPoints(data, pointSize, in, srcPos, out, destPos, numPoints);
}
public void concatenate(Matrix m){
//MatrixUtil.setRotateM(factory.sTemp, 0, (float) (a * 180f / Math.PI), x, y, z);
MatrixUtil.multiplyMM(factory.sTemp, 16, data, 0, m.data, 0);
System.arraycopy(factory.sTemp, 16, data, 0, 16);
type = TYPE_UNKNOWN;
}
/**
* Resets the transformation to the identify matrix
*/
public void reset() {
for (int i = 0; i < 16; i++) {
data[i] = 0;
}
data[0] = data[5] = data[10] = data[15] = 1;
type = TYPE_IDENTITY;
}
/**
* Obtains a reference to the 4x4 matrix cell data in row-major order.
*
* @return A 16-element{@literal float} array representing the 4x4 matrix
* data in row-major order.
*/
public float[] getData() {
return data;
}
/**
* Sets the matrix data. This will accept the data in several different
* formats to facilitate the creation of common matrix use-cases.
* Acceptable Formats
*
*
* Array
* Length Interpretation Example Resulting 4x4
* Matrix
*
* 1
* Apply both {@literal x} and {@literal y} scaling with a single
* value.
* {@code setData(new float[]{2f});}
* {@literal
* [2,0,0,0],
* [0,2,0,0],
* [0,0,1,0],
* [0,0,0,1]}
*
*
* 2
* Applies {@literal x} and {@literal y} scaling. First element is
* {@literal x} scale. Second element is {@literal y} scale.
* {@code setData(new float[]{2f, 3f});}
* {@literal
* [2,0,0,0],
* [0,3,0,0],
* [0,0,1,0],
* [0,0,0,1]}
*
*
* 4
* Recognized as a 2x2 2D transformation matrix.
* {@code setData(new float[]{1f, 2f, 3f, 4f});}
* {@literal
* [1,2,0,0],
* [3,4,0,0],
* [0,0,1,0],
* [0,0,0,1]}
*
*
* 6
* An affine transformation.
* {@code setData(new float[]{1f, 2f, 3f, 4f, 5f, 6f});}
* {@literal
* [1,2,3,0],
* [4,5,6,0],
* [0,0,1,0],
* [0,0,0,1]}
*
*
* 9
* A 3x3 matrix.
* {@code setData(new float[]{1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f});}
* {@literal
* [1,2,3,0],
* [4,5,6,0],
* [7,8,9,0],
* [0,0,0,1]}
*
*
* 12
* The top 3 rows of the 4x4 matrix. This is all the information
* necessary for a 3D transformation since the last row is always
* [0,0,0,1].
* {@code setData(new float[]{1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 10f, 11f, 12f});}
* {@literal
* [ 1, 2, 3, 4],
* [ 5, 6, 7, 8],
* [ 9, 10,11,12],
* [ 0, 0, 0, 1]}
*
*
* 16
* A 4x4 transformation matrix.
* {@code setData(new float[]{1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 10f, 11f, 12f, 13f, 14f, 15f, 16f});}
* {@literal
* [ 1, 2, 3, 4],
* [ 5, 6, 7, 8],
* [ 9,10,11,12],
* [13,14,15,16]}
*
*
*
* @param m The data to populate the matrix. This will always replace the
* matrix data in full.
*/
public void setData(float[] m) {
if (m == null) {
reset();
return;
}
switch (m.length) {
case 1:
reset();
data[0] = m[0];
data[5] = m[0];
break;
case 2:
reset();
data[0] = m[0];
data[5] = m[1];
break;
case 4:
// This is just a 2D transformation
reset();
data[0] = m[0];
data[1] = m[1];
data[4] = m[2];
data[5] = m[3];
break;
case 6:
reset();
data[0] = m[0];
data[1] = m[1];
data[2] = m[2];
data[4] = m[3];
data[5] = m[4];
data[6] = m[5];
break;
case 9:
reset();
data[0] = m[0];
data[1] = m[1];
data[2] = m[2];
data[4] = m[3];
data[5] = m[4];
data[6] = m[5];
data[8] = m[6];
data[9] = m[7];
data[10] = m[8];
break;
case 12:
reset();
System.arraycopy(m, 0, data, 0, 12);
break;
case 16:
System.arraycopy(m, 0, data, 0, 16);
break;
default:
throw new IllegalArgumentException("Transforms must be array of length 1, 2, 4, 6, 9, 12, or 16");
}
}
public Matrix copy() {
float[] data = new float[16];
System.arraycopy(this.data, 0, data, 0, 16);
return Matrix.make(data);
}
/*
* 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.
*/
/**
* 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:
*
* 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]
*
* Vectors are 4 x 1 column vectors stored in order:
*
* v[offset + 0]
* v[offset + 1]
* v[offset + 2]
* v[offset + 3]
*/
private static class MatrixUtil {
private static float clamp(float val){
float abs = Math.abs(val);
if ( Math.abs(abs-Math.round(abs)) < 0.001 ){
return Math.round(val);
}
return val;
}
public static native void transformPoints(float[] data, int pointSize, float[] in, int srcPos, float[] out, int destPos, int numPoints);
/**
* Temporary memory for operations that need temporary matrix data.
*/
//private final static float[] sTemp = new float[32];
/**
* 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.
*
* 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 native static void multiplyMM(float[] result, int resultOffset,
float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);/* {
float[] tmp = result;
float[] mata = lhs;
float[] matb = rhs;
int a0 = lhsOffset;
int b0 = rhsOffset;
//int r0 = resultOffset;
tmp[M00+resultOffset] = clamp(mata[M00+a0] * matb[M00+b0] + mata[M01] * matb[M10+b0] + mata[M02+a0] * matb[M20+b0] + mata[M03+a0] * matb[M30+b0]);
tmp[M01+resultOffset] = clamp(mata[M00+a0] * matb[M01+b0] + mata[M01] * matb[M11+b0] + mata[M02+a0] * matb[M21+b0] + mata[M03+a0] * matb[M31+b0]);
tmp[M02+resultOffset] = clamp(mata[M00+a0] * matb[M02+b0] + mata[M01] * matb[M12+b0] + mata[M02+a0] * matb[M22+b0] + mata[M03+a0] * matb[M32+b0]);
tmp[M03+resultOffset] = clamp(mata[M00+a0] * matb[M03+b0] + mata[M01] * matb[M13+b0] + mata[M02+a0] * matb[M23+b0] + mata[M03+a0] * matb[M33+b0]);
tmp[M10+resultOffset] = clamp(mata[M10+a0] * matb[M00+b0] + mata[M11] * matb[M10+b0] + mata[M12+a0] * matb[M20+b0] + mata[M13+a0] * matb[M30+b0]);
tmp[M11+resultOffset] = clamp(mata[M10+a0] * matb[M01+b0] + mata[M11] * matb[M11+b0] + mata[M12+a0] * matb[M21+b0] + mata[M13+a0] * matb[M31+b0]);
tmp[M12+resultOffset] = clamp(mata[M10+a0] * matb[M02+b0] + mata[M11] * matb[M12+b0] + mata[M12+a0] * matb[M22+b0] + mata[M13+a0] * matb[M32+b0]);
tmp[M13+resultOffset] = clamp(mata[M10+a0] * matb[M03+b0] + mata[M11] * matb[M13+b0] + mata[M12+a0] * matb[M23+b0] + mata[M13+a0] * matb[M33+b0]);
tmp[M20+resultOffset] = clamp(mata[M20+a0] * matb[M00+b0] + mata[M21] * matb[M10+b0] + mata[M22+a0] * matb[M20+b0] + mata[M23+a0] * matb[M30+b0]);
tmp[M21+resultOffset] = clamp(mata[M20+a0] * matb[M01+b0] + mata[M21] * matb[M11+b0] + mata[M22+a0] * matb[M21+b0] + mata[M23+a0] * matb[M31+b0]);
tmp[M22+resultOffset] = clamp(mata[M20+a0] * matb[M02+b0] + mata[M21] * matb[M12+b0] + mata[M22+a0] * matb[M22+b0] + mata[M23+a0] * matb[M32+b0]);
tmp[M23+resultOffset] = clamp(mata[M20+a0] * matb[M03+b0] + mata[M21] * matb[M13+b0] + mata[M22+a0] * matb[M23+b0] + mata[M23+a0] * matb[M33+b0]);
tmp[M30+resultOffset] = clamp(mata[M30+a0] * matb[M00+b0] + mata[M31] * matb[M10+b0] + mata[M32+a0] * matb[M20+b0] + mata[M33+a0] * matb[M30+b0]);
tmp[M31+resultOffset] = clamp(mata[M30+a0] * matb[M01+b0] + mata[M31] * matb[M11+b0] + mata[M32+a0] * matb[M21+b0] + mata[M33+a0] * matb[M31+b0]);
tmp[M32+resultOffset] = clamp(mata[M30+a0] * matb[M02+b0] + mata[M31] * matb[M12+b0] + mata[M32+a0] * matb[M22+b0] + mata[M33+a0] * matb[M32+b0]);
tmp[M33+resultOffset] = clamp(mata[M30+a0] * matb[M03+b0] + mata[M31] * matb[M13+b0] + mata[M32+a0] * matb[M23+b0] + mata[M33+a0] * matb[M33+b0]);
}*/
/**
* 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
*
* 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 void multiplyMV(float[] resultVec,
int resultVecOffset, float[] lhsMat, int lhsMatOffset,
float[] rhsVec, int rhsVecOffset) {
resultVec[resultVecOffset] = clamp(lhsMat[lhsMatOffset] * rhsVec[rhsVecOffset] +
lhsMat[lhsMatOffset+4] * rhsVec[rhsVecOffset+1] +
lhsMat[lhsMatOffset+8] * rhsVec[rhsVecOffset+2] +
lhsMat[lhsMatOffset+12] * rhsVec[rhsVecOffset+3]);
resultVec[resultVecOffset+1] = clamp(lhsMat[lhsMatOffset+1] * rhsVec[rhsVecOffset] +
lhsMat[lhsMatOffset+5] * rhsVec[rhsVecOffset+1] +
lhsMat[lhsMatOffset+9] * rhsVec[rhsVecOffset+2] +
lhsMat[lhsMatOffset+13] * rhsVec[rhsVecOffset+3]);
resultVec[resultVecOffset+2] = clamp(lhsMat[lhsMatOffset+2] * rhsVec[rhsVecOffset] +
lhsMat[lhsMatOffset+6] * rhsVec[rhsVecOffset+1] +
lhsMat[lhsMatOffset+10] * rhsVec[rhsVecOffset+2] +
lhsMat[lhsMatOffset+14] * rhsVec[rhsVecOffset+3]);
resultVec[resultVecOffset+3] = clamp(lhsMat[lhsMatOffset+3] * rhsVec[rhsVecOffset] +
lhsMat[lhsMatOffset+7] * rhsVec[rhsVecOffset+1] +
lhsMat[lhsMatOffset+11] * rhsVec[rhsVecOffset+2] +
lhsMat[lhsMatOffset+15] * rhsVec[rhsVecOffset+3]);
}
/**
* Transposes a 4 x 4 matrix.
*
* 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.
*
* 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 native 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] = clamp(dst0 * invdet);
mInv[ 1 + mInvOffset] = clamp(dst1 * invdet);
mInv[ 2 + mInvOffset] = clamp(dst2 * invdet);
mInv[ 3 + mInvOffset] = clamp(dst3 * invdet);
mInv[ 4 + mInvOffset] = clamp(dst4 * invdet);
mInv[ 5 + mInvOffset] = clamp(dst5 * invdet);
mInv[ 6 + mInvOffset] = clamp(dst6 * invdet);
mInv[ 7 + mInvOffset] = clamp(dst7 * invdet);
mInv[ 8 + mInvOffset] = clamp(dst8 * invdet);
mInv[ 9 + mInvOffset] = clamp(dst9 * invdet);
mInv[10 + mInvOffset] = clamp(dst10 * invdet);
mInv[11 + mInvOffset] = clamp(dst11 * invdet);
mInv[12 + mInvOffset] = clamp(dst12 * invdet);
mInv[13 + mInvOffset] = clamp(dst13 * invdet);
mInv[14 + mInvOffset] = clamp(dst14 * invdet);
mInv[15 + mInvOffset] = clamp(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.
*
* 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] = clamp(m[ mi] * x);
sm[ 4 + smi] = clamp(m[ 4 + mi] * y);
sm[ 8 + smi] = clamp(m[ 8 + mi] * z);
sm[12 + smi] = clamp(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] = clamp(m[ mi] * x);
m[ 4 + mi] = clamp(m[ 4 + mi] * y);
m[ 8 + mi] = clamp(m[ 8 + mi] * z);
}
}
/**
* Translates matrix m by x, y, and z, putting the result in tm.
*
* 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] = clamp(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] = clamp(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).
*
* 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) { synchronized(sTemp) {
* setRotateM(sTemp, 0, a, x, y, z); multiplyMM(rm, rmOffset, m,
* mOffset, sTemp, 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 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) {
// synchronized(sTemp) {
// setRotateM(sTemp, 0, a, x, y, z);
// multiplyMM(sTemp, 16, m, mOffset, sTemp, 0);
// System.arraycopy(sTemp, 16, m, mOffset, 16);
// }
//}
/**
* Creates a matrix for rotation by angle a (in degrees) around the axis
* (x, y, z).
*
* 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 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;
}
/**
* 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 / MatrixUtil.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 / MatrixUtil.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);
}
}
}