RavEngine
Loading...
Searching...
No Matches
simd_quaternion.h
1//----------------------------------------------------------------------------//
2// //
3// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
4// and distributed under the MIT License (MIT). //
5// //
6// Copyright (c) Guillaume Blanc //
7// //
8// Permission is hereby granted, free of charge, to any person obtaining a //
9// copy of this software and associated documentation files (the "Software"), //
10// to deal in the Software without restriction, including without limitation //
11// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
12// and/or sell copies of the Software, and to permit persons to whom the //
13// Software is furnished to do so, subject to the following conditions: //
14// //
15// The above copyright notice and this permission notice shall be included in //
16// all copies or substantial portions of the Software. //
17// //
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
21// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
23// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
24// DEALINGS IN THE SOFTWARE. //
25// //
26//----------------------------------------------------------------------------//
27
28#ifndef OZZ_OZZ_BASE_MATHS_SIMD_QUATERNION_H_
29#define OZZ_OZZ_BASE_MATHS_SIMD_QUATERNION_H_
30
31#include "ozz/base/maths/simd_math.h"
32
33#include <cmath>
34
35// Implement simd quaternion.
36namespace ozz {
37namespace math {
38// Declare the Quaternion type.
40 SimdFloat4 xyzw;
41
42 // Returns the identity quaternion.
43 static OZZ_INLINE SimdQuaternion identity() {
44 const SimdQuaternion quat = {simd_float4::w_axis()};
45 return quat;
46 }
47
48 // the angle in radian.
49 static OZZ_INLINE SimdQuaternion FromAxisAngle(_SimdFloat4 _axis,
50 _SimdFloat4 _angle);
51
52 // Returns a normalized quaternion initialized from an axis and angle cosine
53 // representation.
54 // Assumes the axis part (x, y, z) of _axis_angle is normalized.
55 // _angle.x is the angle cosine in radian, it must be within [-1,1] range.
56 static OZZ_INLINE SimdQuaternion FromAxisCosAngle(_SimdFloat4 _axis,
57 _SimdFloat4 _cos);
58
59 // Returns the quaternion that will rotate vector _from into vector _to,
60 // around their plan perpendicular axis.The input vectors don't need to be
61 // normalized, they can be null also.
62 static OZZ_INLINE SimdQuaternion FromVectors(_SimdFloat4 _from,
63 _SimdFloat4 _to);
64
65 // Returns the quaternion that will rotate vector _from into vector _to,
66 // around their plan perpendicular axis. The input vectors must be normalized.
67 static OZZ_INLINE SimdQuaternion FromUnitVectors(_SimdFloat4 _from,
68 _SimdFloat4 _to);
69
70 // Returns a normalized quaternion initialized from an axis angle
71 // representation.
72 // Assumes the axis part (x, y, z) of _axis_angle is normalized. _angle.x is
73};
74
75// Returns the multiplication of _a and _b. If both _a and _b are normalized,
76// then the result is normalized.
77OZZ_INLINE SimdQuaternion operator*(const SimdQuaternion& _a,
78 const SimdQuaternion& _b) {
79 // Original quaternion multiplication can be swizzled in a simd friendly way
80 // if w is negated, and some w multiplications parts (1st/last) are swaped.
81 //
82 // p1 p2 p3 p4
83 // _a.w * _b.x + _a.x * _b.w + _a.y * _b.z - _a.z * _b.y
84 // _a.w * _b.y + _a.y * _b.w + _a.z * _b.x - _a.x * _b.z
85 // _a.w * _b.z + _a.z * _b.w + _a.x * _b.y - _a.y * _b.x
86 // _a.w * _b.w - _a.x * _b.x - _a.y * _b.y - _a.z * _b.z
87 // ... becomes ->
88 // _a.w * _b.x + _a.x * _b.w + _a.y * _b.z - _a.z * _b.y
89 // _a.w * _b.y + _a.y * _b.w + _a.z * _b.x - _a.x * _b.z
90 // _a.w * _b.z + _a.z * _b.w + _a.x * _b.y - _a.y * _b.x
91 // - (_a.z * _b.z + _a.x * _b.x + _a.y * _b.y - _a.w * _b.w)
92 const SimdFloat4 p1 =
93 Swizzle<3, 3, 3, 2>(_a.xyzw) * Swizzle<0, 1, 2, 2>(_b.xyzw);
94 const SimdFloat4 p2 =
95 Swizzle<0, 1, 2, 0>(_a.xyzw) * Swizzle<3, 3, 3, 0>(_b.xyzw);
96 const SimdFloat4 p13 =
97 MAdd(Swizzle<1, 2, 0, 1>(_a.xyzw), Swizzle<2, 0, 1, 1>(_b.xyzw), p1);
98 const SimdFloat4 p24 =
99 NMAdd(Swizzle<2, 0, 1, 3>(_a.xyzw), Swizzle<1, 2, 0, 3>(_b.xyzw), p2);
100 const SimdQuaternion quat = {Xor(p13 + p24, simd_int4::mask_sign_w())};
101 return quat;
102}
103
104// Returns the conjugate of _q. This is the same as the inverse if _q is
105// normalized. Otherwise the magnitude of the inverse is 1.f/|_q|.
106OZZ_INLINE SimdQuaternion Conjugate(const SimdQuaternion& _q) {
107 const SimdQuaternion quat = {Xor(_q.xyzw, simd_int4::mask_sign_xyz())};
108 return quat;
109}
110
111// Returns the negate of _q. This represent the same rotation as q.
112OZZ_INLINE SimdQuaternion operator-(const SimdQuaternion& _q) {
113 const SimdQuaternion quat = {Xor(_q.xyzw, simd_int4::mask_sign())};
114 return quat;
115}
116
117// Returns the normalized quaternion _q.
118OZZ_INLINE SimdQuaternion Normalize(const SimdQuaternion& _q) {
119 const SimdQuaternion quat = {Normalize4(_q.xyzw)};
120 return quat;
121}
122
123// Returns the normalized quaternion _q if the norm of _q is not 0.
124// Otherwise returns _safer.
125OZZ_INLINE SimdQuaternion NormalizeSafe(const SimdQuaternion& _q,
126 const SimdQuaternion& _safer) {
127 const SimdQuaternion quat = {NormalizeSafe4(_q.xyzw, _safer.xyzw)};
128 return quat;
129}
130
131// Returns the estimated normalized quaternion _q.
132OZZ_INLINE SimdQuaternion NormalizeEst(const SimdQuaternion& _q) {
133 const SimdQuaternion quat = {NormalizeEst4(_q.xyzw)};
134 return quat;
135}
136
137// Returns the estimated normalized quaternion _q if the norm of _q is not 0.
138// Otherwise returns _safer.
139OZZ_INLINE SimdQuaternion NormalizeSafeEst(const SimdQuaternion& _q,
140 const SimdQuaternion& _safer) {
141 const SimdQuaternion quat = {NormalizeSafeEst4(_q.xyzw, _safer.xyzw)};
142 return quat;
143}
144
145// Tests if the _q is a normalized quaternion.
146// Returns the result in the x component of the returned vector. y, z and w are
147// set to 0.
148OZZ_INLINE SimdInt4 IsNormalized(const SimdQuaternion& _q) {
149 return IsNormalized4(_q.xyzw);
150}
151
152// Tests if the _q is a normalized quaternion.
153// Uses the estimated normalization coefficient, that matches estimated math
154// functions (RecpEst, MormalizeEst...).
155// Returns the result in the x component of the returned vector. y, z and w are
156// set to 0.
157OZZ_INLINE SimdInt4 IsNormalizedEst(const SimdQuaternion& _q) {
158 return IsNormalizedEst4(_q.xyzw);
159}
160
161OZZ_INLINE SimdQuaternion SimdQuaternion::FromAxisAngle(_SimdFloat4 _axis,
162 _SimdFloat4 _angle) {
163 assert(AreAllTrue1(IsNormalizedEst3(_axis)) && "axis is not normalized.");
164 const SimdFloat4 half_angle = _angle * simd_float4::Load1(.5f);
165 const SimdFloat4 half_sin = SinX(half_angle);
166 const SimdFloat4 half_cos = CosX(half_angle);
167 const SimdQuaternion quat = {SetW(_axis * SplatX(half_sin), half_cos)};
168 return quat;
169}
170
171OZZ_INLINE SimdQuaternion SimdQuaternion::FromAxisCosAngle(_SimdFloat4 _axis,
172 _SimdFloat4 _cos) {
173 const SimdFloat4 one = simd_float4::one();
174 const SimdFloat4 half = simd_float4::Load1(.5f);
175
176 assert(AreAllTrue1(IsNormalizedEst3(_axis)) && "axis is not normalized.");
177 assert(AreAllTrue1(And(CmpGe(_cos, -one), CmpLe(_cos, one))) &&
178 "cos is not in [-1,1] range.");
179
180 const SimdFloat4 half_cos2 = (one + _cos) * half;
181 const SimdFloat4 half_sin2 = one - half_cos2;
182 const SimdFloat4 half_sincos2 = SetY(half_cos2, half_sin2);
183 const SimdFloat4 half_sincos = Sqrt(half_sincos2);
184 const SimdFloat4 half_sin = SplatY(half_sincos);
185 const SimdQuaternion quat = {SetW(_axis * half_sin, half_sincos)};
186 return quat;
187}
188
189// Returns to an axis angle representation of quaternion _q.
190// Assumes quaternion _q is normalized.
191OZZ_INLINE SimdFloat4 ToAxisAngle(const SimdQuaternion& _q) {
192 assert(AreAllTrue1(IsNormalizedEst4(_q.xyzw)) && "_q is not normalized.");
193 const SimdFloat4 x_axis = simd_float4::x_axis();
194 const SimdFloat4 clamped_w = Clamp(-x_axis, SplatW(_q.xyzw), x_axis);
195 const SimdFloat4 half_angle = ACosX(clamped_w);
196
197 // Assuming quaternion is normalized then s always positive.
198 const SimdFloat4 s = SplatX(SqrtX(NMAdd(clamped_w, clamped_w, x_axis)));
199 // If s is close to zero then direction of axis is not important.
200 const SimdInt4 low = CmpLt(s, simd_float4::Load1(1e-3f));
201 return Select(low, x_axis,
202 SetW(_q.xyzw * RcpEstNR(s), half_angle + half_angle));
203}
204
205OZZ_INLINE SimdQuaternion SimdQuaternion::FromVectors(_SimdFloat4 _from,
206 _SimdFloat4 _to) {
207 // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
208 const SimdFloat4 norm_from_norm_to =
209 SqrtX(Length3Sqr(_from) * Length3Sqr(_to));
210 const float norm_from_norm_to_x = GetX(norm_from_norm_to);
211 if (norm_from_norm_to_x < 1.e-6f) {
212 return SimdQuaternion::identity();
213 }
214
215 const SimdFloat4 real_part = norm_from_norm_to + Dot3(_from, _to);
216 SimdQuaternion quat;
217 if (GetX(real_part) < 1.e-6f * norm_from_norm_to_x) {
218 // If _from and _to are exactly opposite, rotate 180 degrees around an
219 // arbitrary orthogonal axis. Axis normalization can happen later, when we
220 // normalize the quaternion.
221 float from[4];
222 ozz::math::StorePtrU(_from, from);
223 quat.xyzw = std::abs(from[0]) > std::abs(from[2])
224 ? ozz::math::simd_float4::Load(-from[1], from[0], 0.f, 0.f)
225 : ozz::math::simd_float4::Load(0.f, -from[2], from[1], 0.f);
226 } else {
227 // This is the general code path.
228 quat.xyzw = SetW(Cross3(_from, _to), real_part);
229 }
230 return Normalize(quat);
231}
232
233OZZ_INLINE SimdQuaternion SimdQuaternion::FromUnitVectors(_SimdFloat4 _from,
234 _SimdFloat4 _to) {
235 // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
236 assert(ozz::math::AreAllTrue1(
237 And(IsNormalizedEst3(_from), IsNormalizedEst3(_to))) &&
238 "Input vectors must be normalized.");
239
240 const SimdFloat4 real_part =
241 ozz::math::simd_float4::x_axis() + Dot3(_from, _to);
242 if (GetX(real_part) < 1.e-6f) {
243 // If _from and _to are exactly opposite, rotate 180 degrees around an
244 // arbitrary orthogonal axis.
245 // Normalization isn't needed, as from is already.
246 float from[4];
247 ozz::math::StorePtrU(_from, from);
248 SimdQuaternion quat = {
249 std::abs(from[0]) > std::abs(from[2])
250 ? ozz::math::simd_float4::Load(-from[1], from[0], 0.f, 0.f)
251 : ozz::math::simd_float4::Load(0.f, -from[2], from[1], 0.f)};
252 return quat;
253 } else {
254 // This is the general code path.
255 SimdQuaternion quat = {SetW(Cross3(_from, _to), real_part)};
256 return Normalize(quat);
257 }
258}
259
260// Computes the transformation of a Quaternion and a vector _v.
261// This is equivalent to carrying out the quaternion multiplications:
262// _q.conjugate() * (*this) * _q
263// w component of the returned vector is undefined.
264OZZ_INLINE SimdFloat4 TransformVector(const SimdQuaternion& _q,
265 _SimdFloat4 _v) {
266 // http://www.neil.dantam.name/note/dantam-quaternion.pdf
267 // _v + 2.f * cross(_q.xyz, cross(_q.xyz, _v) + _q.w * _v)
268 const SimdFloat4 cross1 = MAdd(SplatW(_q.xyzw), _v, Cross3(_q.xyzw, _v));
269 const SimdFloat4 cross2 = Cross3(_q.xyzw, cross1);
270 return _v + cross2 + cross2;
271}
272} // namespace math
273} // namespace ozz
274#endif // OZZ_OZZ_BASE_MATHS_SIMD_QUATERNION_H_
qua< float, defaultp > quat
Quaternion of single-precision floating-point numbers.
Definition quaternion_float.hpp:35
Definition simd_math_config.h:121
Definition simd_quaternion.h:39