8#include <private/qquick3dobject_p.h>
15
16
17
18
19
20
21
22
23
24
25
26
27
29QQuick3DParticleLineParticle::QQuick3DParticleLineParticle(QQuick3DNode *parent)
30 : QQuick3DParticleSpriteParticle(parent)
34QQuick3DParticleLineParticle::~QQuick3DParticleLineParticle()
39
40
41
42
43
44
45int QQuick3DParticleLineParticle::segmentCount()
const
47 return m_segmentCount;
51
52
53
54
55
56
57
58
59float QQuick3DParticleLineParticle::alphaFade()
const
65
66
67
68
69
70
71
72
73float QQuick3DParticleLineParticle::scaleMultiplier()
const
75 return m_scaleMultiplier;
79
80
81
82
83
84float QQuick3DParticleLineParticle::texcoordMultiplier()
const
86 return m_texcoordMultiplier;
90
91
92
93
94
95
96
97float QQuick3DParticleLineParticle::length()
const
103
104
105
106
107
108
109
110
112float QQuick3DParticleLineParticle::lengthVariation()
const
114 return m_lengthVariation;
118
119
120
121
122
123float QQuick3DParticleLineParticle::lengthDeltaMin()
const
125 return m_lengthDeltaMin;
129
130
131
132
133
134
135int QQuick3DParticleLineParticle::eolFadeOutDuration()
const
137 return m_eolFadeOutDuration;
141
142
143
144
145
146
147
148
149
150
151
154
155
156
157
158QQuick3DParticleLineParticle::TexcoordMode QQuick3DParticleLineParticle::texcoordMode()
const
160 return m_texcoordMode;
163void QQuick3DParticleLineParticle::setSegmentCount(
int count)
165 count = qMax(1, count);
166 if (m_segmentCount == count)
168 m_segmentCount = count;
169 handleSegmentCountChanged();
170 Q_EMIT segmentCountChanged();
173void QQuick3DParticleLineParticle::setAlphaFade(
float fade)
175 fade = qBound(0.0f, fade, 1.0f);
176 if (qFuzzyCompare(m_alphaFade, fade))
179 Q_EMIT alphaFadeChanged();
182void QQuick3DParticleLineParticle::setScaleMultiplier(
float multiplier)
184 multiplier = qBound(0.0f, multiplier, 2.0f);
185 if (qFuzzyCompare(m_scaleMultiplier, multiplier))
187 m_scaleMultiplier = multiplier;
188 Q_EMIT scaleMultiplierChanged();
191void QQuick3DParticleLineParticle::setTexcoordMultiplier(
float multiplier)
193 if (qFuzzyCompare(m_texcoordMultiplier, multiplier))
195 m_texcoordMultiplier = multiplier;
196 Q_EMIT texcoordMultiplierChanged();
199void QQuick3DParticleLineParticle::setLength(
float length)
201 length = length != -1.0f ? qMax(length, 0.0f) : -1.0f;
202 if (qFuzzyCompare(m_length, length))
205 Q_EMIT lengthChanged();
208void QQuick3DParticleLineParticle::setLengthVariation(
float lengthVariation)
210 lengthVariation = qMax(lengthVariation, 0.0f);
211 if (qFuzzyCompare(m_lengthVariation, lengthVariation))
213 m_lengthVariation = lengthVariation;
214 Q_EMIT lengthVariationChanged();
217void QQuick3DParticleLineParticle::setLengthDeltaMin(
float min)
219 min = qMax(min, 0.0f);
220 if (qFuzzyCompare(m_lengthDeltaMin, min))
222 m_lengthDeltaMin = min;
223 Q_EMIT lengthDeltaMinChanged();
226void QQuick3DParticleLineParticle::setEolFadeOutDuration(
int duration)
228 duration = qMax(0, duration);
229 if (duration == m_eolFadeOutDuration)
231 m_eolFadeOutDuration = duration;
232 Q_EMIT eolFadeOutDurationChanged();
235void QQuick3DParticleLineParticle::setTexcoordMode(TexcoordMode mode)
237 if (mode == m_texcoordMode)
239 m_texcoordMode = mode;
240 Q_EMIT texcoordModeChanged();
246 case QQuick3DParticleSpriteParticle::FeatureLevel::Simple:
247 return QSSGRenderParticles::FeatureLevel::Line;
248 case QQuick3DParticleSpriteParticle::FeatureLevel::Mapped:
249 return QSSGRenderParticles::FeatureLevel::LineMapped;
250 case QQuick3DParticleSpriteParticle::FeatureLevel::Animated:
251 return QSSGRenderParticles::FeatureLevel::LineAnimated;
252 case QQuick3DParticleSpriteParticle::FeatureLevel::SimpleVLight:
253 return QSSGRenderParticles::FeatureLevel::LineVLight;
254 case QQuick3DParticleSpriteParticle::FeatureLevel::MappedVLight:
255 return QSSGRenderParticles::FeatureLevel::LineMappedVLight;
256 case QQuick3DParticleSpriteParticle::FeatureLevel::AnimatedVLight:
257 return QSSGRenderParticles::FeatureLevel::LineAnimatedVLight;
259 return QSSGRenderParticles::FeatureLevel::Line;
262QSSGRenderGraphObject *QQuick3DParticleLineParticle::updateLineNode(QSSGRenderGraphObject *node)
264 auto particles =
static_cast<QSSGRenderParticles *>(node);
267 if (sprite() && spriteSequence())
268 frames =
float(spriteSequence()->frameCount());
270 particles->m_sizeModifier = m_scaleMultiplier;
271 particles->m_alphaFade = 1.0f - m_alphaFade;
272 if (m_texcoordMode == TexcoordMode::Fill)
273 particles->m_texcoordScale = frames * m_texcoordMultiplier;
275 particles->m_texcoordScale = frames / particleScale() * m_texcoordMultiplier;
276 particles->m_featureLevel = lineFeatureLevel(m_featureLevel);
281void QQuick3DParticleLineParticle::handleMaxAmountChanged(
int amount)
283 if (m_lineData.size() == amount)
286 m_lineData.resize(m_segmentCount * amount);
287 m_lineHeaderData.resize(amount);
288 QQuick3DParticleSpriteParticle::handleMaxAmountChanged(amount);
291void QQuick3DParticleLineParticle::handleSystemChanged(QQuick3DParticleSystem *system)
293 for (PerEmitterData &value : m_perEmitterData) {
294 delete value.particleUpdateNode;
295 value.particleUpdateNode =
new LineParticleUpdateNode(system);
296 value.particleUpdateNode->m_particle =
this;
300QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
303 QQuick3DParticleLineParticle *lineParticle = qobject_cast<QQuick3DParticleLineParticle *>(m_particle);
304 node = lineParticle->updateParticleNode(
this, node);
305 lineParticle->updateLineNode(node);
306 QQuick3DNode::updateSpatialNode(node);
307 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(lineParticle, node);
308 auto particles =
static_cast<QSSGRenderParticles *>(node);
310 lineParticle->updateLineBuffer(
this, particles);
317void QQuick3DParticleLineParticle ::reset()
319 QQuick3DParticleSpriteParticle::reset();
321 m_lineHeaderData.fill({});
322 m_fadeOutData.clear();
325void QQuick3DParticleLineParticle::commitParticles(
float time)
327 QQuick3DParticleSpriteParticle::commitParticles(time);
329 for (
auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
330 if (time >= iter->beginTime && time < iter->endTime)
333 iter = m_fadeOutData.erase(iter);
337int QQuick3DParticleLineParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
339 if (!m_perEmitterData.contains(emitter)) {
340 m_perEmitterData.insert(emitter, PerEmitterData());
341 auto &perEmitter = m_perEmitterData[emitter];
342 perEmitter.particleUpdateNode =
new LineParticleUpdateNode(system());
343 perEmitter.emitter = emitter;
344 perEmitter.particleUpdateNode->m_particle =
this;
345 perEmitter.emitterIndex = m_nextEmitterIndex++;
347 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
349 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
351 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
355void QQuick3DParticleLineParticle::setParticleData(
int particleIndex,
356 const QVector3D &position,
357 const QVector3D &rotation,
358 const QVector4D &color,
359 float size,
float age,
360 float animationFrame)
362 auto &dst = m_spriteParticleData[particleIndex];
363 bool update = size > 0.0f || dst.size > 0.0f;
364 QQuick3DParticleSpriteParticle::setParticleData(particleIndex, position, rotation, color, size, age, animationFrame);
366 updateLineSegment(particleIndex);
369void QQuick3DParticleLineParticle::resetParticleData(
int particleIndex)
371 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
372 if (header->pointCount) {
373 header->currentIndex = 0;
374 header->pointCount = 0;
376 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
379void QQuick3DParticleLineParticle::saveLineSegment(
int particleIndex,
float time)
381 if (m_eolFadeOutDuration > 0 && m_lineHeaderData[particleIndex].pointCount > 0) {
382 FadeOutLineData data;
383 data.endPoint = m_spriteParticleData[particleIndex];
384 data.beginTime = time;
385 data.endTime = time + m_eolFadeOutDuration * 0.001f;
386 data.timeFactor = 1000.0f / ((
float)m_eolFadeOutDuration);
387 data.header = m_lineHeaderData[particleIndex];
388 data.lineData = m_lineData.mid(particleIndex * m_segmentCount, m_segmentCount);
389 data.emitterIndex = m_spriteParticleData[particleIndex].emitterIndex;
390 m_fadeOutData.emplaceBack(data);
391 clearSegment(particleIndex);
397 float x = qDegreesToRadians(eulerRotation.x());
398 float y = qDegreesToRadians(eulerRotation.y());
399 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
400 return QVector3D(0, 0, -1);
405 return QVector3D(d, -b * c, a * c);
408void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
410 const auto &perEmitter = perEmitterData(updateNode);
411 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
416 for (
int i = 0; i < m_lineHeaderData.size(); i++) {
417 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
420 int totalCount = lineCount;
421 if (m_perEmitterData.size() > 1) {
422 for (
int i = 0; i < m_fadeOutData.size(); i++) {
423 if (m_fadeOutData[i].emitterIndex == perEmitter.emitterIndex)
427 totalCount += m_fadeOutData.size();
430 if (node->m_particleBuffer.particleCount() != totalCount)
431 node->m_particleBuffer.resizeLine(totalCount, m_segmentCount + 1);
433 if (!totalCount)
return;
435 const int segments = m_segmentCount;
436 const int particlesPerSlice = node->m_particleBuffer.particlesPerSlice();
437 const int sliceStride = node->m_particleBuffer.sliceStride();
438 int sliceParticleIdx = 0;
440 char *dest = node->m_particleBuffer.pointer();
443 const LineDataHeader *header = m_lineHeaderData.constData();
444 const LineData *lineData = m_lineData.constData();
445 const SpriteParticleData *src = m_spriteParticleData.constData();
447 auto nextParticle = [](
char *&buffer,
int &slice,
int &sliceParticleIdx,
int particlesPerSlice,
int sliceStride) -> QSSGLineParticle* {
448 QSSGLineParticle *ret =
reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
450 if (sliceParticleIdx == particlesPerSlice) {
452 buffer += sliceStride;
453 sliceParticleIdx = 0;
458 auto genLine = [&](
const SpriteParticleData &sdata,
const LineDataHeader &header,
const LineData *tdata,
459 QSSGBounds3 &bounds,
int segments,
float particleScale,
float alpha,
460 char *&buffer,
int &slice,
int &sliceParticleIdx,
int particlesPerSlice,
int sliceStride,
bool absolute,
bool fill) {
461 QSSGLineParticle *particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
462 int idx = header.currentIndex;
463 particle->color = sdata.color;
464 particle->color.setW(sdata.color.w() * alpha);
465 QVector3D tangent = (tdata[idx].position - sdata.position).normalized();
466 QVector3D binormal = QVector3D::crossProduct(qt_normalFromRotation(sdata.rotation), tangent);
467 particle->binormal = binormal;
468 particle->position = sdata.position;
469 particle->age = sdata.age;
470 particle->animationFrame = sdata.animationFrame;
471 particle->size = sdata.size * particleScale;
472 float partialLength = (tdata[idx].position - sdata.position).length();
473 float length0 = tdata[idx].length + partialLength;
474 particle->length = 0.0f;
475 float lineLength = header.length;
476 int lastIdx = (idx + 1 + segments - header.pointCount) % segments;
477 float lengthScale = -1.0f;
480 particle->length = length0;
485 if (lineLength > 0.0f) {
486 lengthScale = -1.0f / lineLength;
488 float totalLength = tdata[idx].length - tdata[lastIdx].length;
489 if (header.pointCount < segments)
490 totalLength += partialLength;
491 if (!qFuzzyIsNull(totalLength))
492 lengthScale = -1.0f / totalLength;
495 bounds.include(sdata.position);
497 QSSGLineParticle *prevGood = particle;
500 Q_ASSERT(header.pointCount <= m_segmentCount);
502 if (header.length >= 0.0f) {
503 float totalLength = 0;
504 float prevLength = tdata[idx].length + partialLength;
505 for (segmentIdx = 0; segmentIdx < header.pointCount && totalLength < header.length; segmentIdx++) {
506 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
507 particle->size = tdata[idx].size * particleScale;
508 if (particle->size > 0.0f) {
509 bounds.include(tdata[idx].position);
510 particle->color = tdata[idx].color;
511 particle->color.setW(tdata[idx].color.w() * alpha);
512 particle->binormal = tdata[idx].binormal;
513 particle->position = tdata[idx].position;
514 particle->animationFrame = sdata.animationFrame;
515 particle->age = sdata.age;
516 particle->length = (length0 - tdata[idx].length) * lengthScale;
517 float segmentLength = prevLength - tdata[idx].length;
518 prevLength = tdata[idx].length;
519 if (totalLength + segmentLength > header.length) {
520 float diff = totalLength + segmentLength - header.length;
521 particle->position -= tdata[idx].tangent * diff;
522 particle->length -= diff * lengthScale;
523 segmentLength -= diff;
525 totalLength += segmentLength;
529 idx = idx ? (idx - 1) : (segments - 1);
532 for (segmentIdx = 0; segmentIdx < header.pointCount; segmentIdx++) {
533 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
534 particle->size = tdata[idx].size * particleScale;
535 if (particle->size > 0.0f) {
536 bounds.include(tdata[idx].position);
537 particle->color = tdata[idx].color;
538 particle->color.setW(tdata[idx].color.w() * alpha);
539 particle->binormal = tdata[idx].binormal;
540 particle->position = tdata[idx].position;
541 particle->animationFrame = sdata.animationFrame;
542 particle->age = sdata.age;
543 particle->length = (length0 - tdata[idx].length) * lengthScale;
547 idx = idx ? (idx - 1) : (segments - 1);
550 for (;segmentIdx < segments; segmentIdx++) {
551 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
552 *particle = *prevGood;
553 particle->size = 0.0f;
554 particle->length = 0.0f;
555 idx = idx ? (idx - 1) : (segments - 1);
558 if (prevGood == particle && header.length < 0.0f && segments > 1) {
559 prevGood->position -= tdata[prevIdx].tangent * partialLength;
561 prevGood->length -= partialLength * lengthScale;
565 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
566 const bool fill = m_texcoordMode == TexcoordMode::Fill;
568 while (i < lineCount) {
569 if (header->pointCount && header->emitterIndex == perEmitter.emitterIndex) {
570 genLine(*src, *header, lineData, bounds, segments, particleScale(), 1.0f, dest,
571 slice, sliceParticleIdx, particlesPerSlice, sliceStride, absolute, fill);
575 lineData += segments;
579 float time = system()->currentTime() * 0.001f;
580 for (
const FadeOutLineData &fdata : m_fadeOutData) {
581 if (fdata.emitterIndex == perEmitter.emitterIndex) {
582 float factor = 1.0f - (time - fdata.beginTime) * fdata.timeFactor;
583 genLine(fdata.endPoint, fdata.header, fdata.lineData.data(), bounds, segments,
584 particleScale(), factor, dest, slice, sliceParticleIdx, particlesPerSlice,
585 sliceStride, absolute, fill);
588 node->m_particleBuffer.setBounds(bounds);
591void QQuick3DParticleLineParticle::handleSegmentCountChanged()
594 m_lineData.resize(m_segmentCount * m_maxAmount);
596 m_lineHeaderData.resize(m_maxAmount);
597 m_lineHeaderData.fill({});
598 m_fadeOutData.clear();
599 if (!m_spriteParticleData.isEmpty()) {
600 auto count = qMin(m_maxAmount, m_spriteParticleData.size());
601 for (
int i = 0; i < count; i++)
602 m_lineHeaderData[i].emitterIndex = m_spriteParticleData[i].emitterIndex;
606void QQuick3DParticleLineParticle::updateLineSegment(
int particleIndex)
608 if (m_lineData.isEmpty()) {
609 qWarning () <<
"Line particle updated before having been initialized";
612 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
613 int idx = header->currentIndex;
614 LineData *cur = m_lineData.data() + particleIndex * m_segmentCount;
615 LineData *prev = header->pointCount ? cur + idx :
nullptr;
616 const SpriteParticleData &src = m_spriteParticleData.at(particleIndex);
618 if (prev && m_segmentCount > 1) {
619 float length = (prev->position - src.position).length();
620 float minLength = m_lengthDeltaMin;
621 if (header->length >= 0.0f)
622 minLength = header->length / (m_segmentCount - 1);
623 if (length < minLength)
627 if (header->pointCount < m_segmentCount)
628 header->pointCount++;
631 idx = (idx + 1) % m_segmentCount;
632 header->currentIndex = idx;
635 cur->color = src.color;
636 cur->size = src.size;
637 cur->normal = qt_normalFromRotation(src.rotation);
639 if (prev && m_segmentCount == 1) {
640 QVector3D t = prev->position - src.position;
641 float l = t.length();
643 float minLength = m_lengthDeltaMin;
644 if (header->length >= 0.0f)
645 minLength = header->length;
646 cur->position = src.position + minLength * t;
649 cur->binormal = QVector3D::crossProduct(cur->normal, t);
651 cur->position = src.position;
655 if (prev && prev != cur) {
656 prev->tangent = prev->position - src.position;
657 cur->length = prev->tangent.length();
658 prev->tangent /= cur->length;
659 cur->length += prev->length;
660 cur->binormal = QVector3D::crossProduct(cur->normal, prev->tangent);
661 if (header->pointCount == 1)
662 prev->binormal = cur->binormal;
664 prev->binormal = (prev->binormal + cur->binormal).normalized();
668void QQuick3DParticleLineParticle::clearSegment(
int particleIndex)
670 if (m_lineData.isEmpty())
672 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
673 if (header->pointCount) {
674 auto data = m_lineData.begin() + particleIndex * m_segmentCount;
675 std::fill_n(data, m_segmentCount, LineData());
677 header->emitterIndex = -1;
678 header->currentIndex = 0;
679 header->pointCount = 0;
680 header->length = -1.0f;
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)