#include "c4d_quaternion.h" #ifndef __API_INTERN__ #include "c4d_tools.h" #endif #include "ge_math.h" void Quaternion::SetHPB(const Vector64& hpb) { Quaternion qh, qp, qb; qh.SetAxis(Vector64(0, 1, 0), hpb.x); qp.SetAxis(Vector64(1, 0, 0), hpb.y); qb.SetAxis(Vector64(0, 0, 1), hpb.z); *this = QMul(qb, QMul(qp, qh)); } Quaternion QAdd(const Quaternion& q1, const Quaternion& q2) { Quaternion r; r.w = q1.w + q2.w; r.v = q1.v + q2.v; return r; } Quaternion QSub(const Quaternion& q1, const Quaternion& q2) { Quaternion r; r.w = q1.w - q2.w; r.v = q1.v - q2.v; return r; } Quaternion QMul(const Quaternion& q1, const Quaternion& q2) { Quaternion r; r.w = (q1.w * q2.w) - Dot(q1.v, q2.v); r.v = q1.w * q2.v + q2.w * q1.v + Cross(q1.v, q2.v); return r; } Quaternion QMul(const Quaternion& q, Float64 s) { Quaternion r = q; r.w *= s; r.v *= s; return r; } Quaternion QInvert(const Quaternion& q) { Quaternion r; r.w = -q.w; r.v = q.v; return r; } Quaternion QDeriv(const Quaternion& q, const Vector64& w) { Quaternion r; r.w = 0.5 * Dot(-q.v, w); r.v.x = 0.5 * (q.w * w.x - q.v.z * w.y + q.v.y * w.z); r.v.y = 0.5 * (q.v.z * w.x + q.w * w.y - q.v.x * w.z); r.v.z = 0.5 * (-q.v.y * w.x + q.v.x * w.y + q.w * w.z); return r; } Quaternion QNorm(const Quaternion& q) { Quaternion r; Float64 len; len = Sqrt(q.w * q.w + Dot(q.v, q.v)); if (len == 0.0) return q; r.w = q.w / len; r.v = q.v / len; return r; } Quaternion QSlerp(const Quaternion& q1, const Quaternion& q2, const Float64 alfa) { Float64 sum, teta, beta1, beta2; Quaternion q, qd = q2; sum = q1.w * q2.w + Dot(q1.v, q2.v); if (sum < 0.0) { sum = -sum; qd.v = -qd.v; qd.w = -qd.w; } if (1.0 - sum > EPSILON5) { teta = ACos(sum); Float64 sn = 1.0 / Sin(teta); beta1 = Sin((1.0 - alfa) * teta) * sn; beta2 = Sin(alfa * teta) * sn; } else { beta1 = 1.0 - alfa; beta2 = alfa; } q.w = beta1 * q1.w + beta2 * qd.w; q.v.x = beta1 * q1.v.x + beta2 * qd.v.x; q.v.y = beta1 * q1.v.y + beta2 * qd.v.y; q.v.z = beta1 * q1.v.z + beta2 * qd.v.z; return q; } // q0 = i-1, q1 = i, q2 = i+1, q3 = i+2 Quaternion QSquad(const Quaternion& q0, const Quaternion& q1, const Quaternion& q2, const Quaternion& q3, Float64 t) { Float64 k = 2.0 * (1.0 - t) * t; return QSlerp(QSlerp(q0, q3, t), QSlerp(q1, q2, t), k); } Quaternion QLogN(const Quaternion& q) { Float64 theta, scale; Quaternion qq; scale = Sqrt(Dot(q.v, q.v)); theta = Float64(atan2(scale, q.w)); if (scale > 0.0) scale = theta / scale; qq.v *= scale; qq.w = 0.0; return qq; } // Exp: exponentiate quaternion (where q.w==0) Quaternion QExpQ(const Quaternion& q) { Float64 theta, scale; Quaternion qq; theta = Sqrt(Dot(q.v, q.v)); scale = 1.0; if (theta > 0.0) scale = Sin(theta) / theta; qq.v *= scale; qq.w = Cos(theta); return qq; } static Quaternion QTangent(const Quaternion& qn_m1, const Quaternion& qn, const Quaternion& qn_p1) { Quaternion t1, t2; t1 = QMul(QInvert(qn), qn_p1); t2 = QMul(QInvert(qn), qn_m1); t1 = QLogN(t1); t2 = QLogN(t2); t1.v = (t1.v + t2.v) * (-0.25); t1.w = 0.0; // t1 = QAdd(t1,t2); // t1 = QMul(t1,-0.25); t1 = QExpQ(t1); t2 = QMul(qn, t1); return t2; } Quaternion QSpline(const Quaternion& qn_m1, const Quaternion& qn, const Quaternion& qn_p1, const Quaternion& qn_p2, Float64 t) { Quaternion an, an_p1; an = QTangent(qn_m1, qn, qn_p1); an_p1 = QTangent(qn, qn_p1, qn_p2); return QSquad(qn, an, an_p1, qn_p1, t); } Quaternion QBlend(const Quaternion& q1, const Quaternion& q2, const Float64 r) { Float64 w1 = 1.0 - r; Float64 w2 = r; Float64 qq = q1.w * q2.w + Dot(q1.v, q2.v); Float64 z = Sqrt((w1 - w2) * (w1 - w2) + 4.0 * w1 * w2 * qq * qq); Float64 f1 = Sqrt(w1 * (w1 - w2 + z) / (z * (w1 + w2 + z))); Float64 f2 = Sqrt(w2 * (w2 - w1 + z) / (z * (w1 + w2 + z))); Quaternion qm; if (qq < 0.0) qm = QSub(QMul(q1, f1), QMul(q2, f2)); else qm = QAdd(QMul(q1, f1), QMul(q2, f2)); return QNorm(qm); } static Vector64 LGetHPB(const Vector64& v1, const Vector64& v2, const Vector64& v3) { Vector64 rot; Float64 l = Sqrt(v3.x * v3.x + v3.z * v3.z); if (l < 0.00001) { rot.x = 0.0; rot.z = ACos(Dot(Vector64(1.0, 0.0, 0.0), !v1)); if (v3.y > 0.0) { rot.y = PI05; if (v1.z < 0.0) rot.z = PI2 - rot.z; } else { rot.y = -PI05; if (v1.z > 0.0) rot.z = PI2 - rot.z; } } else { if (v3.z > 0.0) rot.x = -ASin(v3.x / l); else rot.x = PI + ASin(v3.x / l); if (rot.x < 0.0) rot.x += PI2; rot.y = ATan(v3.y / l); rot.z = ACos(Dot(Vector64(Cos(rot.x), 0.0, Sin(rot.x)), !v1)); if (v1.y > 0.0) rot.z = PI2 - rot.z; } if (rot.z > PI) rot.z = rot.z - PI2; return rot; } Vector64 Matrix64ToHPB(const Matrix64& m) { return LGetHPB(m.v1, m.v2, m.v3); } // **************************************** inline void LSinCos(Float64 w, Float64& sn, Float64& cn) { sn = Sin(w); cn = Cos(w); } Matrix64 LHPBToMatrix(const Vector64& w) { Float64 cn, sn, ck, sk, cs, ss, cosksinn, sinksinn; LSinCos(w.x, ss, cs); LSinCos(w.y, sn, cn); LSinCos(w.z, sk, ck); cosksinn = ck * sn; sinksinn = sk * sn; return Matrix64(Vector64(0.0), Vector64(ck * cs - sinksinn * ss, -sk * cn, ck * ss + sinksinn * cs), Vector64(sk * cs + cosksinn * ss, ck * cn, sk * ss - cosksinn * cs), Vector64(-cn * ss, sn, cn * cs)); }