向量旋轉與四元組 的運算和轉換
// 版權所有(C) 樑意, 2004
// 最後修改: 2004.8.杭州
#ifndef ROTATION_HEADER<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
#define ROTATION_HEADER
#include"matrix.h"
const double DELTA_ROT=1.0E-10;
template<typename T>
class quaternion;
template<typename T>
class rotation;
template<typename T>
quaternion RotationToQuaternion(const rotation &r)//r已歸一化
{
T cosValue, sinValue, theta;
theta= r.angle/180.0f*M_PI;
cosValue = T(cos(theta/2.0f));
sinValue = T(sin(theta/2.0f));
return quaternion(sinValue*r.axis.x,
sinValue*r.axis.y,
sinValue*r.axis.z,
cosValue);
}
template
rotation QuaternionToRotation(const quaternion &q)//q已歸一化
{
if( ((q.w>1-DELTA_ROT)&&(q.w<1+DELTA_ROT)) ||
((q.w>-1-DELTA_ROT)&&(q.w<-1+DELTA_ROT)) )
{
return rotation();
}
T sinValue, halfTheta;
halfTheta= T(acos(Q.w));
sinValue = T(sin(halfTheta));
if(sinValue < DELTA_ROT)
{
return rotation();
}
return rotation( q.x/sinValue,
q.y/sinValue,q.z/sinValue,
halfTheta * 2.0f * 180.0f /3.14159f);
}
template<typename T>
void QuaternionToMatrix(T M[16], const quaternion & q)//q已歸一化
{
T m[4][4];
T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
// calculate coefficients
x2 = q.x * 2.0f;
y2 = q.y * 2.0f;
z2 = q.z * 2.0f;
xx = q.x * x2;xy = q.x * y2;xz = q.x * z2;
yy = q.y * y2;yz = q.y * z2;zz = q.z * z2;
wx = q.w * x2;wy = q.w * y2;wz = q.w * z2;
m[0][0] = 1.0f - (yy + zz);
m[0][1] = xy + wz;
m[0][2] = xz - wy;
m[0][3] = 0.0f;
m[1][0] = xy - wz;
m[1][1] = 1.0f - (xx + zz);
m[1][2] = yz + wx;
m[1][3] = 0.0f;
m[2][0] = xz + wy;
m[2][1] = yz - wx;
m[2][2] = 1.0f - (xx + yy);
m[2][3] = 0.0f;
m[3][0] = 0;
m[3][1] = 0;
m[3][2] = 0;
m[3][3] = 1;
for(size_t i=0; i<4; i++)
for(size_t j=0; j<4; j++)
M[i*4+j]=m[i][j];
}
template<typename T>
voidQuaternionToMatrix_OpenGL(T M[16], const quaternion & q)
{
T m[4][4];//column major format (OpenGL format)
T wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
// calculate coefficients
x2 = q.x * 2.0f;
y2 = q.y * 2.0f;
z2 = q.z * 2.0f;
xx = q.x * x2;xy = q.x * y2;xz = q.x * z2;
yy = q.y * y2;yz = q.y * z2;zz = q.z * z2;
wx = q.w * x2;wy = q.w * y2;wz = q.w * z2;
m[0][0] = 1.0f - (yy + zz);
m[0][1] = xy - wz;
m[0][2] = xz + wy;
m[0][3] = 0.0f;
m[1][0] = xy + wz;
m[1][1] = 1.0f - (xx + zz);
m[1][2] = yz - wx;
m[1][3] = 0.0f;
m[2][0] = xz - wy;
m[2][1] = yz + wx;
m[2][2] = 1.0f - (xx + yy);
m[2][3] = 0.0f;
m[3][0] = 0;
m[3][1] = 0;
m[3][2] = 0;
m[3][3] = 1;
for(int i=0; i<4; i++)
for(int j=0; j<4; j++)
M[i*4+j]=m[i][j];//column major format (OpenGL format)
}
template<typename T>
quaternion VectorToQuaternion(const vector3d &v)
{
return quaternion( V.x,V.y,V.z,0);
}
template<typename T>
vector3d QuaternionToVector(const quaternion Q)
{
return vector3d(Q.x,Q.y,Q.z);
}
// V 經過 Q 變換後為 V', 函式返回 V'
template<typename T>
vector3d VectorTransform(const vector3d & V, const quaternion &Q)
{
if( ((Q.w>1-DELTA_ROT)&&(Q.w<1+DELTA_ROT)) ||
((Q.w>-1-DELTA_ROT)&&(Q.w<-1+DELTA_ROT)) ) return V;
quaternion A, B, C;
A=VectorToQuaternion(V);
B=Q;
B.Inverse()
C=Q*A*B;
return QuaternionToVector(C);
}
template<typename T>
class rotation
{
public:
vector3d axis;
T angle;
public:
rotation(T x=0,T y=0,T z=1.0,T _angle=0)
{
axis.x=x;
axis.y=y;
axis.z=z;
angle=_angle;
}
rotation(vector3d &v,T _angle=0)
{
axis=v;
angle=_angle;
}
};
template<typename T>
class quaternion
{
public:
T x,y,z,w;
public:
quaternion(T _x=0,T _y=0,T _z=1,T _w=0)
{
x=_x;
y=_y;
z=_z;
w=_w;
}
T Magnitude()
{
return sqrt(x*x+y*y+z*z+w*w);
}
void Normalize(quaternion &out)
{
T m=Magnitude();
if(m>0)
{
out.x=x/m;
out.y=y/m;
out.z=z/m;
out.w=w/m;
}
}
void Normalize()
{
Normalize(*this);
}
void Inverse(quaternion &out)
{
T m=Magnitude();
if(m>0)
{
out.x=-x/m;
out.y=-y/m;
out.z=-z/m;
out.w=-w/m;
}
}
void Inverse()
{
Inverse(*this);
}
void Conjugate(quaternion &out)
{
out.x=-x;
out.y=-y;
out.z=-z;
}
void Conjugate()
{
Conjugate(*this);
}
quaternionoperator +(quaternion &q)
{
return quaternion(x+q.x,y+q.y,z+q.z,w+q.w);
}
quaternionoperator -(quaternion &q)
{
return quaternion(x-q.x,y-q.y,z-q.z,w-q.w);
}
quaternionoperator *(const quaternion &q)
{
return quaternion(w *q.x +x *q.w + y * q.z - z * q.y,
w *q.y + y * q.w + z * q.x - x * q.z,
w *q.z + z * q.w + x * q.y - y * q.x,
w *q.w - x * q.x - y * q.y - z * q.z);
}
quaternionoperator *(T s)
{
return quaternion(x*s,y*s,z*s,w*s);
}
// Nick Bobick 編寫
// Purpose:Spherical Linear Interpolation Between two Quaternions
// Arguments:Two Quaternions, blend factor
quaternion slerp(const quaternion &from,const quaternion &to,T t)
{
Tto1[4];
Tomega, cosom, sinom, scale0, scale1;
// calc cosine
cosom = from.x * to.x + from.y * to.y + from.z * to.z
+ from.w * to.w;
// adjust signs (if necessary)
if ( cosom <0.0 ){ cosom = -cosom; to1[0] = - to.x;
to1[1] = - to.y;
to1[2] = - to.z;
to1[3] = - to.w;
} else{
to1[0] = to.x;
to1[1] = to.y;
to1[2] = to.z;
to1[3] = to.w;
}
// calculate coefficients
if ( (1.0 - cosom) > DELTA_ROT ) {
// standard case (slerp)
omega = T(acos(cosom));
sinom = T(sin(omega));
scale0 = T(sin((1.0 - t) * omega) / sinom);
scale1 = T(sin(t * omega) / sinom);
} else {
// "from" and "to" quaternions are very close
//... so we can do a linear interpolation
scale0 = 1.0f - t;
scale1 = t;
}
// calculate final values
return(scale0 * from.x + scale1 * to1[0],
scale0 * from.y + scale1 * to1[1],
scale0 * from.z + scale1 * to1[2],
scale0 * from.w + scale1 * to1[3]);
}
};
#endif