10#include <private/qquick3dobject_p.h>
17
18
19
20
21
22
23
24
25
26
27
28
29
31QQuick3DParticleLineParticle::QQuick3DParticleLineParticle(QQuick3DNode *parent)
32 : QQuick3DParticleSpriteParticle(parent)
36QQuick3DParticleLineParticle::~QQuick3DParticleLineParticle()
41
42
43
44
45
46
47int QQuick3DParticleLineParticle::segmentCount()
const
49 return m_segmentCount;
53
54
55
56
57
58
59
60
61float QQuick3DParticleLineParticle::alphaFade()
const
67
68
69
70
71
72
73
74
75float QQuick3DParticleLineParticle::scaleMultiplier()
const
77 return m_scaleMultiplier;
81
82
83
84
85
86float QQuick3DParticleLineParticle::texcoordMultiplier()
const
88 return m_texcoordMultiplier;
92
93
94
95
96
97
98
99float QQuick3DParticleLineParticle::length()
const
105
106
107
108
109
110
111
112
114float QQuick3DParticleLineParticle::lengthVariation()
const
116 return m_lengthVariation;
120
121
122
123
124
125float QQuick3DParticleLineParticle::lengthDeltaMin()
const
127 return m_lengthDeltaMin;
131
132
133
134
135
136
137int QQuick3DParticleLineParticle::eolFadeOutDuration()
const
139 return m_eolFadeOutDuration;
143
144
145
146
147
148
149
150
151
152
153
156
157
158
159
160QQuick3DParticleLineParticle::TexcoordMode QQuick3DParticleLineParticle::texcoordMode()
const
162 return m_texcoordMode;
165void QQuick3DParticleLineParticle::setSegmentCount(
int count)
167 count = qMax(1, count);
168 if (m_segmentCount == count)
170 m_segmentCount = count;
171 handleSegmentCountChanged();
172 Q_EMIT segmentCountChanged();
175void QQuick3DParticleLineParticle::setAlphaFade(
float fade)
177 fade = qBound(0.0f, fade, 1.0f);
178 if (qFuzzyCompare(m_alphaFade, fade))
181 Q_EMIT alphaFadeChanged();
184void QQuick3DParticleLineParticle::setScaleMultiplier(
float multiplier)
186 multiplier = qBound(0.0f, multiplier, 2.0f);
187 if (qFuzzyCompare(m_scaleMultiplier, multiplier))
189 m_scaleMultiplier = multiplier;
190 Q_EMIT scaleMultiplierChanged();
193void QQuick3DParticleLineParticle::setTexcoordMultiplier(
float multiplier)
195 if (qFuzzyCompare(m_texcoordMultiplier, multiplier))
197 m_texcoordMultiplier = multiplier;
198 Q_EMIT texcoordMultiplierChanged();
201void QQuick3DParticleLineParticle::setLength(
float length)
203 length = length != -1.0f ? qMax(length, 0.0f) : -1.0f;
204 if (qFuzzyCompare(m_length, length))
207 Q_EMIT lengthChanged();
210void QQuick3DParticleLineParticle::setLengthVariation(
float lengthVariation)
212 lengthVariation = qMax(lengthVariation, 0.0f);
213 if (qFuzzyCompare(m_lengthVariation, lengthVariation))
215 m_lengthVariation = lengthVariation;
216 Q_EMIT lengthVariationChanged();
219void QQuick3DParticleLineParticle::setLengthDeltaMin(
float min)
221 min = qMax(min, 0.0f);
222 if (qFuzzyCompare(m_lengthDeltaMin, min))
224 m_lengthDeltaMin = min;
225 Q_EMIT lengthDeltaMinChanged();
228void QQuick3DParticleLineParticle::setEolFadeOutDuration(
int duration)
230 duration = qMax(0, duration);
231 if (duration == m_eolFadeOutDuration)
233 m_eolFadeOutDuration = duration;
234 Q_EMIT eolFadeOutDurationChanged();
237void QQuick3DParticleLineParticle::setTexcoordMode(TexcoordMode mode)
239 if (mode == m_texcoordMode)
241 m_texcoordMode = mode;
242 Q_EMIT texcoordModeChanged();
248 case QQuick3DParticleSpriteParticle::FeatureLevel::Simple:
249 return QSSGRenderParticles::FeatureLevel::Line;
250 case QQuick3DParticleSpriteParticle::FeatureLevel::Mapped:
251 return QSSGRenderParticles::FeatureLevel::LineMapped;
252 case QQuick3DParticleSpriteParticle::FeatureLevel::Animated:
253 return QSSGRenderParticles::FeatureLevel::LineAnimated;
254 case QQuick3DParticleSpriteParticle::FeatureLevel::SimpleVLight:
255 return QSSGRenderParticles::FeatureLevel::LineVLight;
256 case QQuick3DParticleSpriteParticle::FeatureLevel::MappedVLight:
257 return QSSGRenderParticles::FeatureLevel::LineMappedVLight;
258 case QQuick3DParticleSpriteParticle::FeatureLevel::AnimatedVLight:
259 return QSSGRenderParticles::FeatureLevel::LineAnimatedVLight;
261 return QSSGRenderParticles::FeatureLevel::Line;
264QSSGRenderGraphObject *QQuick3DParticleLineParticle::updateLineNode(QSSGRenderGraphObject *node)
266 auto particles =
static_cast<QSSGRenderParticles *>(node);
269 if (sprite() && spriteSequence())
270 frames =
float(spriteSequence()->frameCount());
272 particles->m_sizeModifier = m_scaleMultiplier;
273 particles->m_alphaFade = 1.0f - m_alphaFade;
274 if (m_texcoordMode == TexcoordMode::Fill)
275 particles->m_texcoordScale = frames * m_texcoordMultiplier;
277 particles->m_texcoordScale = frames / particleScale() * m_texcoordMultiplier;
278 particles->m_featureLevel = lineFeatureLevel(m_featureLevel);
283void QQuick3DParticleLineParticle::handleMaxAmountChanged(
int amount)
285 if (m_lineData.size() == amount)
288 m_lineData.resize(m_segmentCount * amount);
289 m_lineHeaderData.resize(amount);
290 QQuick3DParticleSpriteParticle::handleMaxAmountChanged(amount);
293void QQuick3DParticleLineParticle::handleSystemChanged(QQuick3DParticleSystem *system)
295 for (PerEmitterData &value : m_perEmitterData) {
296 delete value.particleUpdateNode;
297 value.particleUpdateNode =
new LineParticleUpdateNode(system);
298 value.particleUpdateNode->m_particle =
this;
303QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
306 QQuick3DParticleLineParticle *lineParticle = qobject_cast<QQuick3DParticleLineParticle *>(m_particle);
307 node = lineParticle->updateParticleNode(
this, node);
308 lineParticle->updateLineNode(node);
309 QQuick3DNode::updateSpatialNode(node);
310 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(lineParticle, node);
311 auto particles =
static_cast<QSSGRenderParticles *>(node);
313 lineParticle->updateLineBuffer(
this, particles);
320void QQuick3DParticleLineParticle ::reset()
322 QQuick3DParticleSpriteParticle::reset();
324 m_lineHeaderData.fill({});
325 m_fadeOutData.clear();
328void QQuick3DParticleLineParticle::commitParticles(
float time)
330 QQuick3DParticleSpriteParticle::commitParticles(time);
332 for (
auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
333 if (time >= iter->beginTime && time < iter->endTime)
336 iter = m_fadeOutData.erase(iter);
340int QQuick3DParticleLineParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
342 if (!m_perEmitterData.contains(emitter)) {
343 m_perEmitterData.insert(emitter, PerEmitterData());
344 auto &perEmitter = m_perEmitterData[emitter];
345 perEmitter.particleUpdateNode =
new LineParticleUpdateNode(system());
346 perEmitter.emitter = emitter;
347 perEmitter.particleUpdateNode->m_particle =
this;
348 perEmitter.emitterIndex = m_nextEmitterIndex++;
351 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
353 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
355 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
359void QQuick3DParticleLineParticle::setParticleData(
int particleIndex,
360 const QVector3D &position,
361 const QVector3D &rotation,
362 const QVector4D &color,
363 float size,
float age,
364 float animationFrame)
366 auto &dst = m_spriteParticleData[particleIndex];
367 bool update = size > 0.0f || dst.size > 0.0f;
368 QQuick3DParticleSpriteParticle::setParticleData(particleIndex, position, rotation, color, size, age, animationFrame);
370 updateLineSegment(particleIndex);
373void QQuick3DParticleLineParticle::resetParticleData(
int particleIndex)
375 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
376 if (header->pointCount) {
377 header->currentIndex = 0;
378 header->pointCount = 0;
380 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
383void QQuick3DParticleLineParticle::saveLineSegment(
int particleIndex,
float time)
385 if (m_eolFadeOutDuration > 0 && m_lineHeaderData[particleIndex].pointCount > 0) {
386 FadeOutLineData data;
387 data.endPoint = m_spriteParticleData[particleIndex];
388 data.beginTime = time;
389 data.endTime = time + m_eolFadeOutDuration * 0.001f;
390 data.timeFactor = 1000.0f / ((
float)m_eolFadeOutDuration);
391 data.header = m_lineHeaderData[particleIndex];
392 data.lineData = m_lineData.mid(particleIndex * m_segmentCount, m_segmentCount);
393 data.emitterIndex = m_spriteParticleData[particleIndex].emitterIndex;
394 m_fadeOutData.emplaceBack(data);
395 clearSegment(particleIndex);
401 float x = qDegreesToRadians(eulerRotation.x());
402 float y = qDegreesToRadians(eulerRotation.y());
403 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
404 return QVector3D(0, 0, -1);
409 return QVector3D(d, -b * c, a * c);
412void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
414 const auto &perEmitter = perEmitterData(updateNode);
415 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
420 for (
int i = 0; i < m_lineHeaderData.size(); i++) {
421 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
424 int totalCount = lineCount;
425 if (m_perEmitterData.size() > 1) {
426 for (
int i = 0; i < m_fadeOutData.size(); i++) {
427 if (m_fadeOutData[i].emitterIndex == perEmitter.emitterIndex)
431 totalCount += m_fadeOutData.size();
434 if (node->m_particleBuffer.particleCount() != totalCount)
435 node->m_particleBuffer.resizeLine(totalCount, m_segmentCount + 1);
437 if (!totalCount)
return;
439 const int segments = m_segmentCount;
440 const int particlesPerSlice = node->m_particleBuffer.particlesPerSlice();
441 const int sliceStride = node->m_particleBuffer.sliceStride();
442 int sliceParticleIdx = 0;
444 char *dest = node->m_particleBuffer.pointer();
447 const LineDataHeader *header = m_lineHeaderData.constData();
448 const LineData *lineData = m_lineData.constData();
449 const SpriteParticleData *src = m_spriteParticleData.constData();
451 auto nextParticle = [](
char *&buffer,
int &slice,
int &sliceParticleIdx,
int particlesPerSlice,
int sliceStride) -> QSSGLineParticle* {
452 QSSGLineParticle *ret =
reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
454 if (sliceParticleIdx == particlesPerSlice) {
456 buffer += sliceStride;
457 sliceParticleIdx = 0;
462 auto genLine = [&](
const SpriteParticleData &sdata,
const LineDataHeader &header,
const LineData *tdata,
463 QSSGBounds3 &bounds,
int segments,
float particleScale,
float alpha,
464 char *&buffer,
int &slice,
int &sliceParticleIdx,
int particlesPerSlice,
int sliceStride,
bool absolute,
bool fill) {
465 QSSGLineParticle *particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
466 int idx = header.currentIndex;
467 particle->color = sdata.color;
468 particle->color.setW(sdata.color.w() * alpha);
469 QVector3D tangent = (tdata[idx].position - sdata.position).normalized();
470 QVector3D binormal = QVector3D::crossProduct(qt_normalFromRotation(sdata.rotation), tangent);
471 particle->binormal = binormal;
472 particle->position = sdata.position;
473 particle->age = sdata.age;
474 particle->animationFrame = sdata.animationFrame;
475 particle->size = sdata.size * particleScale;
476 float partialLength = (tdata[idx].position - sdata.position).length();
477 float length0 = tdata[idx].length + partialLength;
478 particle->length = 0.0f;
479 float lineLength = header.length;
480 int lastIdx = (idx + 1 + segments - header.pointCount) % segments;
481 float lengthScale = -1.0f;
484 particle->length = length0;
489 if (lineLength > 0.0f) {
490 lengthScale = -1.0f / lineLength;
492 float totalLength = tdata[idx].length - tdata[lastIdx].length;
493 if (header.pointCount < segments)
494 totalLength += partialLength;
495 if (!qFuzzyIsNull(totalLength))
496 lengthScale = -1.0f / totalLength;
499 bounds.include(sdata.position);
501 QSSGLineParticle *prevGood = particle;
504 Q_ASSERT(header.pointCount <= m_segmentCount);
506 if (header.length >= 0.0f) {
507 float totalLength = 0;
508 float prevLength = tdata[idx].length + partialLength;
509 for (segmentIdx = 0; segmentIdx < header.pointCount && totalLength < header.length; segmentIdx++) {
510 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
511 particle->size = tdata[idx].size * particleScale;
512 if (particle->size > 0.0f) {
513 bounds.include(tdata[idx].position);
514 particle->color = tdata[idx].color;
515 particle->color.setW(tdata[idx].color.w() * alpha);
516 particle->binormal = tdata[idx].binormal;
517 particle->position = tdata[idx].position;
518 particle->animationFrame = sdata.animationFrame;
519 particle->age = sdata.age;
520 particle->length = (length0 - tdata[idx].length) * lengthScale;
521 float segmentLength = prevLength - tdata[idx].length;
522 prevLength = tdata[idx].length;
523 if (totalLength + segmentLength > header.length) {
524 float diff = totalLength + segmentLength - header.length;
525 particle->position -= tdata[idx].tangent * diff;
526 particle->length -= diff * lengthScale;
527 segmentLength -= diff;
529 totalLength += segmentLength;
533 idx = idx ? (idx - 1) : (segments - 1);
536 for (segmentIdx = 0; segmentIdx < header.pointCount; segmentIdx++) {
537 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
538 particle->size = tdata[idx].size * particleScale;
539 if (particle->size > 0.0f) {
540 bounds.include(tdata[idx].position);
541 particle->color = tdata[idx].color;
542 particle->color.setW(tdata[idx].color.w() * alpha);
543 particle->binormal = tdata[idx].binormal;
544 particle->position = tdata[idx].position;
545 particle->animationFrame = sdata.animationFrame;
546 particle->age = sdata.age;
547 particle->length = (length0 - tdata[idx].length) * lengthScale;
551 idx = idx ? (idx - 1) : (segments - 1);
554 for (;segmentIdx < segments; segmentIdx++) {
555 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
556 *particle = *prevGood;
557 particle->size = 0.0f;
558 particle->length = 0.0f;
559 idx = idx ? (idx - 1) : (segments - 1);
562 if (prevGood == particle && header.length < 0.0f && segments > 1) {
563 prevGood->position -= tdata[prevIdx].tangent * partialLength;
565 prevGood->length -= partialLength * lengthScale;
569 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
570 const bool fill = m_texcoordMode == TexcoordMode::Fill;
572 while (i < lineCount) {
573 if (header->pointCount && header->emitterIndex == perEmitter.emitterIndex) {
574 genLine(*src, *header, lineData, bounds, segments, particleScale(), 1.0f, dest,
575 slice, sliceParticleIdx, particlesPerSlice, sliceStride, absolute, fill);
579 lineData += segments;
583 float time = system()->currentTime() * 0.001f;
584 for (
const FadeOutLineData &fdata : std::as_const(m_fadeOutData)) {
585 if (fdata.emitterIndex == perEmitter.emitterIndex) {
586 float factor = 1.0f - (time - fdata.beginTime) * fdata.timeFactor;
587 genLine(fdata.endPoint, fdata.header, fdata.lineData.data(), bounds, segments,
588 particleScale(), factor, dest, slice, sliceParticleIdx, particlesPerSlice,
589 sliceStride, absolute, fill);
592 node->m_particleBuffer.setBounds(bounds);
595void QQuick3DParticleLineParticle::handleSegmentCountChanged()
598 m_lineData.resize(m_segmentCount * m_maxAmount);
600 m_lineHeaderData.resize(m_maxAmount);
601 m_lineHeaderData.fill({});
602 m_fadeOutData.clear();
603 if (!m_spriteParticleData.isEmpty()) {
604 auto count = qMin(m_maxAmount, m_spriteParticleData.size());
605 for (
int i = 0; i < count; i++)
606 m_lineHeaderData[i].emitterIndex = m_spriteParticleData[i].emitterIndex;
610void QQuick3DParticleLineParticle::updateLineSegment(
int particleIndex)
612 if (m_lineData.isEmpty()) {
613 qWarning () <<
"Line particle updated before having been initialized";
616 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
617 int idx = header->currentIndex;
618 LineData *cur = m_lineData.data() + particleIndex * m_segmentCount;
619 LineData *prev = header->pointCount ? cur + idx :
nullptr;
620 const SpriteParticleData &src = m_spriteParticleData.at(particleIndex);
622 if (prev && m_segmentCount > 1) {
623 float length = (prev->position - src.position).length();
624 float minLength = m_lengthDeltaMin;
625 if (header->length >= 0.0f)
626 minLength = header->length / (m_segmentCount - 1);
627 if (length < minLength)
631 if (header->pointCount < m_segmentCount)
632 header->pointCount++;
635 idx = (idx + 1) % m_segmentCount;
636 header->currentIndex = idx;
639 cur->color = src.color;
640 cur->size = src.size;
641 cur->normal = qt_normalFromRotation(src.rotation);
643 if (prev && m_segmentCount == 1) {
644 QVector3D t = prev->position - src.position;
645 float l = t.length();
647 float minLength = m_lengthDeltaMin;
648 if (header->length >= 0.0f)
649 minLength = header->length;
650 cur->position = src.position + minLength * t;
653 cur->binormal = QVector3D::crossProduct(cur->normal, t);
655 cur->position = src.position;
659 if (prev && prev != cur) {
660 prev->tangent = prev->position - src.position;
661 cur->length = prev->tangent.length();
662 prev->tangent /= cur->length;
663 cur->length += prev->length;
664 cur->binormal = QVector3D::crossProduct(cur->normal, prev->tangent);
665 if (header->pointCount == 1)
666 prev->binormal = cur->binormal;
668 prev->binormal = (prev->binormal + cur->binormal).normalized();
672void QQuick3DParticleLineParticle::clearSegment(
int particleIndex)
674 if (m_lineData.isEmpty())
676 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
677 if (header->pointCount) {
678 auto data = m_lineData.begin() + particleIndex * m_segmentCount;
679 std::fill_n(data, m_segmentCount, LineData());
681 header->emitterIndex = -1;
682 header->currentIndex = 0;
683 header->pointCount = 0;
684 header->length = -1.0f;
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)