// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com package engine.math; import org.pmw.tinylog.Logger; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * Quaternion 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. * * Quaternion is defined by four floating point numbers: {x y z w}. */ public class Quaternion { private static final Vector3f tmpYaxis = new Vector3f(); private static final Vector3f tmpZaxis = new Vector3f(); private static final Vector3f tmpXaxis = new Vector3f(); public float x, y, z, w; public float angleX, angleY, angleZ; /** * Constructor instantiates a new Quaternion 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 Quaternion 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; float[] angles = new float[3]; angles = this.toAngles(angles); angleX = angles[0]; angleY = angles[1]; angleZ = angles[2]; } /** * Constructor instantiates a new Quaternion object from a * collection of rotation angles. * * @param angles the angles of rotation (x, y, z) that will define the * Quaternion. */ public Quaternion(float[] angles) { fromAngles(angles); } /** * Constructor instantiates a new Quaternion 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 Quaternion 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 the data in a Quaternion 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 void set(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; } /** * Sets the data in this Quaternion object to be equal to the * passed Quaternion object. The values are copied producing a * new object. * * @param q The Quaternion to copy values from. * @return this for chaining */ public Quaternion set(Quaternion q) { this.x = q.x; this.y = q.y; this.z = q.z; this.w = q.w; return this; } /** * 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() { return x == 0 && y == 0 && z == 0 && w == 1; } /** * fromAngles builds a quaternion from the Euler rotation * angles (y,r,p). * * @param angles the Euler angles of rotation (in radians). */ public void fromAngles(float[] angles) { if (angles.length != 3) throw new IllegalArgumentException( "Angles array must have three elements"); fromAngles(angles[0], angles[1], angles[2]); } /** * fromAngles builds a Quaternion from the Euler rotation * angles (y,r,p). Note that we are applying in order: roll, pitch, yaw but * we've ordered them in x, y, and z for convenience. See: * http://www.euclideanspace * .com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm * * @param yaw the Euler yaw of rotation (in radians). (aka Bank, often rot * around x) * @param roll the Euler roll of rotation (in radians). (aka Heading, often * rot around y) * @param pitch the Euler pitch of rotation (in radians). (aka Attitude, often * rot around z) */ public Quaternion fromAngles(float yaw, float roll, float pitch) { float angle; float sinRoll, sinPitch, sinYaw, cosRoll, cosPitch, cosYaw; angle = pitch * 0.5f; sinPitch = FastMath.sin(angle); cosPitch = FastMath.cos(angle); angle = roll * 0.5f; sinRoll = FastMath.sin(angle); cosRoll = FastMath.cos(angle); angle = yaw * 0.5f; sinYaw = FastMath.sin(angle); cosYaw = FastMath.cos(angle); // variables used to reduce multiplication calls. float cosRollXcosPitch = cosRoll * cosPitch; float sinRollXsinPitch = sinRoll * sinPitch; float cosRollXsinPitch = cosRoll * sinPitch; float sinRollXcosPitch = sinRoll * cosPitch; w = (cosRollXcosPitch * cosYaw - sinRollXsinPitch * sinYaw); x = (cosRollXcosPitch * sinYaw + sinRollXsinPitch * cosYaw); y = (sinRollXcosPitch * cosYaw + cosRollXsinPitch * sinYaw); z = (cosRollXsinPitch * cosYaw - sinRollXcosPitch * sinYaw); normalize(); return this; } /** * toAngles returns this quaternion converted to Euler rotation * angles (yaw,roll,pitch).
* See http://www.euclideanspace.com/maths/geometry/rotations/conversions/ * quaternionToEuler/index.htm * * @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; } /** * fromRotationMatrix 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) { // 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; } /** * toRotationMatrix 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); } /** * toRotationMatrix 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; } /** * toRotationMatrix 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) { 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; } /** * getRotationColumn returns one of three columns specified by * the parameter. This column is returned as a Vector3f object. * * @param i the column to retrieve. Must be between 0 and 2. * @return the column specified by the index. * @throws Exception */ public Vector3f getRotationColumn(int i) throws Exception { return getRotationColumn(i, null); } /** * getRotationColumn returns one of three columns specified by * the parameter. This column is returned as a Vector3f 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. * @throws Exception */ public Vector3f getRotationColumn(int i, Vector3f store) throws Exception { 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: throw new Exception("Invalid column index. " + i); } return store; } /** * fromAngleAxis 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; } /** * fromAngleNormalAxis 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; } /** * toAngleAxis sets a given angle and axis to that represented * by the current quaternion. The values are stored as following: 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; } /** * slerp 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 difference */ 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); } /** * add 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); } /** * add 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; } /** * subtract 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); } /** * subtract 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; } /** * mult multiplies this quaternion by a parameter quaternion. * The result is returned as a new quaternion. It should be noted that * quaternion multiplication is not cummulative 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); } /** * mult multiplies this quaternion by a parameter quaternion * (q). 'this' is not modified. It should be noted that quaternion * multiplication is not cummulative so q * p != p * q. *

