1 | // Copyright (C) 2008-2012 NVIDIA Corporation. |
2 | // Copyright (C) 2019 The Qt Company Ltd. |
3 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
4 | |
5 | #ifndef QSSGUTILS_H |
6 | #define QSSGUTILS_H |
7 | |
8 | // |
9 | // W A R N I N G |
10 | // ------------- |
11 | // |
12 | // This file is not part of the Qt API. It exists purely as an |
13 | // implementation detail. This header file may change from version to |
14 | // version without notice, or even be removed. |
15 | // |
16 | // We mean it. |
17 | // |
18 | |
19 | #include <QtQuick3DUtils/private/qtquick3dutilsglobal_p.h> |
20 | #include <QtQuick3DUtils/private/qssgdataref_p.h> |
21 | |
22 | #include <QtGui/QVector2D> |
23 | #include <QtGui/QVector3D> |
24 | #include <QtGui/QQuaternion> |
25 | #include <QtGui/QMatrix3x3> |
26 | #include <QtGui/QMatrix4x4> |
27 | #include <QtGui/QColor> |
28 | |
29 | #include <QtCore/qdebug.h> |
30 | #include <QtCore/QString> |
31 | #include <QtCore/qloggingcategory.h> |
32 | #include <QtCore/QIODevice> |
33 | #include <QtCore/qmath.h> |
34 | |
35 | class tst_RotationDataClass; |
36 | |
37 | QT_BEGIN_NAMESPACE |
38 | |
39 | namespace QSSGUtils { |
40 | |
41 | namespace aux { |
42 | Q_DECL_CONSTEXPR inline float translateConstantAttenuation(float attenuation) { return attenuation; } |
43 | template<int MINATTENUATION = 0, int MAXATTENUATION = 1000> |
44 | Q_DECL_CONSTEXPR inline float translateLinearAttenuation(float attenuation) { return qBound(min: float(MINATTENUATION), val: attenuation, max: float(MAXATTENUATION)) * .01f; } |
45 | template<int MINATTENUATION = 0, int MAXATTENUATION = 1000> |
46 | Q_DECL_CONSTEXPR inline float translateQuadraticAttenuation(float attenuation) { return qBound(min: float(MINATTENUATION), val: attenuation, max: float(MAXATTENUATION)) * .0001f; } |
47 | } |
48 | |
49 | namespace vec2 { |
50 | float Q_QUICK3DUTILS_EXPORT magnitude(const QVector2D &v); |
51 | } |
52 | |
53 | namespace vec3 { |
54 | inline QVector3D minimum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW { return { qMin(a: v1.x(), b: v2.x()), qMin(a: v1.y(), b: v2.y()), qMin(a: v1.z(), b: v2.z()) }; } |
55 | inline QVector3D maximum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW { return { qMax(a: v1.x(), b: v2.x()), qMax(a: v1.y(), b: v2.y()), qMax(a: v1.z(), b: v2.z()) }; } |
56 | bool Q_QUICK3DUTILS_EXPORT isFinite(const QVector3D &v); |
57 | float Q_QUICK3DUTILS_EXPORT magnitude(const QVector3D &v); |
58 | float Q_QUICK3DUTILS_EXPORT magnitudeSquared(const QVector3D &v); |
59 | float Q_QUICK3DUTILS_EXPORT normalize(QVector3D &v); |
60 | } |
61 | |
62 | namespace mat33 { |
63 | QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix3x3 &m, const QVector3D &v); |
64 | } |
65 | |
66 | namespace mat44 { |
67 | QMatrix3x3 Q_QUICK3DUTILS_EXPORT getUpper3x3(const QMatrix4x4 &m); |
68 | void Q_QUICK3DUTILS_EXPORT normalize(QMatrix4x4 &m); |
69 | QVector3D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector3D &v); |
70 | QVector4D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector4D &v); |
71 | QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v); |
72 | QVector4D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector4D &v); |
73 | QVector3D Q_QUICK3DUTILS_EXPORT getPosition(const QMatrix4x4 &m); |
74 | QVector3D Q_QUICK3DUTILS_EXPORT getScale(const QMatrix4x4 &m); |
75 | |
76 | bool Q_QUICK3DUTILS_EXPORT decompose(const QMatrix4x4 &m, QVector3D &position, QVector3D &scale, QQuaternion &rotation); |
77 | |
78 | inline void flip(QMatrix4x4 &matrix) |
79 | { |
80 | // Flip between left-handed and right-handed orientation |
81 | float *writePtr(matrix.data()); |
82 | // rotation conversion |
83 | writePtr[0 * 4 + 2] *= -1; |
84 | writePtr[1 * 4 + 2] *= -1; |
85 | writePtr[2 * 4 + 0] *= -1; |
86 | writePtr[2 * 4 + 1] *= -1; |
87 | // translation conversion |
88 | writePtr[3 * 4 + 2] *= -1; |
89 | } |
90 | |
91 | } |
92 | |
93 | namespace quat { |
94 | bool Q_QUICK3DUTILS_EXPORT isFinite(const QQuaternion &q); |
95 | |
96 | float Q_QUICK3DUTILS_EXPORT magnitude(const QQuaternion &q); |
97 | |
98 | bool Q_QUICK3DUTILS_EXPORT isSane(const QQuaternion &q); |
99 | |
100 | bool Q_QUICK3DUTILS_EXPORT isUnit(const QQuaternion &q); |
101 | |
102 | QVector3D Q_QUICK3DUTILS_EXPORT rotated(const QQuaternion &q, const QVector3D &v); |
103 | |
104 | QVector3D Q_QUICK3DUTILS_EXPORT inverseRotated(const QQuaternion &q, const QVector3D &v); |
105 | } |
106 | |
107 | namespace color { |
108 | QVector4D Q_QUICK3DUTILS_EXPORT sRGBToLinear(const QColor &color); |
109 | QColor Q_QUICK3DUTILS_EXPORT sRGBToLinearColor(const QColor &color); |
110 | } |
111 | |
112 | template<typename TDataType> |
113 | QSSGDataRef<TDataType> PtrAtOffset(quint8 *baseData, quint32 offset, quint32 byteSize) |
114 | { |
115 | return QSSGDataRef<TDataType>(byteSize ? reinterpret_cast<TDataType *>(baseData + offset) : nullptr, |
116 | byteSize / sizeof(TDataType)); |
117 | } |
118 | |
119 | Q_QUICK3DUTILS_EXPORT const char *nonNull(const char *src); |
120 | |
121 | inline QVector3D degToRad(const QVector3D &v) { |
122 | return QVector3D(qDegreesToRadians(degrees: v.x()), qDegreesToRadians(degrees: v.y()), qDegreesToRadians(degrees: v.z())); |
123 | } |
124 | |
125 | inline QVector3D radToDeg(const QVector3D &v) { |
126 | return QVector3D(qRadiansToDegrees(radians: v.x()), qRadiansToDegrees(radians: v.y()), qRadiansToDegrees(radians: v.z())); |
127 | } |
128 | |
129 | namespace rect { |
130 | // Return coordinates in pixels but relative to this rect. |
131 | inline QVector2D toRectRelative(const QRectF &r, const QVector2D &absoluteCoordinates) |
132 | { |
133 | return QVector2D(absoluteCoordinates.x() - float(r.x()), absoluteCoordinates.y() - float(r.y())); |
134 | } |
135 | |
136 | inline QVector2D halfDims(const QRectF &r) |
137 | { |
138 | return QVector2D(float(r.width() / 2.0), float(r.height() / 2.0)); |
139 | } |
140 | |
141 | // Take coordinates in global space and move local space where 0,0 is the center |
142 | // of the rect but return value in pixels, not in normalized -1,1 range |
143 | inline QVector2D toNormalizedRectRelative(const QRectF &r, QVector2D absoluteCoordinates) |
144 | { |
145 | // normalize them |
146 | const QVector2D relativeCoords(toRectRelative(r, absoluteCoordinates)); |
147 | const QVector2D halfD(halfDims(r)); |
148 | const QVector2D normalized((relativeCoords.x() / halfD.x()) - 1.0f, (relativeCoords.y() / halfD.y()) - 1.0f); |
149 | return QVector2D(normalized.x() * halfD.x(), normalized.y() * halfD.y()); |
150 | } |
151 | |
152 | inline QVector2D relativeToNormalizedCoordinates(const QRectF &r, QVector2D rectRelativeCoords) |
153 | { |
154 | return { (rectRelativeCoords.x() / halfDims(r).x()) - 1.0f, (rectRelativeCoords.y() / halfDims(r).y()) - 1.0f }; |
155 | } |
156 | |
157 | // Normalized coordinates are in the range of -1,1 where -1 is the left, bottom edges |
158 | // and 1 is the top,right edges. |
159 | inline QVector2D absoluteToNormalizedCoordinates(const QRectF &r, const QVector2D &absoluteCoordinates) |
160 | { |
161 | return relativeToNormalizedCoordinates(r, rectRelativeCoords: toRectRelative(r, absoluteCoordinates)); |
162 | } |
163 | |
164 | inline QVector2D toAbsoluteCoords(const QRectF &r, const QVector2D &inRelativeCoords) |
165 | { |
166 | return QVector2D(inRelativeCoords.x() + float(r.x()), inRelativeCoords.y() + float(r.y())); |
167 | } |
168 | } |
169 | |
170 | } // namespace QSSGUtils |
171 | |
172 | class RotationData |
173 | { |
174 | public: |
175 | RotationData() = default; |
176 | explicit RotationData(const QVector3D &r) |
177 | : m_quatRot() |
178 | , m_eulerRot(r) |
179 | , m_dirty(Dirty::Quaternion) |
180 | {} |
181 | explicit RotationData(const QQuaternion &r) |
182 | : m_quatRot(r.normalized()) |
183 | , m_eulerRot() |
184 | , m_dirty(Dirty::Euler) |
185 | {} |
186 | |
187 | RotationData &operator=(const QVector3D &r) noexcept |
188 | { |
189 | m_eulerRot = r; |
190 | m_dirty = Dirty::Quaternion; |
191 | return *this; |
192 | } |
193 | RotationData &operator=(const QQuaternion &r) noexcept |
194 | { |
195 | m_quatRot = r.normalized(); |
196 | m_dirty = Dirty::Euler; |
197 | return *this; |
198 | } |
199 | |
200 | friend inline bool operator ==(const RotationData &a, const RotationData &b) { |
201 | if (a.m_dirty == Dirty::None && b.m_dirty == Dirty::None) |
202 | return fuzzyQuaternionCompare(a: a.m_quatRot, b: b.m_quatRot); |
203 | |
204 | return fuzzyQuaternionCompare(a: QQuaternion(a), b: QQuaternion(b)); |
205 | } |
206 | |
207 | friend inline bool operator !=(const RotationData &a, const RotationData &b) { return !(a == b); } |
208 | |
209 | friend inline bool operator ==(const RotationData &a, const QVector3D &eulerRotation) |
210 | { |
211 | if (a.m_dirty == Dirty::None) |
212 | return qFuzzyCompare(v1: a.m_eulerRot, v2: eulerRotation); |
213 | |
214 | return qFuzzyCompare(v1: QVector3D(a), v2: eulerRotation); |
215 | } |
216 | friend inline bool operator !=(const RotationData &a, const QVector3D &eulerRotation) { return !(a == eulerRotation); } |
217 | |
218 | friend inline bool operator ==(const RotationData &a, const QQuaternion &rotation) |
219 | { |
220 | if (a.m_dirty == Dirty::None) |
221 | return fuzzyQuaternionCompare(a: a.m_quatRot, b: rotation); |
222 | |
223 | return fuzzyQuaternionCompare(a: QQuaternion(a), b: rotation); |
224 | } |
225 | friend inline bool operator !=(const RotationData &a, const QQuaternion &rotation) { return !(a == rotation); } |
226 | |
227 | [[nodiscard]] inline QVector3D getEulerRotation() const |
228 | { |
229 | if (m_dirty == Dirty::Euler) { |
230 | m_eulerRot = m_quatRot.toEulerAngles(); |
231 | m_dirty = Dirty::None; |
232 | } |
233 | |
234 | return m_eulerRot; |
235 | } |
236 | |
237 | [[nodiscard]] inline QQuaternion getQuaternionRotation() const |
238 | { |
239 | if (m_dirty == Dirty::Quaternion) { |
240 | m_quatRot = QQuaternion::fromEulerAngles(eulerAngles: m_eulerRot).normalized(); |
241 | m_dirty = Dirty::None; |
242 | } |
243 | |
244 | return m_quatRot; |
245 | } |
246 | |
247 | [[nodiscard]] inline QMatrix3x3 toRotationMatrix() const { return getQuaternionRotation().toRotationMatrix(); } |
248 | |
249 | [[nodiscard]] inline operator QQuaternion() const { return getQuaternionRotation(); } |
250 | [[nodiscard]] inline operator QVector3D() const { return getEulerRotation(); } |
251 | |
252 | private: |
253 | friend class ::tst_RotationDataClass; |
254 | |
255 | static constexpr double dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept |
256 | { |
257 | return double(q1.scalar()) * double(q2.scalar()) |
258 | + double(q1.x()) * double(q2.x()) |
259 | + double(q1.y()) * double(q2.y()) |
260 | + double(q1.z()) * double(q2.z()); |
261 | } |
262 | |
263 | [[nodiscard]] static constexpr bool fuzzyQuaternionCompare(const QQuaternion &a, const QQuaternion &b) |
264 | { |
265 | return qFuzzyCompare(p1: qAbs(t: dotProduct(q1: a, q2: b)), p2: 1.0); |
266 | } |
267 | |
268 | enum class Dirty |
269 | { |
270 | None, |
271 | Quaternion = 0x1, |
272 | Euler = 0x2 |
273 | }; |
274 | |
275 | mutable QQuaternion m_quatRot; // Should always be normalized |
276 | mutable QVector3D m_eulerRot; |
277 | mutable Dirty m_dirty { Dirty::None }; |
278 | }; |
279 | |
280 | namespace DebugViewHelpers { |
281 | template<typename T> |
282 | inline void ensureDebugObjectName(T *node, QObject *src) |
283 | { |
284 | if (!node->debugObjectName.isEmpty()) |
285 | return; |
286 | node->debugObjectName = src->objectName(); |
287 | if (node->debugObjectName.isEmpty()) |
288 | node->debugObjectName = QString::fromLatin1(ba: src->metaObject()->className()); |
289 | if (node->debugObjectName.isEmpty()) |
290 | node->debugObjectName = QString::asprintf(format: "%p" , src); |
291 | } |
292 | } |
293 | |
294 | QT_END_NAMESPACE |
295 | |
296 | #endif // QSSGUTILS_H |
297 | |