1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qquick3dparticleshape_p.h" |
5 | #include "qquick3dparticlerandomizer_p.h" |
6 | #include "qquick3dparticlesystem_p.h" |
7 | #include "qquick3dparticleutils_p.h" |
8 | #include <QtCore/qdir.h> |
9 | #include <QtQml/qqmlfile.h> |
10 | #include <QtQuick3D/private/qquick3dobject_p.h> |
11 | #include <QtQuick3D/private/qquick3dmodel_p.h> |
12 | #include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h> |
13 | #include <algorithm> |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | /*! |
18 | \qmltype ParticleShape3D |
19 | \inherits ParticleAbtractShape3D |
20 | \inqmlmodule QtQuick3D.Particles3D |
21 | \brief Offers 3D shapes for emitters and affectors. |
22 | \since 6.2 |
23 | |
24 | The ParticleShape3D element supports shapes like \c Cube, \c Sphere and \c Cylinder for particles needs. |
25 | For example, emitter can use \l {ParticleEmitter3D::shape}{shape} property to emit particles from the |
26 | shape area. |
27 | |
28 | Shapes don't have position, scale or rotation. Instead, they use parent node for these properties. |
29 | */ |
30 | |
31 | QQuick3DParticleShape::QQuick3DParticleShape(QObject *parent) |
32 | : QQuick3DParticleAbstractShape(parent) |
33 | { |
34 | } |
35 | |
36 | /*! |
37 | \qmlproperty bool ParticleShape3D::fill |
38 | |
39 | This property defines if the shape should be filled or just use the shape outlines. |
40 | |
41 | The default value is \c true. |
42 | */ |
43 | bool QQuick3DParticleShape::fill() const |
44 | { |
45 | return m_fill; |
46 | } |
47 | |
48 | void QQuick3DParticleShape::setFill(bool fill) |
49 | { |
50 | if (m_fill == fill) |
51 | return; |
52 | |
53 | m_fill = fill; |
54 | Q_EMIT fillChanged(); |
55 | } |
56 | |
57 | /*! |
58 | \qmlproperty ShapeType ParticleShape3D::type |
59 | |
60 | This property defines the type of the shape. |
61 | |
62 | The default value is \c ParticleShape3D.Cube. |
63 | */ |
64 | |
65 | /*! |
66 | \qmlproperty enumeration ParticleShape3D::ShapeType |
67 | |
68 | Defines the type of the shape. |
69 | |
70 | \value ParticleShape3D.Cube |
71 | Cube shape. |
72 | \value ParticleShape3D.Sphere |
73 | Sphere shape. |
74 | \value ParticleShape3D.Cylinder |
75 | Cylinder shape. |
76 | */ |
77 | |
78 | QQuick3DParticleShape::ShapeType QQuick3DParticleShape::type() const |
79 | { |
80 | return m_type; |
81 | } |
82 | |
83 | /*! |
84 | \qmlproperty vector3d ParticleShape3D::extents |
85 | |
86 | This property defines the extents of the shape. |
87 | |
88 | The default value for each axis is \c 50. |
89 | */ |
90 | QVector3D QQuick3DParticleShape::extents() const |
91 | { |
92 | return m_extents; |
93 | } |
94 | |
95 | void QQuick3DParticleShape::setType(QQuick3DParticleShape::ShapeType type) |
96 | { |
97 | if (m_type == type) |
98 | return; |
99 | |
100 | m_type = type; |
101 | Q_EMIT typeChanged(); |
102 | } |
103 | |
104 | void QQuick3DParticleShape::setExtents(QVector3D extents) |
105 | { |
106 | if (m_extents == extents) |
107 | return; |
108 | |
109 | m_extents = extents; |
110 | Q_EMIT extentsChanged(); |
111 | } |
112 | |
113 | QVector3D QQuick3DParticleShape::getPosition(int particleIndex) |
114 | { |
115 | if (!parentNode() || !m_system) |
116 | return QVector3D(); |
117 | |
118 | switch (m_type) { |
119 | case QQuick3DParticleShape::ShapeType::Cube: |
120 | return randomPositionCube(particleIndex); |
121 | case QQuick3DParticleShape::ShapeType::Sphere: |
122 | return randomPositionSphere(particleIndex); |
123 | case QQuick3DParticleShape::ShapeType::Cylinder: |
124 | return randomPositionCylinder(particleIndex); |
125 | } |
126 | Q_UNREACHABLE_RETURN(QVector3D()); |
127 | } |
128 | |
129 | QVector3D QQuick3DParticleShape::randomPositionCube(int particleIndex) const |
130 | { |
131 | auto rand = m_system->rand(); |
132 | QVector3D s = m_parentNode->scale() * m_extents; |
133 | float x = s.x() - (rand->get(particleIndex, user: QPRand::Shape1) * s.x() * 2.0f); |
134 | float y = s.y() - (rand->get(particleIndex, user: QPRand::Shape2) * s.y() * 2.0f); |
135 | float z = s.z() - (rand->get(particleIndex, user: QPRand::Shape3) * s.z() * 2.0f); |
136 | if (!m_fill) { |
137 | // Random 0..5 for cube sides |
138 | int side = int(rand->get(particleIndex, user: QPRand::Shape4) * 6); |
139 | if (side == 0) |
140 | x = -s.x(); |
141 | else if (side == 1) |
142 | x = s.x(); |
143 | else if (side == 2) |
144 | y = -s.y(); |
145 | else if (side == 3) |
146 | y = s.y(); |
147 | else if (side == 4) |
148 | z = -s.z(); |
149 | else |
150 | z = s.z(); |
151 | } |
152 | QMatrix4x4 mat; |
153 | mat.rotate(quaternion: m_parentNode->rotation()); |
154 | return mat.mapVector(vector: QVector3D(x, y, z)); |
155 | } |
156 | |
157 | QVector3D QQuick3DParticleShape::randomPositionSphere(int particleIndex) const |
158 | { |
159 | auto rand = m_system->rand(); |
160 | QVector3D scale = m_parentNode->scale() * m_extents; |
161 | float theta = rand->get(particleIndex, user: QPRand::Shape1) * float(M_PI) * 2.0f; |
162 | float v = rand->get(particleIndex, user: QPRand::Shape2); |
163 | float phi = acos(x: (2.0f * v) - 1.0f); |
164 | float r = m_fill ? pow(x: rand->get(particleIndex, user: QPRand::Shape3), y: 1.0f / 3.0f) : 1.0f; |
165 | float x = r * QPSIN(x: phi) * QPCOS(x: theta); |
166 | float y = r * QPSIN(x: phi) * QPSIN(x: theta); |
167 | float z = r * QPCOS(x: phi); |
168 | QVector3D pos(x, y, z); |
169 | pos *= scale; |
170 | QMatrix4x4 mat; |
171 | mat.rotate(quaternion: m_parentNode->rotation()); |
172 | return mat.mapVector(vector: pos); |
173 | } |
174 | |
175 | QVector3D QQuick3DParticleShape::randomPositionCylinder(int particleIndex) const |
176 | { |
177 | auto rand = m_system->rand(); |
178 | QVector3D scale = m_parentNode->scale() * m_extents; |
179 | float y = scale.y() - (rand->get(particleIndex, user: QPRand::Shape1) * scale.y() * 2.0f); |
180 | float r = 1.0f; |
181 | if (m_fill) |
182 | r = sqrt(x: rand->get(particleIndex, user: QPRand::Shape2)); |
183 | float theta = rand->get(particleIndex, user: QPRand::Shape3) * float(M_PI) * 2.0f; |
184 | float x = r * QPCOS(x: theta); |
185 | float z = r * QPSIN(x: theta); |
186 | x = x * scale.x(); |
187 | z = z * scale.z(); |
188 | QVector3D pos(x, y, z); |
189 | QMatrix4x4 mat; |
190 | mat.rotate(quaternion: m_parentNode->rotation()); |
191 | return mat.mapVector(vector: pos); |
192 | } |
193 | |
194 | QT_END_NAMESPACE |
195 | |