Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dparticlemodelparticle.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
7
9
10/*!
11 \qmltype ModelParticle3D
12 \inherits Particle3D
13 \inqmlmodule QtQuick3D.Particles3D
14 \brief Particle using a Qt Quick 3D Model.
15 \since 6.2
16
17 The ModelParticle3D is a logical particle element that creates particles
18 from a Qt Quick 3D \l Model component.
19*/
20
21QQuick3DParticleModelParticle::QQuick3DParticleModelParticle(QQuick3DNode *parent)
22 : QQuick3DParticle(parent)
23 , m_initialScale(1.0f, 1.0f, 1.0f)
24{
25 QObject::connect(this, &QQuick3DParticle::maxAmountChanged, this, [this]() {
26 handleMaxAmountChanged(m_maxAmount);
27 });
28 QObject::connect(this, &QQuick3DParticle::sortModeChanged, this, [this]() {
29 handleSortModeChanged(sortMode());
30 });
31}
32
33void QQuick3DParticleModelParticle::handleMaxAmountChanged(int amount)
34{
35 if (m_particleData.size() == amount)
36 return;
37
38 m_particleData.resize(amount);
39 m_particleData.fill({});
40}
41
42/*!
43 \qmlproperty Component ModelParticle3D::delegate
44
45 The delegate provides a template defining each object instantiated by the particle.
46
47 For example, to allocate 200 red cube particles:
48
49 \qml
50 Component {
51 id: particleComponent
52 Model {
53 source: "#Cube"
54 scale: Qt.vector3d(0.2, 0.2, 0.2)
55 materials: DefaultMaterial { }
56 }
57 }
58
59 ModelParticle3D {
60 id: particleRed
61 delegate: particleComponent
62 maxAmount: 200
63 color: "#ff0000"
64 }
65 \endqml
66
67 \note The content instantiated from the delegate keeps its own
68 \l {QtQuick3D::Node::layers}{layers} assignment; the layers value of the
69 \l ParticleSystem3D is not applied to it. To render the particles on a
70 specific content layer, set the layers property on the nodes in the
71 delegate.
72*/
73QQmlComponent *QQuick3DParticleModelParticle::delegate() const
74{
75 return m_delegate.data();
76}
77
78void QQuick3DParticleModelParticle::setDelegate(QQmlComponent *delegate)
79{
80 if (delegate == m_delegate)
81 return;
82 m_delegate = delegate;
83
84 regenerate();
85 Q_EMIT delegateChanged();
86}
87
89{
90 struct SortData
91 {
92 float age;
93 int index;
94 };
95
96public:
98 void clear() { m_instances.clear(); m_sortData.clear(); }
99 void commit() { sort(); markDirty(); }
100 void addInstance(const QVector3D &position,
101 const QVector3D &scale,
102 const QVector3D &eulerRotation,
103 const QColor &color,
104 float age) {
105 auto entry = calculateTableEntry(position, scale, eulerRotation, color);
106 m_instances.append(reinterpret_cast<char *>(&entry), sizeof(InstanceTableEntry));
107 if (m_ageSorting)
108 m_sortData.append({age, int(m_instances.size() / sizeof(InstanceTableEntry))});
109 }
110 void setSorting(bool enable, bool inverted = false)
111 {
112 m_ageSorting = enable;
113 m_inverted = inverted;
114 }
115protected:
116 QByteArray getInstanceBuffer(int *instanceCount) override
117 {
118 if (instanceCount)
119 *instanceCount = int(m_instances.size() / sizeof(InstanceTableEntry));
120
121 if (!m_ageSorting)
122 return m_instances;
123 return m_sortedInstances;
124 }
125 void sort()
126 {
127 if (!m_ageSorting)
128 return;
129
130 if (m_inverted) {
131 std::sort(m_sortData.begin(), m_sortData.end(), [&](const SortData &a, const SortData &b) {
132 return a.age < b.age;
133 });
134 } else {
135 std::sort(m_sortData.begin(), m_sortData.end(), [&](const SortData &a, const SortData &b) {
136 return a.age > b.age;
137 });
138 }
139 m_sortedInstances.resize(m_instances.size());
140 const InstanceTableEntry *src = reinterpret_cast<InstanceTableEntry *>(m_instances.data());
141 InstanceTableEntry *dst = reinterpret_cast<InstanceTableEntry *>(m_sortedInstances.data());
142 for (auto &e : m_sortData)
143 *dst++ = src[e.index];
144 }
145
146private:
147 QList<SortData> m_sortData;
148 QByteArray m_instances;
149 QByteArray m_sortedInstances;
150 bool m_ageSorting = false;
151 bool m_inverted = false;
152};
153
154void QQuick3DParticleModelParticle::handleSortModeChanged(QQuick3DParticle::SortMode mode)
155{
156 if (m_instanceTable) {
157 if (mode == QQuick3DParticle::SortNewest || mode == QQuick3DParticle::SortOldest)
158 m_instanceTable->setSorting(true, mode == QQuick3DParticle::SortNewest);
159 else
160 m_instanceTable->setSorting(false);
161 m_instanceTable->setDepthSortingEnabled(mode == QQuick3DParticle::SortDistance);
162 }
163}
164
165/*!
166 \qmlproperty Instancing ModelParticle3D::instanceTable
167
168 The instanceTable provides access to the \l Instancing table of the model particle.
169 ModelParticle3D uses an internal instance table to implement efficient rendering.
170 This table can be applied to the \l{Model::instancing}{instancing} property of models
171 that are not part of the particle system.
172
173 It is also possible to use this feature to provide an instancing table without showing any
174 particles. This is done by omitting the \l delegate property. For example:
175
176 \qml
177 ParticleSystem3D {
178 id: psystem
179 ModelParticle3D {
180 id: particleRed
181 maxAmount: 200
182 color: "#ff0000"
183 colorVariation: Qt.vector4d(0.5,0.5,0.5,0.5)
184 }
185
186 ParticleEmitter3D {
187 particle: particleRed
188 velocity: VectorDirection3D {
189 direction: Qt.vector3d(-20, 200, 0)
190 directionVariation: Qt.vector3d(20, 20, 20)
191 }
192 particleScale: 0.2
193 emitRate: 20
194 lifeSpan: 2000
195 }
196 }
197
198 Model {
199 source: "#Sphere"
200 instancing: particleRed.instanceTable
201 materials: PrincipledMaterial {
202 baseColor: "yellow"
203 }
204 }
205 \endqml
206*/
207
208QQuick3DInstancing *QQuick3DParticleModelParticle::instanceTable() const
209{
210 return m_instanceTable;
211}
212
213void QQuick3DParticleModelParticle::clearInstanceTable()
214{
215 if (m_instanceTable)
216 m_instanceTable->clear();
217}
218
219void QQuick3DParticleModelParticle::addInstance(const QVector3D &position, const QVector3D &scale, const QVector3D &eulerRotation, const QColor &color, float age)
220{
221 if (m_instanceTable)
222 m_instanceTable->addInstance(position, scale, eulerRotation, color, age);
223}
224
225void QQuick3DParticleModelParticle::commitInstance()
226{
227 if (m_instanceTable) {
228 m_instanceTable->setHasTransparency(hasTransparency());
229 m_instanceTable->commit();
230 }
231}
232
233static void setInstancing(QQuick3DNode *node, QQuick3DInstancing *instanceTable, float bias)
234{
235 auto *asModel = qobject_cast<QQuick3DModel *>(node);
236 if (asModel) {
237 asModel->setInstancing(instanceTable);
238 asModel->setDepthBias(bias);
239 }
240 const auto children = node->childItems();
241 for (auto *child : children) {
242 auto *childNode = qobject_cast<QQuick3DNode *>(child);
243 if (childNode)
244 setInstancing(childNode, instanceTable, bias);
245 }
246}
247
248void QQuick3DParticleModelParticle::updateDepthBias(float bias)
249{
250 setInstancing(m_node, m_instanceTable, bias);
251}
252
253void QQuick3DParticleModelParticle::regenerate()
254{
255 delete m_node;
256 m_node = nullptr;
257
258 if (!isComponentComplete() || !parentItem())
259 return;
260
261 if (!m_instanceTable) {
262 m_instanceTable = new QQuick3DParticleInstanceTable();
263 m_instanceTable->setParent(this);
264 m_instanceTable->setParentItem(this);
265 emit instanceTableChanged();
266 } else {
267 m_instanceTable->clear();
268 }
269
270 if (m_delegate.isNull())
271 return;
272
273 auto *obj = m_delegate->create(m_delegate->creationContext());
274
275 m_node = qobject_cast<QQuick3DNode *>(obj);
276 if (m_node) {
277 setInstancing(m_node, m_instanceTable, depthBias());
278 auto *particleSystem = system();
279 m_node->setParent(particleSystem);
280 m_node->setParentItem(particleSystem);
281 } else {
282 delete obj;
283 }
284}
285
286void QQuick3DParticleModelParticle::componentComplete()
287{
288 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
289 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
290
291 QQuick3DParticle::componentComplete();
292 regenerate();
293}
294
295void QQuick3DParticleModelParticle::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &value)
296{
297 QQuick3DObject::itemChange(change, value);
298 if (change == ItemParentHasChanged)
299 regenerate();
300}
301
302QT_END_NAMESPACE
QByteArray getInstanceBuffer(int *instanceCount) override
Implement this function to return the contents of the instance table.
void setSorting(bool enable, bool inverted=false)
void addInstance(const QVector3D &position, const QVector3D &scale, const QVector3D &eulerRotation, const QColor &color, float age)
Combined button and popup list for selecting options.
static void setInstancing(QQuick3DNode *node, QQuick3DInstancing *instanceTable, float bias)