You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
5.5 KiB
151 lines
5.5 KiB
// David Eberly, Geometric Tools, Redmond WA 98052
|
|
// Copyright (c) 1998-2021
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// https://www.boost.org/LICENSE_1_0.txt
|
|
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
|
|
// Version: 4.0.2019.08.13
|
|
|
|
#pragma once
|
|
|
|
#include <Mathematics/Quaternion.h>
|
|
|
|
// The spherical linear interpolation (slerp) of unit-length quaternions
|
|
// q0 and q1 for t in [0,1] and theta in (0,pi) is
|
|
// slerp(t,q0,q1) = [sin((1-t)*theta)*q0 + sin(theta)*q1]/sin(theta)
|
|
// where theta is the angle between q0 and q1 [cos(theta) = Dot(q0,q1)].
|
|
// This function is a parameterization of the great spherical arc between
|
|
// q0 and q1 on the unit hypersphere. Moreover, the parameterization is
|
|
// one of normalized arclength--a particle traveling along the arc through
|
|
// time t does so with constant speed.
|
|
//
|
|
// Read the comments in GteChebyshevRatio.h regarding estimates for the
|
|
// ratio sin(t*theta)/sin(theta).
|
|
//
|
|
// When using slerp in animations involving sequences of quaternions, it is
|
|
// typical that the quaternions are preprocessed so that consecutive ones
|
|
// form an acute angle A in [0,pi/2]. Other preprocessing can help with
|
|
// performance. See the function comments in the SLERP class.
|
|
|
|
namespace gte
|
|
{
|
|
template <typename Real>
|
|
class SLERP
|
|
{
|
|
public:
|
|
// The angle between q0 and q1 is in [0,pi). There are no angle
|
|
// restrictions and nothing is precomputed.
|
|
template <int N>
|
|
inline static Quaternion<Real> Estimate(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1)
|
|
{
|
|
static_assert(1 <= N && N <= 16, "Invalid degree.");
|
|
|
|
Real cs = Dot(q0, q1);
|
|
Real sign;
|
|
if (cs >= (Real)0)
|
|
{
|
|
sign = (Real)1;
|
|
}
|
|
else
|
|
{
|
|
cs = -cs;
|
|
sign = (Real)-1;
|
|
}
|
|
|
|
Real f0, f1;
|
|
ChebyshevRatio<Real>::template GetEstimate<N>(t, (Real)1 - cs, f0, f1);
|
|
return q0 * f0 + q1 * (sign * f1);
|
|
}
|
|
|
|
|
|
// The angle between q0 and q1 must be in [0,pi/2]. The suffix R is
|
|
// for 'Restricted'. The preprocessing code is
|
|
// Quaternion<Real> q[n]; // assuming initialized
|
|
// for (i0 = 0, i1 = 1; i1 < n; i0 = i1++)
|
|
// {
|
|
// cosA = Dot(q[i0], q[i1]);
|
|
// if (cosA < 0)
|
|
// {
|
|
// q[i1] = -q[i1]; // now Dot(q[i0], q[i]1) >= 0
|
|
// }
|
|
// }
|
|
template <int N>
|
|
inline static Quaternion<Real> EstimateR(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1)
|
|
{
|
|
static_assert(1 <= N && N <= 16, "Invalid degree.");
|
|
|
|
Real f0, f1;
|
|
ChebyshevRatio<Real>::template GetEstimate<N>(t, (Real)1 - Dot(q0, q1), f0, f1);
|
|
return q0 * f0 + q1 * f1;
|
|
}
|
|
|
|
// The angle between q0 and q1 must be in [0,pi/2]. The suffix R is
|
|
// for 'Restricted' and the suffix P is for 'Preprocessed'. The
|
|
// preprocessing code is
|
|
// Quaternion<Real> q[n]; // assuming initialized
|
|
// Real cosA[n-1], omcosA[n-1]; // to be precomputed
|
|
// for (i0 = 0, i1 = 1; i1 < n; i0 = i1++)
|
|
// {
|
|
// cs = Dot(q[i0], q[i1]);
|
|
// if (cosA[i0] < 0)
|
|
// {
|
|
// q[i1] = -q[i1];
|
|
// cs = -cs;
|
|
// }
|
|
//
|
|
// // for Quaterion<T>::SlerpRP
|
|
// cosA[i0] = cs;
|
|
//
|
|
// // for SLERP<T>::EstimateRP
|
|
// omcosA[i0] = 1 - cs;
|
|
// }
|
|
template <int N>
|
|
inline static Quaternion<Real> EstimateRP(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1,
|
|
Real omcosA)
|
|
{
|
|
static_assert(1 <= N && N <= 16, "Invalid degree.");
|
|
|
|
Real f0, f1;
|
|
ChebyshevRatio<Real>::template GetEstimate<N>(t, omcosA, f0, f1);
|
|
return q0 * f0 + q1 * f1;
|
|
}
|
|
|
|
// The angle between q0 and q1 is A and must be in [0,pi/2].
|
|
// Quaternion qh is slerp(1/2,q0,q1) = (q0+q1)/|q0+q1|, so the angle
|
|
// between q0 and qh is A/2 and the angle between qh and q1 is A/2.
|
|
// The preprocessing code is
|
|
// Quaternion<Real> q[n]; // assuming initialized
|
|
// Quaternion<Real> qh[n-1]; // to be precomputed
|
|
// Real omcosAH[n-1]; // to be precomputed
|
|
// for (i0 = 0, i1 = 1; i1 < n; i0 = i1++)
|
|
// {
|
|
// cosA = Dot(q[i0], q[i1]);
|
|
// if (cosA < 0)
|
|
// {
|
|
// q[i1] = -q[i1];
|
|
// cosA = -cosA;
|
|
// }
|
|
// cosAH = sqrt((1 + cosA)/2);
|
|
// qh[i0] = (q0 + q1) / (2 * cosAH[i0]);
|
|
// omcosAH[i0] = 1 - cosAH;
|
|
// }
|
|
template <int N>
|
|
inline static Quaternion<Real> EstimateRPH(Real t, Quaternion<Real> const& q0, Quaternion<Real> const& q1,
|
|
Quaternion<Real> const& qh, Real omcosAH)
|
|
{
|
|
static_assert(1 <= N && N <= 16, "Invalid degree.");
|
|
|
|
Real f0, f1;
|
|
Real twoT = t * (Real)2;
|
|
if (twoT <= (Real)1)
|
|
{
|
|
ChebyshevRatio<Real>::template GetEstimate<N>(twoT, omcosAH, f0, f1);
|
|
return q0 * f0 + qh * f1;
|
|
}
|
|
else
|
|
{
|
|
ChebyshevRatio<Real>::template GetEstimate<N>(twoT - (Real)1, omcosAH, f0, f1);
|
|
return qh * f0 + q1 * f1;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|