Quaternion-未テスト-実装ファイル
5653 ワード
#include <assert.h>
#include "Quaternion.h"
#include "Matrix4x4.h"
void Quaternion ::Indentity()
{
w = 1 ;
x = y = z = 0 ;
}
void Quaternion ::SetToRotateAboutX(float theta)
{
float thetaOver2 = theta / 2 ;
w = cos(thetaOver2) ;
x = sin(thetaOver2) ;
y = 0 ;
z = 0 ;
}
void Quaternion ::SetToRotateAboutY(float theta)
{
float thetaOver2 = theta / 2 ;
w = cos(thetaOver2) ;
x = 0 ;
y = sin(thetaOver2) ;
z = 0 ;
}
void Quaternion ::SetToRotateAboutZ(float theta)
{
float thetaOver2 = theta / 2 ;
w = cos(thetaOver2) ;
x = 0 ;
y = 0 ;
z = sin(thetaOver2) ;
}
void Quaternion ::SetToRotateAboutAxis(const Vector3& axis, float theta)
{
assert(fabs(Vector3.GetMagnitude(axis) - 1) < 0.01) ;
float thetaOver2 = theta / 2 ;
w = cos(thetaOver2) ;
float temp = sin(thetaOver2) ;
x = axis.x * temp ;
y = axis.y * temp ;
z = axis.z * temp ;
}
void Quaternion ::EulerAnglesToQuaternion(const EulerAngles& e)
{
float bankOver2 = e.bank / 2 ;
Quaternion bank ;
bank.w = cos(bankOver2) ;
bank.x = 0 ;
bank.y = 0 ;
bank.z = sin(bankOver2) ;
float pitchOver2 = e.pitch / 2 ;
Quaternion pitch ;
pitch.w = cos(pitchOver2) ;
pitch.x = sin(pitchOver2) ;
pitch.y = 0 ;
pitch.z = 0 ;
float headingOver2 = e.heading / 2 ;
Quaternion heading ;
heading.w = cos(headingOver2) ;
heading.x = 0 ;
heading.y = sin(headingOver2) ;
heading.z = 0 ;
// bank * pitch * heading
*this = bank * pitch ;
*this = *this * heading ;
}
void Quaternion ::MatrixToQuaternion(const Matrix4x4& m)
{
// , !
float fourWSquaredMinusOne = m.m11 + m.m22 + m.m33 ;
float fourXSquaredMinusOne = m.m11 - m.m22 - m.m33 ;
float fourYSquaredMinusOne = m.m22 - m.m11 - m.m33 ;
float fourZSquaredMinusOne = m.m33 - m.m11 - m.m22 ;
int enumIndex = FourWSquaredMinusOne ;
float max = fourWSquaredMinusOne ;
if (fourXSquaredMinusOne > max)
{
max = fourXSquaredMinusOne ;
enumIndex = FourXSquaredMinusOne ;
}
if (fourYSquaredMinusOne > max)
{
max = fourYSquaredMinusOne ;
enumIndex = FourYSquaredMinusOne ;
}
if (fourZSquaredMinusOne > max)
{
max = fourZSquaredMinusOne ;
enumIndex = FourZSquaredMinusOne ;
}
float biggest = sqrt(max + 1) / 2 ;
switch (enumIndex)
{
case FourWSquaredMinusOne:
w = biggest ;
x = (m.m23 - m.m32) / (4 * w) ;
y = (m.m31 - m.m13) / (4 * w) ;
z = (m.m12 - m.m21) / (4 * w) ;
break ;
case FourXSquaredMinusOne:
x = biggest ;
w = (m.m23 - m.m32) / (4 * x) ;
y = (m.m12 + m.m21) / (4 * x) ;
z = (m.m31 + m.m13) / (4 * x) ;
break ;
case FourYSquaredMinusOne:
y = biggest ;
w = (m.m31 - m.m13) / (4 * y) ;
x = (m.m12 + m.m21) / (4 * y) ;
z = (m.m23 + m.m32) / (4 * y) ;
break ;
case FourZSquaredMinusOne:
z = biggest ;
w = (m.m12 - m.m21) / (4 * z) ;
x = (m.m31 + m.m13) / (4 * z) ;
y = (m.m23 + m.m32) / (4 * z) ;
break ;
}
}
void Quaternion ::Normalize(void)
{
float magnitude = GetMagnitude(*this) ;
w /= magnitude ;
x /= magnitude ;
y /= magnitude ;
z /= magnitude ;
}
float Quaternion ::GetRotationAngle() const
{
return acos(w) * 2 ;
}
Vector3 Quaternion ::GetRotationAxis() const
{
assert(fabs(GetMagnitude(*this) - 1) < 0.01) ;
// sinTheta^1 + cosTheta^2 = 1
float sinTheta = sqrt(1 - w * w) ;
return Vector3(x / sinTheta, y / sinTheta, z / sinTheta) ;
}
void Quaternion ::operator= (const Quaternion& q)
{
w = q.w ;
x = q.x ;
y = q.y ;
z = q.z ;
}
Quaternion Quaternion ::operator * (const Quaternion& q) const
{
Quaternion result ;
result.w = w * q.w - x * q.x - y * q.y - z * q.z ;
result.x = w * q.x + x * q.w + z * q.y - y * q.z ;
result.y = w * q.y + y * q.w + x * q.z - z * q.x ;
result.z = w * q.z + z * q.w + y * q.x - x * q.y ;
return result ;
}
Quaternion& Quaternion ::operator *= (const Quaternion& q)
{
*this = *this * q ;
return *this ;
}
float Quaternion ::GetMagnitude(const Quaternion& q)
{
return sqrt(q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z) ;
}
float Quaternion ::DotProduct(const Quaternion& lhs, const Quaternion& rhs)
{
return lhs.w * rhs.w + lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z ;
}
Quaternion Quaternion ::Slerp(const Quaternion& lhs, const Quaternion& rhs, float t)
{
assert(fabs(GetMagnitude(lhs) - 1) < 0.01) ;
assert(fabs(GetMagnitude(rhs) - 1) < 0.01) ;
if (t < 0)
return lhs ;
else if (t > 1)
return rhs ;
float cosOmega = DotProduct(lhs, rhs) ;
Quaternion workedRhs = lhs ;
if (cosOmega < 0)
{
workedRhs.w = -lhs.w ;
workedRhs.x = -lhs.x ;
workedRhs.y = -lhs.y ;
workedRhs.z = -lhs.z ;
cosOmega = -cosOmega ;
}
float k0, k1 ;
if (cosOmega > 0.9999)
{
k0 = 1 - t ;
k1 = t ;
}
else
{
float sinOmega = sqrt(1 - cosOmega * cosOmega) ;
float omega = atan2(sinOmega, cosOmega) ;
k0 = sin((1 - t) * omega) / sinOmega ;
k1 = sin(t * omega) / sinOmega ;
}
Quaternion result ;
result.w = k0 * lhs.w + k1 * workedRhs.w ;
result.x = k0 * lhs.x + k1 * workedRhs.x ;
result.y = k0 * lhs.y + k1 * workedRhs.y ;
result.z = k0 * lhs.z + k1 * workedRhs.z ;
return result ;
}
Quaternion Quaternion ::Conjugate(const Quaternion& q)
{
Quaternion result ;
result.w = q.w ;
result.x = -q.x ;
result.y = -q.y ;
result.z = -q.z ;
return result ;
}
Quaternion Quaternion ::Pow(const Quaternion& q, float exponent)
{
if (fabs(q.w) > One)
return q ;
float theta = acos(q.w) * 2 ;
float newTheta = theta * exponent ;
q.w = cos(newTheta / 2) ;
float multiple = sin(newTheta) / sin(theta) ;
q.x *= multiple ;
q.y *= multiple ;
q.z *= multiple ;
return q ;
}