754void QQuick3DParticleEmitter::emitParticle(QQuick3DParticle *particle,
float startTime,
const QMatrix4x4 &transform,
755 const QQuaternion &parentRotation,
const QVector3D ¢erPos,
int index,
756 const QVector3D &velocity,
const QVector3D &normal)
760 auto rand = m_system->rand();
762 QQuick3DParticleModelBlendParticle *mbp = qobject_cast<QQuick3DParticleModelBlendParticle *>(particle);
763 if (mbp && mbp->lastParticle())
766 int particleDataIndex = index == -1 ? particle->nextCurrentIndex(
this) : index;
767 if (index == -1 && mbp && mbp->emitMode() == QQuick3DParticleModelBlendParticle::Random)
768 particleDataIndex = mbp->randomIndex(particleDataIndex);
770 auto d = &particle->m_particleData[particleDataIndex];
771 int particleIdIndex = m_system->m_particleIdIndex++;
772 if (m_system->m_particleIdIndex == INT_MAX)
773 m_system->m_particleIdIndex = 0;
776 d->index = particleIdIndex;
777 d->startTime = startTime;
778 d->surfaceNormal = normal;
781 float lifeSpanMs = m_lifeSpanVariation / 1000.0f;
782 float lifeSpanVariationMs = lifeSpanMs - 2.0f * rand->get(particleIdIndex, QPRand::LifeSpanV) * lifeSpanMs;
783 d->lifetime = (m_lifeSpan / 1000.0f) + lifeSpanVariationMs;
786 float sVar = m_particleScaleVariation - 2.0f * rand->get(particleIdIndex, QPRand::ScaleV) * m_particleScaleVariation;
787 float endScale = (m_particleEndScale < 0.0f) ? m_particleScale : m_particleEndScale;
788 float sEndVar = (m_particleEndScaleVariation < 0.0f)
790 : m_particleEndScaleVariation - 2.0f * rand->get(particleIdIndex, QPRand::ScaleEV) * m_particleEndScaleVariation;
791 d->startSize = std::max(0.0f,
float(m_particleScale + sVar));
792 d->endSize = std::max(0.0f,
float(endScale + sEndVar));
795 bool normalBasedVelocity =
false;
796 if (mbp && mbp->modelBlendMode() != QQuick3DParticleModelBlendParticle::Construct) {
798 d->startPosition = mbp->particleCenter(particleDataIndex);
801 QVector3D pos = centerPos;
803 pos += m_shape->getPosition(particleIdIndex);
804 QVariant fill = m_shape->property(
"fill");
805 if (fill.isValid() && !fill.toBool()) {
806 const auto n = m_shape->getSurfaceNormal(particleIdIndex);
807 d->surfaceNormal = n;
808 if (m_emitMode != EmitMode::Default)
809 normalBasedVelocity =
true;
811 }
else if (!normal.isNull() && m_emitMode != EmitMode::Default) {
812 normalBasedVelocity =
true;
814 d->startPosition = transform.map(pos);
820 d->startVelocity = parentRotation * rotation() * m_velocity->sample(*d);
821 if (normalBasedVelocity) {
822 if (m_emitMode == EmitMode::SurfaceNormal)
823 d->startVelocity = rotationFromNormal(d->surfaceNormal).mapVector(d->startVelocity);
824 else if (!velocity.isNull())
825 d->startVelocity = rotationFromNormal(-reflect(velocity, d->surfaceNormal)).mapVector(d->startVelocity);
827 d->startVelocity = rotationFromNormal(-reflect(d->startVelocity.normalized(), d->surfaceNormal)).mapVector(d->startVelocity);
831 if (!m_particleRotation.isNull() || !m_particleRotationVariation.isNull()) {
833 constexpr float step = 127.0f / 360.0f;
834 rot.x = m_particleRotation.x() * step;
835 rot.y = m_particleRotation.y() * step;
836 rot.z = m_particleRotation.z() * step;
837 rot.x += (m_particleRotationVariation.x() - 2.0f * rand->get(particleIdIndex, QPRand::RotXV) * m_particleRotationVariation.x()) * step;
838 rot.y += (m_particleRotationVariation.y() - 2.0f * rand->get(particleIdIndex, QPRand::RotYV) * m_particleRotationVariation.y()) * step;
839 rot.z += (m_particleRotationVariation.z() - 2.0f * rand->get(particleIdIndex, QPRand::RotZV) * m_particleRotationVariation.z()) * step;
840 d->startRotation = rot;
843 if (!m_particleRotationVelocity.isNull() || !m_particleRotationVelocityVariation.isNull()) {
844 float rotVelX = m_particleRotationVelocity.x();
845 float rotVelY = m_particleRotationVelocity.y();
846 float rotVelZ = m_particleRotationVelocity.z();
847 rotVelX += (m_particleRotationVelocityVariation.x() - 2.0f * rand->get(particleIdIndex, QPRand::RotXVV) * m_particleRotationVelocityVariation.x());
848 rotVelY += (m_particleRotationVelocityVariation.y() - 2.0f * rand->get(particleIdIndex, QPRand::RotYVV) * m_particleRotationVelocityVariation.y());
849 rotVelZ += (m_particleRotationVelocityVariation.z() - 2.0f * rand->get(particleIdIndex, QPRand::RotZVV) * m_particleRotationVelocityVariation.z());
853 sign = rotVelX < 0.0f ? -1.0f : 1.0f;
854 rotVelX = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelX))));
855 sign = rotVelY < 0.0f ? -1.0f : 1.0f;
856 rotVelY = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelY))));
857 sign = rotVelZ < 0.0f ? -1.0f : 1.0f;
858 rotVelZ = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelZ))));
859 d->startRotationVelocity = { qint8(rotVelX), qint8(rotVelY), qint8(rotVelZ) };
863 QColor pc = particle->color();
864 QVector4D pcv = particle->colorVariation();
866 if (particle->unifiedColorVariation()) {
868 const int randVar =
int(rand->get(particleIdIndex, QPRand::ColorAV) * 256);
869 r = pc.red() * (1.0f - pcv.x()) + randVar * pcv.x();
870 g = pc.green() * (1.0f - pcv.y()) + randVar * pcv.y();
871 b = pc.blue() * (1.0f - pcv.z()) + randVar * pcv.z();
872 a = pc.alpha() * (1.0f - pcv.w()) + randVar * pcv.w();
874 r = pc.red() * (1.0f - pcv.x()) +
int(rand->get(particleIdIndex, QPRand::ColorRV) * 256) * pcv.x();
875 g = pc.green() * (1.0f - pcv.y()) +
int(rand->get(particleIdIndex, QPRand::ColorGV) * 256) * pcv.y();
876 b = pc.blue() * (1.0f - pcv.z()) +
int(rand->get(particleIdIndex, QPRand::ColorBV) * 256) * pcv.z();
877 a = pc.alpha() * (1.0f - pcv.w()) +
int(rand->get(particleIdIndex, QPRand::ColorAV) * 256) * pcv.w();
879 d->startColor = {r, g, b, a};
882 if (
auto sequence = particle->m_spriteSequence) {
883 if (sequence->duration() > 0) {
884 float animationTimeMs =
float(sequence->duration()) / 1000.0f;
885 float animationTimeVarMs =
float(sequence->durationVariation()) / 1000.0f;
886 animationTimeVarMs = animationTimeVarMs - 2.0f * rand->get(particleIdIndex, QPRand::SpriteAnimationV) * animationTimeVarMs;
888 const float MIN_DURATION = 0.001f;
889 d->animationTime = std::max(MIN_DURATION, animationTimeMs + animationTimeVarMs);
892 d->animationTime = d->lifetime;
895 d->reversed = m_reversed;
898int QQuick3DParticleEmitter::getEmitAmountFromDynamicBursts(
int triggerType)
901 const int currentTime = m_system->time();
902 const int prevTime = m_prevBurstTime;
904 for (
auto *burst : std::as_const(m_emitBursts)) {
905 auto *burstPtr = qobject_cast<QQuick3DParticleDynamicBurst *>(burst);
908 if (!burstPtr->m_enabled)
911 const bool trailTriggering = triggerType && (burstPtr->m_triggerMode) == triggerType;
913 const bool timeTriggeringStart = !triggerType && currentTime >= burstPtr->m_time && prevTime <= burstPtr->m_time;
914 if (trailTriggering || timeTriggeringStart) {
915 int burstAmount = burstPtr->m_amount;
916 if (burstPtr->m_amountVariation > 0) {
917 auto rand = m_system->rand();
918 int randAmount = 2 * rand->get() * burstPtr->m_amountVariation;
919 burstAmount += burstPtr->m_amountVariation - randAmount;
921 if (burstAmount > 0) {
922 if (timeTriggeringStart && burstPtr->m_duration > 0) {
924 BurstEmitData emitData;
925 emitData.startTime = currentTime;
926 emitData.endTime = currentTime + burstPtr->m_duration;
927 emitData.emitAmount = burstAmount;
928 emitData.prevBurstTime = prevTime;
929 m_burstEmitData << emitData;
932 amount += burstAmount;
938 for (
int burstIndex = 0; burstIndex < m_burstEmitData.size(); ++burstIndex) {
939 auto &burstData = m_burstEmitData[burstIndex];
940 const int amountLeft = burstData.emitAmount - burstData.emitCounter;
941 if (currentTime >= burstData.endTime) {
943 amount += amountLeft;
944 m_burstEmitData.removeAt(burstIndex);
947 const int durationTime = currentTime - burstData.prevBurstTime;
948 const int burstDurationTime = burstData.endTime - burstData.startTime;
949 int burstAmount = burstData.emitAmount * (
float(durationTime) /
float(burstDurationTime));
950 burstAmount = std::min(amountLeft, burstAmount);
951 if (burstAmount > 0) {
952 amount += burstAmount;
953 burstData.emitCounter += burstAmount;
954 burstData.prevBurstTime = currentTime;
959 m_prevBurstTime = currentTime;
1015void QQuick3DParticleEmitter::emitParticles()
1026 auto *mbp = qobject_cast<QQuick3DParticleModelBlendParticle *>(m_particle);
1027 if (mbp && mbp->activationNode() && mbp->emitMode() == QQuick3DParticleModelBlendParticle::Activation) {
1029 emitActivationNodeParticles(mbp);
1033 const int systemTime = m_system->currentTime();
1035 if (systemTime < m_prevEmitTime) {
1037 m_prevEmitTime = systemTime;
1041 const int maxLifeSpan = m_lifeSpan + m_lifeSpanVariation;
1042 m_prevEmitTime = std::max(m_prevEmitTime, systemTime - maxLifeSpan);
1046 if (!m_burstGenerated)
1047 generateEmitBursts();
1049 int emitAmount = getEmitAmount() + getEmitAmountFromDynamicBursts();
1055 QMatrix4x4 transform = calculateParticleTransform(parentNode(), m_systemSharedParent);
1056 QQuaternion rotation = calculateParticleRotation(parentNode(), m_systemSharedParent);
1057 QVector3D centerPos = position();
1059 emitAmount = std::min(emitAmount,
int(m_particle->maxAmount()));
1060 for (
int i = 0; i < emitAmount; i++) {
1063 float startTime = (m_prevEmitTime / 1000.0f) + (
float(1+i) / emitAmount) * ((systemTime - m_prevEmitTime) / 1000.0f);
1064 emitParticle(m_particle, startTime, transform, rotation, centerPos);
1067 m_prevEmitTime = systemTime;
1070void QQuick3DParticleEmitter::emitActivationNodeParticles(QQuick3DParticleModelBlendParticle *particle)
1072 QMatrix4x4 matrix = particle->activationNode()->sceneTransform();
1073 QMatrix4x4 actTransform = sceneTransform().inverted() * matrix;
1074 QVector3D front = actTransform.column(2).toVector3D();
1075 QVector3D pos = actTransform.column(3).toVector3D();
1076 float d = QVector3D::dotProduct(pos, front);
1078 const int systemTime = m_system->currentTime();
1082 const int maxLifeSpan = m_lifeSpan + m_lifeSpanVariation;
1083 m_prevEmitTime = std::max(m_prevEmitTime, systemTime - maxLifeSpan);
1085 float startTime = systemTime / 1000.0f;
1087 QMatrix4x4 transform = calculateParticleTransform(parentNode(), m_systemSharedParent);
1088 QQuaternion rotation = calculateParticleRotation(parentNode(), m_systemSharedParent);
1089 QVector3D centerPos = position();
1091 for (
int i = 0; i < particle->maxAmount(); i++) {
1092 if (particle->m_particleData[i].startTime >= 0)
1094 const QVector3D pc = particle->particleCenter(i);
1095 if (QVector3D::dotProduct(front, pc) - d > 0.0f)
1096 emitParticle(particle, startTime, transform, rotation, centerPos, i);
1099 m_prevEmitTime = systemTime;