756void QQuick3DParticleEmitter::emitParticle(QQuick3DParticle *particle,
float startTime,
const QMatrix4x4 &transform,
757 const QQuaternion &parentRotation,
const QVector3D ¢erPos,
int index,
758 const QVector3D &velocity,
const QVector3D &normal)
762 auto rand = m_system->rand();
764 QQuick3DParticleModelBlendParticle *mbp = qobject_cast<QQuick3DParticleModelBlendParticle *>(particle);
765 if (mbp && mbp->lastParticle())
768 int particleDataIndex = index == -1 ? particle->nextCurrentIndex(
this) : index;
769 if (index == -1 && mbp && mbp->emitMode() == QQuick3DParticleModelBlendParticle::Random)
770 particleDataIndex = mbp->randomIndex(particleDataIndex);
772 auto d = &particle->m_particleData[particleDataIndex];
773 int particleIdIndex = m_system->m_particleIdIndex++;
774 if (m_system->m_particleIdIndex == INT_MAX)
775 m_system->m_particleIdIndex = 0;
778 d->index = particleIdIndex;
779 d->startTime = startTime;
780 d->surfaceNormal = normal;
783 float lifeSpanMs = m_lifeSpanVariation / 1000.0f;
784 float lifeSpanVariationMs = lifeSpanMs - 2.0f * rand->get(particleIdIndex, QPRand::LifeSpanV) * lifeSpanMs;
785 d->lifetime = (m_lifeSpan / 1000.0f) + lifeSpanVariationMs;
788 float sVar = m_particleScaleVariation - 2.0f * rand->get(particleIdIndex, QPRand::ScaleV) * m_particleScaleVariation;
789 float endScale = (m_particleEndScale < 0.0f) ? m_particleScale : m_particleEndScale;
790 float sEndVar = (m_particleEndScaleVariation < 0.0f)
792 : m_particleEndScaleVariation - 2.0f * rand->get(particleIdIndex, QPRand::ScaleEV) * m_particleEndScaleVariation;
793 d->startSize = std::max(0.0f,
float(m_particleScale + sVar));
794 d->endSize = std::max(0.0f,
float(endScale + sEndVar));
797 bool normalBasedVelocity =
false;
798 if (mbp && mbp->modelBlendMode() != QQuick3DParticleModelBlendParticle::Construct) {
800 d->startPosition = mbp->particleCenter(particleDataIndex);
803 QVector3D pos = centerPos;
805 pos += m_shape->getPosition(particleIdIndex);
806 QVariant fill = m_shape->property(
"fill");
807 if (fill.isValid() && !fill.toBool()) {
808 const auto n = m_shape->getSurfaceNormal(particleIdIndex);
809 d->surfaceNormal = n;
810 if (m_emitMode != EmitMode::Default)
811 normalBasedVelocity =
true;
813 }
else if (!normal.isNull() && m_emitMode != EmitMode::Default) {
814 normalBasedVelocity =
true;
816 d->startPosition = transform.map(pos);
822 d->startVelocity = parentRotation * rotation() * m_velocity->sample(*d);
823 if (normalBasedVelocity) {
824 if (m_emitMode == EmitMode::SurfaceNormal)
825 d->startVelocity = rotationFromNormal(d->surfaceNormal).mapVector(d->startVelocity);
826 else if (!velocity.isNull())
827 d->startVelocity = rotationFromNormal(-reflect(velocity, d->surfaceNormal)).mapVector(d->startVelocity);
829 d->startVelocity = rotationFromNormal(-reflect(d->startVelocity.normalized(), d->surfaceNormal)).mapVector(d->startVelocity);
833 if (!m_particleRotation.isNull() || !m_particleRotationVariation.isNull()) {
835 constexpr float step = 127.0f / 360.0f;
836 rot.x = m_particleRotation.x() * step;
837 rot.y = m_particleRotation.y() * step;
838 rot.z = m_particleRotation.z() * step;
839 rot.x += (m_particleRotationVariation.x() - 2.0f * rand->get(particleIdIndex, QPRand::RotXV) * m_particleRotationVariation.x()) * step;
840 rot.y += (m_particleRotationVariation.y() - 2.0f * rand->get(particleIdIndex, QPRand::RotYV) * m_particleRotationVariation.y()) * step;
841 rot.z += (m_particleRotationVariation.z() - 2.0f * rand->get(particleIdIndex, QPRand::RotZV) * m_particleRotationVariation.z()) * step;
842 d->startRotation = rot;
845 if (!m_particleRotationVelocity.isNull() || !m_particleRotationVelocityVariation.isNull()) {
846 float rotVelX = m_particleRotationVelocity.x();
847 float rotVelY = m_particleRotationVelocity.y();
848 float rotVelZ = m_particleRotationVelocity.z();
849 rotVelX += (m_particleRotationVelocityVariation.x() - 2.0f * rand->get(particleIdIndex, QPRand::RotXVV) * m_particleRotationVelocityVariation.x());
850 rotVelY += (m_particleRotationVelocityVariation.y() - 2.0f * rand->get(particleIdIndex, QPRand::RotYVV) * m_particleRotationVelocityVariation.y());
851 rotVelZ += (m_particleRotationVelocityVariation.z() - 2.0f * rand->get(particleIdIndex, QPRand::RotZVV) * m_particleRotationVelocityVariation.z());
855 sign = rotVelX < 0.0f ? -1.0f : 1.0f;
856 rotVelX = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelX))));
857 sign = rotVelY < 0.0f ? -1.0f : 1.0f;
858 rotVelY = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelY))));
859 sign = rotVelZ < 0.0f ? -1.0f : 1.0f;
860 rotVelZ = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelZ))));
861 d->startRotationVelocity = { qint8(rotVelX), qint8(rotVelY), qint8(rotVelZ) };
865 QColor pc = particle->color();
866 QVector4D pcv = particle->colorVariation();
868 if (particle->unifiedColorVariation()) {
870 const int randVar =
int(rand->get(particleIdIndex, QPRand::ColorAV) * 256);
871 r = pc.red() * (1.0f - pcv.x()) + randVar * pcv.x();
872 g = pc.green() * (1.0f - pcv.y()) + randVar * pcv.y();
873 b = pc.blue() * (1.0f - pcv.z()) + randVar * pcv.z();
874 a = pc.alpha() * (1.0f - pcv.w()) + randVar * pcv.w();
876 r = pc.red() * (1.0f - pcv.x()) +
int(rand->get(particleIdIndex, QPRand::ColorRV) * 256) * pcv.x();
877 g = pc.green() * (1.0f - pcv.y()) +
int(rand->get(particleIdIndex, QPRand::ColorGV) * 256) * pcv.y();
878 b = pc.blue() * (1.0f - pcv.z()) +
int(rand->get(particleIdIndex, QPRand::ColorBV) * 256) * pcv.z();
879 a = pc.alpha() * (1.0f - pcv.w()) +
int(rand->get(particleIdIndex, QPRand::ColorAV) * 256) * pcv.w();
881 d->startColor = {r, g, b, a};
884 if (
auto sequence = particle->m_spriteSequence) {
885 if (sequence->duration() > 0) {
886 float animationTimeMs =
float(sequence->duration()) / 1000.0f;
887 float animationTimeVarMs =
float(sequence->durationVariation()) / 1000.0f;
888 animationTimeVarMs = animationTimeVarMs - 2.0f * rand->get(particleIdIndex, QPRand::SpriteAnimationV) * animationTimeVarMs;
890 const float MIN_DURATION = 0.001f;
891 d->animationTime = std::max(MIN_DURATION, animationTimeMs + animationTimeVarMs);
894 d->animationTime = d->lifetime;
897 d->reversed = m_reversed;
900int QQuick3DParticleEmitter::getEmitAmountFromDynamicBursts(
int triggerType)
903 const int currentTime = m_system->time();
904 const int prevTime = m_prevBurstTime;
906 for (
auto *burst : std::as_const(m_emitBursts)) {
907 auto *burstPtr = qobject_cast<QQuick3DParticleDynamicBurst *>(burst);
910 if (!burstPtr->m_enabled)
913 const bool trailTriggering = triggerType && (burstPtr->m_triggerMode) == triggerType;
915 const bool timeTriggeringStart = !triggerType && currentTime >= burstPtr->m_time && prevTime <= burstPtr->m_time;
916 if (trailTriggering || timeTriggeringStart) {
917 int burstAmount = burstPtr->m_amount;
918 if (burstPtr->m_amountVariation > 0) {
919 auto rand = m_system->rand();
920 int randAmount = 2 * rand->get() * burstPtr->m_amountVariation;
921 burstAmount += burstPtr->m_amountVariation - randAmount;
923 if (burstAmount > 0) {
924 if (timeTriggeringStart && burstPtr->m_duration > 0) {
926 BurstEmitData emitData;
927 emitData.startTime = currentTime;
928 emitData.endTime = currentTime + burstPtr->m_duration;
929 emitData.emitAmount = burstAmount;
930 emitData.prevBurstTime = prevTime;
931 m_burstEmitData << emitData;
934 amount += burstAmount;
940 for (
int burstIndex = 0; burstIndex < m_burstEmitData.size(); ++burstIndex) {
941 auto &burstData = m_burstEmitData[burstIndex];
942 const int amountLeft = burstData.emitAmount - burstData.emitCounter;
943 if (currentTime >= burstData.endTime) {
945 amount += amountLeft;
946 m_burstEmitData.removeAt(burstIndex);
949 const int durationTime = currentTime - burstData.prevBurstTime;
950 const int burstDurationTime = burstData.endTime - burstData.startTime;
951 int burstAmount = burstData.emitAmount * (
float(durationTime) /
float(burstDurationTime));
952 burstAmount = std::min(amountLeft, burstAmount);
953 if (burstAmount > 0) {
954 amount += burstAmount;
955 burstData.emitCounter += burstAmount;
956 burstData.prevBurstTime = currentTime;
961 m_prevBurstTime = currentTime;
1017void QQuick3DParticleEmitter::emitParticles()
1028 auto *mbp = qobject_cast<QQuick3DParticleModelBlendParticle *>(m_particle);
1029 if (mbp && mbp->activationNode() && mbp->emitMode() == QQuick3DParticleModelBlendParticle::Activation) {
1031 emitActivationNodeParticles(mbp);
1035 const int systemTime = m_system->currentTime();
1037 if (systemTime < m_prevEmitTime) {
1039 m_prevEmitTime = systemTime;
1043 const int maxLifeSpan = m_lifeSpan + m_lifeSpanVariation;
1044 m_prevEmitTime = std::max(m_prevEmitTime, systemTime - maxLifeSpan);
1048 if (!m_burstGenerated)
1049 generateEmitBursts();
1051 int emitAmount = getEmitAmount() + getEmitAmountFromDynamicBursts();
1057 QMatrix4x4 transform = calculateParticleTransform(parentNode(), m_systemSharedParent);
1058 QQuaternion rotation = calculateParticleRotation(parentNode(), m_systemSharedParent);
1059 QVector3D centerPos = position();
1061 emitAmount = std::min(emitAmount,
int(m_particle->maxAmount()));
1062 for (
int i = 0; i < emitAmount; i++) {
1065 float startTime = (m_prevEmitTime / 1000.0f) + (
float(1+i) / emitAmount) * ((systemTime - m_prevEmitTime) / 1000.0f);
1066 emitParticle(m_particle, startTime, transform, rotation, centerPos);
1069 m_prevEmitTime = systemTime;
1072void QQuick3DParticleEmitter::emitActivationNodeParticles(QQuick3DParticleModelBlendParticle *particle)
1074 QMatrix4x4 matrix = particle->activationNode()->sceneTransform();
1075 QMatrix4x4 actTransform = sceneTransform().inverted() * matrix;
1076 QVector3D front = actTransform.column(2).toVector3D();
1077 QVector3D pos = actTransform.column(3).toVector3D();
1078 float d = QVector3D::dotProduct(pos, front);
1080 const int systemTime = m_system->currentTime();
1084 const int maxLifeSpan = m_lifeSpan + m_lifeSpanVariation;
1085 m_prevEmitTime = std::max(m_prevEmitTime, systemTime - maxLifeSpan);
1087 float startTime = systemTime / 1000.0f;
1089 QMatrix4x4 transform = calculateParticleTransform(parentNode(), m_systemSharedParent);
1090 QQuaternion rotation = calculateParticleRotation(parentNode(), m_systemSharedParent);
1091 QVector3D centerPos = position();
1093 for (
int i = 0; i < particle->maxAmount(); i++) {
1094 if (particle->m_particleData[i].startTime >= 0)
1096 const QVector3D pc = particle->particleCenter(i);
1097 if (QVector3D::dotProduct(front, pc) - d > 0.0f)
1098 emitParticle(particle, startTime, transform, rotation, centerPos, i);
1101 m_prevEmitTime = systemTime;