📄 quaternion.java
字号:
/*
* Copyright (c) 2003-2009 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.jme.math;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.logging.Logger;
import com.jme.system.JmeException;
import com.jme.util.export.InputCapsule;
import com.jme.util.export.JMEExporter;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.OutputCapsule;
import com.jme.util.export.Savable;
/**
* <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 class Quaternion implements Externalizable, Savable, Cloneable {
private static final Logger logger = Logger.getLogger(Quaternion.class.getName());
private static final long serialVersionUID = 1L;
public 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;
}
/**
* 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.
*/
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 <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 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;
}
/**
* 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 void fromAngles(float[] angles) {
if (angles.length != 3)
throw new IllegalArgumentException(
"Angles array must have three elements");
fromAngles(angles[0], angles[1], angles[2]);
}
/**
* <code>fromAngles</code> 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;
}
/**
* <code>toAngles</code> returns this quaternion converted to Euler
* rotation angles (yaw,roll,pitch).<br/>
* 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;
}
/**
*
* <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) {
// 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) {
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -