summaryrefslogtreecommitdiffstats
path: root/src/core/transforms
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/transforms')
-rw-r--r--src/core/transforms/matrix4x4_avx2.cpp64
-rw-r--r--src/core/transforms/matrix4x4_avx2_p.h574
-rw-r--r--src/core/transforms/matrix4x4_p.h82
-rw-r--r--src/core/transforms/matrix4x4_sse.cpp64
-rw-r--r--src/core/transforms/matrix4x4_sse_p.h504
-rw-r--r--src/core/transforms/qabstractskeleton.cpp134
-rw-r--r--src/core/transforms/qabstractskeleton.h77
-rw-r--r--src/core/transforms/qabstractskeleton_p.h81
-rw-r--r--src/core/transforms/qarmature.cpp172
-rw-r--r--src/core/transforms/qarmature.h82
-rw-r--r--src/core/transforms/qarmature_p.h83
-rw-r--r--src/core/transforms/qjoint.cpp414
-rw-r--r--src/core/transforms/qjoint.h115
-rw-r--r--src/core/transforms/qjoint_p.h95
-rw-r--r--src/core/transforms/qmath3d_p.h19
-rw-r--r--src/core/transforms/qskeleton.cpp152
-rw-r--r--src/core/transforms/qskeleton.h79
-rw-r--r--src/core/transforms/qskeleton_p.h82
-rw-r--r--src/core/transforms/qskeletonloader.cpp258
-rw-r--r--src/core/transforms/qskeletonloader.h104
-rw-r--r--src/core/transforms/qskeletonloader_p.h98
-rw-r--r--src/core/transforms/sqt_p.h107
-rw-r--r--src/core/transforms/transforms.pri58
-rw-r--r--src/core/transforms/vector3d_p.h74
-rw-r--r--src/core/transforms/vector3d_sse.cpp141
-rw-r--r--src/core/transforms/vector3d_sse_p.h399
-rw-r--r--src/core/transforms/vector4d_p.h74
-rw-r--r--src/core/transforms/vector4d_sse.cpp59
-rw-r--r--src/core/transforms/vector4d_sse_p.h396
29 files changed, 4636 insertions, 5 deletions
diff --git a/src/core/transforms/matrix4x4_avx2.cpp b/src/core/transforms/matrix4x4_avx2.cpp
new file mode 100644
index 000000000..556e778d0
--- /dev/null
+++ b/src/core/transforms/matrix4x4_avx2.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "matrix4x4_avx2_p.h"
+
+#ifdef QT_COMPILER_SUPPORTS_AVX2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QDebug operator<<(QDebug dbg, const Matrix4x4_AVX2 &m)
+{
+ dbg.nospace() << "Matrix4x4_AVX2(" << endl
+ << qSetFieldWidth(10)
+ << m.m11() << m.m12() << m.m13() << m.m14() << endl
+ << m.m21() << m.m22() << m.m23() << m.m24() << endl
+ << m.m31() << m.m32() << m.m33() << m.m34() << endl
+ << m.m41() << m.m42() << m.m43() << m.m44() << endl
+ << qSetFieldWidth(0) << ')';
+ return dbg;
+}
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/core/transforms/matrix4x4_avx2_p.h b/src/core/transforms/matrix4x4_avx2_p.h
new file mode 100644
index 000000000..d5f77041b
--- /dev/null
+++ b/src/core/transforms/matrix4x4_avx2_p.h
@@ -0,0 +1,574 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_MATRIX4X4_AVX2_P_H
+#define QT3DCORE_MATRIX4X4_AVX2_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <Qt3DCore/private/vector4d_p.h>
+#include <Qt3DCore/private/vector3d_p.h>
+#include <private/qsimd_p.h>
+#include <QMatrix4x4>
+
+#ifdef QT_COMPILER_SUPPORTS_AVX2
+
+// Some GCC versions don't have _mm256_set_m128 available
+// Work around that
+#define _mm256_set_m128(va, vb) \
+ _mm256_insertf128_ps(_mm256_castps128_ps256(vb), va, 1)
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class Matrix4x4_AVX2
+{
+public:
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2() { setToIdentity(); }
+ explicit Q_ALWAYS_INLINE Matrix4x4_AVX2(Qt::Initialization) {}
+
+ // Assumes data is 32 bytes aligned (and in column major order)
+ explicit Q_ALWAYS_INLINE Matrix4x4_AVX2(float *data)
+ {
+ m_col12 = _mm256_load_ps(data);
+ m_col34 = _mm256_load_ps(data + 8);
+ }
+
+ // QMatrix4x4::constData returns in column major order
+ explicit Q_ALWAYS_INLINE Matrix4x4_AVX2(const QMatrix4x4 &mat)
+ {
+ // data may not be properly aligned, using unaligned loads
+ const float *data = mat.constData();
+ m_col12 = _mm256_loadu_ps(data);
+ m_col34 = _mm256_loadu_ps(data + 8);
+ }
+
+ // In (row major) but we store in column major order
+ explicit Q_ALWAYS_INLINE Matrix4x4_AVX2(float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44)
+ {
+ m_col12 = _mm256_set_ps(m42, m32, m22, m12, m41, m31, m21, m11);
+ m_col34 = _mm256_set_ps(m44, m34, m24, m14, m43, m33, m23, m13);
+ }
+
+ Q_ALWAYS_INLINE void setToIdentity()
+ {
+ // 23 instructions
+ m_col12 = _mm256_set_ps(0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f);
+ m_col34 = _mm256_set_ps(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
+
+ // 23 instructions
+ // 1, 0, 0, 0
+ // __m128 vec = _mm_set_ss(1.0f);
+ // // 0, 1, 0, 0
+ // // 0b01010001 == 0x51
+ // __m128 tmp = _mm_permute_ps(vec, 0x51);
+
+ // // 1, 0, 0, 0, 0, 1, 0, 0
+ // m_col12 = _mm256_set_m128(tmp, vec);
+
+ // // 0, 0, 1, 0
+ // // 0b01000101 == 0x45
+ // tmp = _mm_permute_ps(vec, 0x45);
+
+ // // 0, 0, 0, 1
+ // // 0b00010101 == 0x15
+ // vec = _mm_permute_ps(vec, 0x15);
+
+ // // 0, 0, 1, 0, 0, 0, 0, 1
+ // m_col34 = _mm256_set_m128(vec, tmp);
+
+ // Using a static identity matrix and assigning it is 27 instructions
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 operator*(const Matrix4x4_AVX2 &other) const
+ {
+ // Shuffling: (Latency 1)
+ // (8 bits -> first two pairs used to select from the first vector, second pairs from second vector)
+
+ // 00 01 10 11, 00 01 10 11
+ // v1 = m11 m12 m13 m14 m21 m22 m23 m24
+ // v2 = m11 m12 m13 m14 m21 m22 m23 m24
+
+ // shuffled with 00 00 00 00
+ // v1[0] v1[0] v2[0] v2[0] v1[0] v1[0] v2[0] v2[0]
+ // -> m11 m11 m11 m11 m21 m21 m21 m21
+
+ // Broadcasting: (Latency 1)
+ // -> n11 n12 n13 n14 broadcasted
+ // n11 n12 n13 n14 n11 n12 n13 n14
+
+ // Multiplying (Latency 5):
+ // m11 m11 m11 m11 m21 m21 m21 m21 * n11 n12 n13 n14 n11 n12 n13 n14
+
+ // -> m11 n11, m11 n12, m11 n13, m11 n14
+ // m21 n11, m21 n12, m21 n13, m21 n14
+
+ // 00 01 10 11, 00 01 10 11
+ // v1 = m11 m12 m13 m14 m21 m22 m23 m24
+ // v2 = m11 m12 m13 m14 m21 m22 m23 m24
+ const __m256 otherCol12 = other.m_col12;
+ const __m256 otherCol34 = other.m_col34;
+ const __m128 col1 = _mm256_extractf128_ps(m_col12, 0);
+ const __m128 col2 = _mm256_extractf128_ps(m_col12, 1);
+ const __m128 col3 = _mm256_extractf128_ps(m_col34, 0);
+ const __m128 col4 = _mm256_extractf128_ps(m_col34, 1);
+
+ // const __m256 col12 = _mm256_load_ps(m);
+ // const __m256 col34 = _mm256_load_ps(m + 8);
+ // const __m128 otherCol1 = _mm_load_ps(other.m);
+ // const __m128 otherCol2 = _mm_load_ps(other.m + 4);
+ // const __m128 otherCol3 = _mm_load_ps(other.m + 8);
+ // const __m128 otherCol4 = _mm_load_ps(other.m + 12);
+
+ __m256 tmp = _mm256_mul_ps(_mm256_shuffle_ps(otherCol12, otherCol12, 0x00), _mm256_broadcast_ps(&col1));
+
+ // shuffled with 01 01 01 01
+ // v1[1] v1[1] v2[1] v2[1] v1[1] v1[1] v2[1] v2[1]
+ // -> m12 m12 m12 m12 m22 m22 m22 m22
+
+
+ // 00 01 10 11, 00 01 10 11
+ // m11 m12 m13 m14, m21 m22 m23 m24 shuffled with 01 01 01 01
+ // -> m12 m12 m12 m12 m22 m22 m22 m22 x n21 n22 n23 n24 n21 n22 n23 n24
+
+ // -> m12 n21, m12 n22, m12 n23, m12 n24,
+ // -> m22 n21, m22 n22, m22 n23, m22 n24
+ tmp = _mm256_add_ps(_mm256_mul_ps(_mm256_shuffle_ps(otherCol12, otherCol12, 0x55), _mm256_broadcast_ps(&col2)), tmp);
+
+ // m11 m12 m13 m14 m11 m12 m13 m14 shuffled with 10 10 10 10
+ // m13 m13 m13 m13, m23 m23 m23 m23
+
+ // Multiplying with other.col3
+ // -> m13 n31, m13 n32, m13 n33, m13 n34
+ // -> m23 n31, m23 n32, m23 n33, m23 n34
+ tmp = _mm256_add_ps(_mm256_mul_ps(_mm256_shuffle_ps(otherCol12, otherCol12, 0xaa), _mm256_broadcast_ps(&col3)), tmp);
+
+ // m11 m12 m13 m14 m11 m12 m13 m14 shuffled with 11 11 11 11
+ // m14 m14 m14 m14 m24 m24 m24 m24
+
+ // -> m14 n41, m14 n42, m14 n43, m14 n44
+ // -> m24 n41, m24 n42, m24 n43, m24 n44
+ tmp = _mm256_add_ps(_mm256_mul_ps(_mm256_shuffle_ps(otherCol12, otherCol12, 0xff), _mm256_broadcast_ps(&col4)), tmp);
+
+ // Which finally gives
+ // c11 -> m11 n11 + m12 n21 + m13 n31 + m14 n41,
+ // c12 -> m11 n12 + m12 n22 + m13 n32 + m14 n42
+ // c13 -> m11 n13 + m12 n23 + m13 n33 + m14 n43
+ // c14 -> m11 n14 + m12 n24 + m13 n34 + m14 n44
+
+ // c21 -> m21 n11 + m22 n21 + m23 n31 + m24 n41,
+ // c12 -> m21 n12 + m22 n22 + m23 n32 + m24 n42
+ // c13 -> m21 n13 + m22 n23 + m23 n33 + m24 n43
+ // c14 -> m21 n14 + m22 n24 + m23 n34 + m24 n44
+
+ __m256 tmp2 = _mm256_mul_ps(_mm256_shuffle_ps(otherCol34, otherCol34, 0x00), _mm256_broadcast_ps(&col1));
+ tmp2 = _mm256_add_ps(_mm256_mul_ps(_mm256_shuffle_ps(otherCol34, otherCol34, 0x55), _mm256_broadcast_ps(&col2)), tmp2);
+ tmp2 = _mm256_add_ps(_mm256_mul_ps(_mm256_shuffle_ps(otherCol34, otherCol34, 0xaa), _mm256_broadcast_ps(&col3)), tmp2);
+ tmp2 = _mm256_add_ps(_mm256_mul_ps(_mm256_shuffle_ps(otherCol34, otherCol34, 0xff), _mm256_broadcast_ps(&col4)), tmp2);
+
+ Matrix4x4_AVX2 c(Qt::Uninitialized);
+ c.m_col12 = tmp;
+ c.m_col34 = tmp2;
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 operator-(const Matrix4x4_AVX2 &other) const
+ {
+ Matrix4x4_AVX2 c(Qt::Uninitialized);
+
+ c.m_col12 = _mm256_sub_ps(m_col12, other.m_col12);
+ c.m_col34 = _mm256_sub_ps(m_col34, other.m_col34);
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 operator+(const Matrix4x4_AVX2 &other) const
+ {
+ Matrix4x4_AVX2 c(Qt::Uninitialized);
+
+ c.m_col12 = _mm256_add_ps(m_col12, other.m_col12);
+ c.m_col34 = _mm256_add_ps(m_col34, other.m_col34);
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 &operator*=(const Matrix4x4_AVX2 &other)
+ {
+ *this = *this * other;
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 &operator-=(const Matrix4x4_AVX2 &other)
+ {
+ *this = *this - other;
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 &operator+=(const Matrix4x4_AVX2 &other)
+ {
+ *this = *this + other;
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 transposed() const
+ {
+ Matrix4x4_AVX2 c(Qt::Uninitialized);
+ const __m128 col1 = _mm256_extractf128_ps(m_col12, 0);
+ const __m128 col2 = _mm256_extractf128_ps(m_col12, 1);
+ const __m128 col3 = _mm256_extractf128_ps(m_col34, 0);
+ const __m128 col4 = _mm256_extractf128_ps(m_col34, 1);
+
+ // ~117 instructions
+ // Matrix4x4_AVX2 c = *this;
+ // _MM_TRANSPOSE4_PS(c.m_col1, c.m_col2, c.m_col3, c.m_col4);
+
+ // ~131 instructions - AVX2
+ // const __m256i indexes = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0);
+ // c.m_col12 = _mm256_permutevar8x32_ps(_mm256_unpacklo_ps(m_col12, m_col34), indexes);
+ // c.m_col34 = _mm256_permutevar8x32_ps(_mm256_unpackhi_ps(m_col12, m_col34), indexes);
+
+ // ~193 instructions
+ // c.m_col12 = _mm256_setr_ps(m_m11, m_m21, m_m31, m_m41, m_m12, m_m22, m_m32, m_m42);
+ // c.m_col34 = _mm256_setr_ps(m_m13, m_m23, m_m33, m_m43, m_m14, m_m24, m_m34, m_m44);
+
+ // ~113 instructions
+ // union {
+ // struct
+ // {
+ // __m256 twin;
+ // };
+ // struct
+ // {
+ // __m128 col1;
+ // __m128 col2;
+ // };
+ // } u;
+
+ // u.twin = _mm256_shuffle_ps(m_col12, m_col34, 0b01000100);
+ // c.m_col1 = _mm_permute_ps(_mm_shuffle_ps(u.col1, u.col2, 0b10001000), 0b11011000);
+ // c.m_col2 = _mm_permute_ps(_mm_shuffle_ps(u.col1, u.col2, 0b11011101), 0b11011000);
+
+ // u.twin = _mm256_shuffle_ps(m_col12, m_col34, 0b11101110);
+ // c.m_col3 = _mm_permute_ps(_mm_shuffle_ps(u.col1, u.col2, 0b10001000), 0b11011000);
+ // c.m_col4 = _mm_permute_ps(_mm_shuffle_ps(u.col1, u.col2, 0b11011101), 0b11011000);
+
+ // ~113 instructions
+ // 0b11011101 == 0xdd
+ // 0b10001000 == 0x88
+ const __m128 tmp1 = _mm_shuffle_ps(col1, col2, 0xdd);
+ const __m128 tmp2 = _mm_shuffle_ps(col1, col2, 0x88);
+ const __m128 tmp3 = _mm_shuffle_ps(col3, col4, 0xdd);
+ const __m128 tmp4 = _mm_shuffle_ps(col3, col4, 0x88);
+ c.m_col12 = _mm256_set_m128(_mm_shuffle_ps(tmp1, tmp3, 0x88), _mm_shuffle_ps(tmp2, tmp4, 0x88));
+ c.m_col34 = _mm256_set_m128(_mm_shuffle_ps(tmp1, tmp3, 0xdd), _mm_shuffle_ps(tmp2, tmp4, 0xdd));
+
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_AVX2 inverted() const
+ {
+ // TO DO: Optimize
+ const QMatrix4x4 mat = toQMatrix4x4();
+ return Matrix4x4_AVX2(mat.inverted());
+ }
+
+ Q_ALWAYS_INLINE bool operator==(const Matrix4x4_AVX2 &other) const
+ {
+ // cmp returns (-1, -1, -1, -1, -1, -1, -1, -1) if the two m256 are equals
+ // movemask takes the most significant bits (8x 1 in this case) which equals 0xff
+ return (_mm256_movemask_ps(_mm256_cmp_ps(m_col12, other.m_col12, _CMP_EQ_OQ)) == 0xff &&
+ _mm256_movemask_ps(_mm256_cmp_ps(m_col34, other.m_col34, _CMP_EQ_OQ)) == 0xff);
+
+ }
+
+ Q_ALWAYS_INLINE bool operator!=(const Matrix4x4_AVX2 &other) const
+ {
+ return !(*this == other);
+ }
+
+ // For some reason _mm256_cvtss_f32 doesn't seem to be defined
+ Q_ALWAYS_INLINE float m11() const { return _mm_cvtss_f32(_mm256_extractf128_ps(m_col12, 0)); }
+ Q_ALWAYS_INLINE float m12() const { return _mm_cvtss_f32(_mm256_extractf128_ps(m_col12, 1)); }
+ Q_ALWAYS_INLINE float m13() const { return _mm_cvtss_f32(_mm256_extractf128_ps(m_col34, 0)); }
+ Q_ALWAYS_INLINE float m14() const { return _mm_cvtss_f32(_mm256_extractf128_ps(m_col34, 1)); }
+
+ Q_ALWAYS_INLINE float m21() const
+ {
+ // 0b01010101 = 0x55
+ const __m128 v = _mm256_extractf128_ps(m_col12, 0);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0x55));
+ }
+ Q_ALWAYS_INLINE float m22() const
+ {
+ // 0b01010101 = 0x55
+ const __m128 v = _mm256_extractf128_ps(m_col12, 1);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0x55));
+ }
+ Q_ALWAYS_INLINE float m23() const
+ {
+ // 0b01010101 = 0x55
+ const __m128 v = _mm256_extractf128_ps(m_col34, 0);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0x55));
+ }
+ Q_ALWAYS_INLINE float m24() const
+ {
+ // 0b01010101 = 0x55
+ const __m128 v = _mm256_extractf128_ps(m_col34, 1);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0x55));
+ }
+
+ Q_ALWAYS_INLINE float m31() const
+ {
+ // 0b10101010 = 0xaa
+ const __m128 v = _mm256_extractf128_ps(m_col12, 0);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xaa));
+ }
+ Q_ALWAYS_INLINE float m32() const
+ {
+ // 0b10101010 = 0xaa
+ const __m128 v = _mm256_extractf128_ps(m_col12, 1);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xaa));
+ }
+ Q_ALWAYS_INLINE float m33() const
+ {
+ // 0b10101010 = 0xaa
+ const __m128 v = _mm256_extractf128_ps(m_col34, 0);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xaa));
+ }
+ Q_ALWAYS_INLINE float m34() const
+ {
+ // 0b10101010 = 0xaa
+ const __m128 v = _mm256_extractf128_ps(m_col34, 1);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xaa));
+ }
+
+ Q_ALWAYS_INLINE float m41() const
+ {
+ // 0b11111111 = 0xff
+ const __m128 v = _mm256_extractf128_ps(m_col12, 0);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xff));
+ }
+ Q_ALWAYS_INLINE float m42() const
+ {
+ // 0b11111111 = 0xff
+ const __m128 v = _mm256_extractf128_ps(m_col12, 1);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xff));
+ }
+ Q_ALWAYS_INLINE float m43() const
+ {
+ // 0b11111111 = 0xff
+ const __m128 v = _mm256_extractf128_ps(m_col34, 0);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xff));
+ }
+ Q_ALWAYS_INLINE float m44() const
+ {
+ // 0b11111111 = 0xff
+ const __m128 v = _mm256_extractf128_ps(m_col34, 1);
+ return _mm_cvtss_f32(_mm_shuffle_ps(v, v, 0xff));
+ }
+ Q_ALWAYS_INLINE QMatrix4x4 toQMatrix4x4() const { return QMatrix4x4(m11(), m12(), m13(), m14(),
+ m21(), m22(), m23(), m24(),
+ m31(), m32(), m33(), m34(),
+ m41(), m42(), m43(), m44()); }
+
+ Q_ALWAYS_INLINE Vector4D row(int index) const
+ {
+ switch (index) {
+ case 0:
+ return Vector4D(m11(), m12(), m13(), m14());
+ case 1:
+ return Vector4D(m21(), m22(), m23(), m24());
+ case 2:
+ return Vector4D(m31(), m32(), m33(), m34());
+ case 3:
+ return Vector4D(m41(), m42(), m43(), m44());
+ default:
+ Q_UNREACHABLE();
+ return Vector4D();
+ }
+ }
+
+ Q_ALWAYS_INLINE Vector4D column(int index) const
+ {
+ Vector4D c(Qt::Uninitialized);
+ switch (index) {
+ case 0:
+ c.m_xyzw = _mm256_extractf128_ps(m_col12, 0);
+ break;
+ case 1:
+ c.m_xyzw = _mm256_extractf128_ps(m_col12, 1);
+ break;
+ case 2:
+ c.m_xyzw = _mm256_extractf128_ps(m_col34, 0);
+ break;
+ case 3:
+ c.m_xyzw = _mm256_extractf128_ps(m_col34, 1);
+ break;
+ default:
+ Q_UNREACHABLE();
+ return Vector4D();
+ }
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE map(const Vector3D_SSE &point) const
+ {
+ return *this * point;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE map(const Vector4D_SSE &point) const
+ {
+ return *this * point;
+ }
+
+ Vector3D_SSE mapVector(const Vector3D_SSE &vector) const
+ {
+ const __m128 row1 = _mm_set_ps(0.0f, m13(), m12(), m11());
+ const __m128 row2 = _mm_set_ps(0.0f, m23(), m22(), m21());
+ const __m128 row3 = _mm_set_ps(0.0f, m33(), m32(), m31());
+
+ const __m128 tmp = _mm_add_ps(_mm_mul_ps(vector.m_xyzw, row1), _mm_mul_ps(vector.m_xyzw, row2));
+
+ Vector3D_SSE v(Qt::Uninitialized);
+ v.m_xyzw = _mm_add_ps(tmp, _mm_mul_ps(vector.m_xyzw, row3));
+ return v;
+ }
+
+ friend Vector4D operator*(const Vector4D &vector, const Matrix4x4_AVX2 &matrix);
+ friend Vector4D operator*(const Matrix4x4_AVX2 &matrix, const Vector4D &vector);
+
+ friend Vector3D operator*(const Vector3D &vector, const Matrix4x4_AVX2 &matrix);
+ friend Vector3D operator*(const Matrix4x4_AVX2 &matrix, const Vector3D &vector);
+
+ friend QT3DCORE_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Matrix4x4_AVX2 &m);
+private:
+ // column major order
+ // aligned on 32 bytes boundaries for AVX, compatible with 16 bytes boundary for SSE
+ // union Q_DECL_ALIGN(32)
+ // {
+ // float m[16];
+ // struct
+ // {
+ // float m_m11, m_m21, m_m31, m_m41;
+ // float m_m12, m_m22, m_m32, m_m42;
+ // float m_m13, m_m23, m_m33, m_m43;
+ // float m_m14, m_m24, m_m34, m_m44;
+ // };
+ // };
+ __m256 m_col12;
+ __m256 m_col34;
+};
+
+Q_ALWAYS_INLINE Vector4D operator*(const Vector4D &vector, const Matrix4x4_AVX2 &matrix)
+{
+ const __m256 vecMultiplier = _mm256_broadcast_ps(&vector.m_xyzw);
+ // a1 a2 a3 a4 b1 b2 b3 b4, c1 c2 c3 c4 d1 d2 d3 d4
+ // a1 + a2, a3 + a4, c1 + c2, c3 + c4
+ // b1 + b2, b3 + b3, d1 + d2, d3 + d4
+
+ const __m256 partialSum = _mm256_hadd_ps(_mm256_mul_ps(matrix.m_col12, vecMultiplier),
+ _mm256_mul_ps(matrix.m_col34, vecMultiplier));
+
+ Vector4D v(Qt::Uninitialized);
+ // a12 + a34, b12 + b34, c12 + c34, d12 + d34
+ // _mm256_permute4x64_pd is AVX2
+ // 0b11011000 == 0xd8
+ const __m256 shuffledSum = _mm256_castpd_ps(_mm256_permute4x64_pd(_mm256_castps_pd(partialSum), 0xd8));
+ v.m_xyzw = _mm_hadd_ps(_mm256_extractf128_ps(shuffledSum, 0), _mm256_extractf128_ps(shuffledSum, 1));
+ return v;
+}
+
+Q_ALWAYS_INLINE Vector4D operator*(const Matrix4x4_AVX2 &matrix, const Vector4D &vector)
+{
+ const Matrix4x4_AVX2 transposed = matrix.transposed();
+ return vector * transposed;
+}
+
+Q_ALWAYS_INLINE Vector3D operator*(const Vector3D &vector, const Matrix4x4_AVX2 &matrix)
+{
+ const __m128 vec4 = _mm_set_ps(1.0f, vector.z(), vector.y(), vector.x());
+ const __m256 vecMultiplier = _mm256_broadcast_ps(&vec4);
+ // a1 a2 a3 a4 b1 b2 b3 b4, c1 c2 c3 c4 d1 d2 d3 d4
+ // a1 + a2, a3 + a4, c1 + c2, c3 + c4
+ // b1 + b2, b3 + b3, d1 + d2, d3 + d4
+ const __m256 partialSum = _mm256_hadd_ps(_mm256_mul_ps(matrix.m_col12, vecMultiplier),
+ _mm256_mul_ps(matrix.m_col34, vecMultiplier));
+
+ // _mm256_permute4x64_pd is AVX2
+ // 0b11011000 == 0xd8
+ const __m256 shuffledSum = _mm256_castpd_ps(_mm256_permute4x64_pd(_mm256_castps_pd(partialSum), 0xd8));
+ // a12 + a34, b12 + b34, c12 + c34, d12 + d34
+ const __m128 result = _mm_hadd_ps(_mm256_extractf128_ps(shuffledSum, 0), _mm256_extractf128_ps(shuffledSum, 1));
+ // 0b11111111 = 0xff
+ const __m128 divisor = _mm_shuffle_ps(result, result, 0xff);
+
+ Vector3D v(Qt::Uninitialized);
+ v.m_xyzw = _mm_div_ps(result, divisor);;
+ return v;
+}
+
+Q_ALWAYS_INLINE Vector3D operator*(const Matrix4x4_AVX2 &matrix, const Vector3D &vector)
+{
+ const Matrix4x4_AVX2 transposed = matrix.transposed();
+ return vector * transposed;
+}
+
+} // Qt3DCore
+
+Q_DECLARE_TYPEINFO(Qt3DCore::Matrix4x4_AVX2, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DCore::Matrix4x4_AVX2)
+
+#endif // QT_COMPILER_SUPPORTS_AVX
+
+#endif // QT3DCORE_MATRIX4X4_AVX2_P_H
diff --git a/src/core/transforms/matrix4x4_p.h b/src/core/transforms/matrix4x4_p.h
new file mode 100644
index 000000000..1aded0f9c
--- /dev/null
+++ b/src/core/transforms/matrix4x4_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_MATRIX4X4_P_H
+#define QT3DCORE_MATRIX4X4_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <private/qsimd_p.h>
+
+#if defined(__AVX2__) && defined(QT_COMPILER_SUPPORTS_AVX2)
+
+#include <Qt3DCore/private/matrix4x4_avx2_p.h>
+
+QT_BEGIN_NAMESPACE
+using Matrix4x4 = Qt3DCore::Matrix4x4_AVX2;
+QT_END_NAMESPACE
+
+#elif defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSE2)
+
+#include <Qt3DCore/private/matrix4x4_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+using Matrix4x4 = Qt3DCore::Matrix4x4_SSE;
+QT_END_NAMESPACE
+
+#else
+
+#include <QMatrix4x4>
+
+QT_BEGIN_NAMESPACE
+using Matrix4x4 = QMatrix4x4;
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QT3DCORE_MATRIX4X4_P_H
diff --git a/src/core/transforms/matrix4x4_sse.cpp b/src/core/transforms/matrix4x4_sse.cpp
new file mode 100644
index 000000000..d35cc2e35
--- /dev/null
+++ b/src/core/transforms/matrix4x4_sse.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "matrix4x4_sse_p.h"
+
+#ifdef QT_COMPILER_SUPPORTS_SSE2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QDebug operator<<(QDebug dbg, const Matrix4x4_SSE &m)
+{
+ dbg.nospace() << "Matrix4x4_SSE(" << endl
+ << qSetFieldWidth(10)
+ << m.m11() << m.m12() << m.m13() << m.m14() << endl
+ << m.m21() << m.m22() << m.m23() << m.m24() << endl
+ << m.m31() << m.m32() << m.m33() << m.m34() << endl
+ << m.m41() << m.m42() << m.m43() << m.m44() << endl
+ << qSetFieldWidth(0) << ')';
+ return dbg;
+}
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT_COMPILER_SUPPORTS_SSE2
diff --git a/src/core/transforms/matrix4x4_sse_p.h b/src/core/transforms/matrix4x4_sse_p.h
new file mode 100644
index 000000000..be314ca4d
--- /dev/null
+++ b/src/core/transforms/matrix4x4_sse_p.h
@@ -0,0 +1,504 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_MATRIX4X4_SSE_P_H
+#define QT3DCORE_MATRIX4X4_SSE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <Qt3DCore/private/vector4d_p.h>
+#include <Qt3DCore/private/vector3d_p.h>
+#include <private/qsimd_p.h>
+#include <QMatrix4x4>
+
+#ifdef QT_COMPILER_SUPPORTS_SSE2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class Matrix4x4_SSE
+{
+public:
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE() { setToIdentity(); }
+ explicit Q_ALWAYS_INLINE Matrix4x4_SSE(Qt::Initialization) {}
+
+ // QMatrix4x4::constData returns in column major order
+ explicit Q_ALWAYS_INLINE Matrix4x4_SSE(const QMatrix4x4 &mat)
+ {
+ // data may not be properly aligned, using unaligned loads
+ const float *data = mat.constData();
+ m_col1 = _mm_loadu_ps(data);
+ m_col2 = _mm_loadu_ps(data + 4);
+ m_col3 = _mm_loadu_ps(data + 8);
+ m_col4 = _mm_loadu_ps(data + 12);
+ }
+
+ // Assumes data is 16 bytes aligned (and in column major order)
+ explicit Q_ALWAYS_INLINE Matrix4x4_SSE(float *data)
+ {
+ m_col1 = _mm_load_ps(data);
+ m_col2 = _mm_load_ps(data + 4);
+ m_col3 = _mm_load_ps(data + 8);
+ m_col4 = _mm_load_ps(data + 12);
+ }
+
+ // In (row major) but we store in column major order
+ explicit Q_ALWAYS_INLINE Matrix4x4_SSE(float m11, float m12, float m13, float m14,
+ float m21, float m22, float m23, float m24,
+ float m31, float m32, float m33, float m34,
+ float m41, float m42, float m43, float m44)
+ {
+ m_col1 = _mm_set_ps(m41, m31, m21, m11);
+ m_col2 = _mm_set_ps(m42, m32, m22, m12);
+ m_col3 = _mm_set_ps(m43, m33, m23, m13);
+ m_col4 = _mm_set_ps(m44, m34, m24, m14);
+ }
+
+ Q_ALWAYS_INLINE void setToIdentity()
+ {
+ m_col1 = _mm_set_ss(1.0f);
+ m_col2 = _mm_set_ps(0.0f, 0.0f, 1.0f, 0.0f);
+ m_col3 = _mm_set_ps(0.0f, 1.0f, 0.0f, 0.0f);
+ m_col4 = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f);
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE operator*(const Matrix4x4_SSE &other) const
+ {
+ Matrix4x4_SSE c(Qt::Uninitialized);
+
+ const __m128 c1 = m_col1;
+ const __m128 c2 = m_col2;
+ const __m128 c3 = m_col3;
+ const __m128 c4 = m_col4;
+
+ // c11, c21, c31, c41
+ // 1) (m11 x n11), (m11 x n21), (m11 x n31), (m11 x n41)
+ // 2) (m11 x n11) + (m21 x n12), (m11 x n21) + (m21 x n22), (m11 x n31) + (m21 x n32), (m11 x n41) + (m21 x n42)
+ // 3) (m11 x n11) + (m21 x n21) + (m31 x n13), (m11 x n21) + (m21 x n22) + (m31 x n 23), (m11 x n31) + (m21 x n32) + (m31 x n33), (m11 x n41) + (m21 x n42) (m31 x n43)
+ // 4) (m11 x n11) + (m21 x n21) + (m31 x n13) + (m41 x n14), (m11 x n21) + (m21 x n22) + (m31 x n 23) + (m41 x n24), (m11 x n31) + (m21 x n32) + (m31 x n33) + (m41 x n34), (m11 x n41) + (m21 x n42) (m31 x n43) + (m41 x n44)
+ __m128 tmp = _mm_mul_ps(_mm_set1_ps(other.m11()), c1);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m21()), c2), tmp);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m31()), c3), tmp);
+ c.m_col1 = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m41()), c4), tmp);
+
+ // c21, c22, c23, c24
+ tmp = _mm_mul_ps(_mm_set1_ps(other.m12()), c1);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m22()), c2), tmp);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m32()), c3), tmp);
+ c.m_col2 = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m42()), c4), tmp);
+
+ // c31, c32, c33, c34
+ tmp = _mm_mul_ps(_mm_set1_ps(other.m13()), c1);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m23()), c2), tmp);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m33()), c3), tmp);
+ c.m_col3 = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m43()), c4), tmp);
+
+ // c41, c42, c43, c44
+ tmp = _mm_mul_ps(_mm_set1_ps(other.m14()), c1);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m24()), c2), tmp);
+ tmp = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m34()), c3), tmp);
+ c.m_col4 = _mm_add_ps(_mm_mul_ps(_mm_set1_ps(other.m44()), c4), tmp);
+
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE operator-(const Matrix4x4_SSE &other) const
+ {
+ Matrix4x4_SSE c(Qt::Uninitialized);
+
+ c.m_col1 = _mm_sub_ps(m_col1, other.m_col1);
+ c.m_col2 = _mm_sub_ps(m_col2, other.m_col2);
+ c.m_col3 = _mm_sub_ps(m_col3, other.m_col3);
+ c.m_col4 = _mm_sub_ps(m_col4, other.m_col4);
+
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE operator+(const Matrix4x4_SSE &other) const
+ {
+ Matrix4x4_SSE c(Qt::Uninitialized);
+
+ c.m_col1 = _mm_add_ps(m_col1, other.m_col1);
+ c.m_col2 = _mm_add_ps(m_col2, other.m_col2);
+ c.m_col3 = _mm_add_ps(m_col3, other.m_col3);
+ c.m_col4 = _mm_add_ps(m_col4, other.m_col4);
+
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE &operator*=(const Matrix4x4_SSE &other)
+ {
+ *this = *this * other;
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE &operator-=(const Matrix4x4_SSE &other)
+ {
+ *this = *this - other;
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE &operator+=(const Matrix4x4_SSE &other)
+ {
+ *this = *this + other;
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE transposed() const
+ {
+ Matrix4x4_SSE c(Qt::Uninitialized);
+
+ // ~113 instructions
+ // 0b11011101 == 0xdd
+ // 0b10001000 == 0x88
+ const __m128 tmp1 = _mm_shuffle_ps(m_col1, m_col2, 0xdd);
+ const __m128 tmp2 = _mm_shuffle_ps(m_col1, m_col2, 0x88);
+ const __m128 tmp3 = _mm_shuffle_ps(m_col3, m_col4, 0xdd);
+ const __m128 tmp4 = _mm_shuffle_ps(m_col3, m_col4, 0x88);
+ c.m_col1 = _mm_shuffle_ps(tmp2, tmp4, 0x88);
+ c.m_col2 = _mm_shuffle_ps(tmp1, tmp3, 0x88);
+ c.m_col3 = _mm_shuffle_ps(tmp2, tmp4, 0xdd);
+ c.m_col4 = _mm_shuffle_ps(tmp1, tmp3, 0xdd);
+
+ return c;
+ }
+
+ Q_ALWAYS_INLINE Matrix4x4_SSE inverted() const
+ {
+ // TO DO: Optimize
+ const QMatrix4x4 mat = toQMatrix4x4();
+ return Matrix4x4_SSE(mat.inverted());
+ }
+
+ Q_ALWAYS_INLINE bool operator==(const Matrix4x4_SSE &other) const
+ {
+ // 0b1111 == 0xf
+ return (_mm_movemask_ps(_mm_cmpeq_ps(m_col1, other.m_col1)) == 0xf &&
+ _mm_movemask_ps(_mm_cmpeq_ps(m_col2, other.m_col2)) == 0xf &&
+ _mm_movemask_ps(_mm_cmpeq_ps(m_col3, other.m_col3)) == 0xf &&
+ _mm_movemask_ps(_mm_cmpeq_ps(m_col4, other.m_col4)) == 0xf);
+ }
+
+ Q_ALWAYS_INLINE bool operator!=(const Matrix4x4_SSE &other) const
+ {
+ return !(*this == other);
+ }
+
+ Q_ALWAYS_INLINE float m11() const { return _mm_cvtss_f32(m_col1); }
+ Q_ALWAYS_INLINE float m12() const { return _mm_cvtss_f32(m_col2); }
+ Q_ALWAYS_INLINE float m13() const { return _mm_cvtss_f32(m_col3); }
+ Q_ALWAYS_INLINE float m14() const { return _mm_cvtss_f32(m_col4); }
+
+ Q_ALWAYS_INLINE float m21() const
+ {
+ // 0b01010101 = 0x55
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col1, m_col1, 0x55));
+ }
+ Q_ALWAYS_INLINE float m22() const
+ {
+ // 0b01010101 = 0x55
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col2, m_col2, 0x55));
+ }
+ Q_ALWAYS_INLINE float m23() const
+ {
+ // 0b01010101 = 0x55
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col3, m_col3, 0x55));
+ }
+ Q_ALWAYS_INLINE float m24() const
+ {
+ // 0b01010101 = 0x55
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col4, m_col4, 0x55));
+ }
+
+ Q_ALWAYS_INLINE float m31() const
+ {
+ // 0b10101010 = 0xaa
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col1, m_col1, 0xaa));
+ }
+ Q_ALWAYS_INLINE float m32() const
+ {
+ // 0b10101010 = 0xaa
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col2, m_col2, 0xaa));
+ }
+ Q_ALWAYS_INLINE float m33() const
+ {
+ // 0b10101010 = 0xaa
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col3, m_col3, 0xaa));
+ }
+ Q_ALWAYS_INLINE float m34() const
+ {
+ // 0b10101010 = 0xaa
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col4, m_col4, 0xaa));
+ }
+
+ Q_ALWAYS_INLINE float m41() const
+ {
+ // 0b11111111 = 0xff
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col1, m_col1, 0xff));
+ }
+ Q_ALWAYS_INLINE float m42() const
+ {
+ // 0b11111111 = 0xff
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col2, m_col2, 0xff));
+ }
+ Q_ALWAYS_INLINE float m43() const
+ {
+ // 0b11111111 = 0xff
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col3, m_col3, 0xff));
+ }
+ Q_ALWAYS_INLINE float m44() const
+ {
+ // 0b11111111 = 0xff
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_col4, m_col4, 0xff));
+ }
+
+ Q_ALWAYS_INLINE Vector4D row(int index) const
+ {
+ switch (index) {
+ case 0:
+ return Vector4D(m11(), m12(), m13(), m14());
+ case 1:
+ return Vector4D(m21(), m22(), m23(), m24());
+ case 2:
+ return Vector4D(m31(), m32(), m33(), m34());
+ case 3:
+ return Vector4D(m41(), m42(), m43(), m44());
+ default:
+ Q_UNREACHABLE();
+ return Vector4D();
+ }
+ }
+
+ Q_ALWAYS_INLINE Vector4D column(int index) const
+ {
+ Vector4D c(Qt::Uninitialized);
+ switch (index) {
+ case 0:
+ c.m_xyzw = m_col1;
+ break;
+ case 1:
+ c.m_xyzw = m_col2;
+ break;
+ case 2:
+ c.m_xyzw = m_col3;
+ break;
+ case 3:
+ c.m_xyzw = m_col4;
+ break;
+ default:
+ Q_UNREACHABLE();
+ return Vector4D();
+ }
+ return c;
+ }
+
+ Q_ALWAYS_INLINE QMatrix4x4 toQMatrix4x4() const { return QMatrix4x4(m11(), m12(), m13(), m14(),
+ m21(), m22(), m23(), m24(),
+ m31(), m32(), m33(), m34(),
+ m41(), m42(), m43(), m44()); }
+
+ Q_ALWAYS_INLINE Vector3D_SSE map(const Vector3D_SSE &point) const
+ {
+ return *this * point;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE map(const Vector4D_SSE &point) const
+ {
+ return *this * point;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE mapVector(const Vector3D_SSE &vector) const
+ {
+ const __m128 row1 = _mm_set_ps(0.0f, m13(), m12(), m11());
+ const __m128 row2 = _mm_set_ps(0.0f, m23(), m22(), m21());
+ const __m128 row3 = _mm_set_ps(0.0f, m33(), m32(), m31());
+
+ const __m128 tmp = _mm_add_ps(_mm_mul_ps(vector.m_xyzw, row1), _mm_mul_ps(vector.m_xyzw, row2));
+
+ Vector3D_SSE v(Qt::Uninitialized);
+ v.m_xyzw = _mm_add_ps(tmp, _mm_mul_ps(vector.m_xyzw, row3));
+ return v;
+ }
+
+ friend Q_ALWAYS_INLINE Vector4D operator*(const Vector4D &vector, const Matrix4x4_SSE &matrix);
+ friend Q_ALWAYS_INLINE Vector4D operator*(const Matrix4x4_SSE &matrix, const Vector4D &vector);
+
+ friend Q_ALWAYS_INLINE Vector3D operator*(const Vector3D &vector, const Matrix4x4_SSE &matrix);
+ friend Q_ALWAYS_INLINE Vector3D operator*(const Matrix4x4_SSE &matrix, const Vector3D &vector);
+
+ friend QT3DCORE_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Matrix4x4_SSE &m);
+private:
+ // Internally we will store the matrix as indicated below
+ // Q_DECL_ALIGN(16) // aligned on 16 bytes boundary for SSE (column major)
+ // struct
+ // {
+ // float m_m11, m_m21, m_m31, m_m41;
+ // float m_m12, m_m22, m_m32, m_m42;
+ // float m_m13, m_m23, m_m33, m_m43;
+ // float m_m14, m_m24, m_m34, m_m44;
+ // };
+ // struct
+ // {
+ // float m[16];
+ // };
+ __m128 m_col1;
+ __m128 m_col2;
+ __m128 m_col3;
+ __m128 m_col4;
+};
+
+Q_ALWAYS_INLINE Vector4D operator*(const Vector4D &vector, const Matrix4x4_SSE &matrix)
+{
+ const __m128 vCol1 = _mm_mul_ps(matrix.m_col1, vector.m_xyzw);
+ const __m128 vCol2 = _mm_mul_ps(matrix.m_col2, vector.m_xyzw);
+ const __m128 vCol3 = _mm_mul_ps(matrix.m_col3, vector.m_xyzw);
+ const __m128 vCol4 = _mm_mul_ps(matrix.m_col4, vector.m_xyzw);
+
+
+ // 0b01000100 == 0x44
+ // 0b11101110 == 0xee
+
+ // vCol1.x, vCol1.y, vCol2.x, vCol2.y
+ __m128 tmp1 = _mm_shuffle_ps(vCol1, vCol2, 0x44);
+ // vCol1.z, vCol1.w, vCol2.z, vCol2.w
+ __m128 tmp2 = _mm_shuffle_ps(vCol1, vCol2, 0xee);
+
+ // vCol1.x + vCol1.z, vCol1.y + vCol1.w, vCol2.x + vCol2.z, vCol2.y + vCol2.w,
+ const __m128 tmpSum01 = _mm_add_ps(tmp1, tmp2);
+
+ // vCol3.x, vCol3.y, vCol4.x, vCol4.y
+ tmp1 = _mm_shuffle_ps(vCol3, vCol4, 0x44);
+ // vCol3.z, vCol3.w, vCol4.z, vCol4.w
+ tmp2 = _mm_shuffle_ps(vCol3, vCol4, 0xee);
+
+ // vCol3.x + vCol3.z, vCol3.y + vCol3.w, vCol4.x + vCol4.z, vCol4.y + vCol4.w,
+ const __m128 tmpSum02 = _mm_add_ps(tmp1, tmp2);
+
+ // 0b10001000 == 0x88
+ // 0b11011101 == 0xdd
+
+ // vCol1.x + vCol1.z, vCol2.x + vCol2.z, vCol3.x + vCol3.z, vCol4.x + vCol4.z,
+ tmp1 = _mm_shuffle_ps(tmpSum01, tmpSum02, 0x88);
+ // vCol1.y + vCol1.w, vCol2.y + vCol2.w, vCol3.y + vCol3.w, vCol4.y + vCol4.w,
+ tmp2 = _mm_shuffle_ps(tmpSum01, tmpSum02, 0xdd);
+
+ Vector4D v(Qt::Uninitialized);
+ v.m_xyzw = _mm_add_ps(tmp1, tmp2);
+ return v;
+}
+
+Q_ALWAYS_INLINE Vector4D operator*(const Matrix4x4_SSE &matrix, const Vector4D &vector)
+{
+ const Matrix4x4_SSE transposed = matrix.transposed();
+ return vector * transposed;
+}
+
+Q_ALWAYS_INLINE Vector3D operator*(const Vector3D &vector, const Matrix4x4_SSE &matrix)
+{
+ const __m128 vec4 = _mm_set_ps(1.0f, vector.z(), vector.y(), vector.x());
+
+ const __m128 vCol1 = _mm_mul_ps(matrix.m_col1, vec4);
+ const __m128 vCol2 = _mm_mul_ps(matrix.m_col2, vec4);
+ const __m128 vCol3 = _mm_mul_ps(matrix.m_col3, vec4);
+ const __m128 vCol4 = _mm_mul_ps(matrix.m_col4, vec4);
+
+ // 0b01000100 == 0x44
+ // 0b11101110 == 0xee
+
+ // vCol1.x, vCol1.y, vCol2.x, vCol2.y
+ __m128 tmp1 = _mm_shuffle_ps(vCol1, vCol2, 0x44);
+ // vCol1.z, vCol1.w, vCol2.z, vCol2.w
+ __m128 tmp2 = _mm_shuffle_ps(vCol1, vCol2, 0xee);
+
+ // vCol1.x + vCol1.z, vCol1.y + vCol1.w, vCol2.x + vCol2.z, vCol2.y + vCol2.w,
+ const __m128 tmpSum01 = _mm_add_ps(tmp1, tmp2);
+
+ // vCol3.x, vCol3.y, vCol4.x, vCol4.y
+ tmp1 = _mm_shuffle_ps(vCol3, vCol4, 0x44);
+ // vCol3.z, vCol3.w, vCol4.z, vCol4.w
+ tmp2 = _mm_shuffle_ps(vCol3, vCol4, 0xee);
+
+ // vCol3.x + vCol3.z, vCol3.y + vCol3.w, vCol4.x + vCol4.z, vCol4.y + vCol4.w,
+ const __m128 tmpSum02 = _mm_add_ps(tmp1, tmp2);
+
+ // 0b10001000 == 0x88
+ // 0b11011101 == 0xdd
+
+ // vCol1.x + vCol1.z, vCol2.x + vCol2.z, vCol3.x + vCol3.z, vCol4.x + vCol4.z,
+ tmp1 = _mm_shuffle_ps(tmpSum01, tmpSum02, 0x88);
+ // vCol1.y + vCol1.w, vCol2.y + vCol2.w, vCol3.y + vCol3.w, vCol4.y + vCol4.w,
+ tmp2 = _mm_shuffle_ps(tmpSum01, tmpSum02, 0xdd);
+
+ const __m128 result = _mm_add_ps(tmp1, tmp2);
+ // 0b11111111 = 0xff
+ const __m128 divisor = _mm_shuffle_ps(result, result, 0xff);
+ Vector3D v(Qt::Uninitialized);
+ v.m_xyzw = _mm_div_ps(result, divisor);
+ return v;
+}
+
+QT3DCORE_PRIVATE_EXPORT Q_ALWAYS_INLINE Vector3D operator*(const Matrix4x4_SSE &matrix, const Vector3D &vector)
+{
+ const Matrix4x4_SSE transposed = matrix.transposed();
+ return vector * transposed;
+}
+
+} // Qt3DCore
+
+
+Q_DECLARE_TYPEINFO(Qt3DCore::Matrix4x4_SSE, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DCore::Matrix4x4_SSE)
+
+#endif // QT_COMPILER_SUPPORTS_SSE2
+
+#endif // QT3DCORE_MATRIX4X4_SSE_P_H
diff --git a/src/core/transforms/qabstractskeleton.cpp b/src/core/transforms/qabstractskeleton.cpp
new file mode 100644
index 000000000..d780987ee
--- /dev/null
+++ b/src/core/transforms/qabstractskeleton.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractskeleton.h"
+#include "qabstractskeleton_p.h"
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QAbstractSkeletonPrivate::QAbstractSkeletonPrivate()
+ : Qt3DCore::QNodePrivate()
+ , m_jointCount(0)
+{
+}
+
+/*!
+ \internal
+ */
+const QAbstractSkeletonPrivate *QAbstractSkeletonPrivate::get(const QAbstractSkeleton *q)
+{
+ return q->d_func();
+}
+
+/*!
+ \qmltype AbstractSkeleton
+ \inqmlmodule Qt3D.Core
+ \inherits Node
+ \instantiates Qt3DCore::QAbstractSkeleton
+ \since 5.10
+ \brief A skeleton contains the joints for a skinned mesh
+
+ Do not use this class directly. You should use SkeletonLoader if loading
+ skeleton data from a file (most likely) or Skeleton if creating the
+ skeleton and skinned mesh data yourself (mainly for people creating
+ editors or tooling).
+*/
+
+/*!
+ \class Qt3DCore::QAbstractSkeleton
+ \inmodule Qt3DCore
+ \inherits Qt3DCore::QNode
+ \since 5.10
+ \brief A skeleton contains the joints for a skinned mesh
+
+ Do not use this class directly. You should use QSkeletonLoader if loading
+ skeleton data from a file (most likely) or QSkeleton if creating the
+ skeleton and skinned mesh data yourself (mainly for people creating
+ editors or tooling).
+*/
+
+/*! \internal */
+QAbstractSkeleton::QAbstractSkeleton(QAbstractSkeletonPrivate &dd, Qt3DCore::QNode *parent)
+ : Qt3DCore::QNode(dd, parent)
+{
+}
+
+/*! \internal */
+QAbstractSkeleton::~QAbstractSkeleton()
+{
+}
+
+/*!
+ \property Qt3DCore::QAbstractSkeleton::jointCount
+
+ Holds the number of joints contained in the skeleton
+*/
+int QAbstractSkeleton::jointCount() const
+{
+ Q_D(const QAbstractSkeleton);
+ return d->m_jointCount;
+}
+
+void QAbstractSkeletonPrivate::setJointCount(int jointCount)
+{
+ Q_Q(QAbstractSkeleton);
+ if (m_jointCount == jointCount)
+ return;
+ m_jointCount = jointCount;
+ const bool block = q->blockNotifications(true);
+ emit q->jointCountChanged(jointCount);
+ q->blockNotifications(block);
+}
+
+void QAbstractSkeleton::sceneChangeEvent(const QSceneChangePtr &change)
+{
+ Q_D(QAbstractSkeleton);
+ if (change->type() == Qt3DCore::PropertyUpdated) {
+ const Qt3DCore::QPropertyUpdatedChangePtr e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change);
+ if (e->propertyName() == QByteArrayLiteral("jointCount"))
+ d->setJointCount(e->value().toInt());
+ }
+}
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
diff --git a/src/core/transforms/qabstractskeleton.h b/src/core/transforms/qabstractskeleton.h
new file mode 100644
index 000000000..3fc13fb70
--- /dev/null
+++ b/src/core/transforms/qabstractskeleton.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QABSTRACTSKELETON_H
+#define QT3DCORE_QABSTRACTSKELETON_H
+
+#include <Qt3DCore/qnode.h>
+#include <Qt3DCore/qt3dcore_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QAbstractSkeletonPrivate;
+
+class QT3DCORESHARED_EXPORT QAbstractSkeleton : public Qt3DCore::QNode
+{
+ Q_OBJECT
+ Q_PROPERTY(int jointCount READ jointCount NOTIFY jointCountChanged)
+
+public:
+ ~QAbstractSkeleton();
+
+ int jointCount() const;
+
+Q_SIGNALS:
+ void jointCountChanged(int jointCount);
+
+protected:
+ QAbstractSkeleton(QAbstractSkeletonPrivate &dd, Qt3DCore::QNode *parent = nullptr);
+ void sceneChangeEvent(const QSceneChangePtr &change) override;
+
+private:
+ Q_DECLARE_PRIVATE(QAbstractSkeleton)
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QABSTRACTSKELETON_H
diff --git a/src/core/transforms/qabstractskeleton_p.h b/src/core/transforms/qabstractskeleton_p.h
new file mode 100644
index 000000000..51d57f8c4
--- /dev/null
+++ b/src/core/transforms/qabstractskeleton_p.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QABSTRACTSKELETON_P_H
+#define QT3DCORE_QABSTRACTSKELETON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qnode_p.h>
+#include <Qt3DCore/private/qskeletoncreatedchange_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QAbstractSkeletonPrivate : public Qt3DCore::QNodePrivate
+{
+public:
+ QAbstractSkeletonPrivate();
+
+ void setJointCount(int jointCount);
+
+ Q_DECLARE_PUBLIC(QAbstractSkeleton)
+ static const QAbstractSkeletonPrivate *get(const QAbstractSkeleton *q);
+
+ QSkeletonCreatedChangeBase::SkeletonType m_type;
+
+ int m_jointCount;
+};
+
+} // namespace Qt3DCore
+
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QABSTRACTSKELETON_P_H
diff --git a/src/core/transforms/qarmature.cpp b/src/core/transforms/qarmature.cpp
new file mode 100644
index 000000000..65a13b262
--- /dev/null
+++ b/src/core/transforms/qarmature.cpp
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qarmature.h"
+#include "qarmature_p.h"
+#include <Qt3DCore/qabstractskeleton.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QArmaturePrivate::QArmaturePrivate()
+ : Qt3DCore::QComponentPrivate()
+ , m_skeleton(nullptr)
+{
+}
+
+/*!
+ \qmltype Armature
+ \inqmlmodule Qt3D.Core
+ \inherits Component3D
+ \instantiates Qt3DCore::QArmature
+ \since 5.10
+ \brief Used to calculate skinning transform matrices and set them on shaders
+
+ The Armature component is aggregated by entities to give them the ability to
+ calculate the palette of skinning transform matrices needed to properly
+ render skinned meshes.
+
+ Each vertex in a skinned mesh is associated (bound) to up to 4 joints in a
+ skeleton. For each joint affecting a vertex the mesh also provides a weight
+ which determines the level of influence of the corresponding joint. The
+ skinning palette used for performing the transformation of skinned vertices
+ is provided by the Armature and is calculated from the joints contained in
+ the referenced skeleton.
+
+ Updating the local transform of a joint results in the skinning matrices
+ being recalculated and the skinned mesh vertices bound to that joint moving
+ accordingly.
+*/
+
+/*!
+ \qmlproperty AbstractSkeleton Armature::skeleton
+
+ Holds the skeleton used to calculate the skinning transform matrix palette.
+*/
+
+/*!
+ \class Qt3DCore::QArmature
+ \inmodule Qt3DCore
+ \inherits Qt3DCore::QComponent
+ \since 5.10
+ \brief Used to calculate skinning transform matrices and set them on shaders
+
+ The Armature component is aggregated by entities to give them the ability to
+ calculate the palette of skinning transform matrices needed to properly
+ render skinned meshes.
+
+ Each vertex in a skinned mesh is associated (bound) to up to 4 joints in a
+ skeleton. For each joint affecting a vertex the mesh also provides a weight
+ which determines the level of influence of the corresponding joint. The
+ skinning palette used for performing the transformation of skinned vertices
+ is provided by the Armature and is calculated from the joints contained in
+ the referenced skeleton.
+
+ Updating the local transform of a joint results in the skinning matrices
+ being recalculated and the skinned mesh vertices bound to that joint moving
+ accordingly.
+*/
+
+/*!
+ Constructs a new QArmature with \a parent.
+*/
+QArmature::QArmature(Qt3DCore::QNode *parent)
+ : Qt3DCore::QComponent(*new QArmaturePrivate, parent)
+{
+}
+
+/*! \internal */
+QArmature::QArmature(QArmaturePrivate &dd, Qt3DCore::QNode *parent)
+ : Qt3DCore::QComponent(dd, parent)
+{
+}
+
+/*! \internal */
+QArmature::~QArmature()
+{
+}
+
+/*!
+ \property Qt3DCore::QArmature::skeleton
+
+ Holds the skeleton used to calculate the skinning transform matrix palette.
+*/
+Qt3DCore::QAbstractSkeleton *QArmature::skeleton() const
+{
+ Q_D(const QArmature);
+ return d->m_skeleton;
+}
+
+void QArmature::setSkeleton(Qt3DCore::QAbstractSkeleton *skeleton)
+{
+ Q_D(QArmature);
+ if (d->m_skeleton != skeleton) {
+ if (d->m_skeleton)
+ d->unregisterDestructionHelper(d->m_skeleton);
+
+ // We need to add it as a child of the current node if it has been declared inline
+ // Or not previously added as a child of the current node so that
+ // 1) The backend gets notified about it's creation
+ // 2) When the current node is destroyed, it gets destroyed as well
+ if (skeleton && !skeleton->parent())
+ skeleton->setParent(this);
+ d->m_skeleton = skeleton;
+
+ // Ensures proper bookkeeping
+ if (d->m_skeleton)
+ d->registerDestructionHelper(d->m_skeleton, &QArmature::setSkeleton, d->m_skeleton);
+
+ emit skeletonChanged(skeleton);
+ }
+}
+
+/*! \internal */
+Qt3DCore::QNodeCreatedChangeBasePtr QArmature::createNodeCreationChange() const
+{
+ auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QArmatureData>::create(this);
+ auto &data = creationChange->data;
+ Q_D(const QArmature);
+ data.skeletonId = qIdForNode(d->m_skeleton);
+ return creationChange;
+}
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
diff --git a/src/core/transforms/qarmature.h b/src/core/transforms/qarmature.h
new file mode 100644
index 000000000..a5ccc9d28
--- /dev/null
+++ b/src/core/transforms/qarmature.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QARMATURE_H
+#define QT3DCORE_QARMATURE_H
+
+#include <Qt3DCore/qcomponent.h>
+#include <Qt3DCore/qt3dcore_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QArmaturePrivate;
+class QAbstractSkeleton;
+
+class QT3DCORESHARED_EXPORT QArmature : public Qt3DCore::QComponent
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt3DCore::QAbstractSkeleton* skeleton READ skeleton WRITE setSkeleton NOTIFY skeletonChanged)
+
+public:
+ explicit QArmature(Qt3DCore::QNode *parent = nullptr);
+ ~QArmature();
+
+ QAbstractSkeleton* skeleton() const;
+
+public Q_SLOTS:
+ void setSkeleton(Qt3DCore::QAbstractSkeleton* skeleton);
+
+Q_SIGNALS:
+ void skeletonChanged(Qt3DCore::QAbstractSkeleton* skeleton);
+
+protected:
+ QArmature(QArmaturePrivate &dd, Qt3DCore::QNode *parent = nullptr);
+
+private:
+ Q_DECLARE_PRIVATE(QArmature)
+ Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QARMATURE_H
diff --git a/src/core/transforms/qarmature_p.h b/src/core/transforms/qarmature_p.h
new file mode 100644
index 000000000..1fc241dc2
--- /dev/null
+++ b/src/core/transforms/qarmature_p.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QARMATURE_P_H
+#define QT3DCORE_QARMATURE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qcomponent_p.h>
+#include <Qt3DCore/qarmature.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QAbstractSkeleton;
+
+class QArmaturePrivate : public Qt3DCore::QComponentPrivate
+{
+public:
+ QArmaturePrivate();
+
+ Q_DECLARE_PUBLIC(QArmature)
+
+ QAbstractSkeleton *m_skeleton;
+};
+
+struct QArmatureData
+{
+ QNodeId skeletonId;
+};
+
+} // namespace Qt3DCore
+
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QARMATURE_P_H
diff --git a/src/core/transforms/qjoint.cpp b/src/core/transforms/qjoint.cpp
new file mode 100644
index 000000000..3f631ec98
--- /dev/null
+++ b/src/core/transforms/qjoint.cpp
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qjoint.h"
+#include "qjoint_p.h"
+
+#include <Qt3DCore/qnodecreatedchange.h>
+#include <Qt3DCore/qpropertynodeaddedchange.h>
+#include <Qt3DCore/qpropertynoderemovedchange.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QJointPrivate::QJointPrivate()
+ : QNodePrivate()
+ , m_inverseBindMatrix()
+ , m_rotation()
+ , m_translation()
+ , m_scale(1.0f, 1.0f, 1.0f)
+{
+}
+
+/*!
+ \qmltype Joint
+ \inqmlmodule Qt3D.Core
+ \inherits Node
+ \instantiates Qt3DCore::QJoint
+ \since 5.10
+ \brief Used to transforms parts of skinned meshes
+
+ The Joint node is used to build skeletons as part of the skinned mesh
+ support in Qt 3D. A joint can be transformed by way of its scale, rotation
+ and translation properties. Any mesh vertices that are bound to the joint
+ will have their transformations updated accordingly.
+*/
+
+/*!
+ \qmlproperty vector3d Joint::scale
+
+ Holds the uniform scale of the joint.
+*/
+
+/*!
+ \qmlproperty quaternion Joint::rotation
+
+ Holds the rotation of the joint as quaternion.
+*/
+
+/*!
+ \qmlproperty vector3d Joint::translation
+
+ Holds the translation of the joint as vector3d.
+*/
+
+/*!
+ \qmlproperty real Joint::rotationX
+
+ Holds the x rotation of the joint as an Euler angle.
+*/
+
+/*!
+ \qmlproperty real Joint::rotationY
+
+ Holds the y rotation of the joint as an Euler angle.
+*/
+
+/*!
+ \qmlproperty real Joint::rotationZ
+
+ Holds the z rotation of the joint as an Euler angle.
+*/
+
+/*!
+ \qmlproperty matrix4x4 inverseBindMatrix
+
+ Holds the inverse bind matrix of the joint. This is used to transform
+ vertices from model space into the space of this joint so they can
+ subsequently be multiplied by the joint's global transform to perform
+ the skinning operation.
+*/
+
+/*!
+ \class Qt3DCore::QJoint
+ \inmodule Qt3DCore
+ \inherits Qt3DCore::QNode
+ \since 5.10
+ \brief Used to transforms parts of skinned meshes
+
+ The QJoint node is used to build skeletons as part of the skinned mesh
+ support in Qt 3D. A joint can be transformed by way of its scale, rotation
+ and translation properties. Any mesh vertices that are bound to the joint
+ will have their transformations updated accordingly.
+*/
+
+/*!
+ Constructs a new QJoint with \a parent.
+*/
+QJoint::QJoint(Qt3DCore::QNode *parent)
+ : QNode(*new QJointPrivate, parent)
+{
+}
+
+/*! \internal */
+QJoint::~QJoint()
+{
+}
+
+/*!
+ \property Qt3DCore::QJoint::scale
+
+ Holds the scale of the joint.
+*/
+QVector3D QJoint::scale() const
+{
+ Q_D(const QJoint);
+ return d->m_scale;
+}
+
+/*!
+ \property Qt3DCore::QJoint::rotation
+
+ Holds the rotation of the joint as QQuaternion.
+*/
+QQuaternion QJoint::rotation() const
+{
+ Q_D(const QJoint);
+ return d->m_rotation;
+}
+
+/*!
+ \property Qt3DCore::QJoint::translation
+
+ Holds the translation of the joint as QVector3D.
+*/
+QVector3D QJoint::translation() const
+{
+ Q_D(const QJoint);
+ return d->m_translation;
+}
+
+/*!
+ \property Qt3DCore::QJoint::inverseBindMatrix
+
+ Holds the inverse bind matrix of the joint. This is used to transform
+ vertices from model space into the space of this joint so they can
+ subsequently be multiplied by the joint's global transform to perform
+ the skinning operation.
+*/
+QMatrix4x4 QJoint::inverseBindMatrix() const
+{
+ Q_D(const QJoint);
+ return d->m_inverseBindMatrix;
+}
+
+/*!
+ \property Qt3DCore::QJoint::rotationX
+
+ Holds the x rotation of the joint as an Euler angle.
+*/
+float QJoint::rotationX() const
+{
+ Q_D(const QJoint);
+ return d->m_eulerRotationAngles.x();
+}
+
+/*!
+ \property Qt3DCore::QJoint::rotationY
+
+ Holds the y rotation of the joint as an Euler angle.
+*/
+float QJoint::rotationY() const
+{
+ Q_D(const QJoint);
+ return d->m_eulerRotationAngles.y();
+}
+
+/*!
+ \property Qt3DCore::QJoint::rotationZ
+
+ Holds the z rotation of the joint as an Euler angle.
+*/
+float QJoint::rotationZ() const
+{
+ Q_D(const QJoint);
+ return d->m_eulerRotationAngles.z();
+}
+
+void QJoint::setScale(const QVector3D &scale)
+{
+ Q_D(QJoint);
+ if (scale == d->m_scale)
+ return;
+
+ d->m_scale = scale;
+ emit scaleChanged(scale);
+}
+
+void QJoint::setRotation(const QQuaternion &rotation)
+{
+ Q_D(QJoint);
+ if (rotation == d->m_rotation)
+ return;
+
+ d->m_rotation = rotation;
+ const QVector3D oldRotation = d->m_eulerRotationAngles;
+ d->m_eulerRotationAngles = d->m_rotation.toEulerAngles();
+ emit rotationChanged(rotation);
+
+ const bool wasBlocked = blockNotifications(true);
+ if (!qFuzzyCompare(d->m_eulerRotationAngles.x(), oldRotation.x()))
+ emit rotationXChanged(d->m_eulerRotationAngles.x());
+ if (!qFuzzyCompare(d->m_eulerRotationAngles.y(), oldRotation.y()))
+ emit rotationYChanged(d->m_eulerRotationAngles.y());
+ if (!qFuzzyCompare(d->m_eulerRotationAngles.z(), oldRotation.z()))
+ emit rotationZChanged(d->m_eulerRotationAngles.z());
+ blockNotifications(wasBlocked);
+}
+
+void QJoint::setTranslation(const QVector3D &translation)
+{
+ Q_D(QJoint);
+ if (translation == d->m_translation)
+ return;
+
+ d->m_translation = translation;
+ emit translationChanged(translation);
+}
+
+void QJoint::setInverseBindMatrix(const QMatrix4x4 &inverseBindMatrix)
+{
+ Q_D(QJoint);
+ if (d->m_inverseBindMatrix == inverseBindMatrix)
+ return;
+
+ d->m_inverseBindMatrix = inverseBindMatrix;
+ emit inverseBindMatrixChanged(inverseBindMatrix);
+}
+
+void QJoint::setRotationX(float rotationX)
+{
+ Q_D(QJoint);
+
+ if (qFuzzyCompare(d->m_eulerRotationAngles.x(), rotationX))
+ return;
+
+ const auto eulers = QVector3D(rotationX,
+ d->m_eulerRotationAngles.y(),
+ d->m_eulerRotationAngles.z());
+ const QQuaternion r = QQuaternion::fromEulerAngles(eulers);
+ setRotation(r);
+}
+
+void QJoint::setRotationY(float rotationY)
+{
+ Q_D(QJoint);
+
+ if (qFuzzyCompare(d->m_eulerRotationAngles.y(), rotationY))
+ return;
+
+ const auto eulers = QVector3D(d->m_eulerRotationAngles.x(),
+ rotationY,
+ d->m_eulerRotationAngles.z());
+ const QQuaternion r = QQuaternion::fromEulerAngles(eulers);
+ setRotation(r);
+}
+
+void QJoint::setRotationZ(float rotationZ)
+{
+ Q_D(QJoint);
+ if (qFuzzyCompare(d->m_eulerRotationAngles.z(), rotationZ))
+ return;
+
+ const auto eulers = QVector3D(d->m_eulerRotationAngles.x(),
+ d->m_eulerRotationAngles.y(),
+ rotationZ);
+ const QQuaternion r = QQuaternion::fromEulerAngles(eulers);
+ setRotation(r);
+}
+
+void QJoint::setName(const QString &name)
+{
+ Q_D(QJoint);
+ if (d->m_name == name)
+ return;
+
+ d->m_name = name;
+ emit nameChanged(name);
+}
+
+void QJoint::setToIdentity()
+{
+ setScale(QVector3D(1.0f, 1.0f, 1.0f));
+ setRotation(QQuaternion());
+ setTranslation(QVector3D());
+}
+
+/*!
+ Adds \a joint as a child of this joint. If \a joint has no parent, then
+ this joint takes ownership of it. Child joints are in the coordinate system
+ of their parent joint.
+*/
+void QJoint::addChildJoint(QJoint *joint)
+{
+ Q_D(QJoint);
+ if (!d->m_childJoints.contains(joint)) {
+ d->m_childJoints.push_back(joint);
+ // Force creation in backend by setting parent
+ if (!joint->parent())
+ joint->setParent(this);
+
+ // Ensures proper bookkeeping
+ d->registerDestructionHelper(joint, &QJoint::removeChildJoint, d->m_childJoints);
+
+ if (d->m_changeArbiter != nullptr) {
+ const auto change = QPropertyNodeAddedChangePtr::create(id(), joint);
+ change->setPropertyName("childJoint");
+ d->notifyObservers(change);
+ }
+ }
+}
+
+/*!
+ Removes \a joint from this joint's list of children. The child joint is not
+ destroyed.
+*/
+void QJoint::removeChildJoint(QJoint *joint)
+{
+ Q_D(QJoint);
+ if (d->m_childJoints.contains(joint)) {
+
+ if (d->m_changeArbiter != nullptr) {
+ const auto change = QPropertyNodeRemovedChangePtr::create(id(), joint);
+ change->setPropertyName("childJoint");
+ d->notifyObservers(change);
+ }
+
+ d->m_childJoints.removeOne(joint);
+
+ // Remove bookkeeping connection
+ d->unregisterDestructionHelper(joint);
+ }
+}
+
+/*!
+ The vector of joints this joint has as children.
+*/
+QVector<QJoint *> QJoint::childJoints() const
+{
+ Q_D(const QJoint);
+ return d->m_childJoints;
+}
+
+QString QJoint::name() const
+{
+ Q_D(const QJoint);
+ return d->m_name;
+}
+
+/*! \internal */
+Qt3DCore::QNodeCreatedChangeBasePtr QJoint::createNodeCreationChange() const
+{
+ auto creationChange = Qt3DCore::QNodeCreatedChangePtr<QJointData>::create(this);
+ auto &data = creationChange->data;
+ Q_D(const QJoint);
+ data.inverseBindMatrix = d->m_inverseBindMatrix;
+ data.childJointIds = qIdsForNodes(d->m_childJoints);
+ data.rotation = d->m_rotation;
+ data.scale = d->m_scale;
+ data.translation = d->m_translation;
+ data.name = d->m_name;
+ return creationChange;
+}
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
diff --git a/src/core/transforms/qjoint.h b/src/core/transforms/qjoint.h
new file mode 100644
index 000000000..fdc3d51a3
--- /dev/null
+++ b/src/core/transforms/qjoint.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QJOINT_H
+#define QT3DCORE_QJOINT_H
+
+#include <Qt3DCore/qnode.h>
+#include <Qt3DCore/qt3dcore_global.h>
+
+#include <QtGui/qmatrix4x4.h>
+#include <QtGui/qquaternion.h>
+#include <QtGui/qvector3d.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QJointPrivate;
+
+class QT3DCORESHARED_EXPORT QJoint : public QNode
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector3D scale READ scale WRITE setScale NOTIFY scaleChanged)
+ Q_PROPERTY(QQuaternion rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+ Q_PROPERTY(QVector3D translation READ translation WRITE setTranslation NOTIFY translationChanged)
+ Q_PROPERTY(QMatrix4x4 inverseBindMatrix READ inverseBindMatrix WRITE setInverseBindMatrix NOTIFY inverseBindMatrixChanged)
+ Q_PROPERTY(float rotationX READ rotationX WRITE setRotationX NOTIFY rotationXChanged)
+ Q_PROPERTY(float rotationY READ rotationY WRITE setRotationY NOTIFY rotationYChanged)
+ Q_PROPERTY(float rotationZ READ rotationZ WRITE setRotationZ NOTIFY rotationZChanged)
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+
+public:
+ explicit QJoint(Qt3DCore::QNode *parent = nullptr);
+ ~QJoint();
+
+ QVector3D scale() const;
+ QQuaternion rotation() const;
+ QVector3D translation() const;
+ QMatrix4x4 inverseBindMatrix() const;
+ float rotationX() const;
+ float rotationY() const;
+ float rotationZ() const;
+ QString name() const;
+
+ void addChildJoint(QJoint *joint);
+ void removeChildJoint(QJoint *joint);
+ QVector<QJoint *> childJoints() const;
+
+public Q_SLOTS:
+ void setScale(const QVector3D &scale);
+ void setRotation(const QQuaternion &rotation);
+ void setTranslation(const QVector3D &translation);
+ void setInverseBindMatrix(const QMatrix4x4 &inverseBindMatrix);
+ void setRotationX(float rotationX);
+ void setRotationY(float rotationY);
+ void setRotationZ(float rotationZ);
+ void setName(const QString &name);
+ void setToIdentity();
+
+Q_SIGNALS:
+ void scaleChanged(const QVector3D &scale);
+ void rotationChanged(const QQuaternion &rotation);
+ void translationChanged(const QVector3D &translation);
+ void inverseBindMatrixChanged(const QMatrix4x4 &inverseBindMatrix);
+ void rotationXChanged(float rotationX);
+ void rotationYChanged(float rotationY);
+ void rotationZChanged(float rotationZ);
+ void nameChanged(const QString &name);
+
+private:
+ Q_DECLARE_PRIVATE(QJoint)
+ Qt3DCore::QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QJOINT_H
diff --git a/src/core/transforms/qjoint_p.h b/src/core/transforms/qjoint_p.h
new file mode 100644
index 000000000..82439d55a
--- /dev/null
+++ b/src/core/transforms/qjoint_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QJOINT_P_H
+#define QT3DCORE_QJOINT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qnode_p.h>
+#include <Qt3DCore/qjoint.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QJoint;
+
+class QJointPrivate : public QNodePrivate
+{
+public:
+ QJointPrivate();
+
+ Q_DECLARE_PUBLIC(QJoint)
+
+ QMatrix4x4 m_inverseBindMatrix;
+ QVector<QJoint *> m_childJoints;
+ QQuaternion m_rotation;
+ QVector3D m_translation;
+ QVector3D m_scale;
+ QString m_name;
+
+ // Not sent to backend. Purely internal convenience
+ QVector3D m_eulerRotationAngles;
+};
+
+struct QJointData
+{
+ QMatrix4x4 inverseBindMatrix;
+ QNodeIdVector childJointIds;
+ QQuaternion rotation;
+ QVector3D translation;
+ QVector3D scale;
+ QString name;
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QJOINT_P_H
diff --git a/src/core/transforms/qmath3d_p.h b/src/core/transforms/qmath3d_p.h
index 8c2afac31..a4f33f0b6 100644
--- a/src/core/transforms/qmath3d_p.h
+++ b/src/core/transforms/qmath3d_p.h
@@ -53,6 +53,7 @@
#include <QtGui/qmatrix4x4.h>
#include <QtGui/qquaternion.h>
#include <QtGui/qvector3d.h>
+#include <Qt3DCore/private/sqt_p.h>
#include <cmath>
@@ -189,6 +190,24 @@ inline void decomposeQMatrix4x4(const QMatrix4x4 &m, QVector3D &position, QQuate
position = QVector3D(m(0, 3), m(1, 3), m(2, 3));
}
+inline void decomposeQMatrix4x4(const QMatrix4x4 &m, Qt3DCore::Sqt &sqt)
+{
+ Q_ASSERT(m.isAffine());
+
+ const QMatrix3x3 m3x3(m.toGenericMatrix<3, 3>());
+
+ QMatrix3x3 rot3x3(Qt::Uninitialized);
+ if (hasScale(m)) {
+ decomposeQMatrix3x3(m3x3, rot3x3, sqt.scale, sqt.translation);
+ } else {
+ // we know there is no scaling part; no need for QDU decomposition
+ sqt.scale = QVector3D(1.0f, 1.0f, 1.0f);
+ rot3x3 = m3x3;
+ }
+ sqt.rotation = QQuaternion::fromRotationMatrix(rot3x3);
+ sqt.translation = QVector3D(m(0, 3), m(1, 3), m(2, 3));
+}
+
QT_END_NAMESPACE
#endif // QT3DCORE_QMATH3D_P_H
diff --git a/src/core/transforms/qskeleton.cpp b/src/core/transforms/qskeleton.cpp
new file mode 100644
index 000000000..c58bc0f8c
--- /dev/null
+++ b/src/core/transforms/qskeleton.cpp
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qskeleton.h"
+#include "qskeleton_p.h"
+#include <Qt3DCore/qjoint.h>
+#include <Qt3DCore/private/qskeletoncreatedchange_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QSkeletonPrivate::QSkeletonPrivate()
+ : QAbstractSkeletonPrivate()
+ , m_rootJoint(nullptr)
+{
+ m_type = QSkeletonCreatedChangeBase::Skeleton;
+}
+
+/*!
+ \qmltype Skeleton
+ \inqmlmodule Qt3D.Core
+ \inherits AbstractSkeleton
+ \instantiates Qt3DCore::QSkeleton
+ \since 5.10
+ \brief Holds the data for a skeleton to be used with skinned meshes
+
+ Use Skeleton if you wish to manually create the joints of a skeleton for
+ use with skinned meshes. This is mainly of use to people creating editors,
+ tooling, or dynamic skeletons. It is more common that a Qt 3D application
+ would simply consume an existing skeleton and skinned mesh as created in
+ a digital content creation tool such as Blender. For this use case, please
+ see SkeletonLoader.
+*/
+
+/*!
+ \qmlproperty Joint Skeleton::rootJoint
+
+ Holds the root joint of the hierarchy of joints forming the skeleton.
+*/
+
+/*!
+ \class Qt3DCore::QSkeleton
+ \inmodule Qt3DCore
+ \inherits Qt3DCore::QAbstractSkeleton
+ \since 5.10
+ \brief Holds the data for a skeleton to be used with skinned meshes
+
+ Use QSkeleton if you wish to manually create the joints of a skeleton for
+ use with skinned meshes. This is mainly of use to people creating editors,
+ tooling, or dynamic skeletons. It is more common that a Qt 3D application
+ would simply consume an existing skeleton and skinned mesh as created in
+ a digital content creation tool such as Blender. For this use case, please
+ see QSkeletonLoader.
+*/
+
+/*!
+ Constructs a new QSkeleton with \a parent.
+*/
+QSkeleton::QSkeleton(Qt3DCore::QNode *parent)
+ : QAbstractSkeleton(*new QSkeletonPrivate, parent)
+{
+}
+
+/*! \internal */
+QSkeleton::~QSkeleton()
+{
+}
+
+/*!
+ \property Qt3DCore::QSkeleton::rootJoint
+
+ Holds the root joint of the hierarchy of joints forming the skeleton.
+*/
+Qt3DCore::QJoint *QSkeleton::rootJoint() const
+{
+ Q_D(const QSkeleton);
+ return d->m_rootJoint;
+}
+
+void QSkeleton::setRootJoint(Qt3DCore::QJoint *rootJoint)
+{
+ Q_D(QSkeleton);
+ if (d->m_rootJoint != rootJoint) {
+ if (d->m_rootJoint)
+ d->unregisterDestructionHelper(d->m_rootJoint);
+
+ // We need to add it as a child of the current node if it has been declared inline
+ // Or not previously added as a child of the current node so that
+ // 1) The backend gets notified about it's creation
+ // 2) When the current node is destroyed, it gets destroyed as well
+ if (rootJoint && !rootJoint->parent())
+ rootJoint->setParent(this);
+ d->m_rootJoint = rootJoint;
+
+ // Ensures proper bookkeeping
+ if (d->m_rootJoint)
+ d->registerDestructionHelper(d->m_rootJoint, &QSkeleton::setRootJoint, d->m_rootJoint);
+
+ emit rootJointChanged(rootJoint);
+ }
+}
+
+/*! \internal */
+Qt3DCore::QNodeCreatedChangeBasePtr QSkeleton::createNodeCreationChange() const
+{
+ auto creationChange = QSkeletonCreatedChangePtr<QSkeletonData>::create(this);
+ auto &data = creationChange->data;
+ Q_D(const QSkeleton);
+ data.rootJointId = qIdForNode(d->m_rootJoint);
+ return creationChange;
+}
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
diff --git a/src/core/transforms/qskeleton.h b/src/core/transforms/qskeleton.h
new file mode 100644
index 000000000..e3c0c9194
--- /dev/null
+++ b/src/core/transforms/qskeleton.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QSKELETON_H
+#define QT3DCORE_QSKELETON_H
+
+#include <Qt3DCore/qabstractskeleton.h>
+#include <Qt3DCore/qt3dcore_global.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QJoint;
+class QSkeletonPrivate;
+
+class QT3DCORESHARED_EXPORT QSkeleton : public QAbstractSkeleton
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt3DCore::QJoint* rootJoint READ rootJoint WRITE setRootJoint NOTIFY rootJointChanged)
+
+public:
+ QSkeleton(Qt3DCore::QNode *parent = nullptr);
+ ~QSkeleton();
+
+ Qt3DCore::QJoint *rootJoint() const;
+
+public Q_SLOTS:
+ void setRootJoint(Qt3DCore::QJoint *rootJoint);
+
+Q_SIGNALS:
+ void rootJointChanged(Qt3DCore::QJoint *rootJoint);
+
+private:
+ Q_DECLARE_PRIVATE(QSkeleton)
+ QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QSKELETON_H
diff --git a/src/core/transforms/qskeleton_p.h b/src/core/transforms/qskeleton_p.h
new file mode 100644
index 000000000..c8b6554f9
--- /dev/null
+++ b/src/core/transforms/qskeleton_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QSKELETON_P_H
+#define QT3DCORE_QSKELETON_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qabstractskeleton_p.h>
+#include <Qt3DCore/qskeleton.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QJoint;
+
+class QSkeletonPrivate : public QAbstractSkeletonPrivate
+{
+public:
+ QSkeletonPrivate();
+
+ Q_DECLARE_PUBLIC(QSkeleton)
+
+ QJoint *m_rootJoint;
+};
+
+struct QSkeletonData
+{
+ QNodeId rootJointId;
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QSKELETON_P_H
diff --git a/src/core/transforms/qskeletonloader.cpp b/src/core/transforms/qskeletonloader.cpp
new file mode 100644
index 000000000..9e0fdae5c
--- /dev/null
+++ b/src/core/transforms/qskeletonloader.cpp
@@ -0,0 +1,258 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qskeletonloader.h"
+#include "qskeletonloader_p.h"
+#include <Qt3DCore/qjoint.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <Qt3DCore/private/qskeletoncreatedchange_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QSkeletonLoaderPrivate::QSkeletonLoaderPrivate()
+ : QAbstractSkeletonPrivate()
+ , m_source()
+ , m_createJoints(false)
+ , m_status(QSkeletonLoader::NotReady)
+ , m_rootJoint(nullptr)
+{
+ m_type = QSkeletonCreatedChangeBase::SkeletonLoader;
+}
+
+void QSkeletonLoaderPrivate::setStatus(QSkeletonLoader::Status status)
+{
+ Q_Q(QSkeletonLoader);
+ if (status != m_status) {
+ m_status = status;
+ const bool blocked = q->blockNotifications(true);
+ emit q->statusChanged(m_status);
+ q->blockNotifications(blocked);
+ }
+}
+
+/*!
+ \qmltype SkeletonLoader
+ \inqmlmodule Qt3D.Core
+ \inherits AbstractSkeleton
+ \instantiates Qt3DCore::QSkeletonLoader
+ \since 5.10
+ \brief Used to load a skeleton of joints from file
+
+ Use SkeletonLoader if you wish to load a whole skeleton from file rather
+ than creating the joints yourself using Skeleton and Joints. Creating a
+ skeleton and binding the vertices of a mesh to the skeleton is most easily
+ performed in a 3D digital content creation tool such as Blender. The
+ resulting skeleton and mesh can then be exported in a suitable format such
+ as glTF 2 for consumption by Qt 3D.
+*/
+
+/*!
+ \qmlproperty url SkeletonLoader::source
+
+ Holds the source url from which to load the skeleton.
+*/
+
+/*!
+ \qmlproperty SkeletonLoader.Status SkeletonLoader::status
+
+ Holds the current status of skeleton loading.
+*/
+
+/*!
+ \class Qt3DCore::QSkeletonLoader
+ \inmodule Qt3DCore
+ \inherits Qt3DCore::QAbstractSkeleton
+ \since 5.10
+ \brief Used to load a skeleton of joints from file
+
+ Use SkeletonLoader if you wish to load a whole skeleton from file rather
+ than creating the joints yourself using Skeleton and Joints. Creating a
+ skeleton and binding the vertices of a mesh to the skeleton is most easily
+ performed in a 3D digital content creation tool such as Blender. The
+ resulting skeleton and mesh can then be exported in a suitable format such
+ as glTF 2 for consumption by Qt 3D.
+*/
+
+/*!
+ \enum QSkeletonLoader::Status
+
+ This enum identifies the status of skeleton.
+
+ \value NotReady The skeleton has not been loaded yet
+ \value Ready The skeleton was successfully loaded
+ \value Error An error occurred while loading the skeleton
+*/
+
+/*!
+ Constructs a new QSkeletonLoader with \a parent.
+*/
+QSkeletonLoader::QSkeletonLoader(Qt3DCore::QNode *parent)
+ : QAbstractSkeleton(*new QSkeletonLoaderPrivate, parent)
+{
+}
+
+/*!
+ Constructs a new QSkeletonLoader with \a parent and sets the \a source.
+*/
+QSkeletonLoader::QSkeletonLoader(const QUrl &source, QNode *parent)
+ : QAbstractSkeleton(*new QSkeletonLoaderPrivate, parent)
+{
+ setSource(source);
+}
+
+/*! \internal */
+QSkeletonLoader::QSkeletonLoader(QSkeletonLoaderPrivate &dd, Qt3DCore::QNode *parent)
+ : QAbstractSkeleton(dd, parent)
+{
+}
+
+/*! \internal */
+QSkeletonLoader::~QSkeletonLoader()
+{
+}
+
+/*!
+ \property Qt3DCore::QSkeletonLoader::source
+
+ Holds the source url from which to load the skeleton.
+*/
+QUrl QSkeletonLoader::source() const
+{
+ Q_D(const QSkeletonLoader);
+ return d->m_source;
+}
+
+/*!
+ \property Qt3DCore::QSkeletonLoader::status
+
+ Holds the current status of skeleton loading.
+*/
+QSkeletonLoader::Status QSkeletonLoader::status() const
+{
+ Q_D(const QSkeletonLoader);
+ return d->m_status;
+}
+
+bool QSkeletonLoader::isCreateJointsEnabled() const
+{
+ Q_D(const QSkeletonLoader);
+ return d->m_createJoints;
+}
+
+Qt3DCore::QJoint *QSkeletonLoader::rootJoint() const
+{
+ Q_D(const QSkeletonLoader);
+ return d->m_rootJoint;
+}
+
+void QSkeletonLoader::setSource(const QUrl &source)
+{
+ Q_D(QSkeletonLoader);
+ if (d->m_source == source)
+ return;
+
+ d->m_source = source;
+ emit sourceChanged(source);
+}
+
+void QSkeletonLoader::setCreateJointsEnabled(bool createJoints)
+{
+ Q_D(QSkeletonLoader);
+ if (d->m_createJoints == createJoints)
+ return;
+
+ d->m_createJoints = createJoints;
+ emit createJointsEnabledChanged(createJoints);
+}
+
+void QSkeletonLoader::setRootJoint(QJoint *rootJoint)
+{
+ Q_D(QSkeletonLoader);
+ if (rootJoint == d->m_rootJoint)
+ return;
+
+ if (d->m_rootJoint)
+ d->unregisterDestructionHelper(d->m_rootJoint);
+
+ if (rootJoint && !rootJoint->parent())
+ rootJoint->setParent(this);
+
+ d->m_rootJoint = rootJoint;
+
+ // Ensures proper bookkeeping
+ if (d->m_rootJoint)
+ d->registerDestructionHelper(d->m_rootJoint, &QSkeletonLoader::setRootJoint, d->m_rootJoint);
+
+ emit rootJointChanged(d->m_rootJoint);
+}
+
+/*! \internal */
+void QSkeletonLoader::sceneChangeEvent(const QSceneChangePtr &change)
+{
+ Q_D(QSkeletonLoader);
+ if (change->type() == Qt3DCore::PropertyUpdated) {
+ auto propertyChange = qSharedPointerCast<QStaticPropertyUpdatedChangeBase>(change);
+ if (propertyChange->propertyName() == QByteArrayLiteral("status")) {
+ const auto e = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(change);
+ d->setStatus(static_cast<QSkeletonLoader::Status>(e->value().toInt()));
+ } else if (propertyChange->propertyName() == QByteArrayLiteral("rootJoint")) {
+ auto typedChange = qSharedPointerCast<QJointChange>(propertyChange);
+ auto rootJoint = std::move(typedChange->data);
+ setRootJoint(rootJoint.release());
+ }
+ }
+ QAbstractSkeleton::sceneChangeEvent(change);
+}
+
+/*! \internal */
+Qt3DCore::QNodeCreatedChangeBasePtr QSkeletonLoader::createNodeCreationChange() const
+{
+ auto creationChange = QSkeletonCreatedChangePtr<QSkeletonLoaderData>::create(this);
+ auto &data = creationChange->data;
+ Q_D(const QSkeletonLoader);
+ data.source = d->m_source;
+ data.createJoints = d->m_createJoints;
+ return creationChange;
+}
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
diff --git a/src/core/transforms/qskeletonloader.h b/src/core/transforms/qskeletonloader.h
new file mode 100644
index 000000000..1185ba6c7
--- /dev/null
+++ b/src/core/transforms/qskeletonloader.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QSKELETONLOADER_H
+#define QT3DCORE_QSKELETONLOADER_H
+
+#include <Qt3DCore/qabstractskeleton.h>
+#include <Qt3DCore/qt3dcore_global.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QJoint;
+class QSkeletonLoaderPrivate;
+
+class QT3DCORESHARED_EXPORT QSkeletonLoader : public QAbstractSkeleton
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(bool createJointsEnabled READ isCreateJointsEnabled WRITE setCreateJointsEnabled NOTIFY createJointsEnabledChanged)
+ Q_PROPERTY(Qt3DCore::QJoint* rootJoint READ rootJoint NOTIFY rootJointChanged)
+
+public:
+ explicit QSkeletonLoader(Qt3DCore::QNode *parent = nullptr);
+ explicit QSkeletonLoader(const QUrl &source,
+ Qt3DCore::QNode *parent = nullptr);
+ ~QSkeletonLoader();
+
+ enum Status {
+ NotReady = 0,
+ Ready,
+ Error
+ };
+ Q_ENUM(Status) // LCOV_EXCL_LINE
+
+ QUrl source() const;
+ Status status() const;
+ bool isCreateJointsEnabled() const;
+ Qt3DCore::QJoint* rootJoint() const;
+
+public Q_SLOTS:
+ void setSource(const QUrl &source);
+ void setCreateJointsEnabled(bool enabled);
+
+Q_SIGNALS:
+ void sourceChanged(const QUrl &source);
+ void statusChanged(Status status);
+ void createJointsEnabledChanged(bool createJointsEnabled);
+ void rootJointChanged(Qt3DCore::QJoint* rootJoint);
+
+protected:
+ explicit QSkeletonLoader(QSkeletonLoaderPrivate &dd, Qt3DCore::QNode *parent = nullptr);
+ void sceneChangeEvent(const Qt3DCore::QSceneChangePtr &change) override;
+
+private:
+ Q_DECLARE_PRIVATE(QSkeletonLoader)
+ QNodeCreatedChangeBasePtr createNodeCreationChange() const override;
+ void setRootJoint(QJoint *rootJoint); // Needed for lifetime management of created joints
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QSKELETONLOADER_H
diff --git a/src/core/transforms/qskeletonloader_p.h b/src/core/transforms/qskeletonloader_p.h
new file mode 100644
index 000000000..ab0babe00
--- /dev/null
+++ b/src/core/transforms/qskeletonloader_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_QSKELETONLOADER_P_H
+#define QT3DCORE_QSKELETONLOADER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <Qt3DCore/private/qabstractskeleton_p.h>
+#include "qskeletonloader.h"
+
+#include <Qt3DCore/private/qtypedpropertyupdatechange_p.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QJoint;
+
+class QSkeletonLoaderPrivate : public QAbstractSkeletonPrivate
+{
+public:
+ QSkeletonLoaderPrivate();
+
+ void setStatus(QSkeletonLoader::Status status);
+
+ Q_DECLARE_PUBLIC(QSkeletonLoader)
+
+ QUrl m_source;
+ bool m_createJoints;
+
+ // Set by the backend
+ QSkeletonLoader::Status m_status;
+ Qt3DCore::QJoint* m_rootJoint;
+};
+
+struct QSkeletonLoaderData
+{
+ QUrl source;
+ bool createJoints;
+};
+
+class QJoint;
+typedef QTypedPropertyUpdatedChange<std::unique_ptr<QJoint>> QJointChange;
+typedef QTypedPropertyUpdatedChangePtr<std::unique_ptr<QJoint>> QJointChangePtr;
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_QSKELETONLOADER_P_H
diff --git a/src/core/transforms/sqt_p.h b/src/core/transforms/sqt_p.h
new file mode 100644
index 000000000..5fdefccc8
--- /dev/null
+++ b/src/core/transforms/sqt_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_SQT_P_H
+#define QT3DCORE_SQT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/qmatrix4x4.h>
+#include <QtGui/qquaternion.h>
+#include <QtGui/qvector3d.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+struct Sqt
+{
+ QQuaternion rotation;
+ QVector3D scale;
+ float pad1;
+ QVector3D translation;
+ float pad2;
+
+ Sqt()
+ : rotation()
+ , scale(1.0f, 1.0f, 1.0f)
+ , translation()
+ {}
+
+ inline QMatrix4x4 toMatrix() const
+ {
+ QMatrix4x4 m;
+ m.translate(translation);
+ m.rotate(rotation);
+ m.scale(scale);
+ return m;
+ }
+
+ bool operator == (const Sqt &rhs) const
+ {
+ return rotation == rhs.rotation
+ && scale == rhs.scale
+ && translation == rhs.translation;
+ }
+};
+
+struct JointNamesAndLocalPoses
+{
+ QVector<QString> names;
+ QVector<Sqt> localPoses;
+};
+
+} // namespace Qt3DCore
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QVector<Qt3DCore::Sqt>)
+Q_DECLARE_METATYPE(Qt3DCore::JointNamesAndLocalPoses)
+
+#endif // QT3DCORE_SQT_P_H
diff --git a/src/core/transforms/transforms.pri b/src/core/transforms/transforms.pri
index 75ae7c7a9..f83c37a0b 100644
--- a/src/core/transforms/transforms.pri
+++ b/src/core/transforms/transforms.pri
@@ -1,10 +1,58 @@
-
SOURCES += \
- $$PWD/qtransform.cpp
+ $$PWD/qtransform.cpp \
+ $$PWD/qjoint.cpp \
+ $$PWD/qabstractskeleton.cpp \
+ $$PWD/qskeleton.cpp \
+ $$PWD/qskeletonloader.cpp \
+ $$PWD/qarmature.cpp
HEADERS += \
- $$PWD/qtransform.h \
- $$PWD/qtransform_p.h \
- $$PWD/qmath3d_p.h
+ $$PWD/qtransform.h \
+ $$PWD/qtransform_p.h \
+ $$PWD/qmath3d_p.h \
+ $$PWD/qjoint.h \
+ $$PWD/qjoint_p.h \
+ $$PWD/qabstractskeleton.h \
+ $$PWD/qabstractskeleton_p.h \
+ $$PWD/qskeleton.h \
+ $$PWD/qskeleton_p.h \
+ $$PWD/qskeletonloader.h \
+ $$PWD/qskeletonloader_p.h \
+ $$PWD/qarmature.h \
+ $$PWD/qarmature_p.h \
+ $$PWD/vector4d_p.h \
+ $$PWD/vector3d_p.h \
+ $$PWD/matrix4x4_p.h \
+ $$PWD/sqt_p.h
INCLUDEPATH += $$PWD
+
+qtConfig(qt3d-simd-sse2) {
+ CONFIG += simd
+
+ SSE2_HEADERS += \
+ $$PWD/vector4d_sse_p.h \
+ $$PWD/vector3d_sse_p.h \
+ $$PWD/matrix4x4_sse_p.h
+
+ SSE2_SOURCES += \
+ $$PWD/matrix4x4_sse.cpp
+
+ # These files contain AVX2 code, only add them to SSE2 if AVX2 not available
+ !qtConfig(qt3d-simd-avx2) {
+ SSE2_SOURCES += \
+ $$PWD/vector4d_sse.cpp \
+ $$PWD/vector3d_sse.cpp
+ }
+}
+
+qtConfig(qt3d-simd-avx2) {
+ CONFIG += simd
+
+ AVX2_HEADERS += \
+ $$PWD/matrix4x4_avx2_p.h
+ AVX2_SOURCES += \
+ $$PWD/matrix4x4_avx2.cpp \
+ $$PWD/vector4d_sse.cpp \
+ $$PWD/vector3d_sse.cpp
+}
diff --git a/src/core/transforms/vector3d_p.h b/src/core/transforms/vector3d_p.h
new file mode 100644
index 000000000..fa72c9f2f
--- /dev/null
+++ b/src/core/transforms/vector3d_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_VECTOR3D_P_H
+#define QT3DCORE_VECTOR3D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <private/qsimd_p.h>
+
+#if (defined(__AVX2__) || defined(__SSE2__)) && defined(QT_COMPILER_SUPPORTS_SSE2)
+
+#include <Qt3DCore/private/vector3d_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+using Vector3D = Qt3DCore::Vector3D_SSE;
+QT_END_NAMESPACE
+
+#else
+
+#include <QVector3D>
+
+QT_BEGIN_NAMESPACE
+using Vector3D = QVector3D;
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QT3DCORE_VECTOR3D_P_H
diff --git a/src/core/transforms/vector3d_sse.cpp b/src/core/transforms/vector3d_sse.cpp
new file mode 100644
index 000000000..151cbb959
--- /dev/null
+++ b/src/core/transforms/vector3d_sse.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qsimd_p.h>
+
+#ifdef __AVX2__
+#include "matrix4x4_avx2_p.h"
+#else
+#include "matrix4x4_sse_p.h"
+#endif
+
+#include "vector3d_sse_p.h"
+#include "vector4d_sse_p.h"
+#include <QDebug>
+
+#ifdef QT_COMPILER_SUPPORTS_SSE2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QDebug operator<<(QDebug dbg, const Vector3D_SSE &v)
+{
+ dbg.nospace() << "Vector3D_SSE(" << v.x() << ", " << v.y() << ", " << v.z() << ") ";
+ return dbg;
+}
+
+Vector3D_SSE::Vector3D_SSE(const Vector4D_SSE &v)
+{
+ m_xyzw = _mm_mul_ps(v.m_xyzw, _mm_set_ps(0.0f, 1.0f, 1.0f, 1.0f));
+}
+
+#ifdef __AVX2__
+
+Vector3D_SSE Vector3D_SSE::unproject(const Matrix4x4_AVX2 &modelView, const Matrix4x4_AVX2 &projection, const QRect &viewport) const
+{
+ const Matrix4x4_AVX2 inverse = (projection * modelView).inverted();
+
+ Vector4D_SSE tmp(*this, 1.0f);
+ tmp.setX((tmp.x() - float(viewport.x())) / float(viewport.width()));
+ tmp.setY((tmp.y() - float(viewport.y())) / float(viewport.height()));
+ tmp = tmp * 2.0f - Vector4D_SSE(1.0f, 1.0f, 1.0f, 1.0f);
+
+ Vector4D_SSE obj = inverse * tmp;
+ if (qFuzzyIsNull(obj.w()))
+ obj.setW(1.0f);
+ obj /= obj.w();
+ return Vector3D_SSE(obj);
+}
+
+Vector3D_SSE Vector3D_SSE::project(const Matrix4x4_AVX2 &modelView, const Matrix4x4_AVX2 &projection, const QRect &viewport) const
+{
+ Vector4D_SSE tmp(*this, 1.0f);
+ tmp = projection * modelView * tmp;
+ if (qFuzzyIsNull(tmp.w()))
+ tmp.setW(1.0f);
+ tmp /= tmp.w();
+
+ tmp = tmp * 0.5f + Vector4D_SSE(0.5f, 0.5f, 0.5f, 0.5f);
+ tmp.setX(tmp.x() * viewport.width() + viewport.x());
+ tmp.setY(tmp.y() * viewport.height() + viewport.y());
+
+ return Vector3D_SSE(tmp);
+}
+
+#else
+
+Vector3D_SSE Vector3D_SSE::unproject(const Matrix4x4_SSE &modelView, const Matrix4x4_SSE &projection, const QRect &viewport) const
+{
+ const Matrix4x4_SSE inverse = (projection * modelView).inverted();
+
+ Vector4D_SSE tmp(*this, 1.0f);
+ tmp.setX((tmp.x() - float(viewport.x())) / float(viewport.width()));
+ tmp.setY((tmp.y() - float(viewport.y())) / float(viewport.height()));
+ tmp = tmp * 2.0f - Vector4D_SSE(1.0f, 1.0f, 1.0f, 1.0f);
+
+ Vector4D_SSE obj = inverse * tmp;
+ if (qFuzzyIsNull(obj.w()))
+ obj.setW(1.0f);
+ obj /= obj.w();
+ return Vector3D_SSE(obj);
+}
+
+Vector3D_SSE Vector3D_SSE::project(const Matrix4x4_SSE &modelView, const Matrix4x4_SSE &projection, const QRect &viewport) const
+{
+ Vector4D_SSE tmp(*this, 1.0f);
+ tmp = projection * modelView * tmp;
+ if (qFuzzyIsNull(tmp.w()))
+ tmp.setW(1.0f);
+ tmp /= tmp.w();
+
+ tmp = tmp * 0.5f + Vector4D_SSE(0.5f, 0.5f, 0.5f, 0.5f);
+ tmp.setX(tmp.x() * viewport.width() + viewport.x());
+ tmp.setY(tmp.y() * viewport.height() + viewport.y());
+
+ return Vector3D_SSE(tmp);
+}
+
+#endif
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT_COMPILER_SUPPORTS_SSE2
diff --git a/src/core/transforms/vector3d_sse_p.h b/src/core/transforms/vector3d_sse_p.h
new file mode 100644
index 000000000..a299d1db9
--- /dev/null
+++ b/src/core/transforms/vector3d_sse_p.h
@@ -0,0 +1,399 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_VECTOR3D_SSE_P_H
+#define QT3DCORE_VECTOR3D_SSE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <Qt3DCore/private/qt3dcore_global_p.h>
+#include <QtCore/private/qsimd_p.h>
+#include <QtCore/QtGlobal>
+#include <QtGui/qvector3d.h>
+#include <QDebug>
+#include <math.h>
+
+#ifdef QT_COMPILER_SUPPORTS_SSE2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class Matrix4x4_SSE;
+class Matrix4x4_AVX2;
+class Vector4D_SSE;
+
+class Vector3D_SSE
+{
+public:
+
+ Q_ALWAYS_INLINE Vector3D_SSE()
+ : m_xyzw(_mm_setzero_ps())
+ {
+ }
+
+ explicit Q_ALWAYS_INLINE Vector3D_SSE(Qt::Initialization) {}
+
+ explicit Q_ALWAYS_INLINE Vector3D_SSE(float x, float y, float z)
+ : m_xyzw(_mm_set_ps(0.0f, z, y, x))
+ {
+ }
+
+ explicit Q_ALWAYS_INLINE Vector3D_SSE(QVector3D v)
+ : m_xyzw(_mm_set_ps(0.0f, v.z(), v.y(), v.x()))
+ {
+ }
+
+ explicit QT3DCORE_PRIVATE_EXPORT Vector3D_SSE(const Vector4D_SSE &v);
+
+ Q_ALWAYS_INLINE Vector3D_SSE &operator+=(Vector3D_SSE vector)
+ {
+ m_xyzw = _mm_add_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE &operator-=(Vector3D_SSE vector)
+ {
+ m_xyzw = _mm_sub_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE &operator*=(Vector3D_SSE vector)
+ {
+ m_xyzw = _mm_mul_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE &operator/=(Vector3D_SSE vector)
+ {
+ m_xyzw = _mm_div_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE &operator*=(float factor)
+ {
+ m_xyzw = _mm_mul_ps(m_xyzw, _mm_set1_ps(factor));
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE &operator/=(float factor)
+ {
+ m_xyzw = _mm_div_ps(m_xyzw, _mm_set1_ps(factor));
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE bool operator==(Vector3D_SSE other) const
+ {
+ // 0b111 == 0x7
+ return ((_mm_movemask_ps(_mm_cmpeq_ps(m_xyzw, other.m_xyzw)) & 0x7) == 0x7);
+ }
+
+ Q_ALWAYS_INLINE bool operator!=(Vector3D_SSE other) const
+ {
+ return !(*this == other);
+ }
+
+ Q_ALWAYS_INLINE QVector3D toQVector3D() const
+ {
+ return QVector3D(x(), y(), z());
+ }
+
+ Q_ALWAYS_INLINE float lengthSquared() const
+ {
+ return Qt3DCore::Vector3D_SSE::dotProduct(*this, *this);
+ }
+
+ Q_ALWAYS_INLINE float length() const
+ {
+ return sqrt(Qt3DCore::Vector3D_SSE::dotProduct(*this, *this));
+ }
+
+ Q_ALWAYS_INLINE float distanceToPoint(const Vector3D_SSE &point) const
+ {
+ return (*this - point).length();
+ }
+
+ Q_ALWAYS_INLINE void normalize()
+ {
+ const float len = length();
+ m_xyzw = _mm_div_ps(m_xyzw, _mm_set_ps1(len));
+ }
+
+ Q_ALWAYS_INLINE Vector3D_SSE normalized() const
+ {
+ Vector3D_SSE v = *this;
+ v.normalize();
+ return v;
+ }
+
+ Q_ALWAYS_INLINE bool isNull() const
+ {
+ // Ignore last bit
+ // 0b111 = 0x7
+ return ((_mm_movemask_ps(_mm_cmpeq_ps(m_xyzw, _mm_set_ps1(0.0f))) & 0x7) == 0x7);
+ }
+
+#ifdef __AVX2__
+ QT3DCORE_PRIVATE_EXPORT Vector3D_SSE unproject(const Matrix4x4_AVX2 &modelView, const Matrix4x4_AVX2 &projection, const QRect &viewport) const;
+ QT3DCORE_PRIVATE_EXPORT Vector3D_SSE project(const Matrix4x4_AVX2 &modelView, const Matrix4x4_AVX2 &projection, const QRect &viewport) const;
+#else
+ QT3DCORE_PRIVATE_EXPORT Vector3D_SSE unproject(const Matrix4x4_SSE &modelView, const Matrix4x4_SSE &projection, const QRect &viewport) const;
+ QT3DCORE_PRIVATE_EXPORT Vector3D_SSE project(const Matrix4x4_SSE &modelView, const Matrix4x4_SSE &projection, const QRect &viewport) const;
+#endif
+
+ Q_ALWAYS_INLINE float x() const { return _mm_cvtss_f32(m_xyzw); }
+
+ Q_ALWAYS_INLINE float y() const
+ {
+ // 0b01010101 = 0x55
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_xyzw, m_xyzw, 0x55));
+ }
+
+ Q_ALWAYS_INLINE float z() const
+ {
+ // 0b10101010 = 0xaa
+ return _mm_cvtss_f32(_mm_unpackhi_ps(m_xyzw, m_xyzw));
+ }
+
+ Q_ALWAYS_INLINE void setX(float x)
+ {
+ m_xyzw = _mm_move_ss(m_xyzw, _mm_set_ss(x));
+ }
+
+ Q_ALWAYS_INLINE void setY(float y)
+ {
+ // m_xyzw = a, b, c, d
+
+ // y, y, y, y
+ const __m128 yVec = _mm_set_ps1(y);
+
+ // y, y, a, a
+ // 0b00000000 == 0x0
+ const __m128 yaVec = _mm_shuffle_ps(yVec, m_xyzw, 0x0);
+
+ // a, y, c, d
+ // 0b11100010 == 0xe2
+ m_xyzw = _mm_shuffle_ps(yaVec, m_xyzw, 0xe2);
+ }
+
+ Q_ALWAYS_INLINE void setZ(float z)
+ {
+ // m_xyzw = a, b, c, d
+
+ // z, z, z, z
+ const __m128 zVec = _mm_set_ps1(z);
+
+ // z, z, d, d
+ // 0b11110000 == 0xf0
+ const __m128 zdVec = _mm_shuffle_ps(zVec, m_xyzw, 0xf0);
+
+ // a, b, z, d
+ // 0b10000100 == 0x84
+ m_xyzw = _mm_shuffle_ps(m_xyzw, zdVec, 0x84);
+ }
+
+ Q_ALWAYS_INLINE float operator[](int idx) const
+ {
+ switch (idx) {
+ case 0:
+ return x();
+ case 1:
+ return y();
+ case 2:
+ return z();
+ default:
+ Q_UNREACHABLE();
+ return 0.0f;
+ }
+ }
+
+ struct DigitWrapper
+ {
+ explicit DigitWrapper(int idx, Vector3D_SSE *vec)
+ : m_vec(vec)
+ , m_idx(idx)
+ {}
+
+ operator float() const
+ {
+ switch (m_idx) {
+ case 0:
+ return m_vec->x();
+ case 1:
+ return m_vec->y();
+ case 2:
+ return m_vec->z();
+ default:
+ Q_UNREACHABLE();
+ return 0.0f;
+ }
+ }
+
+ void operator =(float value)
+ {
+ switch (m_idx) {
+ case 0:
+ m_vec->setX(value);
+ break;
+ case 1:
+ m_vec->setY(value);
+ break;
+ case 2:
+ m_vec->setZ(value);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+
+ private:
+ Vector3D_SSE *m_vec;
+ const int m_idx;
+ };
+
+ Q_ALWAYS_INLINE DigitWrapper operator[](int idx)
+ {
+ return DigitWrapper(idx, this);
+ }
+
+ static Q_ALWAYS_INLINE float dotProduct(Vector3D_SSE a, Vector3D_SSE b)
+ {
+#if defined(__SSE4_1__)
+ // 0b01111111 = 0x7f
+ return _mm_cvtss_f32(_mm_dp_ps(a.m_xyzw, b.m_xyzw, 0x7f));
+#elif defined(__SSE3__)
+ const __m128 mult = _mm_mul_ps(a.m_xyzw, b.m_xyzw);
+ // a + b, c + d, a + d, c + d
+ const __m128 partialSum = _mm_hadd_ps(mult, mult);
+ // c + d, ......
+ // 0x00000001 =
+ const __m128 partialSumShuffle = _mm_shuffle_ps(partialSum, partialSum, 0x1);
+ return _mm_cvtss_f32(_mm_hadd_ps(partialSum, partialSumShuffle));
+#else
+ const __m128 mult = _mm_mul_ps(a.m_xyzw, b.m_xyzw);
+
+ // (multX, 0, 0, 0) + (multY, 0, 0, 0) -> (multX + multY, 0, 0, 0)
+ // 0b11111101 == 0xfd
+ const __m128 shuffled = _mm_shuffle_ps(mult, mult, 0xfd);
+ // (multX + multY, 0, 0, 0) + (multZ, 0, 0, 0);
+ // 0b11111110 == 0xfe
+ const __m128 shuffled2 = _mm_shuffle_ps(mult, mult, 0xfe);
+ const __m128 result = _mm_add_ps(_mm_add_ps(shuffled, mult), shuffled2);
+ return _mm_cvtss_f32(result);
+#endif
+ }
+
+ static Q_ALWAYS_INLINE Vector3D_SSE crossProduct(Vector3D_SSE a, Vector3D_SSE b)
+ {
+ // a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x
+ // (a.y, a.z, a.z, a.x, a.x, a.y) (b.z, b.y, b.x, b.z, b.y, b.x)
+ // (a.y, a.z, a.x) * (b.z, b.x, b.y) - (a.z, a.x, a.y) (b.y, b.z, b.x)
+
+ // 0b11001001 == 0xc9
+ const __m128 a1 = _mm_shuffle_ps(a.m_xyzw, a.m_xyzw, 0xc9);
+ const __m128 b2 = _mm_shuffle_ps(b.m_xyzw, b.m_xyzw, 0xc9);
+ // 0b11010010 == 0xd2
+ const __m128 a2 = _mm_shuffle_ps(a.m_xyzw, a.m_xyzw, 0xd2);
+ const __m128 b1 = _mm_shuffle_ps(b.m_xyzw, b.m_xyzw, 0xd2);
+
+ Vector3D_SSE v(Qt::Uninitialized);
+ v.m_xyzw = _mm_sub_ps(_mm_mul_ps(a1, b1), _mm_mul_ps(a2, b2));
+ return v;
+ }
+
+ friend class Matrix4x4_AVX2;
+ friend class Matrix4x4_SSE;
+ friend class Vector4D_SSE;
+
+ friend QT3DCORE_PRIVATE_EXPORT Vector3D_SSE operator*(const Vector3D_SSE &vector, const Matrix4x4_SSE &matrix);
+ friend QT3DCORE_PRIVATE_EXPORT Vector3D_SSE operator*(const Matrix4x4_SSE &matrix, const Vector3D_SSE &vector);
+
+ friend QT3DCORE_PRIVATE_EXPORT Vector3D_SSE operator*(const Vector3D_SSE &vector, const Matrix4x4_AVX2 &matrix);
+ friend QT3DCORE_PRIVATE_EXPORT Vector3D_SSE operator*(const Matrix4x4_AVX2 &matrix, const Vector3D_SSE &vector);
+
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator+(Vector3D_SSE v1, Vector3D_SSE v2) { return v1 += v2; }
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator-(Vector3D_SSE v1, Vector3D_SSE v2) { return v1 -= v2; }
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator*(float factor, Vector3D_SSE vector) { return vector *= factor; }
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator*(Vector3D_SSE vector, float factor) { return vector *= factor; }
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator*(Vector3D_SSE v1, Vector3D_SSE v2) { return v1 *= v2; }
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator-(Vector3D_SSE vector)
+ {
+ Vector3D_SSE c(Qt::Uninitialized);
+
+ c.m_xyzw = _mm_xor_ps(vector.m_xyzw, _mm_set1_ps(-0.0f));
+
+ return c;
+ }
+
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator/(Vector3D_SSE vector, float divisor) { return vector /= divisor; }
+ friend Q_ALWAYS_INLINE const Vector3D_SSE operator/(Vector3D_SSE vector, Vector3D_SSE divisor) { return vector /= divisor; }
+
+ friend QT3DCORE_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Vector3D_SSE &v);
+ friend Q_ALWAYS_INLINE bool qFuzzyCompare(const Vector3D_SSE& v1, const Vector3D_SSE& v2)
+ {
+ return ::qFuzzyCompare(v1.x(), v2.x()) &&
+ ::qFuzzyCompare(v1.y(), v2.y()) &&
+ ::qFuzzyCompare(v1.z(), v2.z());
+ }
+
+private:
+ // Q_DECL_ALIGN(16) float m[4];// for SSE support
+ __m128 m_xyzw;
+};
+
+} // Qt3DCore
+
+Q_DECLARE_TYPEINFO(Qt3DCore::Vector3D_SSE, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DCore::Vector3D_SSE)
+
+#endif // QT_COMPILER_SUPPORTS_SSE2
+
+#endif // QT3DCORE_VECTOR3D_SSE_P_H
diff --git a/src/core/transforms/vector4d_p.h b/src/core/transforms/vector4d_p.h
new file mode 100644
index 000000000..584eb470b
--- /dev/null
+++ b/src/core/transforms/vector4d_p.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_VECTOR4D_P_H
+#define QT3DCORE_VECTOR4D_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <private/qsimd_p.h>
+
+#if (defined(__AVX2__) || defined(__SSE2__)) && defined(QT_COMPILER_SUPPORTS_SSE2)
+
+#include <Qt3DCore/private/vector4d_sse_p.h>
+
+QT_BEGIN_NAMESPACE
+using Vector4D = Qt3DCore::Vector4D_SSE;
+QT_END_NAMESPACE
+
+#else
+
+#include <QVector4D>
+
+QT_BEGIN_NAMESPACE
+using Vector4D = QVector4D;
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QT3DCORE_VECTOR4D_P_H
diff --git a/src/core/transforms/vector4d_sse.cpp b/src/core/transforms/vector4d_sse.cpp
new file mode 100644
index 000000000..c7c76a694
--- /dev/null
+++ b/src/core/transforms/vector4d_sse.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "vector4d_sse_p.h"
+#include <QDebug>
+
+#ifdef QT_COMPILER_SUPPORTS_SSE2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+QDebug operator<<(QDebug dbg, const Vector4D_SSE &v)
+{
+ dbg.nospace() << "Vector4D_SSE(" << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << ") ";
+ return dbg;
+}
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT_COMPILER_SUPPORTS_SSE2
diff --git a/src/core/transforms/vector4d_sse_p.h b/src/core/transforms/vector4d_sse_p.h
new file mode 100644
index 000000000..ffeca0946
--- /dev/null
+++ b/src/core/transforms/vector4d_sse_p.h
@@ -0,0 +1,396 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DCORE_VECTOR4D_SSE_P_H
+#define QT3DCORE_VECTOR4D_SSE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt3D 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 <Qt3DCore/private/vector3d_p.h>
+#include <QtGui/qvector4d.h>
+
+#ifdef QT_COMPILER_SUPPORTS_SSE2
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class Matrix4x4_SSE;
+class Matrix4x4_AVX2;
+
+class Vector4D_SSE
+{
+public:
+ Q_ALWAYS_INLINE Vector4D_SSE()
+ : m_xyzw(_mm_setzero_ps())
+ {
+ }
+
+ explicit Q_ALWAYS_INLINE Vector4D_SSE(Qt::Initialization) {}
+
+ explicit Q_ALWAYS_INLINE Vector4D_SSE(float x, float y, float z, float w)
+ : m_xyzw(_mm_set_ps(w, z, y, x))
+ {
+ }
+
+ explicit Q_ALWAYS_INLINE Vector4D_SSE(QVector4D v)
+ : m_xyzw(_mm_set_ps(v.w(), v.z(), v.y(), v.x()))
+ {
+ }
+
+ explicit Q_ALWAYS_INLINE Vector4D_SSE(const Vector3D_SSE &vec3, float w = 0.0f)
+ : m_xyzw(vec3.m_xyzw)
+ {
+ setW(w);
+ }
+
+ explicit Q_ALWAYS_INLINE Vector4D_SSE(QVector3D v, float w = 0.0f)
+ : m_xyzw(_mm_set_ps(w, v.z(), v.y(), v.x()))
+ {
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE &operator+=(Vector4D_SSE vector)
+ {
+ m_xyzw = _mm_add_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE &operator-=(Vector4D_SSE vector)
+ {
+ m_xyzw = _mm_sub_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE &operator*=(Vector4D_SSE vector)
+ {
+ m_xyzw = _mm_mul_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE &operator/=(Vector4D_SSE vector)
+ {
+ m_xyzw = _mm_div_ps(m_xyzw, vector.m_xyzw);
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE &operator*=(float factor)
+ {
+ m_xyzw = _mm_mul_ps(m_xyzw, _mm_set1_ps(factor));
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE &operator/=(float factor)
+ {
+ m_xyzw = _mm_div_ps(m_xyzw, _mm_set1_ps(factor));
+ return *this;
+ }
+
+ Q_ALWAYS_INLINE bool operator==(Vector4D_SSE other) const
+ {
+ // 0b1111 == 0xf
+ return (_mm_movemask_ps(_mm_cmpeq_ps(m_xyzw, other.m_xyzw)) == 0xf);
+ }
+
+ Q_ALWAYS_INLINE bool operator!=(Vector4D_SSE other) const
+ {
+ return !(*this == other);
+ }
+
+ Q_ALWAYS_INLINE QVector4D toQVector4D() const
+ {
+ return QVector4D(x(), y(), z(), w());
+ }
+
+ // TODO: Uncomment when we introduce Vector3D_SSE
+ //Q_ALWAYS_INLINE Vector3D_SSE toVector3D() const { return Vector3D_SSE(*this); }
+
+ Q_ALWAYS_INLINE float lengthSquared() const
+ {
+ return dotProduct(*this, *this);
+ }
+
+ Q_ALWAYS_INLINE float length() const
+ {
+ return sqrt(dotProduct(*this, *this));
+ }
+
+ Q_ALWAYS_INLINE void normalize()
+ {
+ const float len = length();
+ m_xyzw = _mm_div_ps(m_xyzw, _mm_set_ps1(len));
+ }
+
+ Q_ALWAYS_INLINE Vector4D_SSE normalized() const
+ {
+ Vector4D_SSE v = *this;
+ v.normalize();
+ return v;
+ }
+
+ Q_ALWAYS_INLINE bool isNull() const
+ {
+ // 0b1111 == 0xf
+ return _mm_movemask_ps(_mm_cmpeq_ps(m_xyzw, _mm_setzero_ps())) == 0xf;
+ }
+
+ Q_ALWAYS_INLINE float x() const { return _mm_cvtss_f32(m_xyzw); }
+
+ Q_ALWAYS_INLINE float y() const
+ {
+ // 0b01010101 = 0x55
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_xyzw, m_xyzw, 0x55));
+ }
+
+ Q_ALWAYS_INLINE float z() const
+ {
+ // 0b10101010 = 0xaa
+ return _mm_cvtss_f32(_mm_unpackhi_ps(m_xyzw, m_xyzw));
+ }
+
+ Q_ALWAYS_INLINE float w() const
+ {
+ // 0b11111111 = 0xff
+ return _mm_cvtss_f32(_mm_shuffle_ps(m_xyzw, m_xyzw, 0xff));
+ }
+
+ Q_ALWAYS_INLINE void setX(float x)
+ {
+ m_xyzw = _mm_move_ss(m_xyzw, _mm_set_ss(x));
+ }
+
+ Q_ALWAYS_INLINE void setY(float y)
+ {
+ // m_xyzw = a, b, c, d
+
+ // y, y, y, y
+ const __m128 yVec = _mm_set_ps1(y);
+
+ // y, y, a, a
+ // 0b00000000 == 0x0
+ const __m128 yaVec = _mm_shuffle_ps(yVec, m_xyzw, 0x0);
+
+ // a, y, c, d
+ // 0b11100010 == 0xe2
+ m_xyzw = _mm_shuffle_ps(yaVec, m_xyzw, 0xe2);
+ }
+
+ Q_ALWAYS_INLINE void setZ(float z)
+ {
+ // m_xyzw = a, b, c, d
+
+ // z, z, z, z
+ const __m128 zVec = _mm_set_ps1(z);
+
+ // z, z, d, d
+ // 0b11110000 == 0xf0
+ const __m128 zdVec = _mm_shuffle_ps(zVec, m_xyzw, 0xf0);
+
+ // a, b, z, d
+ // 0b10000100 == 0x84
+ m_xyzw = _mm_shuffle_ps(m_xyzw, zdVec, 0x84);
+ }
+
+ Q_ALWAYS_INLINE void setW(float w)
+ {
+#ifdef __SSE4_1__
+ const __m128 wVec = _mm_set_ss(w);
+ // insert element 0 of wVec into position 3 in vec3, don't zero anything
+ m_xyzw = _mm_insert_ps(m_xyzw, wVec, 0x30);
+#else
+ // m_xyzw = a, b, c, d
+
+ // w, w, w, w
+ const __m128 wVec = _mm_set_ps1(w);
+
+ // c, c, w, w
+ const __m128 cwVec = _mm_shuffle_ps(m_xyzw, wVec, _MM_SHUFFLE(0, 0, 2, 2));
+
+ // a, b, c, w
+ m_xyzw = _mm_shuffle_ps(m_xyzw, cwVec, _MM_SHUFFLE(2, 0, 1, 0));
+#endif
+ }
+
+ Q_ALWAYS_INLINE float operator[](int idx) const
+ {
+ Q_DECL_ALIGN(16) float vec[4];
+ _mm_store_ps(vec, m_xyzw);
+ return vec[idx];
+ }
+
+ struct DigitWrapper
+ {
+ explicit DigitWrapper(int idx, Vector4D_SSE *vec)
+ : m_vec(vec)
+ , m_idx(idx)
+ {}
+
+ operator float() const
+ {
+ switch (m_idx) {
+ case 0:
+ return m_vec->x();
+ case 1:
+ return m_vec->y();
+ case 2:
+ return m_vec->z();
+ case 3:
+ return m_vec->w();
+ default:
+ Q_UNREACHABLE();
+ return 0.0f;
+ }
+ }
+ void operator =(float value)
+ {
+ switch (m_idx) {
+ case 0:
+ m_vec->setX(value);
+ break;
+ case 1:
+ m_vec->setY(value);
+ break;
+ case 2:
+ m_vec->setZ(value);
+ break;
+ case 3:
+ m_vec->setW(value);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+
+ private:
+ Vector4D_SSE *m_vec;
+ const int m_idx;
+ };
+
+ Q_ALWAYS_INLINE DigitWrapper operator[](int idx)
+ {
+ return DigitWrapper(idx, this);
+ }
+
+ static Q_ALWAYS_INLINE float dotProduct(Vector4D_SSE a, Vector4D_SSE b)
+ {
+#if defined(__SSE4_1__)
+ // 0b11111111 = 0xff
+ return _mm_cvtss_f32(_mm_dp_ps(a.m_xyzw, b.m_xyzw, 0xff));
+#elif defined(__SSE3__)
+ const __m128 mult = _mm_mul_ps(a.m_xyzw, b.m_xyzw);
+ // a + b, c + d, a + d, c + d
+ const __m128 partialSum = _mm_hadd_ps(mult, mult);
+ // c + d, ......
+ // 0x00000001 =
+ const __m128 partialSumShuffle = _mm_shuffle_ps(partialSum, partialSum, 0x1);
+ return _mm_cvtss_f32(_mm_hadd_ps(partialSum, partialSumShuffle));
+#else
+ const __m128 mult = _mm_mul_ps(a.m_xyzw, b.m_xyzw);
+ // (multX, multY, 0, 0) + (multZ, multW, 0, 0) -> (multX + multZ, multY + multW, 0, 0)
+ // 0b00001110 == 0xe
+ const __m128 shuffled = _mm_shuffle_ps(mult, mult, 0xe);
+ __m128 result = _mm_add_ps(shuffled, mult);
+ // (multX + multZ, 0, 0, 0) + (multY + multW, 0, 0, 0);
+ // 0b00000001 == 0x1
+ const __m128 shuffled2 = _mm_shuffle_ps(result, result, 0x1);
+ result = _mm_add_ps(result, shuffled2);
+ return _mm_cvtss_f32(result);
+#endif
+ }
+
+ friend class Matrix4x4_AVX2;
+ friend class Matrix4x4_SSE;
+ friend class Vector3D_SSE;
+
+ friend Vector4D_SSE operator*(const Vector4D_SSE &vector, const Matrix4x4_SSE &matrix);
+ friend Vector4D_SSE operator*(const Matrix4x4_SSE &matrix, const Vector4D_SSE &vector);
+
+ friend Vector4D_SSE operator*(const Vector4D_SSE &vector, const Matrix4x4_AVX2 &matrix);
+ friend Vector4D_SSE operator*(const Matrix4x4_AVX2 &matrix, const Vector4D_SSE &vector);
+
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator+(Vector4D_SSE v1, Vector4D_SSE v2) { return v1 += v2; }
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator-(Vector4D_SSE v1, Vector4D_SSE v2) { return v1 -= v2; }
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator*(float factor, Vector4D_SSE vector) { return vector *= factor; }
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator*(Vector4D_SSE vector, float factor) { return vector *= factor; }
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator*(Vector4D_SSE v1, Vector4D_SSE v2) { return v1 *= v2; }
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator-(Vector4D_SSE vector)
+ {
+ Vector4D_SSE c(Qt::Uninitialized);
+
+ c.m_xyzw = _mm_xor_ps(vector.m_xyzw, _mm_set1_ps(-0.0f));
+
+ return c;
+ }
+
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator/(Vector4D_SSE vector, float divisor) { return vector /= divisor; }
+ friend Q_ALWAYS_INLINE const Vector4D_SSE operator/(Vector4D_SSE vector, Vector4D_SSE divisor) { return vector /= divisor; }
+
+ friend QT3DCORE_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const Vector4D_SSE &v);
+ friend Q_ALWAYS_INLINE bool qFuzzyCompare(const Vector4D_SSE& v1, const Vector4D_SSE& v2)
+ {
+ return ::qFuzzyCompare(v1.x(), v2.x()) &&
+ ::qFuzzyCompare(v1.y(), v2.y()) &&
+ ::qFuzzyCompare(v1.z(), v2.z()) &&
+ ::qFuzzyCompare(v1.w(), v2.w());
+ }
+
+private:
+ // Q_DECL_ALIGN(16) float m[4];// for SSE support
+ __m128 m_xyzw;
+};
+
+} // Qt3DCore
+
+Q_DECLARE_TYPEINFO(Qt3DCore::Vector4D_SSE, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Qt3DCore::Vector4D_SSE)
+
+#endif // QT_COMPILER_SUPPORTS_SSE2
+
+#endif // QT3DCORE_VECTOR4D_SSE_P_H