/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MATH_TQUATHELPERS_H_ #define MATH_TQUATHELPERS_H_ #include #include #include #include #include #include namespace filament { namespace math { namespace details { // ------------------------------------------------------------------------------------- /* * No user serviceable parts here. * * Don't use this file directly, instead include math/quat.h */ /* * TQuatProductOperators implements basic arithmetic and basic compound assignment * operators on a quaternion of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TQuatProductOperators BASE will automatically * get all the functionality here. */ template class QUATERNION, typename T> class TQuatProductOperators { public: /* compound assignment from a another quaternion of the same size but different * element type. */ template constexpr QUATERNION& operator*=(const QUATERNION& r) { QUATERNION& q = static_cast&>(*this); q = q * r; return q; } /* compound assignment products by a scalar */ constexpr QUATERNION& operator*=(T v) { QUATERNION& lhs = static_cast&>(*this); for (size_t i = 0; i < QUATERNION::size(); i++) { lhs[i] *= v; } return lhs; } constexpr QUATERNION& operator/=(T v) { QUATERNION& lhs = static_cast&>(*this); for (size_t i = 0; i < QUATERNION::size(); i++) { lhs[i] /= v; } return lhs; } /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ /* The operators below handle operation between quaternion of the same size * but of a different element type. */ template friend inline constexpr QUATERNION MATH_PURE operator*(const QUATERNION& q, const QUATERNION& r) { // could be written as: // return QUATERNION( // q.w*r.w - dot(q.xyz, r.xyz), // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz)); return QUATERNION( q.w * r.w - q.x * r.x - q.y * r.y - q.z * r.z, q.w * r.x + q.x * r.w + q.y * r.z - q.z * r.y, q.w * r.y - q.x * r.z + q.y * r.w + q.z * r.x, q.w * r.z + q.x * r.y - q.y * r.x + q.z * r.w); } template friend inline constexpr TVec3 MATH_PURE operator*(const QUATERNION& q, const TVec3& v) { // note: if q is known to be a unit quaternion, then this simplifies to: // TVec3 t = 2 * cross(q.xyz, v) // return v + (q.w * t) + cross(q.xyz, t) return imaginary(q * QUATERNION(v, 0) * inverse(q)); } /* For quaternions, we use explicit "by a scalar" products because it's much faster * than going (implicitly) through the quaternion multiplication. * For reference: we could use the code below instead, but it would be a lot slower. * friend inline * constexpr BASE MATH_PURE operator *(const BASE& q, const BASE& r) { * return BASE( * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z, * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y, * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x, * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); * */ friend inline constexpr QUATERNION MATH_PURE operator*(QUATERNION q, T scalar) { // don't pass q by reference because we need a copy anyways return q *= scalar; } friend inline constexpr QUATERNION MATH_PURE operator*(T scalar, QUATERNION q) { // don't pass q by reference because we need a copy anyways return q *= scalar; } friend inline constexpr QUATERNION MATH_PURE operator/(QUATERNION q, T scalar) { // don't pass q by reference because we need a copy anyways return q /= scalar; } }; /* * TQuatFunctions implements functions on a quaternion of type BASE. * * BASE only needs to implement operator[] and size(). * By simply inheriting from TQuatFunctions BASE will automatically * get all the functionality here. */ template class QUATERNION, typename T> class TQuatFunctions { public: /* * NOTE: the functions below ARE NOT member methods. They are friend functions * with they definition inlined with their declaration. This makes these * template functions available to the compiler when (and only when) this class * is instantiated, at which point they're only templated on the 2nd parameter * (the first one, BASE being known). */ template friend inline constexpr T MATH_PURE dot(const QUATERNION& p, const QUATERNION& q) { return p.x * q.x + p.y * q.y + p.z * q.z + p.w * q.w; } friend inline T MATH_PURE norm(const QUATERNION& q) { return std::sqrt(dot(q, q)); } friend inline T MATH_PURE length(const QUATERNION& q) { return norm(q); } friend inline constexpr T MATH_PURE length2(const QUATERNION& q) { return dot(q, q); } friend inline QUATERNION MATH_PURE normalize(const QUATERNION& q) { return length(q) ? q / length(q) : QUATERNION(static_cast(1)); } friend inline constexpr QUATERNION MATH_PURE conj(const QUATERNION& q) { return QUATERNION(q.w, -q.x, -q.y, -q.z); } friend inline constexpr QUATERNION MATH_PURE inverse(const QUATERNION& q) { return conj(q) * (1 / dot(q, q)); } friend inline constexpr T MATH_PURE real(const QUATERNION& q) { return q.w; } friend inline constexpr TVec3 MATH_PURE imaginary(const QUATERNION& q) { return q.xyz; } friend inline constexpr QUATERNION MATH_PURE unreal(const QUATERNION& q) { return QUATERNION(q.xyz, 0); } friend inline constexpr QUATERNION MATH_PURE cross(const QUATERNION& p, const QUATERNION& q) { return unreal(p * q); } friend inline QUATERNION MATH_PURE exp(const QUATERNION& q) { const T nq(norm(q.xyz)); return std::exp(q.w) * QUATERNION((sin(nq) / nq) * q.xyz, cos(nq)); } friend inline QUATERNION MATH_PURE log(const QUATERNION& q) { const T nq(norm(q)); return QUATERNION((std::acos(q.w / nq) / norm(q.xyz)) * q.xyz, std::log(nq)); } friend inline QUATERNION MATH_PURE pow(const QUATERNION& q, T a) { // could also be computed as: exp(a*log(q)); const T nq(norm(q)); const T theta(a * std::acos(q.w / nq)); return std::pow(nq, a) * QUATERNION(normalize(q.xyz) * std::sin(theta), std::cos(theta)); } friend inline QUATERNION MATH_PURE slerp(const QUATERNION& p, const QUATERNION& q, T t) { // could also be computed as: pow(q * inverse(p), t) * p; const T d = dot(p, q); const T absd = std::abs(d); static constexpr T value_eps = T(10) * std::numeric_limits::epsilon(); // Prevent blowing up when slerping between two quaternions that are very near each other. if ((T(1) - absd) < value_eps) { return normalize(lerp(p, q, t)); } const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1))); const T a0 = a * (1 - t); const T a1 = a * t; const T sina = sin(a); if (sina < value_eps) { return normalize(lerp(p, q, t)); } const T isina = 1 / sina; const T s0 = std::sin(a0) * isina; const T s1 = std::sin(a1) * isina; // ensure we're taking the "short" side return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q); } friend inline constexpr QUATERNION MATH_PURE lerp(const QUATERNION& p, const QUATERNION& q, T t) { return ((1 - t) * p) + (t * q); } friend inline constexpr QUATERNION MATH_PURE nlerp(const QUATERNION& p, const QUATERNION& q, T t) { return normalize(lerp(p, q, t)); } friend inline constexpr QUATERNION MATH_PURE positive(const QUATERNION& q) { return q.w < 0 ? -q : q; } }; // ------------------------------------------------------------------------------------- } // namespace details } // namespace math } // namespace filament #endif // MATH_TQUATHELPERS_H_