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 const auto n = m_shape->getSurfaceNormal(particleIdIndex);
808 d->surfaceNormal = n;
809 if (m_emitMode != EmitMode::Default)
810 normalBasedVelocity =
true;
812 }
else if (!normal.isNull() && m_emitMode != EmitMode::Default) {
813 normalBasedVelocity =
true;
815 d->startPosition = transform.map(pos);
821 d->startVelocity = parentRotation * rotation() * m_velocity->sample(*d);
822 if (normalBasedVelocity) {
823 if (m_emitMode == EmitMode::SurfaceNormal)
824 d->startVelocity = rotationFromNormal(d->surfaceNormal).mapVector(d->startVelocity);
825 else if (!velocity.isNull())
826 d->startVelocity = rotationFromNormal(-reflect(velocity, d->surfaceNormal)).mapVector(d->startVelocity);
828 d->startVelocity = rotationFromNormal(-reflect(d->startVelocity.normalized(), d->surfaceNormal)).mapVector(d->startVelocity);
832 if (!m_particleRotation.isNull() || !m_particleRotationVariation.isNull()) {
834 constexpr float step = 127.0f / 360.0f;
835 rot.x = m_particleRotation.x() * step;
836 rot.y = m_particleRotation.y() * step;
837 rot.z = m_particleRotation.z() * step;
838 rot.x += (m_particleRotationVariation.x() - 2.0f * rand->get(particleIdIndex, QPRand::RotXV) * m_particleRotationVariation.x()) * step;
839 rot.y += (m_particleRotationVariation.y() - 2.0f * rand->get(particleIdIndex, QPRand::RotYV) * m_particleRotationVariation.y()) * step;
840 rot.z += (m_particleRotationVariation.z() - 2.0f * rand->get(particleIdIndex, QPRand::RotZV) * m_particleRotationVariation.z()) * step;
841 d->startRotation = rot;
844 if (!m_particleRotationVelocity.isNull() || !m_particleRotationVelocityVariation.isNull()) {
845 float rotVelX = m_particleRotationVelocity.x();
846 float rotVelY = m_particleRotationVelocity.y();
847 float rotVelZ = m_particleRotationVelocity.z();
848 rotVelX += (m_particleRotationVelocityVariation.x() - 2.0f * rand->get(particleIdIndex, QPRand::RotXVV) * m_particleRotationVelocityVariation.x());
849 rotVelY += (m_particleRotationVelocityVariation.y() - 2.0f * rand->get(particleIdIndex, QPRand::RotYVV) * m_particleRotationVelocityVariation.y());
850 rotVelZ += (m_particleRotationVelocityVariation.z() - 2.0f * rand->get(particleIdIndex, QPRand::RotZVV) * m_particleRotationVelocityVariation.z());
854 sign = rotVelX < 0.0f ? -1.0f : 1.0f;
855 rotVelX = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelX))));
856 sign = rotVelY < 0.0f ? -1.0f : 1.0f;
857 rotVelY = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelY))));
858 sign = rotVelZ < 0.0f ? -1.0f : 1.0f;
859 rotVelZ = std::max(-127.0f, std::min<
float>(127.0f, sign * std::sqrt(abs(rotVelZ))));
860 d->startRotationVelocity = { qint8(rotVelX), qint8(rotVelY), qint8(rotVelZ) };
864 QColor pc = particle->color();
865 QVector4D pcv = particle->colorVariation();
867 if (particle->unifiedColorVariation()) {
869 const int randVar =
int(rand->get(particleIdIndex, QPRand::ColorAV) * 256);
870 r = pc.red() * (1.0f - pcv.x()) + randVar * pcv.x();
871 g = pc.green() * (1.0f - pcv.y()) + randVar * pcv.y();
872 b = pc.blue() * (1.0f - pcv.z()) + randVar * pcv.z();
873 a = pc.alpha() * (1.0f - pcv.w()) + randVar * pcv.w();
875 r = pc.red() * (1.0f - pcv.x()) +
int(rand->get(particleIdIndex, QPRand::ColorRV) * 256) * pcv.x();
876 g = pc.green() * (1.0f - pcv.y()) +
int(rand->get(particleIdIndex, QPRand::ColorGV) * 256) * pcv.y();
877 b = pc.blue() * (1.0f - pcv.z()) +
int(rand->get(particleIdIndex, QPRand::ColorBV) * 256) * pcv.z();
878 a = pc.alpha() * (1.0f - pcv.w()) +
int(rand->get(particleIdIndex, QPRand::ColorAV) * 256) * pcv.w();
880 d->startColor = {r, g, b, a};
883 if (
auto sequence = particle->m_spriteSequence) {
884 if (sequence->duration() > 0) {
885 float animationTimeMs =
float(sequence->duration()) / 1000.0f;
886 float animationTimeVarMs =
float(sequence->durationVariation()) / 1000.0f;
887 animationTimeVarMs = animationTimeVarMs - 2.0f * rand->get(particleIdIndex, QPRand::SpriteAnimationV) * animationTimeVarMs;
889 const float MIN_DURATION = 0.001f;
890 d->animationTime = std::max(MIN_DURATION, animationTimeMs + animationTimeVarMs);
893 d->animationTime = d->lifetime;
896 d->reversed = m_reversed;
899int QQuick3DParticleEmitter::getEmitAmountFromDynamicBursts(
int triggerType)
902 const int currentTime = m_system->time();
903 const int prevTime = m_prevBurstTime;
905 for (
auto *burst : std::as_const(m_emitBursts)) {
906 auto *burstPtr = qobject_cast<QQuick3DParticleDynamicBurst *>(burst);
909 if (!burstPtr->m_enabled)
912 const bool trailTriggering = triggerType && (burstPtr->m_triggerMode) == triggerType;
914 const bool timeTriggeringStart = !triggerType && currentTime >= burstPtr->m_time && prevTime <= burstPtr->m_time;
915 if (trailTriggering || timeTriggeringStart) {
916 int burstAmount = burstPtr->m_amount;
917 if (burstPtr->m_amountVariation > 0) {
918 auto rand = m_system->rand();
919 int randAmount = 2 * rand->get() * burstPtr->m_amountVariation;
920 burstAmount += burstPtr->m_amountVariation - randAmount;
922 if (burstAmount > 0) {
923 if (timeTriggeringStart && burstPtr->m_duration > 0) {
925 BurstEmitData emitData;
926 emitData.startTime = currentTime;
927 emitData.endTime = currentTime + burstPtr->m_duration;
928 emitData.emitAmount = burstAmount;
929 emitData.prevBurstTime = prevTime;
930 m_burstEmitData << emitData;
933 amount += burstAmount;
939 for (
int burstIndex = 0; burstIndex < m_burstEmitData.size(); ++burstIndex) {
940 auto &burstData = m_burstEmitData[burstIndex];
941 const int amountLeft = burstData.emitAmount - burstData.emitCounter;
942 if (currentTime >= burstData.endTime) {
944 amount += amountLeft;
945 m_burstEmitData.removeAt(burstIndex);
948 const int durationTime = currentTime - burstData.prevBurstTime;
949 const int burstDurationTime = burstData.endTime - burstData.startTime;
950 int burstAmount = burstData.emitAmount * (
float(durationTime) /
float(burstDurationTime));
951 burstAmount = std::min(amountLeft, burstAmount);
952 if (burstAmount > 0) {
953 amount += burstAmount;
954 burstData.emitCounter += burstAmount;
955 burstData.prevBurstTime = currentTime;
960 m_prevBurstTime = currentTime;
1016void QQuick3DParticleEmitter::emitParticles()
1027 auto *mbp = qobject_cast<QQuick3DParticleModelBlendParticle *>(m_particle);
1028 if (mbp && mbp->activationNode() && mbp->emitMode() == QQuick3DParticleModelBlendParticle::Activation) {
1030 emitActivationNodeParticles(mbp);
1034 const int systemTime = m_system->currentTime();
1036 if (systemTime < m_prevEmitTime) {
1038 m_prevEmitTime = systemTime;
1042 const int maxLifeSpan = m_lifeSpan + m_lifeSpanVariation;
1043 m_prevEmitTime = std::max(m_prevEmitTime, systemTime - maxLifeSpan);
1047 if (!m_burstGenerated)
1048 generateEmitBursts();
1050 int emitAmount = getEmitAmount() + getEmitAmountFromDynamicBursts();
1056 QMatrix4x4 transform = calculateParticleTransform(parentNode(), m_systemSharedParent);
1057 QQuaternion rotation = calculateParticleRotation(parentNode(), m_systemSharedParent);
1058 QVector3D centerPos = position();
1060 emitAmount = std::min(emitAmount,
int(m_particle->maxAmount()));
1061 for (
int i = 0; i < emitAmount; i++) {
1064 float startTime = (m_prevEmitTime / 1000.0f) + (
float(1+i) / emitAmount) * ((systemTime - m_prevEmitTime) / 1000.0f);
1065 emitParticle(m_particle, startTime, transform, rotation, centerPos);
1068 m_prevEmitTime = systemTime;
1071void QQuick3DParticleEmitter::emitActivationNodeParticles(QQuick3DParticleModelBlendParticle *particle)
1073 QMatrix4x4 matrix = particle->activationNode()->sceneTransform();
1074 QMatrix4x4 actTransform = sceneTransform().inverted() * matrix;
1075 QVector3D front = actTransform.column(2).toVector3D();
1076 QVector3D pos = actTransform.column(3).toVector3D();
1077 float d = QVector3D::dotProduct(pos, front);
1079 const int systemTime = m_system->currentTime();
1083 const int maxLifeSpan = m_lifeSpan + m_lifeSpanVariation;
1084 m_prevEmitTime = std::max(m_prevEmitTime, systemTime - maxLifeSpan);
1086 float startTime = systemTime / 1000.0f;
1088 QMatrix4x4 transform = calculateParticleTransform(parentNode(), m_systemSharedParent);
1089 QQuaternion rotation = calculateParticleRotation(parentNode(), m_systemSharedParent);
1090 QVector3D centerPos = position();
1092 for (
int i = 0; i < particle->maxAmount(); i++) {
1093 if (particle->m_particleData[i].startTime >= 0)
1095 const QVector3D pc = particle->particleCenter(i);
1096 if (QVector3D::dotProduct(front, pc) - d > 0.0f)
1097 emitParticle(particle, startTime, transform, rotation, centerPos, i);
1100 m_prevEmitTime = systemTime;