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;
467void QQuick3DParticleSpriteParticle::updateNodes()
469 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
470 value.particleUpdateNode->update();
473void QQuick3DParticleSpriteParticle::markNodesDirty()
475 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
476 value.particleUpdateNode->m_nodeDirty =
true;
479void QQuick3DParticleSpriteParticle::updateNodeLayers()
481 auto *psystem = system();
484 const int layers = psystem->layers();
485 for (
const PerEmitterData &value : std::as_const(m_perEmitterData))
486 value.particleUpdateNode->setLayers(layers);
489void QQuick3DParticleSpriteParticle::updateFeatureLevel()
491 FeatureLevel featureLevel = FeatureLevel::Simple;
492 if (m_lights.isEmpty()) {
494 featureLevel = FeatureLevel::Mapped;
495 if (m_spriteSequence)
496 featureLevel = FeatureLevel::Animated;
498 featureLevel = FeatureLevel::SimpleVLight;
500 featureLevel = FeatureLevel::MappedVLight;
501 if (m_spriteSequence)
502 featureLevel = FeatureLevel::AnimatedVLight;
504 if (featureLevel != m_featureLevel)
505 m_featureLevel = featureLevel;
508void QQuick3DParticleSpriteParticle::componentComplete()
510 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
511 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
513 QQuick3DParticle::componentComplete();
516void QQuick3DParticleSpriteParticle::reset()
518 QQuick3DParticle::reset();
520 m_nextEmitterIndex = 0;
521 m_spriteParticleData.fill({});
524void QQuick3DParticleSpriteParticle::commitParticles(
float)
531int QQuick3DParticleSpriteParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
533 if (!m_perEmitterData.contains(emitter)) {
534 m_perEmitterData.insert(emitter, PerEmitterData());
535 auto &perEmitter = m_perEmitterData[emitter];
536 perEmitter.particleUpdateNode =
new ParticleUpdateNode(system());
537 perEmitter.emitter = emitter;
538 perEmitter.particleUpdateNode->m_particle =
this;
539 perEmitter.emitterIndex = m_nextEmitterIndex++;
542 auto &perEmitter = m_perEmitterData[emitter];
543 int index = QQuick3DParticle::nextCurrentIndex(emitter);
544 if (m_spriteParticleData[index].emitterIndex != perEmitter.emitterIndex) {
545 if (m_spriteParticleData[index].emitterIndex >= 0)
546 perEmitterData(m_spriteParticleData[index].emitterIndex).particleCount--;
547 perEmitter.particleCount++;
549 m_spriteParticleData[index].emitterIndex = perEmitter.emitterIndex;
553void QQuick3DParticleSpriteParticle::setParticleData(
int particleIndex,
554 const QVector3D &position,
555 const QVector3D &rotation,
556 const QVector4D &color,
557 float size,
float age,
558 float animationFrame)
560 auto &dst = m_spriteParticleData[particleIndex];
561 dst = {position, rotation, color, size, age, animationFrame, dst.emitterIndex};
564void QQuick3DParticleSpriteParticle::resetParticleData(
int particleIndex)
566 auto &dst = m_spriteParticleData[particleIndex];
568 dst = {{}, {}, {}, 0.0f, 0.0f, -1.0f, dst.emitterIndex};
571void QQuick3DParticleSpriteParticle::updateParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
573 const auto &perEmitter = perEmitterData(updateNode);
574 const auto &particles = m_spriteParticleData;
575 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
578 const int particleCount = perEmitter.particleCount;
579 if (node->m_particleBuffer.particleCount() != particleCount || m_useAnimatedParticle)
580 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleSimple));
582 m_useAnimatedParticle =
false;
583 char *dest = node->m_particleBuffer.pointer();
584 const SpriteParticleData *src = particles.data();
585 const int pps = node->m_particleBuffer.particlesPerSlice();
586 const int ss = node->m_particleBuffer.sliceStride();
587 const int slices = node->m_particleBuffer.sliceCount();
588 const int emitterIndex = perEmitter.emitterIndex;
591 const auto smode = sortMode();
592 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
593 int offset = m_currentIndex;
594 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
596 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
597 return (linearIndex + offset + wrap) % wrap;
599 for (
int s = 0; s < slices; s++) {
600 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
601 for (
int p = 0; p < pps && i < particleCount; ) {
602 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
603 if (data->emitterIndex == emitterIndex) {
604 if (data->size > 0.0f)
605 bounds.include(data->position);
606 dp->position = data->position;
607 dp->rotation = data->rotation *
float(M_PI / 180.0f);
608 dp->color = data->color;
609 dp->size = data->size * m_particleScale;
620 for (
int s = 0; s < slices; s++) {
621 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
622 for (
int p = 0; p < pps && i < particleCount; ) {
623 if (src->emitterIndex == emitterIndex) {
624 if (src->size > 0.0f)
625 bounds.include(src->position);
626 dp->position = src->position;
627 dp->rotation = src->rotation *
float(M_PI / 180.0f);
628 dp->color = src->color;
629 dp->size = src->size * m_particleScale;
640 node->m_particleBuffer.setBounds(bounds);
643void QQuick3DParticleSpriteParticle::updateAnimatedParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
645 const auto &perEmitter = perEmitterData(updateNode);
646 const auto &particles = m_spriteParticleData;
647 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
650 const int particleCount = perEmitter.particleCount;
651 if (node->m_particleBuffer.particleCount() != particleCount || !m_useAnimatedParticle)
652 node->m_particleBuffer.resize(particleCount,
sizeof(QSSGParticleAnimated));
654 m_useAnimatedParticle =
true;
655 char *dest = node->m_particleBuffer.pointer();
656 const SpriteParticleData *src = particles.data();
657 const int pps = node->m_particleBuffer.particlesPerSlice();
658 const int ss = node->m_particleBuffer.sliceStride();
659 const int slices = node->m_particleBuffer.sliceCount();
660 const int emitterIndex = perEmitter.emitterIndex;
663 const auto smode = sortMode();
664 if (smode == QQuick3DParticle::SortNewest || smode == QQuick3DParticle::SortOldest) {
665 int offset = m_currentIndex;
666 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
668 const auto sourceIndex = [&](
int linearIndex,
int offset,
int wrap) ->
int {
669 return (linearIndex + offset + wrap) % wrap;
671 for (
int s = 0; s < slices; s++) {
672 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
673 for (
int p = 0; p < pps && i < particleCount; ) {
674 const SpriteParticleData *data = src + sourceIndex(li * step, offset, m_maxAmount);
675 if (data->emitterIndex == emitterIndex) {
676 if (data->size > 0.0f)
677 bounds.include(data->position);
678 dp->position = data->position;
679 dp->rotation = data->rotation *
float(M_PI / 180.0f);
680 dp->color = data->color;
681 dp->size = data->size * m_particleScale;
683 dp->animationFrame = data->animationFrame;
693 for (
int s = 0; s < slices; s++) {
694 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
695 for (
int p = 0; p < pps && i < particleCount; ) {
696 if (src->emitterIndex == emitterIndex) {
697 if (src->size > 0.0f)
698 bounds.include(src->position);
699 dp->position = src->position;
700 dp->rotation = src->rotation *
float(M_PI / 180.0f);
701 dp->color = src->color;
702 dp->size = src->size * m_particleScale;
704 dp->animationFrame = src->animationFrame;
714 node->m_particleBuffer.setBounds(bounds);
717void QQuick3DParticleSpriteParticle::updateSceneManager(QQuick3DSceneManager *sceneManager)
721 QQuick3DObjectPrivate::refSceneManager(m_sprite, *sceneManager);
722 QQuick3DObjectPrivate::refSceneManager(m_colorTable, *sceneManager);
724 QQuick3DObjectPrivate::derefSceneManager(m_sprite);
725 QQuick3DObjectPrivate::derefSceneManager(m_colorTable);
730void QQuick3DParticleSpriteParticle::onLightDestroyed(QObject *object)
733 for (
int i = 0; i < m_lights.size(); ++i) {
734 if (m_lights[i] == object) {
735 m_lights.removeAt(i--);
740 updateFeatureLevel();
745void QQuick3DParticleSpriteParticle::qmlAppendLight(QQmlListProperty<QQuick3DAbstractLight> *list, QQuick3DAbstractLight *light)
751 if (light->parentItem()) {
752 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
753 self->m_lights.push_back(light);
754 self->updateFeatureLevel();
755 self->markNodesDirty();
757 connect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
761QQuick3DAbstractLight *QQuick3DParticleSpriteParticle::qmlLightAt(QQmlListProperty<QQuick3DAbstractLight> *list, qsizetype index)
763 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
764 if (index >= self->m_lights.size()) {
765 qWarning(
"The index exceeds the range of valid light targets.");
768 return self->m_lights.at(index);
771qsizetype QQuick3DParticleSpriteParticle::qmlLightsCount(QQmlListProperty<QQuick3DAbstractLight> *list)
773 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
774 return self->m_lights.size();
777void QQuick3DParticleSpriteParticle::qmlClearLights(QQmlListProperty<QQuick3DAbstractLight> *list)
779 QQuick3DParticleSpriteParticle *self =
static_cast<QQuick3DParticleSpriteParticle *>(list->object);
780 for (
const auto &light : std::as_const(self->m_lights)) {
781 if (light->parentItem() ==
nullptr)
782 QQuick3DObjectPrivate::get(light)->derefSceneManager();
783 disconnect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
785 self->m_lights.clear();
786 self->updateFeatureLevel();
787 self->markNodesDirty();
Combined button and popup list for selecting options.
static QSSGRenderParticles::BlendMode mapBlendMode(QQuick3DParticleSpriteParticle::BlendMode mode)