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
qquick3dparticlesystem.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
4#include <QtQuick3D/private/qquick3dquaternionutils_p.h>
10#include <private/qqmldelegatemodel_p.h>
15#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
16#include <qtquick3d_tracepoints_p.h>
17#include <cmath>
18
20
61Q_TRACE_POINT(qtquick3d, QSSG_particleUpdate_entry);
62Q_TRACE_POINT(qtquick3d, QSSG_particleUpdate_exit, int particleCount);
63
65 : QQuick3DNode(parent)
66 , m_running(true)
67 , m_paused(false)
68 , m_initialized(false)
69 , m_componentComplete(false)
70 , m_animation(new QQuick3DParticleSystemAnimation(this))
71 , m_updateAnimation(new QQuick3DParticleSystemUpdate(this))
72 , m_logging(false)
73 , m_loggingData(new QQuick3DParticleSystemLogging(this))
74{
75 connect(m_loggingData, &QQuick3DParticleSystemLogging::loggingIntervalChanged, &m_loggingTimer, [this]() {
76 m_loggingTimer.setInterval(m_loggingData->m_loggingInterval);
77 });
78}
79
81{
82 m_animation->stop();
83 m_updateAnimation->stop();
84
85 for (const auto &connection : std::as_const(m_connections))
87 // purposeful copy
88 const auto particles = m_particles;
89 const auto emitters = m_emitters;
90 const auto trailEmitters = m_trailEmitters;
91 const auto affectors = m_affectors;
92 for (auto *particle : particles)
93 particle->setSystem(nullptr);
94 for (auto *emitter : emitters)
95 emitter->setSystem(nullptr);
96 for (auto *emitter : trailEmitters)
97 emitter->setSystem(nullptr);
98 for (auto *affector : affectors)
99 affector->setSystem(nullptr);
100}
101
114{
115 return m_running;
116}
117
128{
129 return m_paused;
130}
131
143{
144 return m_startTime;
145}
146
169{
170 return m_time;
171}
172
187{
188 return m_useRandomSeed;
189}
190
207{
208 return m_seed;
209}
210
223{
224 return m_logging;
225}
226
240{
241 return m_loggingData;
242}
243
254{
255 for (auto emitter : std::as_const(m_emitters))
256 emitter->reset();
257 for (auto emitter : std::as_const(m_trailEmitters))
258 emitter->reset();
259 for (auto particle : std::as_const(m_particles))
260 particle->reset();
261 m_particleIdIndex = 0;
262}
263
269{
270 return m_currentTime;
271}
272
274{
275 if (m_running != running) {
276 m_running = running;
278 setPaused(false);
279
280 if (m_running)
281 reset();
282
283 if (m_componentComplete && !m_running && m_useRandomSeed)
284 doSeedRandomization();
285
286 (m_running && !isEditorModeOn()) ? m_animation->start() : m_animation->stop();
287 }
288}
289
291{
292 if (m_paused != paused) {
293 m_paused = paused;
294 if (m_animation->state() != QAbstractAnimation::Stopped)
295 m_paused ? m_animation->pause() : m_animation->resume();
297 }
298}
299
301{
302 if (m_startTime == startTime)
303 return;
304
305 m_startTime = startTime;
307}
308
310{
311 if (m_time == time)
312 return;
313
314 // Update the time and mark the system dirty
315 m_time = time;
316 m_updateAnimation->setDirty(true);
317
319}
320
322{
323 if (m_useRandomSeed == randomize)
324 return;
325
326 m_useRandomSeed = randomize;
327 // When set to true, random values are recalculated with a random seed
328 // and random values will become independent of particle index when possible.
329 if (m_useRandomSeed)
330 doSeedRandomization();
331 m_rand.setDeterministic(!m_useRandomSeed);
333}
334
336{
337 if (m_seed == seed)
338 return;
339
340 m_seed = seed;
341 m_rand.init(m_seed);
343}
344
346{
347 if (m_logging == logging)
348 return;
349
350 m_logging = logging;
351
352 resetLoggingVariables();
353 m_loggingData->resetData();
354
355 if (m_logging)
356 m_loggingTimer.start();
357 else
358 m_loggingTimer.stop();
359
361}
362
368{
369 if (m_editorTime == time)
370 return;
371
372 // Update the time and mark the system dirty
373 m_editorTime = time;
374 m_updateAnimation->setDirty(true);
375}
376
378{
380 m_componentComplete = true;
381 m_updateAnimation->start();
382
383 connect(&m_loggingTimer, &QTimer::timeout, this, &QQuick3DParticleSystem::updateLoggingData);
384 m_loggingTimer.setInterval(m_loggingData->m_loggingInterval);
385
386 if (m_useRandomSeed)
387 doSeedRandomization();
388 else
389 m_rand.init(m_seed);
390
391 m_time = 0;
392 m_currentTime = 0;
393 m_editorTime = 0;
394
396
397 // Reset restarts the animation (if running)
398 if (m_animation->state() == QAbstractAnimation::Running)
399 m_animation->stop();
400 if (m_running && !isEditorModeOn())
401 m_animation->start();
402 if (m_paused)
403 m_animation->pause();
404
405 m_initialized = true;
406}
407
408void QQuick3DParticleSystem::refresh()
409{
410 // If the system isn't running, force refreshing by calling update
411 // with the current time. QAbstractAnimation::setCurrentTime() implementation
412 // always calls updateCurrentTime() even if the time would remain the same.
413 if (!m_running || m_paused || isEditorModeOn())
414 m_animation->setCurrentTime(isEditorModeOn() ? m_editorTime : m_time);
415}
416
417void QQuick3DParticleSystem::markDirty()
418{
419 // Mark the system dirty so things are updated at the next frame.
420 m_updateAnimation->setDirty(true);
421}
422
424{
425 int pCount = 0;
426 for (auto particle : std::as_const(m_particles))
427 pCount += particle->maxAmount();
428 return pCount;
429}
430
432{
433 auto *model = qobject_cast<QQuick3DParticleModelParticle *>(particle);
434 if (model) {
435 registerParticleModel(model);
436 return;
437 }
438 auto *sprite = qobject_cast<QQuick3DParticleSpriteParticle *>(particle);
439 if (sprite) {
440 registerParticleSprite(sprite);
441 return;
442 }
443 m_particles << particle;
444}
445
446void QQuick3DParticleSystem::registerParticleModel(QQuick3DParticleModelParticle *m)
447{
448 m_particles << m;
449}
450
451void QQuick3DParticleSystem::registerParticleSprite(QQuick3DParticleSpriteParticle *m)
452{
453 m_particles << m;
454}
455
457{
458 auto *model = qobject_cast<QQuick3DParticleModelParticle *>(particle);
459 if (model) {
460 m_particles.removeAll(particle);
461 return;
462 }
463 auto *sprite = qobject_cast<QQuick3DParticleSpriteParticle *>(particle);
464 if (sprite) {
465 m_particles.removeAll(particle);
466 return;
467 }
468
469 m_particles.removeAll(particle);
470}
471
473{
474 auto te = qobject_cast<QQuick3DParticleTrailEmitter *>(e);
475 if (te)
476 m_trailEmitters << te;
477 else
478 m_emitters << e;
479}
480
482{
483 auto te = qobject_cast<QQuick3DParticleTrailEmitter *>(e);
484 if (te)
485 m_trailEmitters.removeAll(te);
486 else
487 m_emitters.removeAll(e);
488}
489
491{
492 m_affectors << a;
493 m_connections.insert(a, connect(a, &QQuick3DParticleAffector::update, this, &QQuick3DParticleSystem::markDirty));
494}
495
497{
498 QObject::disconnect(m_connections[a]);
499 m_connections.remove(a);
500 m_affectors.removeAll(a);
501}
502
504{
505 if (!m_initialized || isGloballyDisabled() || (isEditorModeOn() && !visible()))
506 return;
507
508 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DParticleUpdate);
509
510 Q_TRACE(QSSG_particleUpdate_entry);
511
512 m_currentTime = currentTime;
513 const float timeS = float(m_currentTime / 1000.0f);
514
515 m_particlesMax = 0;
516 m_particlesUsed = 0;
517 m_updates++;
518
519 m_perfTimer.restart();
520
521 // Emit new particles
522 for (auto emitter : std::as_const(m_emitters))
523 emitter->emitParticles();
524
525 // Prepare Affectors
526 for (auto affector : std::as_const(m_affectors)) {
527 if (affector->m_enabled)
528 affector->prepareToAffect();
529 }
530
531 // Animate current particles
532 for (auto particle : std::as_const(m_particles)) {
533
534 // Collect possible trail emits
535 QVector<TrailEmits> trailEmits;
536 for (auto emitter : std::as_const(m_trailEmitters)) {
537 if (emitter->follow() == particle) {
538 int emitAmount = emitter->getEmitAmount();
539 if (emitAmount > 0 || emitter->hasBursts()) {
540 TrailEmits e;
541 e.emitter = emitter;
542 e.amount = emitAmount;
543 trailEmits << e;
544 }
545 }
546 }
547
548 m_particlesMax += particle->maxAmount();
549
550 QQuick3DParticleSpriteParticle *spriteParticle = qobject_cast<QQuick3DParticleSpriteParticle *>(particle);
551 if (spriteParticle) {
552 processSpriteParticle(spriteParticle, trailEmits, timeS);
553 continue;
554 }
555 QQuick3DParticleModelParticle *modelParticle = qobject_cast<QQuick3DParticleModelParticle *>(particle);
556 if (modelParticle) {
557 processModelParticle(modelParticle, trailEmits, timeS);
558 continue;
559 }
560 QQuick3DParticleModelBlendParticle *mbp = qobject_cast<QQuick3DParticleModelBlendParticle *>(particle);
561 if (mbp) {
562 processModelBlendParticle(mbp, trailEmits, timeS);
563 continue;
564 }
565 }
566
567 // Clear bursts from trailemitters
568 for (auto emitter : std::as_const(m_trailEmitters))
569 emitter->clearBursts();
570
571 m_timeAnimation += m_perfTimer.nsecsElapsed();
572 m_updateAnimation->setDirty(false);
573 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DParticleUpdate, m_particlesUsed, Q_QUICK3D_PROFILE_GET_ID(this));
574
575 Q_TRACE(QSSG_particleUpdate_exit, m_particlesUsed);
576
577}
578
579void QQuick3DParticleSystem::processModelParticle(QQuick3DParticleModelParticle *modelParticle, const QVector<TrailEmits> &trailEmits, float timeS)
580{
581 modelParticle->clearInstanceTable();
582
583 const int c = modelParticle->maxAmount();
584
585 for (int i = 0; i < c; i++) {
586 const auto d = &modelParticle->m_particleData.at(i);
587
588 const float particleTimeEnd = d->startTime + d->lifetime;
589
590 if (timeS < d->startTime || timeS > particleTimeEnd) {
591 if (timeS > particleTimeEnd && d->lifetime > 0.0f) {
592 for (auto trailEmit : std::as_const(trailEmits))
593 trailEmit.emitter->emitTrailParticles(d->startPosition + (d->startVelocity * (particleTimeEnd - d->startTime)), 0, QQuick3DParticleDynamicBurst::TriggerEnd);
594 }
595 // Particle not alive currently
596 continue;
597 }
598
599 const float particleTimeS = timeS - d->startTime;
600 QQuick3DParticleDataCurrent currentData;
601 if (timeS >= d->startTime && d->lifetime <= 0.0f) {
602 for (auto trailEmit : std::as_const(trailEmits))
603 trailEmit.emitter->emitTrailParticles(d->startPosition, 0, QQuick3DParticleDynamicBurst::TriggerStart);
604 }
605 // Process features shared for both model & sprite particles
606 processParticleCommon(currentData, d, particleTimeS);
607
608 // Add a base rotation if alignment requested
609 if (modelParticle->m_alignMode != QQuick3DParticle::AlignNone)
610 processParticleAlignment(currentData, modelParticle, d);
611
612 // 0.0 -> 1.0 during the particle lifetime
613 const float timeChange = std::max(0.0f, std::min(1.0f, particleTimeS / d->lifetime));
614
615 // Scale from initial to endScale
616 currentData.scale = modelParticle->m_initialScale * (d->endSize * timeChange + d->startSize * (1.0f - timeChange));
617
618 // Fade in & out
619 const float particleTimeLeftS = d->lifetime - particleTimeS;
620 processParticleFadeInOut(currentData, modelParticle, particleTimeS, particleTimeLeftS);
621
622 // Affectors
623 for (auto affector : std::as_const(m_affectors)) {
624 // If affector is set to affect only particular particles, check these are included
625 if (affector->m_enabled && (affector->m_particles.isEmpty() || affector->m_particles.contains(modelParticle)))
626 affector->affectParticle(*d, &currentData, particleTimeS);
627 }
628
629 // Emit new particles from trails
630 for (auto trailEmit : std::as_const(trailEmits))
631 trailEmit.emitter->emitTrailParticles(currentData.position, trailEmit.amount, QQuick3DParticleDynamicBurst::TriggerTime);
632
633 const QColor color(currentData.color.r, currentData.color.g, currentData.color.b, currentData.color.a);
634 // Set current particle properties
635 modelParticle->addInstance(currentData.position, currentData.scale, currentData.rotation, color, timeChange);
636 }
637 modelParticle->commitInstance();
638}
639
640static QVector3D mix(const QVector3D &a, const QVector3D &b, float f)
641{
642 return (b - a) * f + a;
643}
644
645void QQuick3DParticleSystem::processModelBlendParticle(QQuick3DParticleModelBlendParticle *particle, const QVector<TrailEmits> &trailEmits, float timeS)
646{
647 const int c = particle->maxAmount();
648
649 for (int i = 0; i < c; i++) {
650 const auto d = &particle->m_particleData.at(i);
651
652 const float particleTimeEnd = d->startTime + d->lifetime;
653
654 if (timeS < d->startTime || timeS > particleTimeEnd) {
655 if (timeS > particleTimeEnd && d->lifetime > 0.0f) {
656 for (auto trailEmit : std::as_const(trailEmits))
657 trailEmit.emitter->emitTrailParticles(d->startPosition + (d->startVelocity * (particleTimeEnd - d->startTime)), 0, QQuick3DParticleDynamicBurst::TriggerEnd);
658 }
659 // Particle not alive currently
660 float age = 0.0f;
661 float size = 0.0f;
663 QVector3D rot;
664 QVector4D color(float(d->startColor.r)/ 255.0f,
665 float(d->startColor.g)/ 255.0f,
666 float(d->startColor.b)/ 255.0f,
667 float(d->startColor.a)/ 255.0f);
668 if (d->startTime > 0.0f && timeS > particleTimeEnd
671 age = 1.0f;
672 size = 1.0f;
673 pos = particle->particleEndPosition(i);
674 rot = particle->particleEndRotation(i);
676 color.setW(0.0f);
679 age = 0.0f;
680 size = 1.0f;
681 pos = particle->particleCenter(i);
683 color.setW(0.0f);
684 }
685 particle->setParticleData(i, pos, rot, color, size, age);
686 continue;
687 }
688
689 const float particleTimeS = timeS - d->startTime;
690 QQuick3DParticleDataCurrent currentData;
691 if (timeS >= d->startTime && d->lifetime <= 0.0f) {
692 for (auto trailEmit : std::as_const(trailEmits))
693 trailEmit.emitter->emitTrailParticles(d->startPosition, 0, QQuick3DParticleDynamicBurst::TriggerStart);
694 }
695
696 // Process features shared for both model & sprite particles
697 processParticleCommon(currentData, d, particleTimeS);
698
699 // 0.0 -> 1.0 during the particle lifetime
700 const float timeChange = std::max(0.0f, std::min(1.0f, particleTimeS / d->lifetime));
701
702 // Scale from initial to endScale
703 const float scale = d->endSize * timeChange + d->startSize * (1.0f - timeChange);
704 currentData.scale = QVector3D(scale, scale, scale);
705
706 // Fade in & out
707 const float particleTimeLeftS = d->lifetime - particleTimeS;
708 processParticleFadeInOut(currentData, particle, particleTimeS, particleTimeLeftS);
709
710 // Affectors
711 for (auto affector : std::as_const(m_affectors)) {
712 // If affector is set to affect only particular particles, check these are included
713 if (affector->m_enabled && (affector->m_particles.isEmpty() || affector->m_particles.contains(particle)))
714 affector->affectParticle(*d, &currentData, particleTimeS);
715 }
716
717 // Emit new particles from trails
718 for (auto trailEmit : std::as_const(trailEmits))
719 trailEmit.emitter->emitTrailParticles(currentData.position, trailEmit.amount, QQuick3DParticleDynamicBurst::TriggerTime);
720
721 // Set current particle properties
722 const QVector4D color(float(currentData.color.r) / 255.0f,
723 float(currentData.color.g) / 255.0f,
724 float(currentData.color.b) / 255.0f,
725 float(currentData.color.a) / 255.0f);
726 float endTimeS = particle->endTime() * 0.001f;
729 && particleTimeLeftS < endTimeS) {
730 QVector3D endPosition = particle->particleEndPosition(i);
731 QVector3D endRotation = particle->particleEndRotation(i);
732 float factor = 1.0f - particleTimeLeftS / endTimeS;
733 currentData.position = mix(currentData.position, endPosition, factor);
734 currentData.rotation = mix(currentData.rotation, endRotation, factor);
735 }
736 particle->setParticleData(i, currentData.position, currentData.rotation,
737 color, currentData.scale.x(), timeChange);
738 }
739 particle->commitParticles();
740}
741
742void QQuick3DParticleSystem::processSpriteParticle(QQuick3DParticleSpriteParticle *spriteParticle, const QVector<TrailEmits> &trailEmits, float timeS)
743{
744 const int c = spriteParticle->maxAmount();
745
746 for (int i = 0; i < c; i++) {
747 const auto d = &spriteParticle->m_particleData.at(i);
748
749 const float particleTimeEnd = d->startTime + d->lifetime;
750 auto &particleData = spriteParticle->m_spriteParticleData[i];
751 if (timeS < d->startTime || timeS > particleTimeEnd) {
752 if (timeS > particleTimeEnd && particleData.age > 0.0f) {
753 for (auto trailEmit : std::as_const(trailEmits))
754 trailEmit.emitter->emitTrailParticles(particleData.position, 0, QQuick3DParticleDynamicBurst::TriggerEnd);
755 auto *lineParticle = qobject_cast<QQuick3DParticleLineParticle *>(spriteParticle);
756 if (lineParticle)
757 lineParticle->saveLineSegment(i, timeS);
758 }
759 // Particle not alive currently
760 spriteParticle->resetParticleData(i);
761 continue;
762 }
763 const float particleTimeS = timeS - d->startTime;
764 QQuick3DParticleDataCurrent currentData;
765 if (timeS >= d->startTime && timeS < particleTimeEnd && particleData.age == 0.0f) {
766 for (auto trailEmit : std::as_const(trailEmits))
767 trailEmit.emitter->emitTrailParticles(d->startPosition, 0, QQuick3DParticleDynamicBurst::TriggerStart);
768 }
769 // Process features shared for both model & sprite particles
770 processParticleCommon(currentData, d, particleTimeS);
771
772 // Add a base rotation if alignment requested
773 if (!spriteParticle->m_billboard && spriteParticle->m_alignMode != QQuick3DParticle::AlignNone)
774 processParticleAlignment(currentData, spriteParticle, d);
775
776 // 0.0 -> 1.0 during the particle lifetime
777 const float timeChange = std::max(0.0f, std::min(1.0f, particleTimeS / d->lifetime));
778
779 // Scale from initial to endScale
780 const float scale = d->endSize * timeChange + d->startSize * (1.0f - timeChange);
781 currentData.scale = QVector3D(scale, scale, scale);
782
783 // Fade in & out
784 const float particleTimeLeftS = d->lifetime - particleTimeS;
785 processParticleFadeInOut(currentData, spriteParticle, particleTimeS, particleTimeLeftS);
786
787 float animationFrame = 0.0f;
788 if (auto sequence = spriteParticle->m_spriteSequence) {
789 // animationFrame range is [0..1) where 0.0 is the beginning of the first frame
790 // and 0.9999 is the end of the last frame.
791 const bool isSingleFrame = (sequence->animationDirection() == QQuick3DParticleSpriteSequence::SingleFrame);
792 float startFrame = sequence->firstFrame(d->index, isSingleFrame);
793 if (sequence->animationDirection() == QQuick3DParticleSpriteSequence::Normal) {
794 animationFrame = fmodf(startFrame + particleTimeS / d->animationTime, 1.0f);
795 } else if (sequence->animationDirection() == QQuick3DParticleSpriteSequence::Reverse) {
796 animationFrame = fmodf(startFrame + 0.9999f - fmodf(particleTimeS / d->animationTime, 1.0f), 1.0f);
797 } else if (sequence->animationDirection() == QQuick3DParticleSpriteSequence::Alternate) {
798 animationFrame = startFrame + particleTimeS / d->animationTime;
799 animationFrame = fabsf(fmodf(1.0f + animationFrame, 2.0f) - 1.0f);
800 } else if (sequence->animationDirection() == QQuick3DParticleSpriteSequence::AlternateReverse) {
801 animationFrame = fmodf(startFrame + 0.9999f, 1.0f) - particleTimeS / d->animationTime;
802 animationFrame = fabsf(fmodf(fabsf(1.0f + animationFrame), 2.0f) - 1.0f);
803 } else {
804 // SingleFrame
805 animationFrame = startFrame;
806 }
807 animationFrame = std::clamp(animationFrame, 0.0f, 0.9999f);
808 }
809
810 // Affectors
811 for (auto affector : std::as_const(m_affectors)) {
812 // If affector is set to affect only particular particles, check these are included
813 if (affector->m_enabled && (affector->m_particles.isEmpty() || affector->m_particles.contains(spriteParticle)))
814 affector->affectParticle(*d, &currentData, particleTimeS);
815 }
816
817 // Emit new particles from trails
818 for (auto trailEmit : std::as_const(trailEmits))
819 trailEmit.emitter->emitTrailParticles(currentData.position, trailEmit.amount, QQuick3DParticleDynamicBurst::TriggerTime);
820
821
822 // Set current particle properties
823 const QVector4D color(float(currentData.color.r) / 255.0f,
824 float(currentData.color.g) / 255.0f,
825 float(currentData.color.b) / 255.0f,
826 float(currentData.color.a) / 255.0f);
827 const QVector3D offset(spriteParticle->offsetX(), spriteParticle->offsetY(), 0);
828 spriteParticle->setParticleData(i, currentData.position + (offset * currentData.scale.x()),
829 currentData.rotation, color, currentData.scale.x(), timeChange,
830 animationFrame);
831 }
832 spriteParticle->commitParticles(timeS);
833}
834
835void QQuick3DParticleSystem::processParticleCommon(QQuick3DParticleDataCurrent &currentData, const QQuick3DParticleData *d, float particleTimeS)
836{
837 m_particlesUsed++;
838
839 currentData.position = d->startPosition;
840
841 // Initial color from start color
842 currentData.color = d->startColor;
843
844 // Initial position from start velocity
845 currentData.position += d->startVelocity * particleTimeS;
846
847 // Initial rotation from start velocity
848 constexpr float step = 360.0f / 127.0f;
849 currentData.rotation = QVector3D(
850 d->startRotation.x * step + abs(d->startRotationVelocity.x) * d->startRotationVelocity.x * particleTimeS,
851 d->startRotation.y * step + abs(d->startRotationVelocity.y) * d->startRotationVelocity.y * particleTimeS,
852 d->startRotation.z * step + abs(d->startRotationVelocity.z) * d->startRotationVelocity.z * particleTimeS);
853}
854
855void QQuick3DParticleSystem::processParticleFadeInOut(QQuick3DParticleDataCurrent &currentData, const QQuick3DParticle *particle, float particleTimeS, float particleTimeLeftS)
856{
857 const float fadeInS = particle->m_fadeInDuration / 1000.0f;
858 const float fadeOutS = particle->m_fadeOutDuration / 1000.0f;
859 if (particleTimeS < fadeInS) {
860 // 0.0 -> 1.0 during the particle fadein
861 const float fadeIn = particleTimeS / fadeInS;
862 if (particle->m_fadeInEffect == QQuick3DParticleModelParticle::FadeOpacity)
863 currentData.color.a *= fadeIn;
864 else if (particle->m_fadeInEffect == QQuick3DParticleModelParticle::FadeScale)
865 currentData.scale *= fadeIn;
866 }
867 if (particleTimeLeftS < fadeOutS) {
868 // 1.0 -> 0.0 during the particle fadeout
869 const float fadeOut = particleTimeLeftS / fadeOutS;
870 if (particle->m_fadeOutEffect == QQuick3DParticleModelParticle::FadeOpacity)
871 currentData.color.a *= fadeOut;
872 else if (particle->m_fadeOutEffect == QQuick3DParticleModelParticle::FadeScale)
873 currentData.scale *= fadeOut;
874 }
875}
876
877void QQuick3DParticleSystem::processParticleAlignment(QQuick3DParticleDataCurrent &currentData, const QQuick3DParticle *particle, const QQuick3DParticleData *d)
878{
880 QQuaternion alignQuat = QQuick3DQuaternionUtils::lookAt(particle->alignTargetPosition(), currentData.position);
881 currentData.rotation = (alignQuat * QQuaternion::fromEulerAngles(currentData.rotation)).toEulerAngles();
883 QQuaternion alignQuat = QQuick3DQuaternionUtils::lookAt(d->startVelocity, QVector3D());
884 currentData.rotation = (alignQuat * QQuaternion::fromEulerAngles(currentData.rotation)).toEulerAngles();
885 }
886}
887
888bool QQuick3DParticleSystem::isGloballyDisabled()
889{
890 static const bool disabled = qEnvironmentVariableIntValue("QT_QUICK3D_DISABLE_PARTICLE_SYSTEMS");
891 return disabled;
892}
893
894bool QQuick3DParticleSystem::isEditorModeOn()
895{
896 static const bool editorMode = qEnvironmentVariableIntValue("QT_QUICK3D_EDITOR_PARTICLE_SYSTEMS");
897 return editorMode;
898}
899
900void QQuick3DParticleSystem::updateLoggingData()
901{
902 if (m_updates == 0)
903 return;
904
905 if (m_loggingData->m_particlesMax != m_particlesMax) {
906 m_loggingData->m_particlesMax = m_particlesMax;
907 Q_EMIT m_loggingData->particlesMaxChanged();
908 }
909 if (m_loggingData->m_particlesUsed != m_particlesUsed) {
910 m_loggingData->m_particlesUsed = m_particlesUsed;
911 Q_EMIT m_loggingData->particlesUsedChanged();
912 }
913 if (m_loggingData->m_updates != m_updates) {
914 m_loggingData->m_updates = m_updates;
915 Q_EMIT m_loggingData->updatesChanged();
916 }
917
918 m_loggingData->updateTimes(m_timeAnimation);
919
921 resetLoggingVariables();
922}
923
924void QQuick3DParticleSystem::resetLoggingVariables()
925{
926 m_particlesMax = 0;
927 m_particlesUsed = 0;
928 m_updates = 0;
929 m_timeAnimation = 0;
930}
931
933{
934 return &m_rand;
935}
936
937void QQuick3DParticleSystem::doSeedRandomization()
938{
939 // Random 1..INT32_MAX, making sure seed changes from the initial 0.
940 setSeed(QRandomGenerator::global()->bounded(1 + (INT32_MAX - 1)));
941}
942
944{
945 int count = 0;
946 for (auto emitter : std::as_const(m_emitters)) {
947 count += emitter->particle() == particle;
948 if (count > 1)
949 return true;
950 }
951 for (auto emitter : std::as_const(m_trailEmitters)) {
952 count += emitter->particle() == particle;
953 if (count > 1)
954 return true;
955 }
956 return false;
957}
958
State state
state of the animation.
void resume()
Resumes the animation after it was paused.
void stop()
Stops the animation.
void start(QAbstractAnimation::DeletionPolicy policy=KeepWhenStopped)
Starts the animation.
void setCurrentTime(int msecs)
void pause()
Pauses the animation.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
qint64 nsecsElapsed() const noexcept
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
qsizetype removeAll(const AT &t)
Definition qlist.h:592
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
size_type remove(const Key &key)
Definition qmap.h:300
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 init(quint32 seed, int size=65536)
void setDeterministic(bool deterministic)
The QQuaternion class represents a quaternion consisting of a vector and scalar.
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QVector3D position
QVector3D particleEndPosition(int particleIndex) const
void setParticleData(int particleIndex, const QVector3D &position, const QVector3D &rotation, const QVector4D &color, float size, float age)
QVector3D particleEndRotation(int particleIndex) const
bool isPaused() const
\qmlproperty bool ParticleSystem3D::paused
void unRegisterParticleEmitter(QQuick3DParticleEmitter *e)
void unRegisterParticle(QQuick3DParticle *particle)
void registerParticleAffector(QQuick3DParticleAffector *a)
bool isShared(const QQuick3DParticle *particle) const
bool isRunning() const
\qmlproperty bool ParticleSystem3D::running
void updateCurrentTime(int currentTime)
void registerParticle(QQuick3DParticle *particle)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QQuick3DParticleSystemLogging * loggingData
void setUseRandomSeed(bool randomize)
void registerParticleEmitter(QQuick3DParticleEmitter *e)
Q_INVOKABLE void reset()
\qmlmethod ParticleSystem3D::reset()
void unRegisterParticleAffector(QQuick3DParticleAffector *a)
void setEditorTime(int time)
Set editor time which in editor mode overwrites the time.
int currentTime() const
Returns the current time of the system (m_time + m_startTime).
QQuick3DParticleSystem(QQuick3DNode *parent=nullptr)
QList< QQuick3DParticleData > m_particleData
QVector3D alignTargetPosition
static Q_INVOKABLE QQuaternion lookAt(const QVector3D &sourcePosition, const QVector3D &targetPosition, const QVector3D &forwardDirection=QVector3D(0, 0, -1), const QVector3D &upDirection=QVector3D(0, 1, 0))
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void setInterval(int msec)
Definition qtimer.cpp:579
void stop()
Stops the timer.
Definition qtimer.cpp:267
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
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
#define this
Definition dialogs.cpp:9
Combined button and popup list for selecting options.
qint64 startTime
DBusConnection * connection
static Q_CONSTINIT QBasicAtomicInt running
GLboolean GLboolean GLboolean b
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLuint color
[2]
GLenum GLuint GLintptr offset
const GLubyte * c
GLenum GLenum GLenum GLenum GLenum scale
static QVector3D mix(const QVector3D &a, const QVector3D &b, float f)
#define Q_QUICK3D_PROFILE_START(Type)
#define Q_QUICK3D_PROFILE_END_WITH_ID(Type, Payload, POID)
#define Q_QUICK3D_PROFILE_GET_ID
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_EMIT
#define Q_TRACE(x,...)
Definition qtrace_p.h:144
#define Q_TRACE_POINT(provider, tracepoint,...)
Definition qtrace_p.h:232
static double currentTime()
#define disabled
QSqlQueryModel * model
[16]
QQuick3DParticleTrailEmitter * emitter