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*/
67QQmlComponent *QQuick3DParticleModelParticle::delegate() const
68{
69 return m_delegate.data();
70}
71
72void QQuick3DParticleModelParticle::setDelegate(QQmlComponent *delegate)
73{
74 if (delegate == m_delegate)
75 return;
76 m_delegate = delegate;
77
78 regenerate();
79 Q_EMIT delegateChanged();
80}
81
83{
84 struct SortData
85 {
86 float age;
87 int index;
88 };
89
90public:
92 void clear() { m_instances.clear(); m_sortData.clear(); }
93 void commit() { sort(); markDirty(); }
94 void addInstance(const QVector3D &position,
95 const QVector3D &scale,
96 const QVector3D &eulerRotation,
97 const QColor &color,
98 float age) {
99 auto entry = calculateTableEntry(position, scale, eulerRotation, color);
100 m_instances.append(reinterpret_cast<char *>(&entry), sizeof(InstanceTableEntry));
101 if (m_ageSorting)
102 m_sortData.append({age, int(m_instances.size() / sizeof(InstanceTableEntry))});
103 }
104 void setSorting(bool enable, bool inverted = false)
105 {
106 m_ageSorting = enable;
107 m_inverted = inverted;
108 }
109protected:
110 QByteArray getInstanceBuffer(int *instanceCount) override
111 {
112 if (instanceCount)
113 *instanceCount = int(m_instances.size() / sizeof(InstanceTableEntry));
114
115 if (!m_ageSorting)
116 return m_instances;
117 return m_sortedInstances;
118 }
119 void sort()
120 {
121 if (!m_ageSorting)
122 return;
123
124 if (m_inverted) {
125 std::sort(m_sortData.begin(), m_sortData.end(), [&](const SortData &a, const SortData &b) {
126 return a.age < b.age;
127 });
128 } else {
129 std::sort(m_sortData.begin(), m_sortData.end(), [&](const SortData &a, const SortData &b) {
130 return a.age > b.age;
131 });
132 }
133 m_sortedInstances.resize(m_instances.size());
134 const InstanceTableEntry *src = reinterpret_cast<InstanceTableEntry *>(m_instances.data());
135 InstanceTableEntry *dst = reinterpret_cast<InstanceTableEntry *>(m_sortedInstances.data());
136 for (auto &e : m_sortData)
137 *dst++ = src[e.index];
138 }
139
140private:
141 QList<SortData> m_sortData;
142 QByteArray m_instances;
143 QByteArray m_sortedInstances;
144 bool m_ageSorting = false;
145 bool m_inverted = false;
146};
147
148void QQuick3DParticleModelParticle::handleSortModeChanged(QQuick3DParticle::SortMode mode)
149{
150 if (m_instanceTable) {
151 if (mode == QQuick3DParticle::SortNewest || mode == QQuick3DParticle::SortOldest)
152 m_instanceTable->setSorting(true, mode == QQuick3DParticle::SortNewest);
153 else
154 m_instanceTable->setSorting(false);
155 m_instanceTable->setDepthSortingEnabled(mode == QQuick3DParticle::SortDistance);
156 }
157}
158
159/*!
160 \qmlproperty Instancing ModelParticle3D::instanceTable
161
162 The instanceTable provides access to the \l Instancing table of the model particle.
163 ModelParticle3D uses an internal instance table to implement efficient rendering.
164 This table can be applied to the \l{Model::instancing}{instancing} property of models
165 that are not part of the particle system.
166
167 It is also possible to use this feature to provide an instancing table without showing any
168 particles. This is done by omitting the \l delegate property. For example:
169
170 \qml
171 ParticleSystem3D {
172 id: psystem
173 ModelParticle3D {
174 id: particleRed
175 maxAmount: 200
176 color: "#ff0000"
177 colorVariation: Qt.vector4d(0.5,0.5,0.5,0.5)
178 }
179
180 ParticleEmitter3D {
181 particle: particleRed
182 velocity: VectorDirection3D {
183 direction: Qt.vector3d(-20, 200, 0)
184 directionVariation: Qt.vector3d(20, 20, 20)
185 }
186 particleScale: 0.2
187 emitRate: 20
188 lifeSpan: 2000
189 }
190 }
191
192 Model {
193 source: "#Sphere"
194 instancing: particleRed.instanceTable
195 materials: PrincipledMaterial {
196 baseColor: "yellow"
197 }
198 }
199 \endqml
200*/
201
202QQuick3DInstancing *QQuick3DParticleModelParticle::instanceTable() const
203{
204 return m_instanceTable;
205}
206
207void QQuick3DParticleModelParticle::clearInstanceTable()
208{
209 if (m_instanceTable)
210 m_instanceTable->clear();
211}
212
213void QQuick3DParticleModelParticle::addInstance(const QVector3D &position, const QVector3D &scale, const QVector3D &eulerRotation, const QColor &color, float age)
214{
215 if (m_instanceTable)
216 m_instanceTable->addInstance(position, scale, eulerRotation, color, age);
217}
218
219void QQuick3DParticleModelParticle::commitInstance()
220{
221 if (m_instanceTable) {
222 m_instanceTable->setHasTransparency(hasTransparency());
223 m_instanceTable->commit();
224 }
225}
226
227static void setInstancing(QQuick3DNode *node, QQuick3DInstancing *instanceTable, float bias)
228{
229 auto *asModel = qobject_cast<QQuick3DModel *>(node);
230 if (asModel) {
231 asModel->setInstancing(instanceTable);
232 asModel->setDepthBias(bias);
233 }
234 const auto children = node->childItems();
235 for (auto *child : children) {
236 auto *childNode = qobject_cast<QQuick3DNode *>(child);
237 if (childNode)
238 setInstancing(childNode, instanceTable, bias);
239 }
240}
241
242void QQuick3DParticleModelParticle::updateDepthBias(float bias)
243{
244 setInstancing(m_node, m_instanceTable, bias);
245}
246
247void QQuick3DParticleModelParticle::regenerate()
248{
249 delete m_node;
250 m_node = nullptr;
251
252 if (!isComponentComplete() || !parentItem())
253 return;
254
255 if (!m_instanceTable) {
256 m_instanceTable = new QQuick3DParticleInstanceTable();
257 m_instanceTable->setParent(this);
258 m_instanceTable->setParentItem(this);
259 emit instanceTableChanged();
260 } else {
261 m_instanceTable->clear();
262 }
263
264 if (m_delegate.isNull())
265 return;
266
267 auto *obj = m_delegate->create(m_delegate->creationContext());
268
269 m_node = qobject_cast<QQuick3DNode *>(obj);
270 if (m_node) {
271 setInstancing(m_node, m_instanceTable, depthBias());
272 auto *particleSystem = system();
273 m_node->setParent(particleSystem);
274 m_node->setParentItem(particleSystem);
275 } else {
276 delete obj;
277 }
278}
279
280void QQuick3DParticleModelParticle::componentComplete()
281{
282 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
283 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
284
285 QQuick3DParticle::componentComplete();
286 regenerate();
287}
288
289void QQuick3DParticleModelParticle::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &value)
290{
291 QQuick3DObject::itemChange(change, value);
292 if (change == ItemParentHasChanged)
293 regenerate();
294}
295
296QT_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)