7#include <QtQuick3D/private/qquick3dobject_p.h>
9#include <QtQuick3DUtils/private/qssgutils_p.h>
14
15
16
17
18
19
20
21
22
24QQuick3DParticleSpriteParticle::QQuick3DParticleSpriteParticle(QQuick3DNode *parent)
25 : QQuick3DParticle(parent)
27 m_connections.insert(
"maxAmount", QObject::connect(
this, &QQuick3DParticle::maxAmountChanged,
this, [
this]() {
28 handleMaxAmountChanged(m_maxAmount);
30 m_connections.insert(
"system", QObject::connect(
this, &QQuick3DParticle::systemChanged,
this, [
this]() {
31 handleSystemChanged(system());
33 m_connections.insert(
"sortMode", QObject::connect(
this, &QQuick3DParticle::sortModeChanged,
this, [
this]() {
38QQuick3DParticleSpriteParticle::~QQuick3DParticleSpriteParticle()
41 m_spriteSequence->m_parentParticle =
nullptr;
42 for (
const auto &connection : std::as_const(m_connections))
43 QObject::disconnect(connection);
46 auto lightList = lights();
47 qmlClearLights(&lightList);
50void QQuick3DParticleSpriteParticle::deleteNodes()
52 for (
const PerEmitterData &value : std::as_const(m_perEmitterData)) {
53 value.particleUpdateNode->m_particle =
nullptr;
54 delete value.particleUpdateNode;
56 m_perEmitterData.clear();
60
61
62
63
64
65
66
67
68
69
70
73
74
75
76
77
78
79QQuick3DParticleSpriteParticle::BlendMode QQuick3DParticleSpriteParticle::blendMode()
const
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101QQuick3DTexture *QQuick3DParticleSpriteParticle::sprite()
const
107
108
109
110
111
112
114QQuick3DParticleSpriteSequence *QQuick3DParticleSpriteParticle::spriteSequence()
const
116 return m_spriteSequence;
120
121
122
123
124
125
126
127
128
129
130bool QQuick3DParticleSpriteParticle::billboard()
const
136
137
138
139
140
141
142
143
144
145
146
147float QQuick3DParticleSpriteParticle::particleScale()
const
149 return m_particleScale;
153
154
155
156
157
158
159
160
161
162QQuick3DTexture *QQuick3DParticleSpriteParticle::colorTable()
const
168
169
170
171
172
173
174
175
177QQmlListProperty<QQuick3DAbstractLight> QQuick3DParticleSpriteParticle::lights()
179 return QQmlListProperty<QQuick3DAbstractLight>(
this,
181 QQuick3DParticleSpriteParticle::qmlAppendLight,
182 QQuick3DParticleSpriteParticle::qmlLightsCount,
183 QQuick3DParticleSpriteParticle::qmlLightAt,
184 QQuick3DParticleSpriteParticle::qmlClearLights);
188
189
190
191
192
193float QQuick3DParticleSpriteParticle::offsetX()
const
199
200
201
202
203
204float QQuick3DParticleSpriteParticle::offsetY()
const
210
211
212
213
214
215
216bool QQuick3DParticleSpriteParticle::castsReflections()
const
218 return m_castsReflections;
221void QQuick3DParticleSpriteParticle::setBlendMode(BlendMode blendMode)
223 if (m_blendMode == blendMode)
225 m_blendMode = blendMode;
227 Q_EMIT blendModeChanged();
230void QQuick3DParticleSpriteParticle::setSprite(QQuick3DTexture *sprite)
232 if (m_sprite == sprite)
235 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DParticleSpriteParticle::setSprite, sprite, m_sprite);
239 Q_EMIT spriteChanged();
242void QQuick3DParticleSpriteParticle::setSpriteSequence(QQuick3DParticleSpriteSequence *spriteSequence)
244 if (m_spriteSequence == spriteSequence)
247 m_spriteSequence = spriteSequence;
248 updateFeatureLevel();
250 Q_EMIT spriteSequenceChanged();
253void QQuick3DParticleSpriteParticle::setBillboard(
bool billboard)
255 if (m_billboard == billboard)
257 m_billboard = billboard;
259 Q_EMIT billboardChanged();
262void QQuick3DParticleSpriteParticle::setParticleScale(
float scale)
264 if (qFuzzyCompare(scale, m_particleScale))
266 m_particleScale = scale;
268 Q_EMIT particleScaleChanged();
271void QQuick3DParticleSpriteParticle::setColorTable(QQuick3DTexture *colorTable)
273 if (m_colorTable == colorTable)
276 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DParticleSpriteParticle::setColorTable, colorTable, m_colorTable);
278 m_colorTable = colorTable;
279 updateFeatureLevel();
281 Q_EMIT colorTableChanged();
284void QQuick3DParticleSpriteParticle::setOffsetX(
float value)
286 if (qFuzzyCompare(value, m_offset.x()))
289 m_offset.setX(value);
290 emit offsetXChanged();
293void QQuick3DParticleSpriteParticle::setOffsetY(
float value)
295 if (qFuzzyCompare(value, m_offset.y()))
298 m_offset.setY(value);
299 emit offsetYChanged();
302void QQuick3DParticleSpriteParticle::setCastsReflections(
bool castsReflections)
304 if (m_castsReflections == castsReflections)
306 m_castsReflections = castsReflections;
307 emit castsReflectionsChanged();
310void QQuick3DParticleSpriteParticle::itemChange(QQuick3DObject::ItemChange change,
311 const QQuick3DObject::ItemChangeData &value)
313 if (change == QQuick3DObject::ItemSceneChange)
314 updateSceneManager(value.sceneManager);
320 case QQuick3DParticleSpriteParticle::SourceOver:
321 return QSSGRenderParticles::BlendMode::SourceOver;
322 case QQuick3DParticleSpriteParticle::Screen:
323 return QSSGRenderParticles::BlendMode::Screen;
324 case QQuick3DParticleSpriteParticle::Multiply:
325 return QSSGRenderParticles::BlendMode::Multiply;
328 Q_UNREACHABLE_RETURN(QSSGRenderParticles::BlendMode::SourceOver);
331QSSGRenderParticles::FeatureLevel QQuick3DParticleSpriteParticle::mapFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel level)
334 case QQuick3DParticleSpriteParticle::Simple:
335 return QSSGRenderParticles::FeatureLevel::Simple;
336 case QQuick3DParticleSpriteParticle::Mapped:
337 return QSSGRenderParticles::FeatureLevel::Mapped;
338 case QQuick3DParticleSpriteParticle::Animated:
339 return QSSGRenderParticles::FeatureLevel::Animated;
340 case QQuick3DParticleSpriteParticle::SimpleVLight:
341 return QSSGRenderParticles::FeatureLevel::SimpleVLight;
342 case QQuick3DParticleSpriteParticle::MappedVLight:
343 return QSSGRenderParticles::FeatureLevel::MappedVLight;
344 case QQuick3DParticleSpriteParticle::AnimatedVLight:
345 return QSSGRenderParticles::FeatureLevel::AnimatedVLight;
348 Q_UNREACHABLE_RETURN(QSSGRenderParticles::FeatureLevel::Simple);
351QSSGRenderGraphObject *QQuick3DParticleSpriteParticle::ParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
354 node = m_particle->updateParticleNode(
this, node);
355 QQuick3DNode::updateSpatialNode(node);
356 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(m_particle, node);
357 auto particles =
static_cast<QSSGRenderParticles *>(node);
359 if (m_particle->m_featureLevel == QQuick3DParticleSpriteParticle::Animated || m_particle->m_featureLevel == QQuick3DParticleSpriteParticle::AnimatedVLight)
360 m_particle->updateAnimatedParticleBuffer(
this, particles);
362 m_particle->updateParticleBuffer(
this, particles);
369QQuick3DParticleSpriteParticle::PerEmitterData &QQuick3DParticleSpriteParticle::perEmitterData(
const QQuick3DNode *updateNode)
371 for (
auto &perEmitter : m_perEmitterData) {
372 if (perEmitter.particleUpdateNode == updateNode)
375 return n_noPerEmitterData;
378QQuick3DParticleSpriteParticle::PerEmitterData &QQuick3DParticleSpriteParticle::perEmitterData(
int emitterIndex)
380 for (
auto &perEmitter : m_perEmitterData) {
381 if (perEmitter.emitterIndex == emitterIndex)
384 return n_noPerEmitterData;
387QSSGRenderGraphObject *QQuick3DParticleSpriteParticle::updateParticleNode(
const ParticleUpdateNode *updateNode,
388 QSSGRenderGraphObject *node)
392 node =
new QSSGRenderParticles();
395 auto particles =
static_cast<QSSGRenderParticles *>(node);
396 const auto &perEmitter = perEmitterData(updateNode);
398 if (!updateNode->m_nodeDirty)
401 if (perEmitter.particleCount == 0)
405 particles->m_sprite = m_sprite->getRenderImage();
407 particles->m_sprite =
nullptr;
409 if (m_spriteSequence) {
410 particles->m_spriteImageCount = m_spriteSequence->m_frameCount;
411 particles->m_blendImages = m_spriteSequence->m_interpolate;
413 particles->m_spriteImageCount = 1;
414 particles->m_blendImages =
true;
417 particles->m_hasTransparency = hasTransparency();
420 particles->m_colorTable = m_colorTable->getRenderImage();
422 particles->m_colorTable =
nullptr;
424 if (!m_lights.isEmpty()) {
426 QVarLengthArray<QSSGRenderLight *, 4> lightNodes;
427 for (
auto light : std::as_const(m_lights)) {
428 auto lightPrivate = QQuick3DObjectPrivate::get(light);
429 auto lightNode =
static_cast<QSSGRenderLight *>(lightPrivate->spatialNode);
430 lightNodes.append(lightNode);
432 particles->m_lights = lightNodes;
435 particles->m_blendMode = mapBlendMode(m_blendMode);
436 particles->m_billboard = m_billboard;
437 particles->m_depthBiasSq = QSSGRenderNode::signedSquared(perEmitter.emitter->depthBias());
438 particles->m_featureLevel = mapFeatureLevel(m_featureLevel);
439 particles->m_depthSorting = sortMode() == QQuick3DParticle::SortDistance;
440 particles->m_castsReflections = m_castsReflections;
445void QQuick3DParticleSpriteParticle::handleMaxAmountChanged(
int amount)
447 if (m_particleData.size() == amount)
450 m_particleData.resize(amount);
451 m_spriteParticleData.resize(amount);
455void QQuick3DParticleSpriteParticle::handleSystemChanged(QQuick3DParticleSystem *system)
457 for (PerEmitterData &value : m_perEmitterData) {
458 delete value.particleUpdateNode;
459 value.particleUpdateNode =
new ParticleUpdateNode(system);
460 value.particleUpdateNode->m_particle =
this;
464void QQuick3DParticleSpriteParticle::updateNodes()
466 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
467 value.particleUpdateNode->update();
470void QQuick3DParticleSpriteParticle::markNodesDirty()
472 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
473 value.particleUpdateNode->m_nodeDirty =
true;
476void QQuick3DParticleSpriteParticle::updateFeatureLevel()
478 FeatureLevel featureLevel = FeatureLevel::Simple;
479 if (m_lights.isEmpty()) {
481 featureLevel = FeatureLevel::Mapped;
482 if (m_spriteSequence)
483 featureLevel = FeatureLevel::Animated;
485 featureLevel = FeatureLevel::SimpleVLight;
487 featureLevel = FeatureLevel::MappedVLight;
488 if (m_spriteSequence)
489 featureLevel = FeatureLevel::AnimatedVLight;
491 if (featureLevel != m_featureLevel)
492 m_featureLevel = featureLevel;
495void QQuick3DParticleSpriteParticle::componentComplete()
497 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
498 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
500 QQuick3DParticle::componentComplete();
503void QQuick3DParticleSpriteParticle::reset()
505 QQuick3DParticle::reset();
507 m_nextEmitterIndex = 0;
508 m_spriteParticleData.fill({});
511void QQuick3DParticleSpriteParticle::commitParticles(
float)
518int QQuick3DParticleSpriteParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
520 if (!m_perEmitterData.contains(emitter)) {
521 m_perEmitterData.insert(emitter, PerEmitterData());
522 auto &perEmitter = m_perEmitterData[emitter];
523 perEmitter.particleUpdateNode =
new ParticleUpdateNode(system());
524 perEmitter.emitter = emitter;
525 perEmitter.particleUpdateNode->m_particle =
this;
526 perEmitter.emitterIndex = m_nextEmitterIndex++;
528 auto &perEmitter = m_perEmitterData[emitter];
529 int index = QQuick3DParticle::nextCurrentIndex(emitter);
530 if (m_spriteParticleData[index].emitterIndex != perEmitter.emitterIndex) {
531 if (m_spriteParticleData[index].emitterIndex >= 0)
532 perEmitterData(m_spriteParticleData[index].emitterIndex).particleCount--;
533 perEmitter.particleCount++;
535 m_spriteParticleData[index].emitterIndex = perEmitter.emitterIndex;
539void QQuick3DParticleSpriteParticle::setParticleData(
int particleIndex,
540 const QVector3D &position,
541 const QVector3D &rotation,
542 const QVector4D &color,
543 float size,
float age,
544 float animationFrame)
546 auto &dst = m_spriteParticleData[particleIndex];
547 dst = {position, rotation, color, size, age, animationFrame, dst.emitterIndex};
550void QQuick3DParticleSpriteParticle::resetParticleData(
int particleIndex)
552 auto &dst = m_spriteParticleData[particleIndex];
554 dst = {{}, {}, {}, 0.0f, 0.0f, -1.0f, dst.emitterIndex};
557void QQuick3DParticleSpriteParticle::updateParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
559 const auto &perEmitter = perEmitterData(updateNode);
560 const auto &particles = m_spriteParticleData;
561 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
564 const int particleCount = perEmitter.particleCount;
565 if (node->m_particleBuffer.particleCount() != particleCount || m_useAnimatedParticle)
566 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleSimple));
568 m_useAnimatedParticle =
false;
569 char *dest = node->m_particleBuffer.pointer();
570 const SpriteParticleData *src = particles.data();
571 const int pps = node->m_particleBuffer.particlesPerSlice();
572 const int ss = node->m_particleBuffer.sliceStride();
573 const int slices = node->m_particleBuffer.sliceCount();
574 const int emitterIndex = perEmitter.emitterIndex;
577 const auto smode = sortMode();
578 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
579 int offset = m_currentIndex;
580 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
582 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
583 return (linearIndex + offset + wrap) % wrap;
585 for (
int s = 0; s < slices; s++) {
586 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
587 for (
int p = 0; p < pps && i < particleCount; ) {
588 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
589 if (data->emitterIndex == emitterIndex) {
590 if (data->size > 0.0f)
591 bounds.include(data->position);
592 dp->position = data->position;
593 dp->rotation = data->rotation *
float(M_PI / 180.0f);
594 dp->color = data->color;
595 dp->size = data->size * m_particleScale;
606 for (
int s = 0; s < slices; s++) {
607 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
608 for (
int p = 0; p < pps && i < particleCount; ) {
609 if (src->emitterIndex == emitterIndex) {
610 if (src->size > 0.0f)
611 bounds.include(src->position);
612 dp->position = src->position;
613 dp->rotation = src->rotation *
float(M_PI / 180.0f);
614 dp->color = src->color;
615 dp->size = src->size * m_particleScale;
626 node->m_particleBuffer.setBounds(bounds);
629void QQuick3DParticleSpriteParticle::updateAnimatedParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
631 const auto &perEmitter = perEmitterData(updateNode);
632 const auto &particles = m_spriteParticleData;
633 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
636 const int particleCount = perEmitter.particleCount;
637 if (node->m_particleBuffer.particleCount() != particleCount || !m_useAnimatedParticle)
638 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleAnimated));
640 m_useAnimatedParticle =
true;
641 char *dest = node->m_particleBuffer.pointer();
642 const SpriteParticleData *src = particles.data();
643 const int pps = node->m_particleBuffer.particlesPerSlice();
644 const int ss = node->m_particleBuffer.sliceStride();
645 const int slices = node->m_particleBuffer.sliceCount();
646 const int emitterIndex = perEmitter.emitterIndex;
649 const auto smode = sortMode();
650 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
651 int offset = m_currentIndex;
652 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
654 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
655 return (linearIndex + offset + wrap) % wrap;
657 for (
int s = 0; s < slices; s++) {
658 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
659 for (
int p = 0; p < pps && i < particleCount; ) {
660 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
661 if (data->emitterIndex == emitterIndex) {
662 if (data->size > 0.0f)
663 bounds.include(data->position);
664 dp->position = data->position;
665 dp->rotation = data->rotation *
float(M_PI / 180.0f);
666 dp->color = data->color;
667 dp->size = data->size * m_particleScale;
669 dp->animationFrame = data->animationFrame;
679 for (
int s = 0; s < slices; s++) {
680 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
681 for (
int p = 0; p < pps && i < particleCount; ) {
682 if (src->emitterIndex == emitterIndex) {
683 if (src->size > 0.0f)
684 bounds.include(src->position);
685 dp->position = src->position;
686 dp->rotation = src->rotation *
float(M_PI / 180.0f);
687 dp->color = src->color;
688 dp->size = src->size * m_particleScale;
690 dp->animationFrame = src->animationFrame;
700 node->m_particleBuffer.setBounds(bounds);
703void QQuick3DParticleSpriteParticle::updateSceneManager(QQuick3DSceneManager *sceneManager)
707 QQuick3DObjectPrivate::refSceneManager(m_sprite, *sceneManager);
708 QQuick3DObjectPrivate::refSceneManager(m_colorTable, *sceneManager);
710 QQuick3DObjectPrivate::derefSceneManager(m_sprite);
711 QQuick3DObjectPrivate::derefSceneManager(m_colorTable);
716void QQuick3DParticleSpriteParticle::onLightDestroyed(QObject *object)
719 for (
int i = 0; i < m_lights.size(); ++i) {
720 if (m_lights[i] == object) {
721 m_lights.removeAt(i--);
726 updateFeatureLevel();
731void QQuick3DParticleSpriteParticle::qmlAppendLight(QQmlListProperty<QQuick3DAbstractLight> *list, QQuick3DAbstractLight *light)
737 if (light->parentItem()) {
738 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
739 self->m_lights.push_back(light);
740 self->updateFeatureLevel();
741 self->markNodesDirty();
743 connect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
747QQuick3DAbstractLight *QQuick3DParticleSpriteParticle::qmlLightAt(QQmlListProperty<QQuick3DAbstractLight> *list, qsizetype index)
749 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
750 if (index >= self->m_lights.size()) {
751 qWarning(
"The index exceeds the range of valid light targets.");
754 return self->m_lights.at(index);
757qsizetype QQuick3DParticleSpriteParticle::qmlLightsCount(QQmlListProperty<QQuick3DAbstractLight> *list)
759 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
760 return self->m_lights.size();
763void QQuick3DParticleSpriteParticle::qmlClearLights(QQmlListProperty<QQuick3DAbstractLight> *list)
765 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
766 for (
const auto &light : std::as_const(self->m_lights)) {
767 if (light->parentItem() ==
nullptr)
768 QQuick3DObjectPrivate::get(light)->derefSceneManager();
769 disconnect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
771 self->m_lights.clear();
772 self->updateFeatureLevel();
773 self->markNodesDirty();
static QSSGRenderParticles::BlendMode mapBlendMode(QQuick3DParticleSpriteParticle::BlendMode mode)