aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/qssgutils_p.h
blob: ab39c00fe5b172d337566bc8e93b4e8d19ee6004 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// Copyright (C) 2008-2012 NVIDIA Corporation.
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#ifndef QSSGUTILS_H
#define QSSGUTILS_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <QtQuick3DUtils/private/qtquick3dutilsglobal_p.h>
#include <QtQuick3DUtils/private/qssgdataref_p.h>

#include <QtGui/QVector2D>
#include <QtGui/QVector3D>
#include <QtGui/QQuaternion>
#include <QtGui/QMatrix3x3>
#include <QtGui/QMatrix4x4>
#include <QtGui/QColor>

#include <QtCore/qdebug.h>
#include <QtCore/QString>
#include <QtCore/qloggingcategory.h>
#include <QtCore/QIODevice>
#include <QtCore/qmath.h>

class tst_RotationDataClass;

QT_BEGIN_NAMESPACE

namespace QSSGUtils {

namespace aux {
Q_DECL_CONSTEXPR inline float translateConstantAttenuation(float attenuation) { return attenuation; }
template<int MINATTENUATION = 0, int MAXATTENUATION = 1000>
Q_DECL_CONSTEXPR inline float translateLinearAttenuation(float attenuation) { return qBound(float(MINATTENUATION), attenuation, float(MAXATTENUATION)) * .01f; }
template<int MINATTENUATION = 0, int MAXATTENUATION = 1000>
Q_DECL_CONSTEXPR inline float translateQuadraticAttenuation(float attenuation) { return qBound(float(MINATTENUATION), attenuation, float(MAXATTENUATION)) * .0001f; }
}

namespace vec2 {
float Q_QUICK3DUTILS_EXPORT magnitude(const QVector2D &v);
}

namespace vec3 {
inline QVector3D minimum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW { return { qMin(v1.x(), v2.x()), qMin(v1.y(), v2.y()), qMin(v1.z(), v2.z()) }; }
inline QVector3D maximum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW { return { qMax(v1.x(), v2.x()), qMax(v1.y(), v2.y()), qMax(v1.z(), v2.z()) }; }
bool Q_QUICK3DUTILS_EXPORT isFinite(const QVector3D &v);
float Q_QUICK3DUTILS_EXPORT magnitude(const QVector3D &v);
float Q_QUICK3DUTILS_EXPORT magnitudeSquared(const QVector3D &v);
float Q_QUICK3DUTILS_EXPORT normalize(QVector3D &v);
}

namespace mat33 {
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix3x3 &m, const QVector3D &v);
}

namespace mat44 {
QMatrix3x3 Q_QUICK3DUTILS_EXPORT getUpper3x3(const QMatrix4x4 &m);
void Q_QUICK3DUTILS_EXPORT normalize(QMatrix4x4 &m);
QVector3D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector3D &v);
QVector4D Q_QUICK3DUTILS_EXPORT rotate(const QMatrix4x4 &m, const QVector4D &v);
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v);
QVector4D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector4D &v);
QVector3D Q_QUICK3DUTILS_EXPORT getPosition(const QMatrix4x4 &m);
QVector3D Q_QUICK3DUTILS_EXPORT getScale(const QMatrix4x4 &m);

bool Q_QUICK3DUTILS_EXPORT decompose(const QMatrix4x4 &m, QVector3D &position, QVector3D &scale, QQuaternion &rotation);

inline void flip(QMatrix4x4 &matrix)
{
    // Flip between left-handed and right-handed orientation
    float *writePtr(matrix.data());
    // rotation conversion
    writePtr[0 * 4 + 2] *= -1;
    writePtr[1 * 4 + 2] *= -1;
    writePtr[2 * 4 + 0] *= -1;
    writePtr[2 * 4 + 1] *= -1;
    // translation conversion
    writePtr[3 * 4 + 2] *= -1;
}

}

