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 = std::array{
30 connect(
this, &QQuick3DParticle::maxAmountChanged,
this, [
this] {
31 handleMaxAmountChanged(m_maxAmount);
33 connect(
this, &QQuick3DParticle::systemChanged,
this, [
this] {
34 handleSystemChanged(system());
36 connect(
this, &QQuick3DParticle::sortModeChanged,
37 this, &QQuick3DParticleSpriteParticle::markNodesDirty),
41QQuick3DParticleSpriteParticle::~QQuick3DParticleSpriteParticle()
44 m_spriteSequence->m_parentParticle =
nullptr;
45 for (
auto &connection : m_connections)
46 QObject::disconnect(connection);
49 auto lightList = lights();
50 qmlClearLights(&lightList);
53void QQuick3DParticleSpriteParticle::deleteNodes()
55 for (
const PerEmitterData &value : std::as_const(m_perEmitterData)) {
56 value.particleUpdateNode->m_particle =
nullptr;
57 delete value.particleUpdateNode;
59 m_perEmitterData.clear();
63
64
65
66
67
68
69
70
71
72
73
76
77
78
79
80
81
82QQuick3DParticleSpriteParticle::BlendMode QQuick3DParticleSpriteParticle::blendMode()
const
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104QQuick3DTexture *QQuick3DParticleSpriteParticle::sprite()
const
110
111
112
113
114
115
117QQuick3DParticleSpriteSequence *QQuick3DParticleSpriteParticle::spriteSequence()
const
119 return m_spriteSequence;
123
124
125
126
127
128
129
130
131
132
133bool QQuick3DParticleSpriteParticle::billboard()
const
139
140
141
142
143
144
145
146
147
148
149
150float QQuick3DParticleSpriteParticle::particleScale()
const
152 return m_particleScale;
156
157
158
159
160
161
162
163
164
165QQuick3DTexture *QQuick3DParticleSpriteParticle::colorTable()
const
171
172
173
174
175
176
177
178
180QQmlListProperty<QQuick3DAbstractLight> QQuick3DParticleSpriteParticle::lights()
182 return QQmlListProperty<QQuick3DAbstractLight>(
this,
184 QQuick3DParticleSpriteParticle::qmlAppendLight,
185 QQuick3DParticleSpriteParticle::qmlLightsCount,
186 QQuick3DParticleSpriteParticle::qmlLightAt,
187 QQuick3DParticleSpriteParticle::qmlClearLights);
191
192
193
194
195
196float QQuick3DParticleSpriteParticle::offsetX()
const
202
203
204
205
206
207float QQuick3DParticleSpriteParticle::offsetY()
const
213
214
215
216
217
218
219bool QQuick3DParticleSpriteParticle::castsReflections()
const
221 return m_castsReflections;
224void QQuick3DParticleSpriteParticle::setBlendMode(BlendMode blendMode)
226 if (m_blendMode == blendMode)
228 m_blendMode = blendMode;
230 Q_EMIT blendModeChanged();
233void QQuick3DParticleSpriteParticle::setSprite(QQuick3DTexture *sprite)
235 if (m_sprite == sprite)
238 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DParticleSpriteParticle::setSprite, sprite, m_sprite);
242 Q_EMIT spriteChanged();
245void QQuick3DParticleSpriteParticle::setSpriteSequence(QQuick3DParticleSpriteSequence *spriteSequence)
247 if (m_spriteSequence == spriteSequence)
250 m_spriteSequence = spriteSequence;
251 updateFeatureLevel();
253 Q_EMIT spriteSequenceChanged();
256void QQuick3DParticleSpriteParticle::setBillboard(
bool billboard)
258 if (m_billboard == billboard)
260 m_billboard = billboard;
262 Q_EMIT billboardChanged();
265void QQuick3DParticleSpriteParticle::setParticleScale(
float scale)
267 if (qFuzzyCompare(scale, m_particleScale))
269 m_particleScale = scale;
271 Q_EMIT particleScaleChanged();
274void QQuick3DParticleSpriteParticle::setColorTable(QQuick3DTexture *colorTable)
276 if (m_colorTable == colorTable)
279 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DParticleSpriteParticle::setColorTable, colorTable, m_colorTable);
281 m_colorTable = colorTable;
282 updateFeatureLevel();
284 Q_EMIT colorTableChanged();
287void QQuick3DParticleSpriteParticle::setOffsetX(
float value)
289 if (qFuzzyCompare(value, m_offset.x()))
292 m_offset.setX(value);
293 emit offsetXChanged();
296void QQuick3DParticleSpriteParticle::setOffsetY(
float value)
298 if (qFuzzyCompare(value, m_offset.y()))
301 m_offset.setY(value);
302 emit offsetYChanged();
305void QQuick3DParticleSpriteParticle::setCastsReflections(
bool castsReflections)
307 if (m_castsReflections == castsReflections)
309 m_castsReflections = castsReflections;
310 emit castsReflectionsChanged();
313void QQuick3DParticleSpriteParticle::itemChange(QQuick3DObject::ItemChange change,
314 const QQuick3DObject::ItemChangeData &value)
316 if (change == QQuick3DObject::ItemSceneChange)
317 updateSceneManager(value.sceneManager);
323 case QQuick3DParticleSpriteParticle::SourceOver:
324 return QSSGRenderParticles::BlendMode::SourceOver;
325 case QQuick3DParticleSpriteParticle::Screen:
326 return QSSGRenderParticles::BlendMode::Screen;
327 case QQuick3DParticleSpriteParticle::Multiply:
328 return QSSGRenderParticles::BlendMode::Multiply;
331 Q_UNREACHABLE_RETURN(QSSGRenderParticles::BlendMode::SourceOver);
334QSSGRenderParticles::FeatureLevel QQuick3DParticleSpriteParticle::mapFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel level)
337 case QQuick3DParticleSpriteParticle::Simple:
338 return QSSGRenderParticles::FeatureLevel::Simple;
339 case QQuick3DParticleSpriteParticle::Mapped:
340 return QSSGRenderParticles::FeatureLevel::Mapped;
341 case QQuick3DParticleSpriteParticle::Animated:
342 return QSSGRenderParticles::FeatureLevel::Animated;
343 case QQuick3DParticleSpriteParticle::SimpleVLight:
344 return QSSGRenderParticles::FeatureLevel::SimpleVLight;
345 case QQuick3DParticleSpriteParticle::MappedVLight:
346 return QSSGRenderParticles::FeatureLevel::MappedVLight;
347 case QQuick3DParticleSpriteParticle::AnimatedVLight:
348 return QSSGRenderParticles::FeatureLevel::AnimatedVLight;
351 Q_UNREACHABLE_RETURN(QSSGRenderParticles::FeatureLevel::Simple);
354QSSGRenderGraphObject *QQuick3DParticleSpriteParticle::ParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
357 node = m_particle->updateParticleNode(
this, node);
358 QQuick3DNode::updateSpatialNode(node);
359 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(m_particle, node);
360 auto particles =
static_cast<QSSGRenderParticles *>(node);
362 if (m_particle->m_featureLevel == QQuick3DParticleSpriteParticle::Animated || m_particle->m_featureLevel == QQuick3DParticleSpriteParticle::AnimatedVLight)
363 m_particle->updateAnimatedParticleBuffer(
this, particles);
365 m_particle->updateParticleBuffer(
this, particles);
372QQuick3DParticleSpriteParticle::PerEmitterData &QQuick3DParticleSpriteParticle::perEmitterData(
const QQuick3DNode *updateNode)
374 for (
auto &perEmitter : m_perEmitterData) {
375 if (perEmitter.particleUpdateNode == updateNode)
378 return n_noPerEmitterData;
381QQuick3DParticleSpriteParticle::PerEmitterData &QQuick3DParticleSpriteParticle::perEmitterData(
int emitterIndex)
383 for (
auto &perEmitter : m_perEmitterData) {
384 if (perEmitter.emitterIndex == emitterIndex)
387 return n_noPerEmitterData;
390QSSGRenderGraphObject *QQuick3DParticleSpriteParticle::updateParticleNode(
const ParticleUpdateNode *updateNode,
391 QSSGRenderGraphObject *node)
395 node =
new QSSGRenderParticles();
398 auto particles =
static_cast<QSSGRenderParticles *>(node);
399 const auto &perEmitter = perEmitterData(updateNode);
401 if (!updateNode->m_nodeDirty)
404 if (perEmitter.particleCount == 0)
408 particles->m_sprite = m_sprite->getRenderImage();
410 particles->m_sprite =
nullptr;
412 if (m_spriteSequence) {
413 particles->m_spriteImageCount = m_spriteSequence->m_frameCount;
414 particles->m_blendImages = m_spriteSequence->m_interpolate;
416 particles->m_spriteImageCount = 1;
417 particles->m_blendImages =
true;
420 particles->m_hasTransparency = hasTransparency();
423 particles->m_colorTable = m_colorTable->getRenderImage();
425 particles->m_colorTable =
nullptr;
427 if (!m_lights.isEmpty()) {
429 QVarLengthArray<QSSGRenderLight *, 4> lightNodes;
430 for (
auto light : std::as_const(m_lights)) {
431 auto lightPrivate = QQuick3DObjectPrivate::get(light);
432 auto lightNode =
static_cast<QSSGRenderLight *>(lightPrivate->spatialNode);
433 lightNodes.append(lightNode);
435 particles->m_lights = lightNodes;
438 particles->m_blendMode = mapBlendMode(m_blendMode);
439 particles->m_billboard = m_billboard;
440 particles->m_depthBiasSq = QSSGRenderNode::signedSquared(perEmitter.emitter->depthBias());
441 particles->m_featureLevel = mapFeatureLevel(m_featureLevel);
442 particles->m_depthSorting = sortMode() == QQuick3DParticle::SortDistance;
443 particles->m_castsReflections = m_castsReflections;
448void QQuick3DParticleSpriteParticle::handleMaxAmountChanged(
int amount)
450 if (m_particleData.size() == amount)
453 m_particleData.resize(amount);
454 m_spriteParticleData.resize(amount);
458void QQuick3DParticleSpriteParticle::handleSystemChanged(QQuick3DParticleSystem *system)
460 for (PerEmitterData &value : m_perEmitterData) {
461 delete value.particleUpdateNode;
462 value.particleUpdateNode =
new ParticleUpdateNode(system);
463 value.particleUpdateNode->m_particle =
this;
468void QQuick3DParticleSpriteParticle::updateNodes()
470 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
471 value.particleUpdateNode->update();
474void QQuick3DParticleSpriteParticle::markNodesDirty()
476 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
477 value.particleUpdateNode->m_nodeDirty =
true;
480void QQuick3DParticleSpriteParticle::updateNodeLayers()
482 auto *psystem = system();
485 const int layers = psystem->layers();
486 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
487 value.particleUpdateNode->setLayers(layers);
490void QQuick3DParticleSpriteParticle::updateFeatureLevel()
492 FeatureLevel featureLevel = FeatureLevel::Simple;
493 if (m_lights.isEmpty()) {
495 featureLevel = FeatureLevel::Mapped;
496 if (m_spriteSequence)
497 featureLevel = FeatureLevel::Animated;
499 featureLevel = FeatureLevel::SimpleVLight;
501 featureLevel = FeatureLevel::MappedVLight;
502 if (m_spriteSequence)
503 featureLevel = FeatureLevel::AnimatedVLight;
505 if (featureLevel != m_featureLevel)
506 m_featureLevel = featureLevel;
509void QQuick3DParticleSpriteParticle::componentComplete()
511 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
512 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
514 QQuick3DParticle::componentComplete();
517void QQuick3DParticleSpriteParticle::reset()
519 QQuick3DParticle::reset();
521 m_nextEmitterIndex = 0;
522 m_spriteParticleData.fill({});
525void QQuick3DParticleSpriteParticle::commitParticles(
float)
532int QQuick3DParticleSpriteParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
534 if (!m_perEmitterData.contains(emitter)) {
535 m_perEmitterData.insert(emitter, PerEmitterData());
536 auto &perEmitter = m_perEmitterData[emitter];
537 perEmitter.particleUpdateNode =
new ParticleUpdateNode(system());
538 perEmitter.emitter = emitter;
539 perEmitter.particleUpdateNode->m_particle =
this;
540 perEmitter.emitterIndex = m_nextEmitterIndex++;
543 auto &perEmitter = m_perEmitterData[emitter];
544 int index = QQuick3DParticle::nextCurrentIndex(emitter);
545 if (m_spriteParticleData[index].emitterIndex != perEmitter.emitterIndex) {
546 if (m_spriteParticleData[index].emitterIndex >= 0)
547 perEmitterData(m_spriteParticleData[index].emitterIndex).particleCount--;
548 perEmitter.particleCount++;
550 m_spriteParticleData[index].emitterIndex = perEmitter.emitterIndex;
554void QQuick3DParticleSpriteParticle::setParticleData(
int particleIndex,
555 const QVector3D &position,
556 const QVector3D &rotation,
557 const QVector4D &color,
558 float size,
float age,
559 float animationFrame)
561 auto &dst = m_spriteParticleData[particleIndex];
562 dst = {position, rotation, color, size, age, animationFrame, dst.emitterIndex};
565void QQuick3DParticleSpriteParticle::resetParticleData(
int particleIndex)
567 auto &dst = m_spriteParticleData[particleIndex];
569 dst = {{}, {}, {}, 0.0f, 0.0f, -1.0f, dst.emitterIndex};
572void QQuick3DParticleSpriteParticle::updateParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
574 const auto &perEmitter = perEmitterData(updateNode);
575 const auto &particles = m_spriteParticleData;
576 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
579 const int particleCount = perEmitter.particleCount;
580 if (node->m_particleBuffer.particleCount() != particleCount || m_useAnimatedParticle)
581 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleSimple));
583 m_useAnimatedParticle =
false;
584 char *dest = node->m_particleBuffer.pointer();
585 const SpriteParticleData *src = particles.data();
586 const int pps = node->m_particleBuffer.particlesPerSlice();
587 const int ss = node->m_particleBuffer.sliceStride();
588 const int slices = node->m_particleBuffer.sliceCount();
589 const int emitterIndex = perEmitter.emitterIndex;
592 const auto smode = sortMode();
593 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
594 int offset = m_currentIndex;
595 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
597 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
598 return (linearIndex + offset + wrap) % wrap;
600 for (
int s = 0; s < slices; s++) {
601 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
602 for (
int p = 0; p < pps && i < particleCount; ) {
603 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
604 if (data->emitterIndex == emitterIndex) {
605 if (data->size > 0.0f)
606 bounds.include(data->position);
607 dp->position = data->position;
608 dp->rotation = data->rotation *
float(M_PI / 180.0f);
609 dp->color = data->color;
610 dp->size = data->size * m_particleScale;
621 for (
int s = 0; s < slices; s++) {
622 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
623 for (
int p = 0; p < pps && i < particleCount; ) {
624 if (src->emitterIndex == emitterIndex) {
625 if (src->size > 0.0f)
626 bounds.include(src->position);
627 dp->position = src->position;
628 dp->rotation = src->rotation *
float(M_PI / 180.0f);
629 dp->color = src->color;
630 dp->size = src->size * m_particleScale;
641 node->m_particleBuffer.setBounds(bounds);
644void QQuick3DParticleSpriteParticle::updateAnimatedParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
646 const auto &perEmitter = perEmitterData(updateNode);
647 const auto &particles = m_spriteParticleData;
648 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
651 const int particleCount = perEmitter.particleCount;
652 if (node->m_particleBuffer.particleCount() != particleCount || !m_useAnimatedParticle)
653 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleAnimated));
655 m_useAnimatedParticle =
true;
656 char *dest = node->m_particleBuffer.pointer();
657 const SpriteParticleData *src = particles.data();
658 const int pps = node->m_particleBuffer.particlesPerSlice();
659 const int ss = node->m_particleBuffer.sliceStride();
660 const int slices = node->m_particleBuffer.sliceCount();
661 const int emitterIndex = perEmitter.emitterIndex;
664 const auto smode = sortMode();
665 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
666 int offset = m_currentIndex;
667 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
669 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
670 return (linearIndex + offset + wrap) % wrap;
672 for (
int s = 0; s < slices; s++) {
673 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
674 for (
int p = 0; p < pps && i < particleCount; ) {
675 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
676 if (data->emitterIndex == emitterIndex) {
677 if (data->size > 0.0f)
678 bounds.include(data->position);
679 dp->position = data->position;
680 dp->rotation = data->rotation *
float(M_PI / 180.0f);
681 dp->color = data->color;
682 dp->size = data->size * m_particleScale;
684 dp->animationFrame = data->animationFrame;
694 for (
int s = 0; s < slices; s++) {
695 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
696 for (
int p = 0; p < pps && i < particleCount; ) {
697 if (src->emitterIndex == emitterIndex) {
698 if (src->size > 0.0f)
699 bounds.include(src->position);
700 dp->position = src->position;
701 dp->rotation = src->rotation *
float(M_PI / 180.0f);
702 dp->color = src->color;
703 dp->size = src->size * m_particleScale;
705 dp->animationFrame = src->animationFrame;
715 node->m_particleBuffer.setBounds(bounds);
718void QQuick3DParticleSpriteParticle::updateSceneManager(QQuick3DSceneManager *sceneManager)
722 QQuick3DObjectPrivate::refSceneManager(m_sprite, *sceneManager);
723 QQuick3DObjectPrivate::refSceneManager(m_colorTable, *sceneManager);
725 QQuick3DObjectPrivate::derefSceneManager(m_sprite);
726 QQuick3DObjectPrivate::derefSceneManager(m_colorTable);
731void QQuick3DParticleSpriteParticle::onLightDestroyed(QObject *object)
734 for (
int i = 0; i < m_lights.size(); ++i) {
735 if (m_lights[i] == object) {
736 m_lights.removeAt(i--);
741 updateFeatureLevel();
746void QQuick3DParticleSpriteParticle::qmlAppendLight(QQmlListProperty<QQuick3DAbstractLight> *list, QQuick3DAbstractLight *light)
752 if (light->parentItem()) {
753 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
754 self->m_lights.push_back(light);
755 self->updateFeatureLevel();
756 self->markNodesDirty();
758 connect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
762QQuick3DAbstractLight *QQuick3DParticleSpriteParticle::qmlLightAt(QQmlListProperty<QQuick3DAbstractLight> *list, qsizetype index)
764 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
765 if (index >= self->m_lights.size()) {
766 qWarning(
"The index exceeds the range of valid light targets.");
769 return self->m_lights.at(index);
772qsizetype QQuick3DParticleSpriteParticle::qmlLightsCount(QQmlListProperty<QQuick3DAbstractLight> *list)
774 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
775 return self->m_lights.size();
778void QQuick3DParticleSpriteParticle::qmlClearLights(QQmlListProperty<QQuick3DAbstractLight> *list)
780 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
781 for (
const auto &light : std::as_const(self->m_lights)) {
782 if (light->parentItem() ==
nullptr)
783 QQuick3DObjectPrivate::get(light)->derefSceneManager();
784 disconnect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
786 self->m_lights.clear();
787 self->updateFeatureLevel();
788 self->markNodesDirty();
Combined button and popup list for selecting options.
static QSSGRenderParticles::BlendMode mapBlendMode(QQuick3DParticleSpriteParticle::BlendMode mode)