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
35class tst_RotationDataClass;
36
37QT_BEGIN_NAMESPACE
38
39enum class HandleType
40{
41 Layer,
42 Node,
43 Model,
44};
45
46template <HandleType>
47class QSSGRenderStorageHandle
48{
49public:
50 QSSGRenderStorageHandle() = default;
51 explicit QSSGRenderStorageHandle(quint32 ctx, quint32 version, quint32 index)
52 : m_ctx(ctx), m_version(version), m_index(index)
53 {
54 }
55
56 // NOTE: Version and index should always be > 0 when used.
57 bool hasId() const { return m_id != 0; }
58
59 quint32 context() const { return quint32(m_ctx); }
60 quint32 version() const { return quint32(m_version); }
61 quint32 index() const { return quint32(m_index); }
62
63 quint64 id() const { return m_id; }
64
65private:
66 union {
67 struct {
68 quint64 m_ctx : 16;
69 quint64 m_version : 16;
70 quint64 m_index : 32;
71 };
72 quint64 m_id = 0;
73 };
74};
75
76using QSSGRenderLayerHandle = QSSGRenderStorageHandle<HandleType::Layer>;
77using QSSGRenderNodeHandle = QSSGRenderStorageHandle<HandleType::Node>;
78using QSSGRenderModelHandle = QSSGRenderStorageHandle<HandleType::Model>;
79
80namespace QSSGUtils {
81
82namespace aux {
83Q_DECL_CONSTEXPR inline float translateConstantAttenuation(float attenuation) { return attenuation; }
84template<int MINATTENUATION = 0, int MAXATTENUATION = 1000>
85Q_DECL_CONSTEXPR inline float translateLinearAttenuation(float attenuation) { return qBound(min: float(MINATTENUATION), val: attenuation, max: float(MAXATTENUATION)) * .01f; }
86template<int MINATTENUATION = 0, int MAXATTENUATION = 1000>
87Q_DECL_CONSTEXPR inline float translateQuadraticAttenuation(float attenuation) { return qBound(min: float(MINATTENUATION), val: attenuation, max: float(MAXATTENUATION)) * .0001f; }
88}
89
90namespace vec2 {
91float Q_QUICK3DUTILS_EXPORT magnitude(const QVector2D &v);
92}
93
94namespace vec3 {
95inline 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()) }; }
96inline 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()) }; }
97bool Q_QUICK3DUTILS_EXPORT isFinite(const QVector3D &v);
98float Q_QUICK3DUTILS_EXPORT magnitude(const QVector3D &v);
99float Q_QUICK3DUTILS_EXPORT magnitudeSquared(const QVector3D &v);
100float Q_QUICK3DUTILS_EXPORT normalize(QVector3D &v);
101}
102
103namespace mat33 {
104QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix3x3 &m, const QVector3D &v);
105}
106
107namespace mat44 {
108QMatrix3x3 Q_QUICK3DUTILS_EXPORT getUpper3x3(const QMatrix4x4 &m);
109void Q_QUICK3DUTILS_EXPORT normalize(QMatrix4x4 &m);
110QVector3D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector3D &v);
111QVector4D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector4D &v);
112QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v);
113QVector4D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector4D &v);
114QVector3D Q_QUICK3DUTILS_EXPORT getPosition(const QMatrix4x4 &m);
115QVector3D Q_QUICK3DUTILS_EXPORT getScale(const QMatrix4x4 &m);
116
117bool Q_QUICK3DUTILS_EXPORT decompose(const QMatrix4x4 &m, QVector3D &position, QVector3D &scale, QQuaternion &rotation);
118
119inline void flip(QMatrix4x4 &matrix)
120{
121 // Flip between left-handed and right-handed orientation
122 float *writePtr(matrix.data());
123 // rotation conversion
124 writePtr[0 * 4 + 2] *= -1;
125 writePtr[1 * 4 + 2] *= -1;
126 writePtr[2 * 4 + 0] *= -1;
127 writePtr[2 * 4 + 1] *= -1;
128 // translation conversion
129 writePtr[3 * 4 + 2] *= -1;
130}
131
132}
133
134namespace quat {
135bool Q_QUICK3DUTILS_EXPORT isFinite(const QQuaternion &q);
136
137float Q_QUICK3DUTILS_EXPORT magnitude(const QQuaternion &q);
138
139bool Q_QUICK3DUTILS_EXPORT isSane(const QQuaternion &q);
140
141bool Q_QUICK3DUTILS_EXPORT isUnit(const QQuaternion &q);
142
143QVector3D Q_QUICK3DUTILS_EXPORT rotated(const QQuaternion &q, const QVector3D &v);
144
145QVector3D Q_QUICK3DUTILS_EXPORT inverseRotated(const QQuaternion &q, const QVector3D &v);
146}
147
148namespace color {
149QColor Q_QUICK3DUTILS_EXPORT linearTosRGB(const QVector4D &linearColorFactor);
150QVector4D Q_QUICK3DUTILS_EXPORT sRGBToLinear(const QColor &color);
151QColor Q_QUICK3DUTILS_EXPORT sRGBToLinearColor(const QColor &color);
152}
153
154template<typename TDataType>
155QSSGDataRef<TDataType> PtrAtOffset(quint8 *baseData, quint32 offset, quint32 byteSize)
156{
157 return QSSGDataRef<TDataType>(byteSize ? reinterpret_cast<TDataType *>(baseData + offset) : nullptr,
158 byteSize / sizeof(TDataType));
159}
160
161Q_QUICK3DUTILS_EXPORT const char *nonNull(const char *src);
162
163inline QVector3D degToRad(const QVector3D &v) {
164 return QVector3D(qDegreesToRadians(degrees: v.x()), qDegreesToRadians(degrees: v.y()), qDegreesToRadians(degrees: v.z()));
165}
166
167inline QVector3D radToDeg(const QVector3D &v) {
168 return QVector3D(qRadiansToDegrees(radians: v.x()), qRadiansToDegrees(radians: v.y()), qRadiansToDegrees(radians: v.z()));
169}
170
171namespace rect {
172// Return coordinates in pixels but relative to this rect.
173inline QVector2D toRectRelative(const QRectF &r, const QVector2D &absoluteCoordinates)
174{
175 return QVector2D(absoluteCoordinates.x() - float(r.x()), absoluteCoordinates.y() - float(r.y()));
176}
177
178inline QVector2D halfDims(const QRectF &r)
179{
180 return QVector2D(float(r.width() / 2.0), float(r.height() / 2.0));
181}
182
183// Take coordinates in global space and move local space where 0,0 is the center
184// of the rect but return value in pixels, not in normalized -1,1 range
185inline QVector2D toNormalizedRectRelative(const QRectF &r, QVector2D absoluteCoordinates)
186{
187 // normalize them
188 const QVector2D relativeCoords(toRectRelative(r, absoluteCoordinates));
189 const QVector2D halfD(halfDims(r));
190 const QVector2D normalized((relativeCoords.x() / halfD.x()) - 1.0f, (relativeCoords.y() / halfD.y()) - 1.0f);
191 return QVector2D(normalized.x() * halfD.x(), normalized.y() * halfD.y());
192}
193
194inline QVector2D relativeToNormalizedCoordinates(const QRectF &r, QVector2D rectRelativeCoords)
195{
196 return { (rectRelativeCoords.x() / halfDims(r).x()) - 1.0f, (rectRelativeCoords.y() / halfDims(r).y()) - 1.0f };
197}
198
199// Normalized coordinates are in the range of -1,1 where -1 is the left, bottom edges
200// and 1 is the top,right edges.
201inline QVector2D absoluteToNormalizedCoordinates(const QRectF &r, const QVector2D &absoluteCoordinates)
202{
203 return relativeToNormalizedCoordinates(r, rectRelativeCoords: toRectRelative(r, absoluteCoordinates));
204}
205
206inline QVector2D toAbsoluteCoords(const QRectF &r, const QVector2D &inRelativeCoords)
207{
208 return QVector2D(inRelativeCoords.x() + float(r.x()), inRelativeCoords.y() + float(r.y()));
209}
210}
211
212} // namespace QSSGUtils
213
214class RotationData
215{
216public:
217 RotationData() = default;
218 explicit RotationData(const QVector3D &r)
219 : m_quatRot()
220 , m_eulerRot(r)
221 , m_dirty(Dirty::Quaternion)
222 {}
223 explicit RotationData(const QQuaternion &r)
224 : m_quatRot(r.normalized())
225 , m_eulerRot()
226 , m_dirty(Dirty::Euler)
227 {}
228
229 RotationData &operator=(const QVector3D &r) noexcept
230 {
231 m_eulerRot = r;
232 m_dirty = Dirty::Quaternion;
233 return *this;
234 }
235 RotationData &operator=(const QQuaternion &r) noexcept
236 {
237 m_quatRot = r.normalized();
238 m_dirty = Dirty::Euler;
239 return *this;
240 }
241
242 friend inline bool operator ==(const RotationData &a, const RotationData &b) {
243 if (a.m_dirty == Dirty::None && b.m_dirty == Dirty::None)
244 return fuzzyQuaternionCompare(a: a.m_quatRot, b: b.m_quatRot);
245
246 return fuzzyQuaternionCompare(a: QQuaternion(a), b: QQuaternion(b));
247 }
248
249 friend inline bool operator !=(const RotationData &a, const RotationData &b) { return !(a == b); }
250
251 friend inline bool operator ==(const RotationData &a, const QVector3D &eulerRotation)
252 {
253 if (a.m_dirty == Dirty::None)
254 return qFuzzyCompare(v1: a.m_eulerRot, v2: eulerRotation);
255
256 return qFuzzyCompare(v1: QVector3D(a), v2: eulerRotation);
257 }
258 friend inline bool operator !=(const RotationData &a, const QVector3D &eulerRotation) { return !(a == eulerRotation); }
259
260 friend inline bool operator ==(const RotationData &a, const QQuaternion &rotation)
261 {
262 if (a.m_dirty == Dirty::None)
263 return fuzzyQuaternionCompare(a: a.m_quatRot, b: rotation);
264
265 return fuzzyQuaternionCompare(a: QQuaternion(a), b: rotation);
266 }
267 friend inline bool operator !=(const RotationData &a, const QQuaternion &rotation) { return !(a == rotation); }
268
269 [[nodiscard]] inline QVector3D getEulerRotation() const
270 {
271 if (m_dirty == Dirty::Euler) {
272 m_eulerRot = m_quatRot.toEulerAngles();
273 m_dirty = Dirty::None;
274 }
275
276 return m_eulerRot;
277 }
278
279 [[nodiscard]] inline QQuaternion getQuaternionRotation() const
280 {
281 if (m_dirty == Dirty::Quaternion) {
282 m_quatRot = QQuaternion::fromEulerAngles(angles: m_eulerRot).normalized();
283 m_dirty = Dirty::None;
284 }
285
286 return m_quatRot;
287 }
288
289 [[nodiscard]] inline QMatrix3x3 toRotationMatrix() const { return getQuaternionRotation().toRotationMatrix(); }
290
291 [[nodiscard]] inline operator QQuaternion() const { return getQuaternionRotation(); }
292 [[nodiscard]] inline operator QVector3D() const { return getEulerRotation(); }
293
294private:
295 friend class ::tst_RotationDataClass;
296
297 static constexpr double dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept
298 {
299 return double(q1.scalar()) * double(q2.scalar())
300 + double(q1.x()) * double(q2.x())
301 + double(q1.y()) * double(q2.y())
302 + double(q1.z()) * double(q2.z());
303 }
304
305 [[nodiscard]] static constexpr bool fuzzyQuaternionCompare(const QQuaternion &a, const QQuaternion &b)
306 {
307 return qFuzzyCompare(p1: qAbs(t: dotProduct(q1: a, q2: b)), p2: 1.0);
308 }
309
310 enum class Dirty
311 {
312 None,
313 Quaternion = 0x1,
314 Euler = 0x2
315 };
316
317 mutable QQuaternion m_quatRot; // Should always be normalized
318 mutable QVector3D m_eulerRot;
319 mutable Dirty m_dirty { Dirty::None };
320};
321
322namespace DebugViewHelpers {
323template<typename T>
324inline void ensureDebugObjectName(T *node, QObject *src)
325{
326 if (!node->debugObjectName.isEmpty())
327 return;
328 node->debugObjectName = src->objectName();
329 if (node->debugObjectName.isEmpty())
330 node->debugObjectName = QString::fromLatin1(ba: src->metaObject()->className());
331 if (node->debugObjectName.isEmpty())
332 node->debugObjectName = QString::asprintf(format: "%p", src);
333}
334}
335
336QT_END_NAMESPACE
337
338#endif // QSSGUTILS_H
339

source code of qtquick3d/src/utils/qssgutils_p.h