Line data Source code
1 : /**
2 : Copyright 2013 BlackBerry Inc.
3 : Copyright (c) 2014-2015 Chukong Technologies
4 : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
5 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
6 :
7 : Licensed under the Apache License, Version 2.0 (the "License");
8 : you may not use this file except in compliance with the License.
9 : You may obtain a copy of the License at
10 :
11 : http://www.apache.org/licenses/LICENSE-2.0
12 :
13 : Unless required by applicable law or agreed to in writing, software
14 : distributed under the License is distributed on an "AS IS" BASIS,
15 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : See the License for the specific language governing permissions and
17 : limitations under the License.
18 :
19 : Original file from GamePlay3D: http://gameplay3d.org
20 :
21 : This file was modified to fit the cocos2d-x project
22 : This file was modified for stappler project
23 : */
24 :
25 : #ifndef STAPPLER_GEOM_SPQUATERNION_H_
26 : #define STAPPLER_GEOM_SPQUATERNION_H_
27 :
28 : #include "SPVec3.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler::geom {
31 :
32 : class Mat4;
33 :
34 : /**
35 : * Defines a 4-element quaternion that represents the orientation of an object in space.
36 : *
37 : * Quaternions are typically used as a replacement for euler angles and rotation matrices
38 : * as a way to achieve smooth interpolation and avoid gimbal lock.
39 : *
40 : * Note that this quaternion class does not automatically keep the quaternion normalized.
41 : * Therefore, care must be taken to normalize the quaternion when necessary, by calling
42 : * the normalize method.
43 : * This class provides three methods for doing quaternion interpolation: lerp, slerp, and squad.
44 : *
45 : * lerp (linear interpolation): the interpolation curve gives a straight line in quaternion
46 : * space. It is simple and fast to compute. The only problem is that it does not provide
47 : * constant angular velocity. Note that a constant velocity is not necessarily a requirement
48 : * for a curve;
49 : * slerp (spherical linear interpolation): the interpolation curve forms a great arc on the
50 : * quaternion unit sphere. Slerp provides constant angular velocity;
51 : * squad (spherical spline interpolation): interpolating between a series of rotations using
52 : * slerp leads to the following problems:
53 : * - the curve is not smooth at the control points;
54 : * - the angular velocity is not constant;
55 : * - the angular velocity is not continuous at the control points.
56 : *
57 : * Since squad is continuously differentiable, it remedies the first and third problems
58 : * mentioned above.
59 : * The slerp method provided here is intended for interpolation of principal rotations.
60 : * It treats +q and -q as the same principal rotation and is at liberty to use the negative
61 : * of either input. The resulting path is always the shorter arc.
62 : *
63 : * The lerp method provided here interpolates strictly in quaternion space. Note that the
64 : * resulting path may pass through the origin if interpolating between a quaternion and its
65 : * exact negative.
66 : *
67 : * As an example, consider the following quaternions:
68 : *
69 : * q1 = (0.6, 0.8, 0.0, 0.0),
70 : * q2 = (0.0, 0.6, 0.8, 0.0),
71 : * q3 = (0.6, 0.0, 0.8, 0.0), and
72 : * q4 = (-0.8, 0.0, -0.6, 0.0).
73 : * For the point p = (1.0, 1.0, 1.0), the following figures show the trajectories of p
74 : * using lerp, slerp, and squad.
75 : */
76 : class Quaternion {
77 : public:
78 : static void multiply(const Quaternion& q1, const Quaternion& q2, Quaternion* dst);
79 : static void lerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
80 :
81 : /**
82 : * Interpolates between two quaternions using spherical linear interpolation.
83 : *
84 : * Spherical linear interpolation provides smooth transitions between different
85 : * orientations and is often useful for animating models or cameras in 3D.
86 : *
87 : * Note: For accurate interpolation, the input quaternions must be at (or close to) unit length.
88 : * This method does not automatically normalize the input quaternions, so it is up to the
89 : * caller to ensure they call normalize beforehand, if necessary.
90 : *
91 : * @param q1 The first quaternion.
92 : * @param q2 The second quaternion.
93 : * @param t The interpolation coefficient.
94 : * @param dst A quaternion to store the result in.
95 : */
96 : static void slerp(const Quaternion& q1, const Quaternion& q2, float t, Quaternion* dst);
97 :
98 : /**
99 : * Interpolates over a series of quaternions using spherical spline interpolation.
100 : *
101 : * Spherical spline interpolation provides smooth transitions between different
102 : * orientations and is often useful for animating models or cameras in 3D.
103 : *
104 : * Note: For accurate interpolation, the input quaternions must be unit.
105 : * This method does not automatically normalize the input quaternions,
106 : * so it is up to the caller to ensure they call normalize beforehand, if necessary.
107 : *
108 : * @param q1 The first quaternion.
109 : * @param q2 The second quaternion.
110 : * @param s1 The first control point.
111 : * @param s2 The second control point.
112 : * @param t The interpolation coefficient.
113 : * @param dst A quaternion to store the result in.
114 : */
115 : static void squad(const Quaternion& q1, const Quaternion& q2,
116 : const Quaternion& s1,const Quaternion& s2, float t, Quaternion* dst);
117 :
118 : float x;
119 : float y;
120 : float z;
121 : float w;
122 :
123 86291 : constexpr Quaternion() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) { }
124 4 : constexpr Quaternion(float xx, float yy, float zz, float ww) : x(xx), y(yy), z(zz), w(ww) { }
125 :
126 : /** Constructs a quaternion equal to the rotational part of the specified matrix */
127 : Quaternion(const Mat4& m);
128 :
129 6584 : constexpr Quaternion(const Vec3 &eulerAngles) {
130 6584 : float halfRadx = eulerAngles.x / 2.f, halfRady = eulerAngles.y, halfRadz = -eulerAngles.z / 2.f;
131 6584 : float coshalfRadx = std::cos(halfRadx), sinhalfRadx = std::sin(halfRadx),
132 6584 : coshalfRady = std::cos(halfRady), sinhalfRady = std::sin(halfRady),
133 6584 : coshalfRadz = std::cos(halfRadz), sinhalfRadz = std::sin(halfRadz);
134 :
135 6584 : x = sinhalfRadx * coshalfRady * coshalfRadz - coshalfRadx * sinhalfRady * sinhalfRadz;
136 6584 : y = coshalfRadx * sinhalfRady * coshalfRadz + sinhalfRadx * coshalfRady * sinhalfRadz;
137 6584 : z = coshalfRadx * coshalfRady * sinhalfRadz - sinhalfRadx * sinhalfRady * coshalfRadz;
138 6584 : w = coshalfRadx * coshalfRady * coshalfRadz + sinhalfRadx * sinhalfRady * sinhalfRadz;
139 6584 : }
140 :
141 : /** Constructs a quaternion equal to the rotation from the specified axis and angle. */
142 25 : constexpr Quaternion(const Vec3& axis, float angle) {
143 25 : float halfAngle = angle * 0.5f;
144 25 : float sinHalfAngle = std::sin(halfAngle);
145 :
146 25 : Vec3 normal(axis);
147 25 : normal.normalize();
148 25 : x = normal.x * sinHalfAngle;
149 25 : y = normal.y * sinHalfAngle;
150 25 : z = normal.z * sinHalfAngle;
151 25 : w = std::cos(halfAngle);
152 25 : }
153 :
154 : constexpr Quaternion(const Quaternion& copy) = default;
155 :
156 21 : constexpr bool operator==(const Quaternion &q) const { return q.x == x && q.y == y && q.z == z && q.w == w; }
157 : constexpr bool operator!=(const Quaternion &q) const { return q.x != x || q.y != y || q.z != z || q.w != w; }
158 :
159 21 : constexpr Vec3 toEulerAngles() const {
160 21 : Vec3 ret;
161 21 : ret.x = atan2f(2.f * (w * x + y * z), 1.f - 2.f * (x * x + y * y));
162 21 : ret.y = asinf(2.f * (w * y - z * x));
163 21 : ret.z = - atanf(2.f * (w * z + x * y) / (1.f - 2.f * (y * y + z * z)));
164 21 : return ret;
165 : }
166 :
167 : constexpr bool isIdentity() const { return x == 0.0f && y == 0.0f && z == 0.0f && w == 1.0f; }
168 : constexpr bool isZero() const { return x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f; }
169 : constexpr void conjugate() { x = -x; y = -y; z = -z; }
170 :
171 : constexpr Quaternion getConjugated() const {
172 : Quaternion q(*this);
173 : q.conjugate();
174 : return q;
175 : }
176 :
177 : bool inverse();
178 : void multiply(const Quaternion& q);
179 : void normalize();
180 :
181 : Quaternion getInversed() const;
182 : Quaternion getNormalized() const;
183 :
184 : float toAxisAngle(Vec3* e) const;
185 :
186 : const Quaternion operator*(const Quaternion& q) const {
187 : Quaternion result(*this);
188 : result.multiply(q);
189 : return result;
190 : }
191 :
192 : Vec3 operator*(const Vec3& v) const {
193 : Vec3 uv, uuv;
194 : Vec3 qvec(x, y, z);
195 : Vec3::cross(qvec, v, &uv);
196 : Vec3::cross(qvec, uv, &uuv);
197 :
198 : uv *= (2.0f * w);
199 : uuv *= 2.0f;
200 :
201 : return v + uv + uuv;
202 : }
203 :
204 : Quaternion& operator*=(const Quaternion& q) {
205 : multiply(q);
206 : return *this;
207 : }
208 :
209 : static const Quaternion IDENTITY;
210 : static const Quaternion ZERO;
211 : };
212 :
213 : #ifndef __LCC__
214 :
215 : constexpr const Quaternion Quaternion::IDENTITY(0.0f, 0.0f, 0.0f, 1.0f);
216 : constexpr const Quaternion Quaternion::ZERO(0.0f, 0.0f, 0.0f, 0.0f);
217 :
218 : #endif
219 :
220 : }
221 :
222 : #endif /* STAPPLER_GEOM_SPQUATERNION_H_ */
|