namespace quat {
bool Q_QUICK3DUTILS_EXPORT isFinite(const QQuaternion &q);

float Q_QUICK3DUTILS_EXPORT magnitude(const QQuaternion &q);

bool Q_QUICK3DUTILS_EXPORT isSane(const QQuaternion &q);

bool Q_QUICK3DUTILS_EXPORT isUnit(const QQuaternion &q);

QVector3D Q_QUICK3DUTILS_EXPORT rotated(const QQuaternion &q, const QVector3D &v);

QVector3D Q_QUICK3DUTILS_EXPORT inverseRotated(const QQuaternion &q, const QVector3D &v);
}

namespace color {
QColor Q_QUICK3DUTILS_EXPORT linearTosRGB(const QVector4D &linearColorFactor);
QVector4D Q_QUICK3DUTILS_EXPORT sRGBToLinear(const QColor &color);
QColor Q_QUICK3DUTILS_EXPORT sRGBToLinearColor(const QColor &color);
}

template<typename TDataType>
QSSGDataRef<TDataType> PtrAtOffset(quint8 *baseData, quint32 offset, quint32 byteSize)
{
    return QSSGDataRef<TDataType>(byteSize ? reinterpret_cast<TDataType *>(baseData + offset) : nullptr,
                                    byteSize / sizeof(TDataType));
}

Q_QUICK3DUTILS_EXPORT const char *nonNull(const char *src);

inline QVector3D degToRad(const QVector3D &v) {
    return QVector3D(qDegreesToRadians(v.x()), qDegreesToRadians(v.y()), qDegreesToRadians(v.z()));
}

inline QVector3D radToDeg(const QVector3D &v) {
    return QVector3D(qRadiansToDegrees(v.x()), qRadiansToDegrees(v.y()), qRadiansToDegrees(v.z()));
}

namespace rect {
// Return coordinates in pixels but relative to this rect.
inline QVector2D toRectRelative(const QRectF &r, const QVector2D &absoluteCoordinates)
{
    return QVector2D(absoluteCoordinates.x() - float(r.x()), absoluteCoordinates.y() - float(r.y()));
}

inline QVector2D halfDims(const QRectF &r)
{
    return QVector2D(float(r.width() / 2.0), float(r.height() / 2.0));
}

// Take coordinates in global space and move local space where 0,0 is the center
// of the rect but return value in pixels, not in normalized -1,1 range
inline QVector2D toNormalizedRectRelative(const QRectF &r, QVector2D absoluteCoordinates)
{
    // normalize them
    const QVector2D relativeCoords(toRectRelative(r, absoluteCoordinates));
    const QVector2D halfD(halfDims(r));
    const QVector2D normalized((relativeCoords.x() / halfD.x()) - 1.0f, (relativeCoords.y() / halfD.y()) - 1.0f);
    return QVector2D(normalized.x() * halfD.x(), normalized.y() * halfD.y());
}

inline QVector2D relativeToNormalizedCoordinates(const QRectF &r, QVector2D rectRelativeCoords)
{
    return { (rectRelativeCoords.x() / halfDims(r).x()) - 1.0f, (rectRelativeCoords.y() / halfDims(r).y()) - 1.0f };
}

// Normalized coordinates are in the range of -1,1 where -1 is the left, bottom edges
// and 1 is the top,right edges.
inline QVector2D absoluteToNormalizedCoordinates(const QRectF &r, const QVector2D &absoluteCoordinates)
{
    return relativeToNormalizedCoordinates(r, toRectRelative(r, absoluteCoordinates));
}

inline QVector2D toAbsoluteCoords(const QRectF &r, const QVector2D &inRelativeCoords)
{
    return QVector2D(inRelativeCoords.x() + float(r.x()), inRelativeCoords.y() + float(r.y()));
}
}

} // namespace QSSGUtils

class RotationData
{
public:
    RotationData() = default;
    explicit RotationData(const QVector3D &r)
        : m_quatRot()
        , m_eulerRot(r)
        , m_dirty(Dirty::Quaternion)
    {}
    explicit RotationData(const QQuaternion &r)
        : m_quatRot(r.normalized())
        , m_eulerRot()
        , m_dirty(Dirty::Euler)
    {}

