Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquick3dparticlespriteparticle.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
6
7#include <QtQuick3D/private/qquick3dobject_p.h>
8
9#include <QtQuick3DUtils/private/qssgutils_p.h>
10
12
25 : QQuick3DParticle(parent)
26{
27 m_connections.insert("maxAmount", QObject::connect(this, &QQuick3DParticle::maxAmountChanged, this, [this]() {
29 }));
30 m_connections.insert("system", QObject::connect(this, &QQuick3DParticle::systemChanged, this, [this]() {
32 }));
33 m_connections.insert("sortMode", QObject::connect(this, &QQuick3DParticle::sortModeChanged, this, [this]() {
35 }));
36}
37
39{
41 m_spriteSequence->m_parentParticle = nullptr;
42 for (const auto &connection : std::as_const(m_connections))
45
46 auto lightList = lights();
47 qmlClearLights(&lightList);
48}
49
51{
52 for (const PerEmitterData &value : std::as_const(m_perEmitterData)) {
53 value.particleUpdateNode->m_particle = nullptr;
54 delete value.particleUpdateNode;
55 }
57}
58
83
102{
103 return m_sprite;
104}
105
118
131{
132 return m_billboard;
133}
134
148{
149 return m_particleScale;
150}
151
163{
164 return m_colorTable;
165}
166
177QQmlListProperty<QQuick3DAbstractLight> QQuick3DParticleSpriteParticle::lights()
178{
179 return QQmlListProperty<QQuick3DAbstractLight>(this,
180 nullptr,
181 QQuick3DParticleSpriteParticle::qmlAppendLight,
182 QQuick3DParticleSpriteParticle::qmlLightsCount,
183 QQuick3DParticleSpriteParticle::qmlLightAt,
184 QQuick3DParticleSpriteParticle::qmlClearLights);
185}
186
194{
195 return m_offset.x();
196}
197
205{
206 return m_offset.y();
207}
208
217{
218 return m_castsReflections;
219}
220
222{
223 if (m_blendMode == blendMode)
224 return;
225 m_blendMode = blendMode;
228}
229
231{
232 if (m_sprite == sprite)
233 return;
234
236
237 m_sprite = sprite;
240}
241
252
254{
255 if (m_billboard == billboard)
256 return;
257 m_billboard = billboard;
260}
261
263{
264 if (qFuzzyCompare(scale, m_particleScale))
265 return;
266 m_particleScale = scale;
269}
270
272{
273 if (m_colorTable == colorTable)
274 return;
275
277
278 m_colorTable = colorTable;
279 updateFeatureLevel();
282}
283
285{
286 if (qFuzzyCompare(value, m_offset.x()))
287 return;
288
289 m_offset.setX(value);
290 emit offsetXChanged();
291}
292
294{
295 if (qFuzzyCompare(value, m_offset.y()))
296 return;
297
298 m_offset.setY(value);
299 emit offsetYChanged();
300}
301
302void QQuick3DParticleSpriteParticle::setCastsReflections(bool castsReflections)
303{
304 if (m_castsReflections == castsReflections)
305 return;
306 m_castsReflections = castsReflections;
307 emit castsReflectionsChanged();
308}
309
310void QQuick3DParticleSpriteParticle::itemChange(QQuick3DObject::ItemChange change,
311 const QQuick3DObject::ItemChangeData &value)
312{
313 if (change == QQuick3DObject::ItemSceneChange)
314 updateSceneManager(value.sceneManager);
315}
316
330
331QSSGRenderParticles::FeatureLevel QQuick3DParticleSpriteParticle::mapFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel level)
332{
333 switch (level) {
346 }
347
348 Q_UNREACHABLE_RETURN(QSSGRenderParticles::FeatureLevel::Simple);
349}
350
352{
353 if (m_particle) {
354 node = m_particle->updateParticleNode(this, node);
357 auto particles = static_cast<QSSGRenderParticles *>(node);
358
360 m_particle->updateAnimatedParticleBuffer(this, particles);
361 else
362 m_particle->updateParticleBuffer(this, particles);
363
364 m_nodeDirty = false;
365 }
366 return node;
367}
368
370{
371 for (auto &perEmitter : m_perEmitterData) {
372 if (perEmitter.particleUpdateNode == updateNode)
373 return perEmitter;
374 }
375 return n_noPerEmitterData;
376}
377
379{
380 for (auto &perEmitter : m_perEmitterData) {
381 if (perEmitter.emitterIndex == emitterIndex)
382 return perEmitter;
383 }
384 return n_noPerEmitterData;
385}
386
389{
390 if (!node) {
391 markAllDirty();
392 node = new QSSGRenderParticles();
393 }
394
395 auto particles = static_cast<QSSGRenderParticles *>(node);
396 const auto &perEmitter = perEmitterData(updateNode);
397
398 if (!updateNode->m_nodeDirty)
399 return particles;
400
401 if (perEmitter.particleCount == 0)
402 return particles;
403
404 if (m_sprite)
405 particles->m_sprite = m_sprite->getRenderImage();
406 else
407 particles->m_sprite = nullptr;
408
409 if (m_spriteSequence) {
410 particles->m_spriteImageCount = m_spriteSequence->m_frameCount;
411 particles->m_blendImages = m_spriteSequence->m_interpolate;
412 } else {
413 particles->m_spriteImageCount = 1;
414 particles->m_blendImages = true;
415 }
416
417 particles->m_hasTransparency = hasTransparency();
418
419 if (m_colorTable)
420 particles->m_colorTable = m_colorTable->getRenderImage();
421 else
422 particles->m_colorTable = nullptr;
423
424 if (!m_lights.isEmpty()) {
425 // Matches to QSSGRenderParticles lights
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);
431 }
432 particles->m_lights = lightNodes;
433 }
434
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;
441
442 return particles;
443}
444
446{
447 if (m_particleData.size() == amount)
448 return;
449
450 m_particleData.resize(amount);
451 m_spriteParticleData.resize(amount);
452 reset();
453}
454
456{
458 delete value.particleUpdateNode;
459 value.particleUpdateNode = new ParticleUpdateNode(system);
460 value.particleUpdateNode->m_particle = this;
461 }
462}
463
465{
466 for (const PerEmitterData &value : std::as_const(m_perEmitterData))
467 value.particleUpdateNode->update();
468}
469
471{
472 for (const PerEmitterData &value : std::as_const(m_perEmitterData))
473 value.particleUpdateNode->m_nodeDirty = true;
474}
475
476void QQuick3DParticleSpriteParticle::updateFeatureLevel()
477{
478 FeatureLevel featureLevel = FeatureLevel::Simple;
479 if (m_lights.isEmpty()) {
480 if (m_colorTable)
481 featureLevel = FeatureLevel::Mapped;
483 featureLevel = FeatureLevel::Animated;
484 } else {
485 featureLevel = FeatureLevel::SimpleVLight;
486 if (m_colorTable)
487 featureLevel = FeatureLevel::MappedVLight;
489 featureLevel = FeatureLevel::AnimatedVLight;
490 }
491 if (featureLevel != m_featureLevel)
492 m_featureLevel = featureLevel;
493}
494
496{
497 if (!system() && qobject_cast<QQuick3DParticleSystem *>(parentItem()))
498 setSystem(qobject_cast<QQuick3DParticleSystem *>(parentItem()));
499
501}
502
510
517
519{
520 if (!m_perEmitterData.contains(emitter)) {
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++;
527 }
528 auto &perEmitter = m_perEmitterData[emitter];
530 if (m_spriteParticleData[index].emitterIndex != perEmitter.emitterIndex) {
531 if (m_spriteParticleData[index].emitterIndex >= 0)
533 perEmitter.particleCount++;
534 }
535 m_spriteParticleData[index].emitterIndex = perEmitter.emitterIndex;
536 return index;
537}
538
540 const QVector3D &position,
541 const QVector3D &rotation,
542 const QVector4D &color,
543 float size, float age,
544 float animationFrame)
545{
546 auto &dst = m_spriteParticleData[particleIndex];
547 dst = {position, rotation, color, size, age, animationFrame, dst.emitterIndex};
548}
549
551{
552 auto &dst = m_spriteParticleData[particleIndex];
553 if (dst.size > 0.0f)
554 dst = {{}, {}, {}, 0.0f, 0.0f, -1.0f, dst.emitterIndex};
555}
556
557void QQuick3DParticleSpriteParticle::updateParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
558{
559 const auto &perEmitter = perEmitterData(updateNode);
560 const auto &particles = m_spriteParticleData;
561 QSSGRenderParticles *node = static_cast<QSSGRenderParticles *>(spatialNode);
562 if (!node)
563 return;
564 const int particleCount = perEmitter.particleCount;
565 if (node->m_particleBuffer.particleCount() != particleCount || m_useAnimatedParticle)
566 node->m_particleBuffer.resize(particleCount, sizeof(QSSGParticleSimple));
567
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;
575 int i = 0;
576 QSSGBounds3 bounds;
577 const auto smode = sortMode();
580 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
581 int li = 0;
582 const auto sourceIndex = [&](int linearIndex, int offset, int wrap) -> int {
583 return (linearIndex + offset + wrap) % wrap;
584 };
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;
596 dp->age = data->age;
597 dp++;
598 p++;
599 i++;
600 }
601 li++;
602 }
603 dest += ss;
604 }
605 } else {
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;
616 dp->age = src->age;
617 dp++;
618 p++;
619 i++;
620 }
621 src++;
622 }
623 dest += ss;
624 }
625 }
626 node->m_particleBuffer.setBounds(bounds);
627}
628
629void QQuick3DParticleSpriteParticle::updateAnimatedParticleBuffer(ParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
630{
631 const auto &perEmitter = perEmitterData(updateNode);
632 const auto &particles = m_spriteParticleData;
633 QSSGRenderParticles *node = static_cast<QSSGRenderParticles *>(spatialNode);
634 if (!node)
635 return;
636 const int particleCount = perEmitter.particleCount;
637 if (node->m_particleBuffer.particleCount() != particleCount || !m_useAnimatedParticle)
638 node->m_particleBuffer.resize(particleCount, sizeof(QSSGParticleAnimated));
639
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;
647 int i = 0;
648 QSSGBounds3 bounds;
649 const auto smode = sortMode();
652 int step = (smode == QQuick3DParticle::SortNewest) ? -1 : 1;
653 int li = 0;
654 const auto sourceIndex = [&](int linearIndex, int offset, int wrap) -> int {
655 return (linearIndex + offset + wrap) % wrap;
656 };
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;
668 dp->age = data->age;
669 dp->animationFrame = data->animationFrame;
670 dp++;
671 p++;
672 i++;
673 }
674 li++;
675 }
676 dest += ss;
677 }
678 } else {
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;
689 dp->age = src->age;
690 dp->animationFrame = src->animationFrame;
691 dp++;
692 p++;
693 i++;
694 }
695 src++;
696 }
697 dest += ss;
698 }
699 }
700 node->m_particleBuffer.setBounds(bounds);
701}
702
703void QQuick3DParticleSpriteParticle::updateSceneManager(QQuick3DSceneManager *sceneManager)
704{
705 // Check all the resource value's scene manager, and update as necessary.
706 if (sceneManager) {
707 QQuick3DObjectPrivate::refSceneManager(m_sprite, *sceneManager);
708 QQuick3DObjectPrivate::refSceneManager(m_colorTable, *sceneManager);
709 } else {
712 }
713}
714
715// Lights
716void QQuick3DParticleSpriteParticle::onLightDestroyed(QObject *object)
717{
718 bool found = false;
719 for (int i = 0; i < m_lights.size(); ++i) {
720 if (m_lights[i] == object) {
721 m_lights.removeAt(i--);
722 found = true;
723 }
724 }
725 if (found) {
726 updateFeatureLevel();
728 }
729}
730
731void QQuick3DParticleSpriteParticle::qmlAppendLight(QQmlListProperty<QQuick3DAbstractLight> *list, QQuick3DAbstractLight *light)
732{
733 if (!light)
734 return;
735
736 // Light must be id of an existing View3D light and not inline light element
737 if (light->parentItem()) {
739 self->m_lights.push_back(light);
740 self->updateFeatureLevel();
741 self->markNodesDirty();
742 // Make sure ligths are removed when destroyed
743 connect(light, &QQuick3DParticleSpriteParticle::destroyed, self, &QQuick3DParticleSpriteParticle::onLightDestroyed);
744 }
745}
746
747QQuick3DAbstractLight *QQuick3DParticleSpriteParticle::qmlLightAt(QQmlListProperty<QQuick3DAbstractLight> *list, qsizetype index)
748{
750 if (index >= self->m_lights.size()) {
751 qWarning("The index exceeds the range of valid light targets.");
752 return nullptr;
753 }
754 return self->m_lights.at(index);
755}
756
757qsizetype QQuick3DParticleSpriteParticle::qmlLightsCount(QQmlListProperty<QQuick3DAbstractLight> *list)
758{
760 return self->m_lights.size();
761}
762
763void QQuick3DParticleSpriteParticle::qmlClearLights(QQmlListProperty<QQuick3DAbstractLight> *list)
764{
766 for (const auto &light : std::as_const(self->m_lights)) {
767 if (light->parentItem() == nullptr)
768 QQuick3DObjectPrivate::get(light)->derefSceneManager();
769 light->disconnect(self, SLOT(onLightDestroyed(QObject*)));
770 }
771 self->m_lights.clear();
772 self->updateFeatureLevel();
773 self->markNodesDirty();
774}
775
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
qsizetype size() const noexcept
Definition qlist.h:397
void resize(qsizetype size)
Definition qlist.h:403
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
void clear()
Definition qmap.h:289
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node) override
void refSceneManager(QQuick3DSceneManager &)
static QQuick3DObjectPrivate * get(QQuick3DObject *item)
static void attachWatcher(Context *context, Setter setter, Object3D *newO, Object3D *oldO)
Attach a object-destroyed-watcher to an object that's not owned.
virtual void markAllDirty()
QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node) override
virtual void setParticleData(int particleIndex, const QVector3D &position, const QVector3D &rotation, const QVector4D &color, float size, float age, float animationFrame)
QQuick3DParticleSpriteParticle(QQuick3DNode *parent=nullptr)
\qmltype SpriteParticle3D \inherits Particle3D \inqmlmodule QtQuick3D.Particles3D
virtual void handleSystemChanged(QQuick3DParticleSystem *system)
int nextCurrentIndex(const QQuick3DParticleEmitter *emitter) override
QQmlListProperty< QQuick3DAbstractLight > lights
\qmlproperty list<Light> SpriteParticle3D::lights
virtual void resetParticleData(int particleIndex)
void setBlendMode(QQuick3DParticleSpriteParticle::BlendMode blendMode)
QVector< SpriteParticleData > m_spriteParticleData
QMap< const QQuick3DParticleEmitter *, PerEmitterData > m_perEmitterData
PerEmitterData & perEmitterData(const QQuick3DNode *updateNode)
void setSpriteSequence(QQuick3DParticleSpriteSequence *spriteSequence)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void setColorTable(QQuick3DTexture *colorTable)
bool castsReflections
\qmlproperty bool SpriteParticle3D::castsReflections
QSSGRenderGraphObject * updateParticleNode(const ParticleUpdateNode *updateNode, QSSGRenderGraphObject *node)
QQuick3DParticleSpriteSequence * spriteSequence
void itemChange(ItemChange, const ItemChangeData &) override
void sortModeChanged()
void maxAmountChanged()
QList< QQuick3DParticleData > m_particleData
QQuick3DParticleSpriteSequence * m_spriteSequence
virtual int nextCurrentIndex(const QQuick3DParticleEmitter *emitter)
void setSystem(QQuick3DParticleSystem *system)
QQuick3DParticleSystem * system() const
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QSSGRenderImage * getRenderImage()
Class representing 3D range or axis aligned bounding box.
void include(const QVector3D &v)
expands the volume to include v
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
void push_back(QChar c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.h:957
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr void setX(float x) noexcept
Sets the x coordinate of this point to the given finite x coordinate.
Definition qvectornd.h:674
constexpr void setY(float y) noexcept
Sets the y coordinate of this point to the given finite y coordinate.
Definition qvectornd.h:675
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
Combined button and popup list for selecting options.
QString self
Definition language.cpp:58
DBusConnection * connection
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qWarning
Definition qlogging.h:166
#define M_PI
Definition qmath.h:209
#define SLOT(a)
Definition qobjectdefs.h:52
GLenum mode
GLenum GLuint GLint level
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
GLuint color
[2]
GLenum GLenum dst
GLenum GLuint GLintptr offset
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
static QSSGRenderParticles::BlendMode mapBlendMode(QQuick3DParticleSpriteParticle::BlendMode mode)
#define Q_QUICK3D_PROFILE_ASSIGN_ID_SG(obj, bgnode)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
static QT_BEGIN_NAMESPACE QAsn1Element wrap(quint8 type, const QAsn1Element &child)
#define Q_EMIT
#define emit
ptrdiff_t qsizetype
Definition qtypes.h:165
QList< int > list
[14]
void resize(int particleCount, int particleSize=sizeof(QSSGParticleSimple))
void setBounds(const QSSGBounds3 &bounds)
static float signedSquared(float val)
QSSGParticleBuffer m_particleBuffer