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 | inline void flip(QMatrix4x4 &matrix) |
77 | { |
78 | // Flip between left-handed and right-handed orientation |
79 | float *writePtr(matrix.data()); |
80 | // rotation conversion |
81 | writePtr[0 * 4 + 2] *= -1; |
82 | writePtr[1 * 4 + 2] *= -1; |
83 | writePtr[2 * 4 + 0] *= -1; |
84 | writePtr[2 * 4 + 1] *= -1; |
85 | // translation conversion |
86 | writePtr[3 * 4 + 2] *= -1; |
87 | } |
88 | |
89 | } |
90 | |
91 | namespace quat { |
92 | bool Q_QUICK3DUTILS_EXPORT isFinite(const QQuaternion &q); |
93 | |
94 | float Q_QUICK3DUTILS_EXPORT magnitude(const QQuaternion &q); |
95 | |
96 | bool Q_QUICK3DUTILS_EXPORT isSane(const QQuaternion &q); |
97 | |
98 | bool Q_QUICK3DUTILS_EXPORT isUnit(const QQuaternion &q); |
99 | |
100 | QVector3D Q_QUICK3DUTILS_EXPORT rotated(const QQuaternion &q, const QVector3D &v); |
101 | |
102 | QVector3D Q_QUICK3DUTILS_EXPORT inverseRotated(const QQuaternion &q, const QVector3D &v); |
103 | } |
104 | |
105 | namespace color { |
106 | QVector4D Q_QUICK3DUTILS_EXPORT sRGBToLinear(const QColor &color); |
107 | QColor Q_QUICK3DUTILS_EXPORT sRGBToLinearColor(const QColor &color); |
108 | } |
109 | |
110 | template<typename TDataType> |
111 | QSSGDataRef<TDataType> PtrAtOffset(quint8 *baseData, quint32 offset, quint32 byteSize) |
112 | { |
113 | return QSSGDataRef<TDataType>(byteSize ? reinterpret_cast<TDataType *>(baseData + offset) : nullptr, |
114 | byteSize / sizeof(TDataType)); |
115 | } |
116 | |
117 | Q_QUICK3DUTILS_EXPORT const char *nonNull(const char *src); |
118 | |
119 | inline QVector3D degToRad(const QVector3D &v) { |
120 | return QVector3D(qDegreesToRadians(degrees: v.x()), qDegreesToRadians(degrees: v.y()), qDegreesToRadians(degrees: v.z())); |
121 | } |
122 | |
123 | inline QVector3D radToDeg(const QVector3D &v) { |
124 | return QVector3D(qRadiansToDegrees(radians: v.x()), qRadiansToDegrees(radians: v.y()), qRadiansToDegrees(radians: v.z())); |
125 | } |
126 | |
127 | namespace rect { |
128 | // Return coordinates in pixels but relative to this rect. |
129 | inline QVector2D toRectRelative(const QRectF &r, const QVector2D &absoluteCoordinates) |
130 | { |
131 | return QVector2D(absoluteCoordinates.x() - float(r.x()), absoluteCoordinates.y() - float(r.y())); |
132 | } |
133 | |
134 | inline QVector2D halfDims(const QRectF &r) |
135 | { |
136 | return QVector2D(float(r.width() / 2.0), float(r.height() / 2.0)); |
137 | } |
138 | |
139 | // Take coordinates in global space and move local space where 0,0 is the center |
140 | // of the rect but return value in pixels, not in normalized -1,1 range |
141 | inline QVector2D toNormalizedRectRelative(const QRectF &r, QVector2D absoluteCoordinates) |
142 | { |
143 | // normalize them |
144 | const QVector2D relativeCoords(toRectRelative(r, absoluteCoordinates)); |
145 | const QVector2D halfD(halfDims(r)); |
146 | const QVector2D normalized((relativeCoords.x() / halfD.x()) - 1.0f, (relativeCoords.y() / halfD.y()) - 1.0f); |
147 | return QVector2D(normalized.x() * halfD.x(), normalized.y() * halfD.y()); |
148 | } |
149 | |
150 | inline QVector2D relativeToNormalizedCoordinates(const QRectF &r, QVector2D rectRelativeCoords) |
151 | { |
152 | return { (rectRelativeCoords.x() / halfDims(r).x()) - 1.0f, (rectRelativeCoords.y() / halfDims(r).y()) - 1.0f }; |
153 | } |
154 | |
155 | // Normalized coordinates are in the range of -1,1 where -1 is the left, bottom edges |
156 | // and 1 is the top,right edges. |
157 | inline QVector2D absoluteToNormalizedCoordinates(const QRectF &r, const QVector2D &absoluteCoordinates) |
158 | { |
159 | return relativeToNormalizedCoordinates(r, rectRelativeCoords: toRectRelative(r, absoluteCoordinates)); |
160 | } |
161 | |
162 | inline QVector2D toAbsoluteCoords(const QRectF &r, const QVector2D &inRelativeCoords) |
163 | { |
164 | return QVector2D(inRelativeCoords.x() + float(r.x()), inRelativeCoords.y() + float(r.y())); |
165 | } |
166 | } |
167 | |
168 | } // namespace QSSGUtils |
169 | |
170 | class RotationData |
171 | { |
172 | public: |
173 | RotationData() = default; |
174 | explicit RotationData(const QVector3D &r) |
175 | : m_quatRot() |
176 | , m_eulerRot(r) |
177 | , m_dirty(Dirty::Quaternion) |
178 | {} |
179 | explicit RotationData(const QQuaternion &r) |
180 | : m_quatRot(r.normalized()) |
181 | , m_eulerRot() |
182 | , m_dirty(Dirty::Euler) |
183 | {} |
184 | |
185 | RotationData &operator=(const QVector3D &r) noexcept |
186 | { |
187 | m_eulerRot = r; |
188 | m_dirty = Dirty::Quaternion; |
189 | return *this; |
190 | } |
191 | RotationData &operator=(const QQuaternion &r) noexcept |
192 | { |
193 | m_quatRot = r.normalized(); |
194 | m_dirty = Dirty::Euler; |
195 | return *this; |
196 | } |
197 | |
198 | friend inline bool operator ==(const RotationData &a, const RotationData &b) { |
199 | if (a.m_dirty == Dirty::None && b.m_dirty == Dirty::None) |
200 | return fuzzyQuaternionCompare(a: a.m_quatRot, b: b.m_quatRot); |
201 | |
202 | return fuzzyQuaternionCompare(a: QQuaternion(a), b: QQuaternion(b)); |
203 | } |
204 | |
205 | friend inline bool operator !=(const RotationData &a, const RotationData &b) { return !(a == b); } |
206 | |
207 | friend inline bool operator ==(const RotationData &a, const QVector3D &eulerRotation) |
208 | { |
209 | if (a.m_dirty == Dirty::None) |
210 | return qFuzzyCompare(v1: a.m_eulerRot, v2: eulerRotation); |
211 | |
212 | return qFuzzyCompare(v1: QVector3D(a), v2: eulerRotation); |
213 | } |
214 | friend inline bool operator !=(const RotationData &a, const QVector3D &eulerRotation) { return !(a == eulerRotation); } |
215 | |
216 | friend inline bool operator ==(const RotationData &a, const QQuaternion &rotation) |
217 | { |
218 | if (a.m_dirty == Dirty::None) |
219 | return fuzzyQuaternionCompare(a: a.m_quatRot, b: rotation); |
220 | |
221 | return fuzzyQuaternionCompare(a: QQuaternion(a), b: rotation); |
222 | } |
223 | friend inline bool operator !=(const RotationData &a, const QQuaternion &rotation) { return !(a == rotation); } |
224 | |
225 | [[nodiscard]] inline QVector3D getEulerRotation() const |
226 | { |
227 | if (m_dirty == Dirty::Euler) { |
228 | m_eulerRot = m_quatRot.toEulerAngles(); |
229 | m_dirty = Dirty::None; |
230 | } |
231 | |
232 | return m_eulerRot; |
233 | } |
234 | |
235 | [[nodiscard]] inline QQuaternion getQuaternionRotation() const |
236 | { |
237 | if (m_dirty == Dirty::Quaternion) { |
238 | m_quatRot = QQuaternion::fromEulerAngles(eulerAngles: m_eulerRot).normalized(); |
239 | m_dirty = Dirty::None; |
240 | } |
241 | |
242 | return m_quatRot; |
243 | } |
244 | |
245 | [[nodiscard]] inline QMatrix3x3 toRotationMatrix() const { return getQuaternionRotation().toRotationMatrix(); } |
246 | |
247 | [[nodiscard]] inline operator QQuaternion() const { return getQuaternionRotation(); } |
248 | [[nodiscard]] inline operator QVector3D() const { return getEulerRotation(); } |
249 | |
250 | private: |
251 | friend class ::tst_RotationDataClass; |
252 | |
253 | [[nodiscard]] constexpr static inline bool fuzzyQuaternionCompare(const QQuaternion &a, const QQuaternion &b) |
254 | { |
255 | // This 'e' will give better precision than qtbase's qFuzzyCompare for QQuaternion |
256 | constexpr float e = std::numeric_limits<float>::epsilon(); |
257 | return (qAbs(t: 1.0f - qAbs(t: QQuaternion::dotProduct(q1: a, q2: b))) <= e); |
258 | } |
259 | |
260 | enum class Dirty |
261 | { |
262 | None, |
263 | Quaternion = 0x1, |
264 | Euler = 0x2 |
265 | }; |
266 | |
267 | mutable QQuaternion m_quatRot; // Should always be normalized |
268 | mutable QVector3D m_eulerRot; |
269 | mutable Dirty m_dirty { Dirty::None }; |
270 | }; |
271 | |
272 | namespace DebugViewHelpers { |
273 | template<typename T> |
274 | inline void ensureDebugObjectName(T *node, QObject *src) |
275 | { |
276 | if (!node->debugObjectName.isEmpty()) |
277 | return; |
278 | node->debugObjectName = src->objectName(); |
279 | if (node->debugObjectName.isEmpty()) |
280 | node->debugObjectName = QString::fromLatin1(ba: src->metaObject()->className()); |
281 | if (node->debugObjectName.isEmpty()) |
282 | node->debugObjectName = QString::asprintf(format: "%p" , src); |
283 | } |
284 | } |
285 | |
286 | QT_END_NAMESPACE |
287 | |
288 | #endif // QSSGUTILS_H |
289 | |