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
15QT_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
31QQuick3DParticleShape::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*/
43bool QQuick3DParticleShape::fill() const
44{
45 return m_fill;
46}
47
48void 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
78QQuick3DParticleShape::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*/
90QVector3D QQuick3DParticleShape::extents() const
91{
92 return m_extents;
93}
94
95void QQuick3DParticleShape::setType(QQuick3DParticleShape::ShapeType type)
96{
97 if (m_type == type)
98 return;
99
100 m_type = type;
101 Q_EMIT typeChanged();
102}
103
104void QQuick3DParticleShape::setExtents(QVector3D extents)
105{
106 if (m_extents == extents)
107 return;
108
109 m_extents = extents;
110 Q_EMIT extentsChanged();
111}
112
113QVector3D 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
129QVector3D 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
157QVector3D 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
175QVector3D 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
194QT_END_NAMESPACE
195

source code of qtquick3d/src/quick3dparticles/qquick3dparticleshape.cpp