* It IS safe for q 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 (may be null). If * non-null, the input values of 'res' will be ignored and * replaced. * @return If specified res is null, then a new Quaternion; otherwise * returns the populated 'res'. */ 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; float[] angles = new float[3]; angles = res.toAngles(angles); res.angleX = angles[0]; res.angleY = angles[1]; res.angleZ = angles[2]; return res; } /** * apply 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; } /** * fromAxes creates a Quaternion 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]); } /** * fromAxes creates a Quaternion 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); } /** * toAxes 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. * @throws Exception */ public void toAxes(Vector3f axis[]) throws Exception { Matrix3f tempMat = toRotationMatrix(); axis[0] = tempMat.getColumn(0, axis[0]); axis[1] = tempMat.getColumn(1, axis[1]); axis[2] = tempMat.getColumn(2, axis[2]); } /** * mult multiplies this quaternion by a parameter vector. The * result is returned as a new vector. 'this' is not modified. * * @param v the vector to multiply this quaternion by. * @return the new vector. */ public Vector3f mult(Vector3f v) { return mult(v, null); } /** * mult multiplies this quaternion by a parameter vector. The * result is stored in the supplied vector This method is very poorly named, * since the specified vector is modified and, contrary to the other *Local * methods in this and other jME classes, 'this' remains unchanged. * * @param v the vector which this Quaternion multiplies. * @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; } /** * mult multiplies this quaternion by a parameter vector. The * result is returned as a new vector. 'this' is not modified. * * @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; } /** * mult 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); } /** * mult 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; } /** * dot 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; } /** * norm 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; } /** * normalize normalizes the current Quaternion */ public void normalize() { float n = FastMath.invSqrt(norm()); x *= n; y *= n; z *= n; w *= n; } /** * inverse 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; } /** * inverse 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 norma 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; } /** * negate inverts the values of the quaternion. */ public void negate() { x *= -1; y *= -1; z *= -1; w *= -1; } /** * equals 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; return Float.compare(w, comp.w) == 0; } /** * hashCode 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; } /** * readExternal builds a quaternion from an * ObjectInput object.
* 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(); } /** * writeExternal writes this quaternion out to a * ObjectOutput 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); } /** * lookAt 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) { tmpZaxis.set(direction).normalizeLocal(); tmpXaxis.set(up).crossLocal(direction).normalizeLocal(); tmpYaxis.set(direction).crossLocal(tmpXaxis).normalizeLocal(); fromAxes(tmpXaxis, tmpYaxis, tmpZaxis); } /** * @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); } /** * @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) { Logger.error(e); throw new AssertionError(); // can not happen } } public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } public float getZ() { return z; } public void setZ(float z) { this.z = z; } public float getW() { return w; } public void setW(float w) { this.w = w; } }