diff options
Diffstat (limited to 'src/core/transforms')
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 |