    RotationData &operator=(const QVector3D &r) noexcept
    {
        m_eulerRot = r;
        m_dirty = Dirty::Quaternion;
        return *this;
    }
    RotationData &operator=(const QQuaternion &r) noexcept
    {
        m_quatRot = r.normalized();
        m_dirty = Dirty::Euler;
        return *this;
    }

    friend inline bool operator ==(const RotationData &a, const RotationData &b) {
        if (a.m_dirty == Dirty::None && b.m_dirty == Dirty::None)
            return fuzzyQuaternionCompare(a.m_quatRot, b.m_quatRot);

        return fuzzyQuaternionCompare(QQuaternion(a), QQuaternion(b));
    }

    friend inline bool operator !=(const RotationData &a, const RotationData &b) { return !(a == b); }

    friend inline bool operator ==(const RotationData &a, const QVector3D &eulerRotation)
    {
        if (a.m_dirty == Dirty::None)
            return qFuzzyCompare(a.m_eulerRot, eulerRotation);

        return qFuzzyCompare(QVector3D(a), eulerRotation);
    }
    friend inline bool operator !=(const RotationData &a, const QVector3D &eulerRotation) { return !(a == eulerRotation); }

    friend inline bool operator ==(const RotationData &a, const QQuaternion &rotation)
    {
        if (a.m_dirty == Dirty::None)
            return fuzzyQuaternionCompare(a.m_quatRot, rotation);

        return fuzzyQuaternionCompare(QQuaternion(a), rotation);
    }
    friend inline bool operator !=(const RotationData &a, const QQuaternion &rotation) { return !(a == rotation); }

    [[nodiscard]] inline QVector3D getEulerRotation() const
    {
        if (m_dirty == Dirty::Euler) {
            m_eulerRot = m_quatRot.toEulerAngles();
            m_dirty = Dirty::None;
        }

        return m_eulerRot;
    }

    [[nodiscard]] inline QQuaternion getQuaternionRotation() const
    {
        if (m_dirty == Dirty::Quaternion) {
            m_quatRot = QQuaternion::fromEulerAngles(m_eulerRot).normalized();
            m_dirty = Dirty::None;
        }

        return m_quatRot;
    }

    [[nodiscard]] inline QMatrix3x3 toRotationMatrix() const { return getQuaternionRotation().toRotationMatrix(); }

    [[nodiscard]] inline operator QQuaternion() const { return getQuaternionRotation(); }
    [[nodiscard]] inline operator QVector3D() const { return getEulerRotation(); }

private:
    friend class ::tst_RotationDataClass;

    static constexpr double dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept
    {
        return double(q1.scalar()) * double(q2.scalar())
                + double(q1.x()) * double(q2.x())
                + double(q1.y()) * double(q2.y())
                + double(q1.z()) * double(q2.z());
    }

    [[nodiscard]] static constexpr bool fuzzyQuaternionCompare(const QQuaternion &a, const QQuaternion &b)
    {
        return qFuzzyCompare(qAbs(dotProduct(a, b)), 1.0);
    }

    enum class Dirty
    {
        None,
        Quaternion = 0x1,
        Euler = 0x2
    };

    mutable QQuaternion m_quatRot; // Should always be normalized
    mutable QVector3D m_eulerRot;
    mutable Dirty m_dirty { Dirty::None };
};

namespace DebugViewHelpers {
template<typename T>
inline void ensureDebugObjectName(T *node, QObject *src)
{
    if (!node->debugObjectName.isEmpty())
        return;
    node->debugObjectName = src->objectName();
    if (node->debugObjectName.isEmpty())
        node->debugObjectName = QString::fromLatin1(src->metaObject()->className());
    if (node->debugObjectName.isEmpty())
        node->debugObjectName = QString::asprintf("%p", src);
}
}

QT_END_NAMESPACE

#endif // QSSGUTILS_H