All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.math.Matrix3f Maven / Gradle / Ivy

There is a newer version: 3.7.0-stable
Show newest version
/*
 * Copyright (c) 2009-2022 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * 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.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT OWNER 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.
 */
package com.jme3.math;

import com.jme3.export.*;
import com.jme3.util.BufferUtils;
import com.jme3.util.TempVars;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Logger;

/**
 * A 3x3 matrix composed of 9 single-precision elements, used to represent
 * linear transformations of 3-D coordinates, such as rotations, reflections,
 * and scaling.
 *
 * 

Element numbering is (row, column), so m01 is the element in row 0, * column 1. * *

For pure rotations, the {@link com.jme3.math.Quaternion} class provides a * more efficient representation. * *

With one exception, the methods with names ending in "Local" modify the * current instance. They are used to avoid creating garbage. * * @author Mark Powell * @author Joshua Slack */ public final class Matrix3f implements Savable, Cloneable, java.io.Serializable { static final long serialVersionUID = 1; private static final Logger logger = Logger.getLogger(Matrix3f.class.getName()); /** * The element in row 0, column 0. */ protected float m00; /** * The element in row 0, column 1. */ protected float m01; /** * The element in row 0, column 2. */ protected float m02; /** * The element in row 1, column 0. */ protected float m10; /** * The element in row 1, column 1. */ protected float m11; /** * The element in row 1, column 2. */ protected float m12; /** * The element in row 2, column 0. */ protected float m20; /** * The element in row 2, column 1. */ protected float m21; /** * The element in row 2, column 2. */ protected float m22; /** * Shared instance of the all-zero matrix. Do not modify! */ public static final Matrix3f ZERO = new Matrix3f(0, 0, 0, 0, 0, 0, 0, 0, 0); /** * Shared instance of the identity matrix (diagonals = 1, other elements = * 0). Do not modify! */ public static final Matrix3f IDENTITY = new Matrix3f(); /** * Instantiates an identity matrix (diagonals = 1, other elements = 0). */ public Matrix3f() { loadIdentity(); } /** * Instantiates a matrix with specified elements. * * @param m00 the desired value for row 0, column 0 * @param m01 the desired value for row 0, column 1 * @param m02 the desired value for row 0, column 2 * @param m10 the desired value for row 1, column 0 * @param m11 the desired value for row 1, column 1 * @param m12 the desired value for row 1, column 2 * @param m20 the desired value for row 2, column 0 * @param m21 the desired value for row 2, column 1 * @param m22 the desired value for row 2, column 2 */ public Matrix3f(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m20 = m20; this.m21 = m21; this.m22 = m22; } /** * Instantiates a copy of the matrix argument. If the argument is null, an * identity matrix is produced. * * @param mat the matrix to copy (unaffected) or null for identity */ public Matrix3f(Matrix3f mat) { set(mat); } /** * Replaces all 9 elements with their absolute values. */ public void absoluteLocal() { m00 = FastMath.abs(m00); m01 = FastMath.abs(m01); m02 = FastMath.abs(m02); m10 = FastMath.abs(m10); m11 = FastMath.abs(m11); m12 = FastMath.abs(m12); m20 = FastMath.abs(m20); m21 = FastMath.abs(m21); m22 = FastMath.abs(m22); } /** * Copies the matrix argument. If the argument is null, the current instance * is set to identity (diagonals = 1, other elements = 0). * * @param matrix the matrix to copy (unaffected) or null for identity * @return the (modified) current instance (for chaining) */ public Matrix3f set(Matrix3f matrix) { if (null == matrix) { loadIdentity(); } else { m00 = matrix.m00; m01 = matrix.m01; m02 = matrix.m02; m10 = matrix.m10; m11 = matrix.m11; m12 = matrix.m12; m20 = matrix.m20; m21 = matrix.m21; m22 = matrix.m22; } return this; } /** * Returns the element at the specified position. The matrix is unaffected. * * @param i the row index (0, 1, or 2) * @param j the column index (0, 1, or 2) * @return the value of the element at (i, j) * @throws IllegalArgumentException if either index isn't 0, 1, or 2 */ @SuppressWarnings("fallthrough") public float get(int i, int j) { switch (i) { case 0: switch (j) { case 0: return m00; case 1: return m01; case 2: return m02; } case 1: switch (j) { case 0: return m10; case 1: return m11; case 2: return m12; } case 2: switch (j) { case 0: return m20; case 1: return m21; case 2: return m22; } } logger.warning("Invalid matrix index."); throw new IllegalArgumentException("Invalid indices into matrix."); } /** * Copies the matrix to the specified array. The matrix is unaffected. * *

If the array has 16 elements, then the matrix is treated as if it * contained the 1st 3 rows and 1st 3 columns of a 4x4 matrix. * * @param data storage for the elements (not null, length=9 or 16) * @param rowMajor true to store the elements in row-major order (m00, m01, * ...) or false to store them in column-major order (m00, m10, ...) * @throws IndexOutOfBoundsException if {@code data} doesn't have 9 or 16 * elements * @see #fillFloatArray(float[], boolean) */ public void get(float[] data, boolean rowMajor) { if (data.length == 9) { if (rowMajor) { data[0] = m00; data[1] = m01; data[2] = m02; data[3] = m10; data[4] = m11; data[5] = m12; data[6] = m20; data[7] = m21; data[8] = m22; } else { data[0] = m00; data[1] = m10; data[2] = m20; data[3] = m01; data[4] = m11; data[5] = m21; data[6] = m02; data[7] = m12; data[8] = m22; } } else if (data.length == 16) { if (rowMajor) { data[0] = m00; data[1] = m01; data[2] = m02; data[4] = m10; data[5] = m11; data[6] = m12; data[8] = m20; data[9] = m21; data[10] = m22; } else { data[0] = m00; data[1] = m10; data[2] = m20; data[4] = m01; data[5] = m11; data[6] = m21; data[8] = m02; data[9] = m12; data[10] = m22; } } else { throw new IndexOutOfBoundsException("Array size must be 9 or 16 in Matrix3f.get()."); } } /** * Normalizes the matrix and returns the result in the argument. The current * instance is unaffected, unless it's {@code store}. * * @param store storage for the result, or null for a new Matrix3f * @return either {@code store} or a new Matrix3f */ public Matrix3f normalize(Matrix3f store) { if (store == null) { store = new Matrix3f(); } float mag = 1.0f / FastMath.sqrt( m00 * m00 + m10 * m10 + m20 * m20); store.m00 = m00 * mag; store.m10 = m10 * mag; store.m20 = m20 * mag; mag = 1.0f / FastMath.sqrt( m01 * m01 + m11 * m11 + m21 * m21); store.m01 = m01 * mag; store.m11 = m11 * mag; store.m21 = m21 * mag; store.m02 = store.m10 * store.m21 - store.m11 * store.m20; store.m12 = store.m01 * store.m20 - store.m00 * store.m21; store.m22 = store.m00 * store.m11 - store.m01 * store.m10; return store; } /** * Normalizes the matrix and returns the (modified) current instance. * * @return the (modified) current instance (for chaining) */ public Matrix3f normalizeLocal() { return normalize(this); } /** * Returns the specified column. The matrix is unaffected. * *

If the matrix is a pure rotation, each column contains one of the * basis vectors. * * @param i the column index (0, 1, or 2) * @return a new Vector3f * @throws IllegalArgumentException if {@code i} isn't 0, 1, or 2 * @see Quaternion#getRotationColumn(int) */ public Vector3f getColumn(int i) { return getColumn(i, null); } /** * Returns the specified column. The matrix is unaffected. * *

If the matrix is a pure rotation, each column contains one of the * basis vectors. * * @param i the column index (0, 1, or 2) * @param store storage for the result, or null for a new Vector3f * @return either {@code store} or a new Vector3f * @throws IllegalArgumentException if {@code i} isn't 0, 1, or 2 * @see Quaternion#getRotationColumn(int, com.jme3.math.Vector3f) */ public Vector3f getColumn(int i, Vector3f store) { if (store == null) { store = new Vector3f(); } switch (i) { case 0: store.x = m00; store.y = m10; store.z = m20; break; case 1: store.x = m01; store.y = m11; store.z = m21; break; case 2: store.x = m02; store.y = m12; store.z = m22; break; default: logger.warning("Invalid column index."); throw new IllegalArgumentException("Invalid column index. " + i); } return store; } /** * Returns the specified row. The matrix is unaffected. * * @param i the row index (0, 1, or 2) * @return a new Vector3f * @throws IllegalArgumentException if {@code i} isn't 0, 1, or 2 */ public Vector3f getRow(int i) { return getRow(i, null); } /** * Returns the specified row. The matrix is unaffected. * * @param i the row index (0, 1, or 2) * @param store storage for the result, or null for a new Vector3f * @return either {@code store} or a new Vector3f * @throws IllegalArgumentException if {@code i} isn't 0, 1, or 2 */ public Vector3f getRow(int i, Vector3f store) { if (store == null) { store = new Vector3f(); } switch (i) { case 0: store.x = m00; store.y = m01; store.z = m02; break; case 1: store.x = m10; store.y = m11; store.z = m12; break; case 2: store.x = m20; store.y = m21; store.z = m22; break; default: logger.warning("Invalid row index."); throw new IllegalArgumentException("Invalid row index. " + i); } return store; } /** * Copies the matrix to a new FloatBuffer. The matrix is unaffected. * * @return a new, rewound FloatBuffer containing all 9 elements in row-major * order (m00, m01, ...) */ public FloatBuffer toFloatBuffer() { FloatBuffer fb = BufferUtils.createFloatBuffer(9); fb.put(m00).put(m01).put(m02); fb.put(m10).put(m11).put(m12); fb.put(m20).put(m21).put(m22); fb.rewind(); return fb; } /** * Copies the matrix to the specified FloatBuffer, starting at its current * position. The matrix is unaffected. * * @param fb storage for the elements (not null, must have space to put 9 * more floats) * @param columnMajor true to store the elements in column-major order (m00, * m10, ...) or false to store them in row-major order (m00, m01, ...) * @return {@code fb}, its position advanced by 9 */ public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) { // if (columnMajor){ // fb.put(m00).put(m10).put(m20); // fb.put(m01).put(m11).put(m21); // fb.put(m02).put(m12).put(m22); // }else{ // fb.put(m00).put(m01).put(m02); // fb.put(m10).put(m11).put(m12); // fb.put(m20).put(m21).put(m22); // } TempVars vars = TempVars.get(); fillFloatArray(vars.matrixWrite, columnMajor); fb.put(vars.matrixWrite, 0, 9); vars.release(); return fb; } /** * Copies the matrix to the 1st 9 elements of the specified array. The * matrix is unaffected. * * @param f storage for the elements (not null, length≥9) * @param columnMajor true to store the elements in column-major order (m00, * m10, ...) or false to store them in row-major order (m00, m01, ...) * @see #get(float[], boolean) */ public void fillFloatArray(float[] f, boolean columnMajor) { if (columnMajor) { f[0] = m00; f[1] = m10; f[2] = m20; f[3] = m01; f[4] = m11; f[5] = m21; f[6] = m02; f[7] = m12; f[8] = m22; } else { f[0] = m00; f[1] = m01; f[2] = m02; f[3] = m10; f[4] = m11; f[5] = m12; f[6] = m20; f[7] = m21; f[8] = m22; } } /** * Sets the specified column. * * @param i which column to set (0, 1, or 2) * @param column the desired element values (unaffected) or null for none * @return the (modified) current instance (for chaining) * @throws IllegalArgumentException if {@code i} isn't 0, 1, or 2 */ public Matrix3f setColumn(int i, Vector3f column) { if (column == null) { logger.warning("Column is null. Ignoring."); return this; } switch (i) { case 0: m00 = column.x; m10 = column.y; m20 = column.z; break; case 1: m01 = column.x; m11 = column.y; m21 = column.z; break; case 2: m02 = column.x; m12 = column.y; m22 = column.z; break; default: logger.warning("Invalid column index."); throw new IllegalArgumentException("Invalid column index. " + i); } return this; } /** * Sets the specified row. * * @param i which row to set (0, 1, or 2) * @param row the desired element values (unaffected) or null for none * @return the (modified) current instance (for chaining) * @throws IllegalArgumentException if {@code i} isn't 0, 1, or 2 */ public Matrix3f setRow(int i, Vector3f row) { if (row == null) { logger.warning("Row is null. Ignoring."); return this; } switch (i) { case 0: m00 = row.x; m01 = row.y; m02 = row.z; break; case 1: m10 = row.x; m11 = row.y; m12 = row.z; break; case 2: m20 = row.x; m21 = row.y; m22 = row.z; break; default: logger.warning("Invalid row index."); throw new IllegalArgumentException("Invalid row index. " + i); } return this; } /** * Sets the specified element. * * @param i the row index (0, 1, or 2) * @param j the column index (0, 1, or 2) * @param value desired value for the element at (i, j) * @return the (modified) current instance (for chaining) * @throws IllegalArgumentException if either index isn't 0, 1, or 2 */ @SuppressWarnings("fallthrough") public Matrix3f set(int i, int j, float value) { switch (i) { case 0: switch (j) { case 0: m00 = value; return this; case 1: m01 = value; return this; case 2: m02 = value; return this; } case 1: switch (j) { case 0: m10 = value; return this; case 1: m11 = value; return this; case 2: m12 = value; return this; } case 2: switch (j) { case 0: m20 = value; return this; case 1: m21 = value; return this; case 2: m22 = value; return this; } } logger.warning("Invalid matrix index."); throw new IllegalArgumentException("Invalid indices into matrix."); } /** * Copies all 9 elements from the specified 2-dimensional array. * * @param matrix the input array (not null, length=3, the first element * having length=3, the other elements having length≥3, unaffected) * @return the (modified) current instance (for chaining) * @throws IllegalArgumentException if the array is the wrong size */ public Matrix3f set(float[][] matrix) { if (matrix.length != 3 || matrix[0].length != 3) { throw new IllegalArgumentException( "Array must be of size 9."); } m00 = matrix[0][0]; m01 = matrix[0][1]; m02 = matrix[0][2]; m10 = matrix[1][0]; m11 = matrix[1][1]; m12 = matrix[1][2]; m20 = matrix[2][0]; m21 = matrix[2][1]; m22 = matrix[2][2]; return this; } /** * Configures from the specified column vectors. If the vectors form an * orthonormal basis, the result will be a pure rotation matrix. * * @param uAxis the desired value for column 0 (not null, unaffected) * @param vAxis the desired value for column 1 (not null, unaffected) * @param wAxis the desired value for column 2 (not null, unaffected) * @see Quaternion#fromAxes(com.jme3.math.Vector3f[]) */ public void fromAxes(Vector3f uAxis, Vector3f vAxis, Vector3f wAxis) { m00 = uAxis.x; m10 = uAxis.y; m20 = uAxis.z; m01 = vAxis.x; m11 = vAxis.y; m21 = vAxis.z; m02 = wAxis.x; m12 = wAxis.y; m22 = wAxis.z; } /** * Copies all 9 elements from the array argument, in row-major order. * * @param matrix the input array (not null, length=9, unaffected) * @return the (modified) current instance (for chaining) * @throws IllegalArgumentException if the array has length != 9 */ public Matrix3f set(float[] matrix) { return set(matrix, true); } /** * Copies all 9 elements from the specified array. * * @param matrix the input array (not null, length=9, unaffected) * @param rowMajor true to read the elements in row-major order (m00, m01, * ...) or false to read them in column-major order (m00, m10, ...) * @return the (modified) current instance (for chaining) * @throws IllegalArgumentException if the array has length != 9 */ public Matrix3f set(float[] matrix, boolean rowMajor) { if (matrix.length != 9) { throw new IllegalArgumentException( "Array must be of size 9."); } if (rowMajor) { m00 = matrix[0]; m01 = matrix[1]; m02 = matrix[2]; m10 = matrix[3]; m11 = matrix[4]; m12 = matrix[5]; m20 = matrix[6]; m21 = matrix[7]; m22 = matrix[8]; } else { m00 = matrix[0]; m01 = matrix[3]; m02 = matrix[6]; m10 = matrix[1]; m11 = matrix[4]; m12 = matrix[7]; m20 = matrix[2]; m21 = matrix[5]; m22 = matrix[8]; } return this; } /** * Configures as a rotation matrix equivalent to the argument. * * @param quaternion the input quaternion (not null, unaffected) * @return the (modified) current instance (for chaining) */ public Matrix3f set(Quaternion quaternion) { return quaternion.toRotationMatrix(this); } /** * Configures as an identity matrix (diagonals = 1, other elements = 0). */ public void loadIdentity() { m01 = m02 = m10 = m12 = m20 = m21 = 0; m00 = m11 = m22 = 1; } /** * Tests for exact identity. The matrix is unaffected. * * @return true if equal to {@link #IDENTITY}, otherwise false */ public boolean isIdentity() { return (m00 == 1 && m01 == 0 && m02 == 0) && (m10 == 0 && m11 == 1 && m12 == 0) && (m20 == 0 && m21 == 0 && m22 == 1); } /** * Sets all 9 elements to form a pure rotation matrix with the specified * rotation angle and axis of rotation. This method creates garbage, so use * {@link #fromAngleNormalAxis(float, com.jme3.math.Vector3f)} if the axis * is known to be normalized. * * @param angle the desired rotation angle (in radians) * @param axis the desired axis of rotation (not null, unaffected) */ public void fromAngleAxis(float angle, Vector3f axis) { Vector3f normAxis = axis.normalize(); fromAngleNormalAxis(angle, normAxis); } /** * Sets all 9 elements to form a rotation matrix with the specified rotation * angle and normalized axis of rotation. If the axis might not be * normalized, use {@link #fromAngleAxis(float, com.jme3.math.Vector3f)} * instead. * * @param angle the desired rotation angle (in radians) * @param axis the desired axis of rotation (not null, length=1, unaffected) */ public void fromAngleNormalAxis(float angle, Vector3f axis) { float fCos = FastMath.cos(angle); float fSin = FastMath.sin(angle); float fOneMinusCos = ((float) 1.0) - fCos; float fX2 = axis.x * axis.x; float fY2 = axis.y * axis.y; float fZ2 = axis.z * axis.z; float fXYM = axis.x * axis.y * fOneMinusCos; float fXZM = axis.x * axis.z * fOneMinusCos; float fYZM = axis.y * axis.z * fOneMinusCos; float fXSin = axis.x * fSin; float fYSin = axis.y * fSin; float fZSin = axis.z * fSin; m00 = fX2 * fOneMinusCos + fCos; m01 = fXYM - fZSin; m02 = fXZM + fYSin; m10 = fXYM + fZSin; m11 = fY2 * fOneMinusCos + fCos; m12 = fYZM - fXSin; m20 = fXZM - fYSin; m21 = fYZM + fXSin; m22 = fZ2 * fOneMinusCos + fCos; } /** * Multiplies with the argument matrix and returns the product as a new * instance. The current instance is unaffected. * *

Note that matrix multiplication is noncommutative, so generally * q * p != p * q. * * @param mat the right factor (not null, unaffected) * @return {@code this} times {@code mat} (a new Matrix3f) */ public Matrix3f mult(Matrix3f mat) { return mult(mat, null); } /** * Multiplies with the specified matrix and returns the product in a 3rd * matrix. The current instance is unaffected unless it's {@code product}. * *

Note that matrix multiplication is noncommutative, so generally * q * p != p * q. * *

It is safe for {@code mat} and {@code product} to be the same object. * * @param mat the right factor (not null, unaffected unless it's {@code * product}) * @param product storage for the product, or null for a new Matrix3f * @return {@code this} times {@code mat} (either {@code product} or a new * Matrix3f) */ public Matrix3f mult(Matrix3f mat, Matrix3f product) { float temp00, temp01, temp02; float temp10, temp11, temp12; float temp20, temp21, temp22; if (product == null) { product = new Matrix3f(); } temp00 = m00 * mat.m00 + m01 * mat.m10 + m02 * mat.m20; temp01 = m00 * mat.m01 + m01 * mat.m11 + m02 * mat.m21; temp02 = m00 * mat.m02 + m01 * mat.m12 + m02 * mat.m22; temp10 = m10 * mat.m00 + m11 * mat.m10 + m12 * mat.m20; temp11 = m10 * mat.m01 + m11 * mat.m11 + m12 * mat.m21; temp12 = m10 * mat.m02 + m11 * mat.m12 + m12 * mat.m22; temp20 = m20 * mat.m00 + m21 * mat.m10 + m22 * mat.m20; temp21 = m20 * mat.m01 + m21 * mat.m11 + m22 * mat.m21; temp22 = m20 * mat.m02 + m21 * mat.m12 + m22 * mat.m22; product.m00 = temp00; product.m01 = temp01; product.m02 = temp02; product.m10 = temp10; product.m11 = temp11; product.m12 = temp12; product.m20 = temp20; product.m21 = temp21; product.m22 = temp22; return product; } /** * Applies the linear transformation to the vector argument and returns the * result as a new vector. The matrix is unaffected. * *

This can also be described as multiplying the matrix by a column * vector. * * @param vec the coordinates to transform (not null, unaffected) * @return a new Vector3f */ public Vector3f mult(Vector3f vec) { return mult(vec, null); } /** * Applies the linear transformation to specified vector and stores the * result in another vector. The matrix is unaffected. * *

This can also be described as multiplying the matrix by a column * vector. * *

It is safe for {@code vec} and {@code product} to be the same object. * * @param vec the coordinates to transform (not null, unaffected unless it's * {@code product}) * @param product storage for the result, or null for a new Vector3f * @return either {@code product} or a new Vector3f */ public Vector3f mult(Vector3f vec, Vector3f product) { if (null == product) { product = new Vector3f(); } float x = vec.x; float y = vec.y; float z = vec.z; product.x = m00 * x + m01 * y + m02 * z; product.y = m10 * x + m11 * y + m12 * z; product.z = m20 * x + m21 * y + m22 * z; return product; } /** * Multiplies by the scalar argument and returns the (modified) current * instance. * * @param scale the scaling factor * @return the (modified) current instance (for chaining) */ public Matrix3f multLocal(float scale) { m00 *= scale; m01 *= scale; m02 *= scale; m10 *= scale; m11 *= scale; m12 *= scale; m20 *= scale; m21 *= scale; m22 *= scale; return this; } /** * Applies the linear transformation to the vector argument and returns the * (modified) argument. If the argument is null, null is returned. * *

Despite the name, the current instance is unaffected. * * @param vec the vector to transform (modified if not null) * @return {@code vec} or null */ public Vector3f multLocal(Vector3f vec) { if (vec == null) { return null; } float x = vec.x; float y = vec.y; vec.x = m00 * x + m01 * y + m02 * vec.z; vec.y = m10 * x + m11 * y + m12 * vec.z; vec.z = m20 * x + m21 * y + m22 * vec.z; return vec; } /** * Multiplies by the matrix argument and returns the (modified) current * instance. * *

Note that matrix multiplication is noncommutative, so generally * q * p != p * q. * * @param mat the right factor (not null, unaffected unless it's * {@code this}) * @return the (modified) current instance */ public Matrix3f multLocal(Matrix3f mat) { return mult(mat, this); } /** * Transposes the matrix and returns the (modified) current instance. * * @return the (modified) current instance */ public Matrix3f transposeLocal() { // float[] tmp = new float[9]; // get(tmp, false); // set(tmp, true); float tmp = m01; m01 = m10; m10 = tmp; tmp = m02; m02 = m20; m20 = tmp; tmp = m12; m12 = m21; m21 = tmp; return this; } /** * Returns the multiplicative inverse as a new matrix. If the current * instance is singular, an all-zero matrix is returned. In either case, the * current instance is unaffected. * * @return a new Matrix3f */ public Matrix3f invert() { return invert(null); } /** * Returns the multiplicative inverse in the specified storage. If the * current instance is singular, an all-zero matrix is returned. In either * case, the current instance is unaffected. * *

If {@code this} and {@code store} are the same object, the result is * undefined. Use {@link #invertLocal()} instead. * * @param store storage for the result, or null for a new Matrix3f * @return either {@code store} or a new Matrix3f */ public Matrix3f invert(Matrix3f store) { if (store == null) { store = new Matrix3f(); } float det = determinant(); if (FastMath.abs(det) <= FastMath.FLT_EPSILON) { return store.zero(); } store.m00 = m11 * m22 - m12 * m21; store.m01 = m02 * m21 - m01 * m22; store.m02 = m01 * m12 - m02 * m11; store.m10 = m12 * m20 - m10 * m22; store.m11 = m00 * m22 - m02 * m20; store.m12 = m02 * m10 - m00 * m12; store.m20 = m10 * m21 - m11 * m20; store.m21 = m01 * m20 - m00 * m21; store.m22 = m00 * m11 - m01 * m10; store.multLocal(1f / det); return store; } /** * Inverts the matrix and returns the (modified) current instance. If the * current instance is singular, all elements will be set to zero. * * @return the (modified) current instance (for chaining) */ public Matrix3f invertLocal() { float det = determinant(); if (FastMath.abs(det) <= 0f) { return zero(); } float f00 = m11 * m22 - m12 * m21; float f01 = m02 * m21 - m01 * m22; float f02 = m01 * m12 - m02 * m11; float f10 = m12 * m20 - m10 * m22; float f11 = m00 * m22 - m02 * m20; float f12 = m02 * m10 - m00 * m12; float f20 = m10 * m21 - m11 * m20; float f21 = m01 * m20 - m00 * m21; float f22 = m00 * m11 - m01 * m10; m00 = f00; m01 = f01; m02 = f02; m10 = f10; m11 = f11; m12 = f12; m20 = f20; m21 = f21; m22 = f22; multLocal(1f / det); return this; } /** * Returns the adjoint as a new matrix. The current instance is unaffected. * * @return a new Matrix3f */ public Matrix3f adjoint() { return adjoint(null); } /** * Returns the adjoint in the specified storage. The current instance is * unaffected. * *

If {@code this} and {@code store} are the same object, the result is * undefined. * * @param store storage for the result, or null for a new Matrix3f * @return either {@code store} or a new Matrix3f */ public Matrix3f adjoint(Matrix3f store) { if (store == null) { store = new Matrix3f(); } store.m00 = m11 * m22 - m12 * m21; store.m01 = m02 * m21 - m01 * m22; store.m02 = m01 * m12 - m02 * m11; store.m10 = m12 * m20 - m10 * m22; store.m11 = m00 * m22 - m02 * m20; store.m12 = m02 * m10 - m00 * m12; store.m20 = m10 * m21 - m11 * m20; store.m21 = m01 * m20 - m00 * m21; store.m22 = m00 * m11 - m01 * m10; return store; } /** * Returns the determinant. The matrix is unaffected. * * @return the determinant */ public float determinant() { float fCo00 = m11 * m22 - m12 * m21; float fCo10 = m12 * m20 - m10 * m22; float fCo20 = m10 * m21 - m11 * m20; float fDet = m00 * fCo00 + m01 * fCo10 + m02 * fCo20; return fDet; } /** * Sets all elements to zero. * * @return the (modified) current instance (for chaining) */ public Matrix3f zero() { m00 = m01 = m02 = m10 = m11 = m12 = m20 = m21 = m22 = 0.0f; return this; } /** * Transposes the matrix and returns the (modified) current instance. * *

This method is inconsistent with JME naming conventions, but has been * preserved for backwards compatibility. To preserve the current instance, * {@link #transposeNew()}. * *

TODO deprecate in favor of transposeLocal() * * @return the (modified) current instance (for chaining) */ public Matrix3f transpose() { return transposeLocal(); } /** * Returns the transpose as a new instance. The current instance is * unaffected. * * @return a new Matrix3f */ public Matrix3f transposeNew() { Matrix3f ret = new Matrix3f(m00, m10, m20, m01, m11, m21, m02, m12, m22); return ret; } /** * Returns a string representation of the matrix, which is unaffected. For * example, an identity matrix would be represented by: *

     * Matrix3f
     * [
     *  1.0  0.0  0.0
     *  0.0  1.0  0.0
     *  0.0  0.0  1.0
     * ]
     * 
* * @return the string representation */ @Override public String toString() { StringBuilder result = new StringBuilder("Matrix3f\n[\n"); result.append(" "); result.append(m00); result.append(" "); result.append(m01); result.append(" "); result.append(m02); result.append(" \n"); result.append(" "); result.append(m10); result.append(" "); result.append(m11); result.append(" "); result.append(m12); result.append(" \n"); result.append(" "); result.append(m20); result.append(" "); result.append(m21); result.append(" "); result.append(m22); result.append(" \n]"); return result.toString(); } /** * Returns a hash code. If two matrices are logically equivalent, they will * return the same hash code. The current instance is unaffected. * * @return the hash-code value * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int hash = 37; hash = 37 * hash + Float.floatToIntBits(m00); hash = 37 * hash + Float.floatToIntBits(m01); hash = 37 * hash + Float.floatToIntBits(m02); hash = 37 * hash + Float.floatToIntBits(m10); hash = 37 * hash + Float.floatToIntBits(m11); hash = 37 * hash + Float.floatToIntBits(m12); hash = 37 * hash + Float.floatToIntBits(m20); hash = 37 * hash + Float.floatToIntBits(m21); hash = 37 * hash + Float.floatToIntBits(m22); return hash; } /** * Tests for exact equality with the argument, distinguishing -0 from 0. The * current instance is unaffected. * * @param o the object to compare (may be null, unaffected) * @return true if equal, otherwise false */ @Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) { return false; } if (this == o) { return true; } Matrix3f comp = (Matrix3f) o; if (Float.compare(m00, comp.m00) != 0) { return false; } if (Float.compare(m01, comp.m01) != 0) { return false; } if (Float.compare(m02, comp.m02) != 0) { return false; } if (Float.compare(m10, comp.m10) != 0) { return false; } if (Float.compare(m11, comp.m11) != 0) { return false; } if (Float.compare(m12, comp.m12) != 0) { return false; } if (Float.compare(m20, comp.m20) != 0) { return false; } if (Float.compare(m21, comp.m21) != 0) { return false; } if (Float.compare(m22, comp.m22) != 0) { return false; } return true; } /** * Serializes to the specified exporter, for example when saving to a J3O * file. The current instance is unaffected. * * @param e the exporter to use (not null) * @throws IOException from the exporter */ @Override public void write(JmeExporter e) throws IOException { OutputCapsule cap = e.getCapsule(this); cap.write(m00, "m00", 1); cap.write(m01, "m01", 0); cap.write(m02, "m02", 0); cap.write(m10, "m10", 0); cap.write(m11, "m11", 1); cap.write(m12, "m12", 0); cap.write(m20, "m20", 0); cap.write(m21, "m21", 0); cap.write(m22, "m22", 1); } /** * De-serializes from the specified importer, for example when loading from a * J3O file. * * @param importer the importer to use (not null) * @throws IOException from the importer */ @Override public void read(JmeImporter importer) throws IOException { InputCapsule cap = importer.getCapsule(this); m00 = cap.readFloat("m00", 1); m01 = cap.readFloat("m01", 0); m02 = cap.readFloat("m02", 0); m10 = cap.readFloat("m10", 0); m11 = cap.readFloat("m11", 1); m12 = cap.readFloat("m12", 0); m20 = cap.readFloat("m20", 0); m21 = cap.readFloat("m21", 0); m22 = cap.readFloat("m22", 1); } /** * Configures a rotation matrix that rotates the specified start direction * to the specified end direction. * *

See Tomas Möller, John F. Hughes "Efficiently Building a Matrix to * Rotate One Vector to Another" Journal of Graphics Tools, 4(4):1-4, 1999. * * @param start the start direction (not null, length=1, unaffected) * @param end the end direction (not null, length=1, unaffected) * */ public void fromStartEndVectors(Vector3f start, Vector3f end) { Vector3f v = new Vector3f(); float e, h, f; start.cross(end, v); e = start.dot(end); f = (e < 0) ? -e : e; // if "from" and "to" vectors are nearly parallel if (f > 1.0f - FastMath.ZERO_TOLERANCE) { Vector3f u = new Vector3f(); Vector3f x = new Vector3f(); float c1, c2, c3; /* coefficients for later use */ int i, j; x.x = (start.x > 0.0) ? start.x : -start.x; x.y = (start.y > 0.0) ? start.y : -start.y; x.z = (start.z > 0.0) ? start.z : -start.z; if (x.x < x.y) { if (x.x < x.z) { x.x = 1.0f; x.y = x.z = 0.0f; } else { x.z = 1.0f; x.x = x.y = 0.0f; } } else { if (x.y < x.z) { x.y = 1.0f; x.x = x.z = 0.0f; } else { x.z = 1.0f; x.x = x.y = 0.0f; } } u.x = x.x - start.x; u.y = x.y - start.y; u.z = x.z - start.z; v.x = x.x - end.x; v.y = x.y - end.y; v.z = x.z - end.z; c1 = 2.0f / u.dot(u); c2 = 2.0f / v.dot(v); c3 = c1 * c2 * u.dot(v); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { float val = -c1 * u.get(i) * u.get(j) - c2 * v.get(i) * v.get(j) + c3 * v.get(i) * u.get(j); set(i, j, val); } float val = get(i, i); set(i, i, val + 1.0f); } } else { // the most common case, unless "start"="end", or "start"=-"end" float hvx, hvz, hvxy, hvxz, hvyz; h = 1.0f / (1.0f + e); hvx = h * v.x; hvz = h * v.z; hvxy = hvx * v.y; hvxz = hvx * v.z; hvyz = hvz * v.y; set(0, 0, e + hvx * v.x); set(0, 1, hvxy - v.z); set(0, 2, hvxz + v.y); set(1, 0, hvxy + v.z); set(1, 1, e + h * v.y * v.y); set(1, 2, hvyz - v.x); set(2, 0, hvxz - v.y); set(2, 1, hvyz + v.x); set(2, 2, e + hvz * v.z); } } /** * Scales each column by the corresponding element of the argument. * * @param scale the scale factors: X scales column 0, Y scales column 1, Z * scales column 2 (not null, unaffected) */ public void scale(Vector3f scale) { m00 *= scale.x; m10 *= scale.x; m20 *= scale.x; m01 *= scale.y; m11 *= scale.y; m21 *= scale.y; m02 *= scale.z; m12 *= scale.z; m22 *= scale.z; } /** * Tests for an identity matrix, with 0.0001 tolerance. The current instance * is unaffected. * * @return true if all elements are within 0.0001 of an identity matrix */ static boolean equalIdentity(Matrix3f mat) { if (Math.abs(mat.m00 - 1) > 1e-4) { return false; } if (Math.abs(mat.m11 - 1) > 1e-4) { return false; } if (Math.abs(mat.m22 - 1) > 1e-4) { return false; } if (Math.abs(mat.m01) > 1e-4) { return false; } if (Math.abs(mat.m02) > 1e-4) { return false; } if (Math.abs(mat.m10) > 1e-4) { return false; } if (Math.abs(mat.m12) > 1e-4) { return false; } if (Math.abs(mat.m20) > 1e-4) { return false; } if (Math.abs(mat.m21) > 1e-4) { return false; } return true; } /** * Creates a copy. The current instance is unaffected. * * @return a new instance, equivalent to the current one */ @Override public Matrix3f clone() { try { return (Matrix3f) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); // can not happen } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy