/* * Copyright (c) 2009-2012 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.TempVars; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.logging.Logger; /** * <code>Quaternion</code> defines a single example of a more general class of * hypercomplex numbers. Quaternions extends a rotation in three dimensions to a * rotation in four dimensions. This avoids "gimbal lock" and allows for smooth * continuous rotation. * * <code>Quaternion</code> is defined by four floating point numbers: {x y z * w}. * * @author Mark Powell * @author Joshua Slack */ public final class Quaternion implements Savable, Cloneable, java.io.Serializable { static final long serialVersionUID = 1; private static final Logger logger = Logger.getLogger(Quaternion.class.getName()); /** * Represents the identity quaternion rotation (0, 0, 0, 1). */ public static final Quaternion IDENTITY = new Quaternion(); public static final Quaternion DIRECTION_Z = new Quaternion(); public static final Quaternion ZERO = new Quaternion(0, 0, 0, 0); static { DIRECTION_Z.fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z); } protected float x, y, z, w; /** * Constructor instantiates a new <code>Quaternion</code> object * initializing all values to zero, except w which is initialized to 1. * */ public Quaternion() { x = 0; y = 0; z = 0; w = 1; } /** * Constructor instantiates a new <code>Quaternion</code> object from the * given list of parameters. * * @param x * the x value of the quaternion. * @param y * the y value of the quaternion. * @param z * the z value of the quaternion. * @param w * the w value of the quaternion. */ public Quaternion(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; } public float getX() { return x; } public float getY() { return y; } public float getZ() { return z; } public float getW() { return w; } /** * sets the data in a <code>Quaternion</code> object from the given list * of parameters. * * @param x * the x value of the quaternion. * @param y * the y value of the quaternion. * @param z * the z value of the quaternion. * @param w * the w value of the quaternion. * @return this */ public Quaternion set(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; return this; } /** * Sets the data in this <code>Quaternion</code> object to be equal to the * passed <code>Quaternion</code> object. The values are copied producing * a new object. * * @param q * The Quaternion to copy values from. * @return this */ public Quaternion set(Quaternion q) { this.x = q.x; this.y = q.y; this.z = q.z; this.w = q.w; return this; } /** * Constructor instantiates a new <code>Quaternion</code> object from a * collection of rotation angles. * * @param angles * the angles of rotation (x, y, z) that will define the * <code>Quaternion</code>. */ public Quaternion(float[] angles) { fromAngles(angles); } /** * Constructor instantiates a new <code>Quaternion</code> object from an * interpolation between two other quaternions. * * @param q1 * the first quaternion. * @param q2 * the second quaternion. * @param interp * the amount to interpolate between the two quaternions. */ public Quaternion(Quaternion q1, Quaternion q2, float interp) { slerp(q1, q2, interp); } /** * Constructor instantiates a new <code>Quaternion</code> object from an * existing quaternion, creating a copy. * * @param q * the quaternion to copy. */ public Quaternion(Quaternion q) { this.x = q.x; this.y = q.y; this.z = q.z; this.w = q.w; } /** * Sets this Quaternion to {0, 0, 0, 1}. Same as calling set(0,0,0,1). */ public void loadIdentity() { x = y = z = 0; w = 1; } /** * @return true if this Quaternion is {0,0,0,1} */ public boolean isIdentity() { if (x == 0 && y == 0 && z == 0 && w == 1) { return true; } else { return false; } } /** * <code>fromAngles</code> builds a quaternion from the Euler rotation * angles (y,r,p). * * @param angles * the Euler angles of rotation (in radians). */ public Quaternion fromAngles(float[] angles) { if (angles.length != 3) { throw new IllegalArgumentException( "Angles array must have three elements"); } return fromAngles(angles[0], angles[1], angles[2]); } /** * <code>fromAngles</code> builds a Quaternion from the Euler rotation * angles (x,y,z) aka (pitch, yaw, rall)). Note that we are applying in order: (y, z, x) aka (yaw, roll, pitch) but * we've ordered them in x, y, and z for convenience. * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm">http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm</a> * * @param xAngle * the Euler pitch of rotation (in radians). (aka Attitude, often rot * around x) * @param yAngle * the Euler yaw of rotation (in radians). (aka Heading, often * rot around y) * @param zAngle * the Euler roll of rotation (in radians). (aka Bank, often * rot around z) */ public Quaternion fromAngles(float xAngle, float yAngle, float zAngle) { float angle; float sinY, sinZ, sinX, cosY, cosZ, cosX; angle = zAngle * 0.5f; sinZ = FastMath.sin(angle); cosZ = FastMath.cos(angle); angle = yAngle * 0.5f; sinY = FastMath.sin(angle); cosY = FastMath.cos(angle); angle = xAngle * 0.5f; sinX = FastMath.sin(angle); cosX = FastMath.cos(angle); // variables used to reduce multiplication calls. float cosYXcosZ = cosY * cosZ; float sinYXsinZ = sinY * sinZ; float cosYXsinZ = cosY * sinZ; float sinYXcosZ = sinY * cosZ; w = (cosYXcosZ * cosX - sinYXsinZ * sinX); x = (cosYXcosZ * sinX + sinYXsinZ * cosX); y = (sinYXcosZ * cosX + cosYXsinZ * sinX); z = (cosYXsinZ * cosX - sinYXcosZ * sinX); normalizeLocal(); return this; } /** * <code>toAngles</code> returns this quaternion converted to Euler * rotation angles (yaw,roll,pitch).<br/> * Note that the result is not always 100% accurate due to the implications of euler angles. * @see <a href="http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm">http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToEuler/index.htm</a> * * @param angles * the float[] in which the angles should be stored, or null if * you want a new float[] to be created * @return the float[] in which the angles are stored. */ public float[] toAngles(float[] angles) { if (angles == null) { angles = new float[3]; } else if (angles.length != 3) { throw new IllegalArgumentException("Angles array must have three elements"); } float sqw = w * w; float sqx = x * x; float sqy = y * y; float sqz = z * z; float unit = sqx + sqy + sqz + sqw; // if normalized is one, otherwise // is correction factor float test = x * y + z * w; if (test > 0.499 * unit) { // singularity at north pole angles[1] = 2 * FastMath.atan2(x, w); angles[2] = FastMath.HALF_PI; angles[0] = 0; } else if (test < -0.499 * unit) { // singularity at south pole angles[1] = -2 * FastMath.atan2(x, w); angles[2] = -FastMath.HALF_PI; angles[0] = 0; } else { angles[1] = FastMath.atan2(2 * y * w - 2 * x * z, sqx - sqy - sqz + sqw); // roll or heading angles[2] = FastMath.asin(2 * test / unit); // pitch or attitude angles[0] = FastMath.atan2(2 * x * w - 2 * y * z, -sqx + sqy - sqz + sqw); // yaw or bank } return angles; } /** * * <code>fromRotationMatrix</code> generates a quaternion from a supplied * matrix. This matrix is assumed to be a rotational matrix. * * @param matrix * the matrix that defines the rotation. */ public Quaternion fromRotationMatrix(Matrix3f matrix) { return fromRotationMatrix(matrix.m00, matrix.m01, matrix.m02, matrix.m10, matrix.m11, matrix.m12, matrix.m20, matrix.m21, matrix.m22); } public Quaternion fromRotationMatrix(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { // first normalize the forward (F), up (U) and side (S) vectors of the rotation matrix // so that the scale does not affect the rotation float lengthSquared = m00 * m00 + m10 * m10 + m20 * m20; if (lengthSquared != 1f && lengthSquared != 0f) { lengthSquared = 1.0f / FastMath.sqrt(lengthSquared); m00 *= lengthSquared; m10 *= lengthSquared; m20 *= lengthSquared; } lengthSquared = m01 * m01 + m11 * m11 + m21 * m21; if (lengthSquared != 1f && lengthSquared != 0f) { lengthSquared = 1.0f / FastMath.sqrt(lengthSquared); m01 *= lengthSquared; m11 *= lengthSquared; m21 *= lengthSquared; } lengthSquared = m02 * m02 + m12 * m12 + m22 * m22; if (lengthSquared != 1f && lengthSquared != 0f) { lengthSquared = 1.0f / FastMath.sqrt(lengthSquared); m02 *= lengthSquared; m12 *= lengthSquared; m22 *= lengthSquared; } // Use the Graphics Gems code, from // ftp://ftp.cis.upenn.edu/pub/graphics/shoemake/quatut.ps.Z // *NOT* the "Matrix and Quaternions FAQ", which has errors! // the trace is the sum of the diagonal elements; see // http://mathworld.wolfram.com/MatrixTrace.html float t = m00 + m11 + m22; // we protect the division by s by ensuring that s>=1 if (t >= 0) { // |w| >= .5 float s = FastMath.sqrt(t + 1); // |s|>=1 ... w = 0.5f * s; s = 0.5f / s; // so this division isn't bad x = (m21 - m12) * s; y = (m02 - m20) * s; z = (m10 - m01) * s; } else if ((m00 > m11) && (m00 > m22)) { float s = FastMath.sqrt(1.0f + m00 - m11 - m22); // |s|>=1 x = s * 0.5f; // |x| >= .5 s = 0.5f / s; y = (m10 + m01) * s; z = (m02 + m20) * s; w = (m21 - m12) * s; } else if (m11 > m22) { float s = FastMath.sqrt(1.0f + m11 - m00 - m22); // |s|>=1 y = s * 0.5f; // |y| >= .5 s = 0.5f / s; x = (m10 + m01) * s; z = (m21 + m12) * s; w = (m02 - m20) * s; } else { float s = FastMath.sqrt(1.0f + m22 - m00 - m11); // |s|>=1 z = s * 0.5f; // |z| >= .5 s = 0.5f / s; x = (m02 + m20) * s; y = (m21 + m12) * s; w = (m10 - m01) * s; } return this; } /** * <code>toRotationMatrix</code> converts this quaternion to a rotational * matrix. Note: the result is created from a normalized version of this quat. * * @return the rotation matrix representation of this quaternion. */ public Matrix3f toRotationMatrix() { Matrix3f matrix = new Matrix3f(); return toRotationMatrix(matrix); } /** * <code>toRotationMatrix</code> converts this quaternion to a rotational * matrix. The result is stored in result. * * @param result * The Matrix3f to store the result in. * @return the rotation matrix representation of this quaternion. */ public Matrix3f toRotationMatrix(Matrix3f result) { float norm = norm(); // we explicitly test norm against one here, saving a division // at the cost of a test and branch. Is it worth it? float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0; // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs // will be used 2-4 times each. float xs = x * s; float ys = y * s; float zs = z * s; float xx = x * xs; float xy = x * ys; float xz = x * zs; float xw = w * xs; float yy = y * ys; float yz = y * zs; float yw = w * ys; float zz = z * zs; float zw = w * zs; // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here result.m00 = 1 - (yy + zz); result.m01 = (xy - zw); result.m02 = (xz + yw); result.m10 = (xy + zw); result.m11 = 1 - (xx + zz); result.m12 = (yz - xw); result.m20 = (xz - yw); result.m21 = (yz + xw); result.m22 = 1 - (xx + yy); return result; } /** * <code>toRotationMatrix</code> converts this quaternion to a rotational * matrix. The result is stored in result. 4th row and 4th column values are * untouched. Note: the result is created from a normalized version of this quat. * * @param result * The Matrix4f to store the result in. * @return the rotation matrix representation of this quaternion. */ public Matrix4f toRotationMatrix(Matrix4f result) { TempVars tempv = TempVars.get(); Vector3f originalScale = tempv.vect1; result.toScaleVector(originalScale); result.setScale(1, 1, 1); float norm = norm(); // we explicitly test norm against one here, saving a division // at the cost of a test and branch. Is it worth it? float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0; // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs // will be used 2-4 times each. float xs = x * s; float ys = y * s; float zs = z * s; float xx = x * xs; float xy = x * ys; float xz = x * zs; float xw = w * xs; float yy = y * ys; float yz = y * zs; float yw = w * ys; float zz = z * zs; float zw = w * zs; // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here result.m00 = 1 - (yy + zz); result.m01 = (xy - zw); result.m02 = (xz + yw); result.m10 = (xy + zw); result.m11 = 1 - (xx + zz); result.m12 = (yz - xw); result.m20 = (xz - yw); result.m21 = (yz + xw); result.m22 = 1 - (xx + yy); result.setScale(originalScale); tempv.release(); return result; } /** * <code>getRotationColumn</code> returns one of three columns specified * by the parameter. This column is returned as a <code>Vector3f</code> * object. * * @param i * the column to retrieve. Must be between 0 and 2. * @return the column specified by the index. */ public Vector3f getRotationColumn(int i) { return getRotationColumn(i, null); } /** * <code>getRotationColumn</code> returns one of three columns specified * by the parameter. This column is returned as a <code>Vector3f</code> * object. The value is retrieved as if this quaternion was first normalized. * * @param i * the column to retrieve. Must be between 0 and 2. * @param store * the vector object to store the result in. if null, a new one * is created. * @return the column specified by the index. */ public Vector3f getRotationColumn(int i, Vector3f store) { if (store == null) { store = new Vector3f(); } float norm = norm(); if (norm != 1.0f) { norm = FastMath.invSqrt(norm); } float xx = x * x * norm; float xy = x * y * norm; float xz = x * z * norm; float xw = x * w * norm; float yy = y * y * norm; float yz = y * z * norm; float yw = y * w * norm; float zz = z * z * norm; float zw = z * w * norm; switch (i) { case 0: store.x = 1 - 2 * (yy + zz); store.y = 2 * (xy + zw); store.z = 2 * (xz - yw); break; case 1: store.x = 2 * (xy - zw); store.y = 1 - 2 * (xx + zz); store.z = 2 * (yz + xw); break; case 2: store.x = 2 * (xz + yw); store.y = 2 * (yz - xw); store.z = 1 - 2 * (xx + yy); break; default: logger.warning("Invalid column index."); throw new IllegalArgumentException("Invalid column index. " + i); } return store; } /** * <code>fromAngleAxis</code> sets this quaternion to the values specified * by an angle and an axis of rotation. This method creates an object, so * use fromAngleNormalAxis if your axis is already normalized. * * @param angle * the angle to rotate (in radians). * @param axis * the axis of rotation. * @return this quaternion */ public Quaternion fromAngleAxis(float angle, Vector3f axis) { Vector3f normAxis = axis.normalize(); fromAngleNormalAxis(angle, normAxis); return this; } /** * <code>fromAngleNormalAxis</code> sets this quaternion to the values * specified by an angle and a normalized axis of rotation. * * @param angle * the angle to rotate (in radians). * @param axis * the axis of rotation (already normalized). */ public Quaternion fromAngleNormalAxis(float angle, Vector3f axis) { if (axis.x == 0 && axis.y == 0 && axis.z == 0) { loadIdentity(); } else { float halfAngle = 0.5f * angle; float sin = FastMath.sin(halfAngle); w = FastMath.cos(halfAngle); x = sin * axis.x; y = sin * axis.y; z = sin * axis.z; } return this; } /** * <code>toAngleAxis</code> sets a given angle and axis to that * represented by the current quaternion. The values are stored as * follows: The axis is provided as a parameter and built by the method, * the angle is returned as a float. * * @param axisStore * the object we'll store the computed axis in. * @return the angle of rotation in radians. */ public float toAngleAxis(Vector3f axisStore) { float sqrLength = x * x + y * y + z * z; float angle; if (sqrLength == 0.0f) { angle = 0.0f; if (axisStore != null) { axisStore.x = 1.0f; axisStore.y = 0.0f; axisStore.z = 0.0f; } } else { angle = (2.0f * FastMath.acos(w)); if (axisStore != null) { float invLength = (1.0f / FastMath.sqrt(sqrLength)); axisStore.x = x * invLength; axisStore.y = y * invLength; axisStore.z = z * invLength; } } return angle; } /** * <code>slerp</code> sets this quaternion's value as an interpolation * between two other quaternions. * * @param q1 * the first quaternion. * @param q2 * the second quaternion. * @param t * the amount to interpolate between the two quaternions. */ public Quaternion slerp(Quaternion q1, Quaternion q2, float t) { // Create a local quaternion to store the interpolated quaternion if (q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w) { this.set(q1); return this; } float result = (q1.x * q2.x) + (q1.y * q2.y) + (q1.z * q2.z) + (q1.w * q2.w); if (result < 0.0f) { // Negate the second quaternion and the result of the dot product q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; result = -result; } // Set the first and second scale for the interpolation float scale0 = 1 - t; float scale1 = t; // Check if the angle between the 2 quaternions was big enough to // warrant such calculations if ((1 - result) > 0.1f) {// Get the angle between the 2 quaternions, // and then store the sin() of that angle float theta = FastMath.acos(result); float invSinTheta = 1f / FastMath.sin(theta); // Calculate the scale for q1 and q2, according to the angle and // it's sine value scale0 = FastMath.sin((1 - t) * theta) * invSinTheta; scale1 = FastMath.sin((t * theta)) * invSinTheta; } // Calculate the x, y, z and w values for the quaternion by using a // special // form of linear interpolation for quaternions. this.x = (scale0 * q1.x) + (scale1 * q2.x); this.y = (scale0 * q1.y) + (scale1 * q2.y); this.z = (scale0 * q1.z) + (scale1 * q2.z); this.w = (scale0 * q1.w) + (scale1 * q2.w); // Return the interpolated quaternion return this; } /** * Sets the values of this quaternion to the slerp from itself to q2 by * changeAmnt * * @param q2 * Final interpolation value * @param changeAmnt * The amount diffrence */ public void slerp(Quaternion q2, float changeAmnt) { if (this.x == q2.x && this.y == q2.y && this.z == q2.z && this.w == q2.w) { return; } float result = (this.x * q2.x) + (this.y * q2.y) + (this.z * q2.z) + (this.w * q2.w); if (result < 0.0f) { // Negate the second quaternion and the result of the dot product q2.x = -q2.x; q2.y = -q2.y; q2.z = -q2.z; q2.w = -q2.w; result = -result; } // Set the first and second scale for the interpolation float scale0 = 1 - changeAmnt; float scale1 = changeAmnt; // Check if the angle between the 2 quaternions was big enough to // warrant such calculations if ((1 - result) > 0.1f) { // Get the angle between the 2 quaternions, and then store the sin() // of that angle float theta = FastMath.acos(result); float invSinTheta = 1f / FastMath.sin(theta); // Calculate the scale for q1 and q2, according to the angle and // it's sine value scale0 = FastMath.sin((1 - changeAmnt) * theta) * invSinTheta; scale1 = FastMath.sin((changeAmnt * theta)) * invSinTheta; } // Calculate the x, y, z and w values for the quaternion by using a // special // form of linear interpolation for quaternions. this.x = (scale0 * this.x) + (scale1 * q2.x); this.y = (scale0 * this.y) + (scale1 * q2.y); this.z = (scale0 * this.z) + (scale1 * q2.z); this.w = (scale0 * this.w) + (scale1 * q2.w); } /** * Sets the values of this quaternion to the nlerp from itself to q2 by blend. * @param q2 * @param blend */ public void nlerp(Quaternion q2, float blend) { float dot = dot(q2); float blendI = 1.0f - blend; if (dot < 0.0f) { x = blendI * x - blend * q2.x; y = blendI * y - blend * q2.y; z = blendI * z - blend * q2.z; w = blendI * w - blend * q2.w; } else { x = blendI * x + blend * q2.x; y = blendI * y + blend * q2.y; z = blendI * z + blend * q2.z; w = blendI * w + blend * q2.w; } normalizeLocal(); } /** * <code>add</code> adds the values of this quaternion to those of the * parameter quaternion. The result is returned as a new quaternion. * * @param q * the quaternion to add to this. * @return the new quaternion. */ public Quaternion add(Quaternion q) { return new Quaternion(x + q.x, y + q.y, z + q.z, w + q.w); } /** * <code>add</code> adds the values of this quaternion to those of the * parameter quaternion. The result is stored in this Quaternion. * * @param q * the quaternion to add to this. * @return This Quaternion after addition. */ public Quaternion addLocal(Quaternion q) { this.x += q.x; this.y += q.y; this.z += q.z; this.w += q.w; return this; } /** * <code>subtract</code> subtracts the values of the parameter quaternion * from those of this quaternion. The result is returned as a new * quaternion. * * @param q * the quaternion to subtract from this. * @return the new quaternion. */ public Quaternion subtract(Quaternion q) { return new Quaternion(x - q.x, y - q.y, z - q.z, w - q.w); } /** * <code>subtract</code> subtracts the values of the parameter quaternion * from those of this quaternion. The result is stored in this Quaternion. * * @param q * the quaternion to subtract from this. * @return This Quaternion after subtraction. */ public Quaternion subtractLocal(Quaternion q) { this.x -= q.x; this.y -= q.y; this.z -= q.z; this.w -= q.w; return this; } /** * <code>mult</code> multiplies this quaternion by a parameter quaternion. * The result is returned as a new quaternion. It should be noted that * quaternion multiplication is not commutative so q * p != p * q. * * @param q * the quaternion to multiply this quaternion by. * @return the new quaternion. */ public Quaternion mult(Quaternion q) { return mult(q, null); } /** * <code>mult</code> multiplies this quaternion by a parameter quaternion. * The result is returned as a new quaternion. It should be noted that * quaternion multiplication is not commutative so q * p != p * q. * * It IS safe for q and res to be the same object. * It IS NOT safe for this and res to be the same object. * * @param q * the quaternion to multiply this quaternion by. * @param res * the quaternion to store the result in. * @return the new quaternion. */ public Quaternion mult(Quaternion q, Quaternion res) { if (res == null) { res = new Quaternion(); } float qw = q.w, qx = q.x, qy = q.y, qz = q.z; res.x = x * qw + y * qz - z * qy + w * qx; res.y = -x * qz + y * qw + z * qx + w * qy; res.z = x * qy - y * qx + z * qw + w * qz; res.w = -x * qx - y * qy - z * qz + w * qw; return res; } /** * <code>apply</code> multiplies this quaternion by a parameter matrix * internally. * * @param matrix * the matrix to apply to this quaternion. */ public void apply(Matrix3f matrix) { float oldX = x, oldY = y, oldZ = z, oldW = w; fromRotationMatrix(matrix); float tempX = x, tempY = y, tempZ = z, tempW = w; x = oldX * tempW + oldY * tempZ - oldZ * tempY + oldW * tempX; y = -oldX * tempZ + oldY * tempW + oldZ * tempX + oldW * tempY; z = oldX * tempY - oldY * tempX + oldZ * tempW + oldW * tempZ; w = -oldX * tempX - oldY * tempY - oldZ * tempZ + oldW * tempW; } /** * * <code>fromAxes</code> creates a <code>Quaternion</code> that * represents the coordinate system defined by three axes. These axes are * assumed to be orthogonal and no error checking is applied. Thus, the user * must insure that the three axes being provided indeed represents a proper * right handed coordinate system. * * @param axis * the array containing the three vectors representing the * coordinate system. */ public Quaternion fromAxes(Vector3f[] axis) { if (axis.length != 3) { throw new IllegalArgumentException( "Axis array must have three elements"); } return fromAxes(axis[0], axis[1], axis[2]); } /** * * <code>fromAxes</code> creates a <code>Quaternion</code> that * represents the coordinate system defined by three axes. These axes are * assumed to be orthogonal and no error checking is applied. Thus, the user * must insure that the three axes being provided indeed represents a proper * right handed coordinate system. * * @param xAxis vector representing the x-axis of the coordinate system. * @param yAxis vector representing the y-axis of the coordinate system. * @param zAxis vector representing the z-axis of the coordinate system. */ public Quaternion fromAxes(Vector3f xAxis, Vector3f yAxis, Vector3f zAxis) { return fromRotationMatrix(xAxis.x, yAxis.x, zAxis.x, xAxis.y, yAxis.y, zAxis.y, xAxis.z, yAxis.z, zAxis.z); } /** * * <code>toAxes</code> takes in an array of three vectors. Each vector * corresponds to an axis of the coordinate system defined by the quaternion * rotation. * * @param axis * the array of vectors to be filled. */ public void toAxes(Vector3f axis[]) { Matrix3f tempMat = toRotationMatrix(); axis[0] = tempMat.getColumn(0, axis[0]); axis[1] = tempMat.getColumn(1, axis[1]); axis[2] = tempMat.getColumn(2, axis[2]); } /** * <code>mult</code> multiplies this quaternion by a parameter vector. The * result is returned as a new vector. * * @param v * the vector to multiply this quaternion by. * @return the new vector. */ public Vector3f mult(Vector3f v) { return mult(v, null); } /** * <code>mult</code> multiplies this quaternion by a parameter vector. The * result is stored in the supplied vector * * @param v * the vector to multiply this quaternion by. * @return v */ public Vector3f multLocal(Vector3f v) { float tempX, tempY; tempX = w * w * v.x + 2 * y * w * v.z - 2 * z * w * v.y + x * x * v.x + 2 * y * x * v.y + 2 * z * x * v.z - z * z * v.x - y * y * v.x; tempY = 2 * x * y * v.x + y * y * v.y + 2 * z * y * v.z + 2 * w * z * v.x - z * z * v.y + w * w * v.y - 2 * x * w * v.z - x * x * v.y; v.z = 2 * x * z * v.x + 2 * y * z * v.y + z * z * v.z - 2 * w * y * v.x - y * y * v.z + 2 * w * x * v.y - x * x * v.z + w * w * v.z; v.x = tempX; v.y = tempY; return v; } /** * Multiplies this Quaternion by the supplied quaternion. The result is * stored in this Quaternion, which is also returned for chaining. Similar * to this *= q. * * @param q * The Quaternion to multiply this one by. * @return This Quaternion, after multiplication. */ public Quaternion multLocal(Quaternion q) { float x1 = x * q.w + y * q.z - z * q.y + w * q.x; float y1 = -x * q.z + y * q.w + z * q.x + w * q.y; float z1 = x * q.y - y * q.x + z * q.w + w * q.z; w = -x * q.x - y * q.y - z * q.z + w * q.w; x = x1; y = y1; z = z1; return this; } /** * Multiplies this Quaternion by the supplied quaternion. The result is * stored in this Quaternion, which is also returned for chaining. Similar * to this *= q. * * @param qx - * quat x value * @param qy - * quat y value * @param qz - * quat z value * @param qw - * quat w value * * @return This Quaternion, after multiplication. */ public Quaternion multLocal(float qx, float qy, float qz, float qw) { float x1 = x * qw + y * qz - z * qy + w * qx; float y1 = -x * qz + y * qw + z * qx + w * qy; float z1 = x * qy - y * qx + z * qw + w * qz; w = -x * qx - y * qy - z * qz + w * qw; x = x1; y = y1; z = z1; return this; } /** * <code>mult</code> multiplies this quaternion by a parameter vector. The * result is returned as a new vector. * * @param v * the vector to multiply this quaternion by. * @param store * the vector to store the result in. It IS safe for v and store * to be the same object. * @return the result vector. */ public Vector3f mult(Vector3f v, Vector3f store) { if (store == null) { store = new Vector3f(); } if (v.x == 0 && v.y == 0 && v.z == 0) { store.set(0, 0, 0); } else { float vx = v.x, vy = v.y, vz = v.z; store.x = w * w * vx + 2 * y * w * vz - 2 * z * w * vy + x * x * vx + 2 * y * x * vy + 2 * z * x * vz - z * z * vx - y * y * vx; store.y = 2 * x * y * vx + y * y * vy + 2 * z * y * vz + 2 * w * z * vx - z * z * vy + w * w * vy - 2 * x * w * vz - x * x * vy; store.z = 2 * x * z * vx + 2 * y * z * vy + z * z * vz - 2 * w * y * vx - y * y * vz + 2 * w * x * vy - x * x * vz + w * w * vz; } return store; } /** * <code>mult</code> multiplies this quaternion by a parameter scalar. The * result is returned as a new quaternion. * * @param scalar * the quaternion to multiply this quaternion by. * @return the new quaternion. */ public Quaternion mult(float scalar) { return new Quaternion(scalar * x, scalar * y, scalar * z, scalar * w); } /** * <code>mult</code> multiplies this quaternion by a parameter scalar. The * result is stored locally. * * @param scalar * the quaternion to multiply this quaternion by. * @return this. */ public Quaternion multLocal(float scalar) { w *= scalar; x *= scalar; y *= scalar; z *= scalar; return this; } /** * <code>dot</code> calculates and returns the dot product of this * quaternion with that of the parameter quaternion. * * @param q * the quaternion to calculate the dot product of. * @return the dot product of this and the parameter quaternion. */ public float dot(Quaternion q) { return w * q.w + x * q.x + y * q.y + z * q.z; } /** * <code>norm</code> returns the norm of this quaternion. This is the dot * product of this quaternion with itself. * * @return the norm of the quaternion. */ public float norm() { return w * w + x * x + y * y + z * z; } // /** // * <code>normalize</code> normalizes the current <code>Quaternion</code> // * @deprecated The naming of this method doesn't follow convention. // * Please use {@link Quaternion#normalizeLocal() } instead. // */ // @Deprecated // public void normalize() { // float n = FastMath.invSqrt(norm()); // x *= n; // y *= n; // z *= n; // w *= n; // } /** * <code>normalize</code> normalizes the current <code>Quaternion</code>. * The result is stored internally. */ public Quaternion normalizeLocal() { float n = FastMath.invSqrt(norm()); x *= n; y *= n; z *= n; w *= n; return this; } /** * <code>inverse</code> returns the inverse of this quaternion as a new * quaternion. If this quaternion does not have an inverse (if its normal is * 0 or less), then null is returned. * * @return the inverse of this quaternion or null if the inverse does not * exist. */ public Quaternion inverse() { float norm = norm(); if (norm > 0.0) { float invNorm = 1.0f / norm; return new Quaternion(-x * invNorm, -y * invNorm, -z * invNorm, w * invNorm); } // return an invalid result to flag the error return null; } /** * <code>inverse</code> calculates the inverse of this quaternion and * returns this quaternion after it is calculated. If this quaternion does * not have an inverse (if it's normal is 0 or less), nothing happens * * @return the inverse of this quaternion */ public Quaternion inverseLocal() { float norm = norm(); if (norm > 0.0) { float invNorm = 1.0f / norm; x *= -invNorm; y *= -invNorm; z *= -invNorm; w *= invNorm; } return this; } /** * <code>negate</code> inverts the values of the quaternion. * */ public void negate() { x *= -1; y *= -1; z *= -1; w *= -1; } /** * * <code>toString</code> creates the string representation of this * <code>Quaternion</code>. The values of the quaternion are displaced (x, * y, z, w), in the following manner: <br> * (x, y, z, w) * * @return the string representation of this object. * @see java.lang.Object#toString() */ @Override public String toString() { return "(" + x + ", " + y + ", " + z + ", " + w + ")"; } /** * <code>equals</code> determines if two quaternions are logically equal, * that is, if the values of (x, y, z, w) are the same for both quaternions. * * @param o * the object to compare for equality * @return true if they are equal, false otherwise. */ @Override public boolean equals(Object o) { if (!(o instanceof Quaternion)) { return false; } if (this == o) { return true; } Quaternion comp = (Quaternion) o; if (Float.compare(x, comp.x) != 0) { return false; } if (Float.compare(y, comp.y) != 0) { return false; } if (Float.compare(z, comp.z) != 0) { return false; } if (Float.compare(w, comp.w) != 0) { return false; } return true; } /** * * <code>hashCode</code> returns the hash code value as an integer and is * supported for the benefit of hashing based collection classes such as * Hashtable, HashMap, HashSet etc. * * @return the hashcode for this instance of Quaternion. * @see java.lang.Object#hashCode() */ @Override public int hashCode() { int hash = 37; hash = 37 * hash + Float.floatToIntBits(x); hash = 37 * hash + Float.floatToIntBits(y); hash = 37 * hash + Float.floatToIntBits(z); hash = 37 * hash + Float.floatToIntBits(w); return hash; } /** * <code>readExternal</code> builds a quaternion from an * <code>ObjectInput</code> object. <br> * NOTE: Used with serialization. Not to be called manually. * * @param in * the ObjectInput value to read from. * @throws IOException * if the ObjectInput value has problems reading a float. * @see java.io.Externalizable */ public void readExternal(ObjectInput in) throws IOException { x = in.readFloat(); y = in.readFloat(); z = in.readFloat(); w = in.readFloat(); } /** * <code>writeExternal</code> writes this quaternion out to a * <code>ObjectOutput</code> object. NOTE: Used with serialization. Not to * be called manually. * * @param out * the object to write to. * @throws IOException * if writing to the ObjectOutput fails. * @see java.io.Externalizable */ public void writeExternal(ObjectOutput out) throws IOException { out.writeFloat(x); out.writeFloat(y); out.writeFloat(z); out.writeFloat(w); } /** * <code>lookAt</code> is a convienence method for auto-setting the * quaternion based on a direction and an up vector. It computes * the rotation to transform the z-axis to point into 'direction' * and the y-axis to 'up'. * * @param direction * where to look at in terms of local coordinates * @param up * a vector indicating the local up direction. * (typically {0, 1, 0} in jME.) */ public void lookAt(Vector3f direction, Vector3f up) { TempVars vars = TempVars.get(); vars.vect3.set(direction).normalizeLocal(); vars.vect1.set(up).crossLocal(direction).normalizeLocal(); vars.vect2.set(direction).crossLocal(vars.vect1).normalizeLocal(); fromAxes(vars.vect1, vars.vect2, vars.vect3); vars.release(); } public void write(JmeExporter e) throws IOException { OutputCapsule cap = e.getCapsule(this); cap.write(x, "x", 0); cap.write(y, "y", 0); cap.write(z, "z", 0); cap.write(w, "w", 1); } public void read(JmeImporter e) throws IOException { InputCapsule cap = e.getCapsule(this); x = cap.readFloat("x", 0); y = cap.readFloat("y", 0); z = cap.readFloat("z", 0); w = cap.readFloat("w", 1); } /** * @return A new quaternion that describes a rotation that would point you * in the exact opposite direction of this Quaternion. */ public Quaternion opposite() { return opposite(null); } /** * FIXME: This seems to have singularity type issues with angle == 0, possibly others such as PI. * @param store * A Quaternion to store our result in. If null, a new one is * created. * @return The store quaternion (or a new Quaterion, if store is null) that * describes a rotation that would point you in the exact opposite * direction of this Quaternion. */ public Quaternion opposite(Quaternion store) { if (store == null) { store = new Quaternion(); } Vector3f axis = new Vector3f(); float angle = toAngleAxis(axis); store.fromAngleAxis(FastMath.PI + angle, axis); return store; } /** * @return This Quaternion, altered to describe a rotation that would point * you in the exact opposite direction of where it is pointing * currently. */ public Quaternion oppositeLocal() { return opposite(this); } @Override public Quaternion clone() { try { return (Quaternion) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); // can not happen } } }