9#include <QtQuick3D/private/qquick3dobject_p.h>
11#include <QtQuick3DUtils/private/qssgutils_p.h>
16
17
18
19
20
21
22
23
24
26QQuick3DParticleSpriteParticle::QQuick3DParticleSpriteParticle(QQuick3DNode *parent)
27 : QQuick3DParticle(parent)
29 m_connections.insert(
"maxAmount", QObject::connect(
this, &QQuick3DParticle::maxAmountChanged,
this, [
this]() {
30 handleMaxAmountChanged(m_maxAmount);
32 m_connections.insert(
"system", QObject::connect(
this, &QQuick3DParticle::systemChanged,
this, [
this]() {
33 handleSystemChanged(system());
35 m_connections.insert(
"sortMode", QObject::connect(
this, &QQuick3DParticle::sortModeChanged,
this, [
this]() {
40QQuick3DParticleSpriteParticle::~QQuick3DParticleSpriteParticle()
43 m_spriteSequence->m_parentParticle =
nullptr;
44 for (
const auto &connection : std::as_const(m_connections))
45 QObject::disconnect(connection);
48 auto lightList = lights();
49 qmlClearLights(&lightList);
52void QQuick3DParticleSpriteParticle::deleteNodes()
54 for (
const PerEmitterData &value : std::as_const(m_perEmitterData)) {
55 value.particleUpdateNode->m_particle =
nullptr;
56 delete value.particleUpdateNode;
58 m_perEmitterData.clear();
62
63
64
65
66
67
68
69
70
71
72
75
76
77
78
79
80
81QQuick3DParticleSpriteParticle::BlendMode QQuick3DParticleSpriteParticle::blendMode()
const
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103QQuick3DTexture *QQuick3DParticleSpriteParticle::sprite()
const
109
110
111
112
113
114
116QQuick3DParticleSpriteSequence *QQuick3DParticleSpriteParticle::spriteSequence()
const
118 return m_spriteSequence;
122
123
124
125
126
127
128
129
130
131
132bool QQuick3DParticleSpriteParticle::billboard()
const
138
139
140
141
142
143
144
145
146
147
148
149float QQuick3DParticleSpriteParticle::particleScale()
const
151 return m_particleScale;
155
156
157
158
159
160
161
162
163
164QQuick3DTexture *QQuick3DParticleSpriteParticle::colorTable()
const
170
171
172
173
174
175
176
177
179QQmlListProperty<QQuick3DAbstractLight> QQuick3DParticleSpriteParticle::lights()
181 return QQmlListProperty<QQuick3DAbstractLight>(
this,
183 QQuick3DParticleSpriteParticle::qmlAppendLight,
184 QQuick3DParticleSpriteParticle::qmlLightsCount,
185 QQuick3DParticleSpriteParticle::qmlLightAt,
186 QQuick3DParticleSpriteParticle::qmlClearLights);
190
191
192
193
194
195float QQuick3DParticleSpriteParticle::offsetX()
const
201
202
203
204
205
206float QQuick3DParticleSpriteParticle::offsetY()
const
212
213
214
215
216
217
218bool QQuick3DParticleSpriteParticle::castsReflections()
const
220 return m_castsReflections;
223void QQuick3DParticleSpriteParticle::setBlendMode(BlendMode blendMode)
225 if (m_blendMode == blendMode)
227 m_blendMode = blendMode;
229 Q_EMIT blendModeChanged();
232void QQuick3DParticleSpriteParticle::setSprite(QQuick3DTexture *sprite)
234 if (m_sprite == sprite)
237 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DParticleSpriteParticle::setSprite, sprite, m_sprite);
241 Q_EMIT spriteChanged();
244void QQuick3DParticleSpriteParticle::setSpriteSequence(QQuick3DParticleSpriteSequence *spriteSequence)
246 if (m_spriteSequence == spriteSequence)
249 m_spriteSequence = spriteSequence;
250 updateFeatureLevel();
252 Q_EMIT spriteSequenceChanged();
255void QQuick3DParticleSpriteParticle::setBillboard(
bool billboard)
257 if (m_billboard == billboard)
259 m_billboard = billboard;
261 Q_EMIT billboardChanged();
264void QQuick3DParticleSpriteParticle::setParticleScale(
float scale)
266 if (qFuzzyCompare(scale, m_particleScale))
268 m_particleScale = scale;
270 Q_EMIT particleScaleChanged();
273void QQuick3DParticleSpriteParticle::setColorTable(QQuick3DTexture *colorTable)
275 if (m_colorTable == colorTable)
278 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DParticleSpriteParticle::setColorTable, colorTable, m_colorTable);
280 m_colorTable = colorTable;
281 updateFeatureLevel();
283 Q_EMIT colorTableChanged();
286void QQuick3DParticleSpriteParticle::setOffsetX(
float value)
288 if (qFuzzyCompare(value, m_offset.x()))
291 m_offset.setX(value);
292 emit offsetXChanged();
295void QQuick3DParticleSpriteParticle::setOffsetY(
float value)
297 if (qFuzzyCompare(value, m_offset.y()))
300 m_offset.setY(value);
301 emit offsetYChanged();
304void QQuick3DParticleSpriteParticle::setCastsReflections(
bool castsReflections)
306 if (m_castsReflections == castsReflections)
308 m_castsReflections = castsReflections;
309 emit castsReflectionsChanged();
312void QQuick3DParticleSpriteParticle::itemChange(QQuick3DObject::ItemChange change,
313 const QQuick3DObject::ItemChangeData &value)
315 if (change == QQuick3DObject::ItemSceneChange)
316 updateSceneManager(value.sceneManager);
322 case QQuick3DParticleSpriteParticle::SourceOver:
323 return QSSGRenderParticles::BlendMode::SourceOver;
324 case QQuick3DParticleSpriteParticle::Screen:
325 return QSSGRenderParticles::BlendMode::Screen;
326 case QQuick3DParticleSpriteParticle::Multiply:
327 return QSSGRenderParticles::BlendMode::Multiply;
330 Q_UNREACHABLE_RETURN(QSSGRenderParticles::BlendMode::SourceOver);
333QSSGRenderParticles::FeatureLevel QQuick3DParticleSpriteParticle::mapFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel level)
336 case QQuick3DParticleSpriteParticle::Simple:
337 return QSSGRenderParticles::FeatureLevel::Simple;
338 case QQuick3DParticleSpriteParticle::Mapped:
339 return QSSGRenderParticles::FeatureLevel::Mapped;
340 case QQuick3DParticleSpriteParticle::Animated:
341 return QSSGRenderParticles::FeatureLevel::Animated;
342 case QQuick3DParticleSpriteParticle::SimpleVLight:
343 return QSSGRenderParticles::FeatureLevel::SimpleVLight;
344 case QQuick3DParticleSpriteParticle::MappedVLight:
345 return QSSGRenderParticles::FeatureLevel::MappedVLight;
346 case QQuick3DParticleSpriteParticle::AnimatedVLight:
347 return QSSGRenderParticles::FeatureLevel::AnimatedVLight;
350 Q_UNREACHABLE_RETURN(QSSGRenderParticles::FeatureLevel::Simple);
353QSSGRenderGraphObject *QQuick3DParticleSpriteParticle::ParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
356 node = m_particle->updateParticleNode(
this, node);
357 QQuick3DNode::updateSpatialNode(node);
358 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(m_particle, node);
359 auto particles =
static_cast<QSSGRenderParticles *>(node);
361 if (m_particle->m_featureLevel == QQuick3DParticleSpriteParticle::Animated || m_particle->m_featureLevel == QQuick3DParticleSpriteParticle::AnimatedVLight)
362 m_particle->updateAnimatedParticleBuffer(
this, particles);
364 m_particle->updateParticleBuffer(
this, particles);
371QQuick3DParticleSpriteParticle::PerEmitterData &QQuick3DParticleSpriteParticle::perEmitterData(
const QQuick3DNode *updateNode)
373 for (
auto &perEmitter : m_perEmitterData) {
374 if (perEmitter.particleUpdateNode == updateNode)
377 return n_noPerEmitterData;
380QQuick3DParticleSpriteParticle::PerEmitterData &QQuick3DParticleSpriteParticle::perEmitterData(
int emitterIndex)
382 for (
auto &perEmitter : m_perEmitterData) {
383 if (perEmitter.emitterIndex == emitterIndex)
386 return n_noPerEmitterData;
389QSSGRenderGraphObject *QQuick3DParticleSpriteParticle::updateParticleNode(
const ParticleUpdateNode *updateNode,
390 QSSGRenderGraphObject *node)
394 node =
new QSSGRenderParticles();
397 auto particles =
static_cast<QSSGRenderParticles *>(node);
398 const auto &perEmitter = perEmitterData(updateNode);
400 if (!updateNode->m_nodeDirty)
403 if (perEmitter.particleCount == 0)
407 particles->m_sprite = m_sprite->getRenderImage();
409 particles->m_sprite =
nullptr;
411 if (m_spriteSequence) {
412 particles->m_spriteImageCount = m_spriteSequence->m_frameCount;
413 particles->m_blendImages = m_spriteSequence->m_interpolate;
415 particles->m_spriteImageCount = 1;
416 particles->m_blendImages =
true;
419 particles->m_hasTransparency = hasTransparency();
422 particles->m_colorTable = m_colorTable->getRenderImage();
424 particles->m_colorTable =
nullptr;
426 if (!m_lights.isEmpty()) {
428 QVarLengthArray<QSSGRenderLight *, 4> lightNodes;
429 for (
auto light : std::as_const(m_lights)) {
430 auto lightPrivate = QQuick3DObjectPrivate::get(light);
431 auto lightNode =
static_cast<QSSGRenderLight *>(lightPrivate->spatialNode);
432 lightNodes.append(lightNode);
434 particles->m_lights = lightNodes;
437 particles->m_blendMode = mapBlendMode(m_blendMode);
438 particles->m_billboard = m_billboard;
439 particles->m_depthBiasSq = QSSGRenderNode::signedSquared(perEmitter.emitter->depthBias());
440 particles->m_featureLevel = mapFeatureLevel(m_featureLevel);
441 particles->m_depthSorting = sortMode() == QQuick3DParticle::SortDistance;
442 particles->m_castsReflections = m_castsReflections;
447void QQuick3DParticleSpriteParticle::handleMaxAmountChanged(
int amount)
449 if (m_particleData.size() == amount)
452 m_particleData.resize(amount);
453 m_spriteParticleData.resize(amount);
457void QQuick3DParticleSpriteParticle::handleSystemChanged(QQuick3DParticleSystem *system)
459 for (PerEmitterData &value : m_perEmitterData) {
460 delete value.particleUpdateNode;
461 value.particleUpdateNode =
new ParticleUpdateNode(system);
462 value.particleUpdateNode->m_particle =
this;
466void QQuick3DParticleSpriteParticle::updateNodes()
468 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
469 value.particleUpdateNode->update();
472void QQuick3DParticleSpriteParticle::markNodesDirty()
474 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
475 value.particleUpdateNode->m_nodeDirty =
true;
478void QQuick3DParticleSpriteParticle::updateFeatureLevel()
480 FeatureLevel featureLevel = FeatureLevel::Simple;
481 if (m_lights.isEmpty()) {
483 featureLevel = FeatureLevel::Mapped;
484 if (m_spriteSequence)
485 featureLevel = FeatureLevel::Animated;
487 featureLevel = FeatureLevel::SimpleVLight;
489 featureLevel = FeatureLevel::MappedVLight;
490 if (m_spriteSequence)
491 featureLevel = FeatureLevel::AnimatedVLight;
493 if (featureLevel != m_featureLevel)
494 m_featureLevel = featureLevel;
497void QQuick3DParticleSpriteParticle::componentComplete()
499 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
500 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
502 QQuick3DParticle::componentComplete();
505void QQuick3DParticleSpriteParticle::reset()
507 QQuick3DParticle::reset();
509 m_nextEmitterIndex = 0;
510 m_spriteParticleData.fill({});
513void QQuick3DParticleSpriteParticle::commitParticles(
float)
520int QQuick3DParticleSpriteParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
522 if (!m_perEmitterData.contains(emitter)) {
523 m_perEmitterData.insert(emitter, PerEmitterData());
524 auto &perEmitter = m_perEmitterData[emitter];
525 perEmitter.particleUpdateNode =
new ParticleUpdateNode(system());
526 perEmitter.emitter = emitter;
527 perEmitter.particleUpdateNode->m_particle =
this;
528 perEmitter.emitterIndex = m_nextEmitterIndex++;
530 auto &perEmitter = m_perEmitterData[emitter];
531 int index = QQuick3DParticle::nextCurrentIndex(emitter);
532 if (m_spriteParticleData[index].emitterIndex != perEmitter.emitterIndex) {
533 if (m_spriteParticleData[index].emitterIndex >= 0)
534 perEmitterData(m_spriteParticleData[index].emitterIndex).particleCount--;
535 perEmitter.particleCount++;
537 m_spriteParticleData[index].emitterIndex = perEmitter.emitterIndex;
541void QQuick3DParticleSpriteParticle::setParticleData(
int particleIndex,
542 const QVector3D &position,
543 const QVector3D &rotation,
544 const QVector4D &color,
545 float size,
float age,
546 float animationFrame)
548 auto &dst = m_spriteParticleData[particleIndex];
549 dst = {position, rotation, color, size, age, animationFrame, dst.emitterIndex};
552void QQuick3DParticleSpriteParticle::resetParticleData(
int particleIndex)
554 auto &dst = m_spriteParticleData[particleIndex];
556 dst = {{}, {}, {}, 0.0f, 0.0f, -1.0f, dst.emitterIndex};
559void QQuick3DParticleSpriteParticle::updateParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
561 const auto &perEmitter = perEmitterData(updateNode);
562 const auto &particles = m_spriteParticleData;
563 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
566 const int particleCount = perEmitter.particleCount;
567 if (node->m_particleBuffer.particleCount() != particleCount || m_useAnimatedParticle)
568 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleSimple));
570 m_useAnimatedParticle =
false;
571 char *dest = node->m_particleBuffer.pointer();
572 const SpriteParticleData *src = particles.data();
573 const int pps = node->m_particleBuffer.particlesPerSlice();
574 const int ss = node->m_particleBuffer.sliceStride();
575 const int slices = node->m_particleBuffer.sliceCount();
576 const int emitterIndex = perEmitter.emitterIndex;
579 const auto smode = sortMode();
580 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
581 int offset = m_currentIndex;
582 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
584 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
585 return (linearIndex + offset + wrap) % wrap;
587 for (
int s = 0; s < slices; s++) {
588 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
589 for (
int p = 0; p < pps && i < particleCount; ) {
590 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
591 if (data->emitterIndex == emitterIndex) {
592 if (data->size > 0.0f)
593 bounds.include(data->position);
594 dp->position = data->position;
595 dp->rotation = data->rotation *
float(M_PI / 180.0f);
596 dp->color = data->color;
597 dp->size = data->size * m_particleScale;
608 for (
int s = 0; s < slices; s++) {
609 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
610 for (
int p = 0; p < pps && i < particleCount; ) {
611 if (src->emitterIndex == emitterIndex) {
612 if (src->size > 0.0f)
613 bounds.include(src->position);
614 dp->position = src->position;
615 dp->rotation = src->rotation *
float(M_PI / 180.0f);
616 dp->color = src->color;
617 dp->size = src->size * m_particleScale;
628 node->m_particleBuffer.setBounds(bounds);
631void QQuick3DParticleSpriteParticle::updateAnimatedParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
633 const auto &perEmitter = perEmitterData(updateNode);
634 const auto &particles = m_spriteParticleData;
635 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
638 const int particleCount = perEmitter.particleCount;
639 if (node->m_particleBuffer.particleCount() != particleCount || !m_useAnimatedParticle)
640 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleAnimated));
642 m_useAnimatedParticle =
true;
643 char *dest = node->m_particleBuffer.pointer();
644 const SpriteParticleData *src = particles.data();
645 const int pps = node->m_particleBuffer.particlesPerSlice();
646 const int ss = node->m_particleBuffer.sliceStride();
647 const int slices = node->m_particleBuffer.sliceCount();
648 const int emitterIndex = perEmitter.emitterIndex;
651 const auto smode = sortMode();
652 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
653 int offset = m_currentIndex;
654 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
656 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
657 return (linearIndex + offset + wrap) % wrap;
659 for (
int s = 0; s < slices; s++) {
660 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
661 for (
int p = 0; p < pps && i < particleCount; ) {
662 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
663 if (data->emitterIndex == emitterIndex) {
664 if (data->size > 0.0f)
665 bounds.include(data->position);
666 dp->position = data->position;
667 dp->rotation = data->rotation *
float(M_PI / 180.0f);
668 dp->color = data->color;
669 dp->size = data->size * m_particleScale;
671 dp->animationFrame = data->animationFrame;
681 for (
int s = 0; s < slices; s++) {
682 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
683 for (
int p = 0; p < pps && i < particleCount; ) {
684 if (src->emitterIndex == emitterIndex) {
685 if (src->size > 0.0f)
686 bounds.include(src->position);
687 dp->position = src->position;
688 dp->rotation = src->rotation *
float(M_PI / 180.0f);
689 dp->color = src->color;
690 dp->size = src->size * m_particleScale;
692 dp->animationFrame = src->animationFrame;
702 node->m_particleBuffer.setBounds(bounds);
705void QQuick3DParticleSpriteParticle::updateSceneManager(QQuick3DSceneManager *sceneManager)
709 QQuick3DObjectPrivate::refSceneManager(m_sprite, *sceneManager);
710 QQuick3DObjectPrivate::refSceneManager(m_colorTable, *sceneManager);
712 QQuick3DObjectPrivate::derefSceneManager(m_sprite);
713 QQuick3DObjectPrivate::derefSceneManager(m_colorTable);
718void QQuick3DParticleSpriteParticle::onLightDestroyed(QObject *object)
721 for (
int i = 0; i < m_lights.size(); ++i) {
722 if (m_lights[i] == object) {
723 m_lights.removeAt(i--);
728 updateFeatureLevel();
733void QQuick3DParticleSpriteParticle::qmlAppendLight(QQmlListProperty<QQuick3DAbstractLight> *list, QQuick3DAbstractLight *light)
739 if (light->parentItem()) {
740 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
741 self->m_lights.push_back(light);
742 self->updateFeatureLevel();
743 self->markNodesDirty();
745 connect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
749QQuick3DAbstractLight *QQuick3DParticleSpriteParticle::qmlLightAt(QQmlListProperty<QQuick3DAbstractLight> *list, qsizetype index)
751 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
752 if (index >= self->m_lights.size()) {
753 qWarning(
"The index exceeds the range of valid light targets.");
756 return self->m_lights.at(index);
759qsizetype QQuick3DParticleSpriteParticle::qmlLightsCount(QQmlListProperty<QQuick3DAbstractLight> *list)
761 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
762 return self->m_lights.size();
765void QQuick3DParticleSpriteParticle::qmlClearLights(QQmlListProperty<QQuick3DAbstractLight> *list)
767 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
768 for (
const auto &light : std::as_const(self->m_lights)) {
769 if (light->parentItem() ==
nullptr)
770 QQuick3DObjectPrivate::get(light)->derefSceneManager();
771 disconnect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
773 self->m_lights.clear();
774 self->updateFeatureLevel();
775 self->markNodesDirty();
Combined button and popup list for selecting options.
static QSSGRenderParticles::BlendMode mapBlendMode(QQuick3DParticleSpriteParticle::BlendMode mode)