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;
302QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
305 QQuick3DParticleLineParticle *lineParticle = qobject_cast<QQuick3DParticleLineParticle *>(m_particle);
306 node = lineParticle->updateParticleNode(
this, node);
307 lineParticle->updateLineNode(node);
308 QQuick3DNode::updateSpatialNode(node);
309 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(lineParticle, node);
310 auto particles =
static_cast<QSSGRenderParticles *>(node);
312 lineParticle->updateLineBuffer(
this, particles);
319void QQuick3DParticleLineParticle ::reset()
321 QQuick3DParticleSpriteParticle::reset();
323 m_lineHeaderData.fill({});
324 m_fadeOutData.clear();
327void QQuick3DParticleLineParticle::commitParticles(
float time)
329 QQuick3DParticleSpriteParticle::commitParticles(time);
331 for (
auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
332 if (time >= iter->beginTime && time < iter->endTime)
335 iter = m_fadeOutData.erase(iter);
339int QQuick3DParticleLineParticle::nextCurrentIndex(
const QQuick3DParticleEmitter *emitter)
341 if (!m_perEmitterData.contains(emitter)) {
342 m_perEmitterData.insert(emitter, PerEmitterData());
343 auto &perEmitter = m_perEmitterData[emitter];
344 perEmitter.particleUpdateNode =
new LineParticleUpdateNode(system());
345 perEmitter.emitter = emitter;
346 perEmitter.particleUpdateNode->m_particle =
this;
347 perEmitter.emitterIndex = m_nextEmitterIndex++;
349 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
351 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
353 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
357void QQuick3DParticleLineParticle::setParticleData(
int particleIndex,
358 const QVector3D &position,
359 const QVector3D &rotation,
360 const QVector4D &color,
361 float size,
float age,
362 float animationFrame)
364 auto &dst = m_spriteParticleData[particleIndex];
365 bool update = size > 0.0f || dst.size > 0.0f;
366 QQuick3DParticleSpriteParticle::setParticleData(particleIndex, position, rotation, color, size, age, animationFrame);
368 updateLineSegment(particleIndex);
371void QQuick3DParticleLineParticle::resetParticleData(
int particleIndex)
373 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
374 if (header->pointCount) {
375 header->currentIndex = 0;
376 header->pointCount = 0;
378 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
381void QQuick3DParticleLineParticle::saveLineSegment(
int particleIndex,
float time)
383 if (m_eolFadeOutDuration > 0 && m_lineHeaderData[particleIndex].pointCount > 0) {
384 FadeOutLineData data;
385 data.endPoint = m_spriteParticleData[particleIndex];
386 data.beginTime = time;
387 data.endTime = time + m_eolFadeOutDuration * 0.001f;
388 data.timeFactor = 1000.0f / ((
float)m_eolFadeOutDuration);
389 data.header = m_lineHeaderData[particleIndex];
390 data.lineData = m_lineData.mid(particleIndex * m_segmentCount, m_segmentCount);
391 data.emitterIndex = m_spriteParticleData[particleIndex].emitterIndex;
392 m_fadeOutData.emplaceBack(data);
393 clearSegment(particleIndex);
399 float x = qDegreesToRadians(eulerRotation.x());
400 float y = qDegreesToRadians(eulerRotation.y());
401 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
402 return QVector3D(0, 0, -1);
407 return QVector3D(d, -b * c, a * c);
410void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
412 const auto &perEmitter = perEmitterData(updateNode);
413 QSSGRenderParticles *node =
static_cast<QSSGRenderParticles *>(spatialNode);
418 for (
int i = 0; i < m_lineHeaderData.size(); i++) {
419 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
422 int totalCount = lineCount;
423 if (m_perEmitterData.size() > 1) {
424 for (
int i = 0; i < m_fadeOutData.size(); i++) {
425 if (m_fadeOutData[i].emitterIndex == perEmitter.emitterIndex)
429 totalCount += m_fadeOutData.size();
432 if (node->m_particleBuffer.particleCount() != totalCount)
433 node->m_particleBuffer.resizeLine(totalCount, m_segmentCount + 1);
435 if (!totalCount)
return;
437 const int segments = m_segmentCount;
438 const int particlesPerSlice = node->m_particleBuffer.particlesPerSlice();
439 const int sliceStride = node->m_particleBuffer.sliceStride();
440 int sliceParticleIdx = 0;
442 char *dest = node->m_particleBuffer.pointer();
445 const LineDataHeader *header = m_lineHeaderData.constData();
446 const LineData *lineData = m_lineData.constData();
447 const SpriteParticleData *src = m_spriteParticleData.constData();
449 auto nextParticle = [](
char *&buffer,
int &slice,
int &sliceParticleIdx,
int particlesPerSlice,
int sliceStride) -> QSSGLineParticle* {
450 QSSGLineParticle *ret =
reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
452 if (sliceParticleIdx == particlesPerSlice) {
454 buffer += sliceStride;
455 sliceParticleIdx = 0;
460 auto genLine = [&](
const SpriteParticleData &sdata,
const LineDataHeader &header,
const LineData *tdata,
461 QSSGBounds3 &bounds,
int segments,
float particleScale,
float alpha,
462 char *&buffer,
int &slice,
int &sliceParticleIdx,
int particlesPerSlice,
int sliceStride,
bool absolute,
bool fill) {
463 QSSGLineParticle *particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
464 int idx = header.currentIndex;
465 particle->color = sdata.color;
466 particle->color.setW(sdata.color.w() * alpha);
467 QVector3D tangent = (tdata[idx].position - sdata.position).normalized();
468 QVector3D binormal = QVector3D::crossProduct(qt_normalFromRotation(sdata.rotation), tangent);
469 particle->binormal = binormal;
470 particle->position = sdata.position;
471 particle->age = sdata.age;
472 particle->animationFrame = sdata.animationFrame;
473 particle->size = sdata.size * particleScale;
474 float partialLength = (tdata[idx].position - sdata.position).length();
475 float length0 = tdata[idx].length + partialLength;
476 particle->length = 0.0f;
477 float lineLength = header.length;
478 int lastIdx = (idx + 1 + segments - header.pointCount) % segments;
479 float lengthScale = -1.0f;
482 particle->length = length0;
487 if (lineLength > 0.0f) {
488 lengthScale = -1.0f / lineLength;
490 float totalLength = tdata[idx].length - tdata[lastIdx].length;
491 if (header.pointCount < segments)
492 totalLength += partialLength;
493 if (!qFuzzyIsNull(totalLength))
494 lengthScale = -1.0f / totalLength;
497 bounds.include(sdata.position);
499 QSSGLineParticle *prevGood = particle;
502 Q_ASSERT(header.pointCount <= m_segmentCount);
504 if (header.length >= 0.0f) {
505 float totalLength = 0;
506 float prevLength = tdata[idx].length + partialLength;
507 for (segmentIdx = 0; segmentIdx < header.pointCount && totalLength < header.length; segmentIdx++) {
508 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
509 particle->size = tdata[idx].size * particleScale;
510 if (particle->size > 0.0f) {
511 bounds.include(tdata[idx].position);
512 particle->color = tdata[idx].color;
513 particle->color.setW(tdata[idx].color.w() * alpha);
514 particle->binormal = tdata[idx].binormal;
515 particle->position = tdata[idx].position;
516 particle->animationFrame = sdata.animationFrame;
517 particle->age = sdata.age;
518 particle->length = (length0 - tdata[idx].length) * lengthScale;
519 float segmentLength = prevLength - tdata[idx].length;
520 prevLength = tdata[idx].length;
521 if (totalLength + segmentLength > header.length) {
522 float diff = totalLength + segmentLength - header.length;
523 particle->position -= tdata[idx].tangent * diff;
524 particle->length -= diff * lengthScale;
525 segmentLength -= diff;
527 totalLength += segmentLength;
531 idx = idx ? (idx - 1) : (segments - 1);
534 for (segmentIdx = 0; segmentIdx < header.pointCount; segmentIdx++) {
535 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
536 particle->size = tdata[idx].size * particleScale;
537 if (particle->size > 0.0f) {
538 bounds.include(tdata[idx].position);
539 particle->color = tdata[idx].color;
540 particle->color.setW(tdata[idx].color.w() * alpha);
541 particle->binormal = tdata[idx].binormal;
542 particle->position = tdata[idx].position;
543 particle->animationFrame = sdata.animationFrame;
544 particle->age = sdata.age;
545 particle->length = (length0 - tdata[idx].length) * lengthScale;
549 idx = idx ? (idx - 1) : (segments - 1);
552 for (;segmentIdx < segments; segmentIdx++) {
553 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
554 *particle = *prevGood;
555 particle->size = 0.0f;
556 particle->length = 0.0f;
557 idx = idx ? (idx - 1) : (segments - 1);
560 if (prevGood == particle && header.length < 0.0f && segments > 1) {
561 prevGood->position -= tdata[prevIdx].tangent * partialLength;
563 prevGood->length -= partialLength * lengthScale;
567 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
568 const bool fill = m_texcoordMode == TexcoordMode::Fill;
570 while (i < lineCount) {
571 if (header->pointCount && header->emitterIndex == perEmitter.emitterIndex) {
572 genLine(*src, *header, lineData, bounds, segments, particleScale(), 1.0f, dest,
573 slice, sliceParticleIdx, particlesPerSlice, sliceStride, absolute, fill);
577 lineData += segments;
581 float time = system()->currentTime() * 0.001f;
582 for (
const FadeOutLineData &fdata : m_fadeOutData) {
583 if (fdata.emitterIndex == perEmitter.emitterIndex) {
584 float factor = 1.0f - (time - fdata.beginTime) * fdata.timeFactor;
585 genLine(fdata.endPoint, fdata.header, fdata.lineData.data(), bounds, segments,
586 particleScale(), factor, dest, slice, sliceParticleIdx, particlesPerSlice,
587 sliceStride, absolute, fill);
590 node->m_particleBuffer.setBounds(bounds);
593void QQuick3DParticleLineParticle::handleSegmentCountChanged()
596 m_lineData.resize(m_segmentCount * m_maxAmount);
598 m_lineHeaderData.resize(m_maxAmount);
599 m_lineHeaderData.fill({});
600 m_fadeOutData.clear();
601 if (!m_spriteParticleData.isEmpty()) {
602 auto count = qMin(m_maxAmount, m_spriteParticleData.size());
603 for (
int i = 0; i < count; i++)
604 m_lineHeaderData[i].emitterIndex = m_spriteParticleData[i].emitterIndex;
608void QQuick3DParticleLineParticle::updateLineSegment(
int particleIndex)
610 if (m_lineData.isEmpty()) {
611 qWarning () <<
"Line particle updated before having been initialized";
614 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
615 int idx = header->currentIndex;
616 LineData *cur = m_lineData.data() + particleIndex * m_segmentCount;
617 LineData *prev = header->pointCount ? cur + idx :
nullptr;
618 const SpriteParticleData &src = m_spriteParticleData.at(particleIndex);
620 if (prev && m_segmentCount > 1) {
621 float length = (prev->position - src.position).length();
622 float minLength = m_lengthDeltaMin;
623 if (header->length >= 0.0f)
624 minLength = header->length / (m_segmentCount - 1);
625 if (length < minLength)
629 if (header->pointCount < m_segmentCount)
630 header->pointCount++;
633 idx = (idx + 1) % m_segmentCount;
634 header->currentIndex = idx;
637 cur->color = src.color;
638 cur->size = src.size;
639 cur->normal = qt_normalFromRotation(src.rotation);
641 if (prev && m_segmentCount == 1) {
642 QVector3D t = prev->position - src.position;
643 float l = t.length();
645 float minLength = m_lengthDeltaMin;
646 if (header->length >= 0.0f)
647 minLength = header->length;
648 cur->position = src.position + minLength * t;
651 cur->binormal = QVector3D::crossProduct(cur->normal, t);
653 cur->position = src.position;
657 if (prev && prev != cur) {
658 prev->tangent = prev->position - src.position;
659 cur->length = prev->tangent.length();
660 prev->tangent /= cur->length;
661 cur->length += prev->length;
662 cur->binormal = QVector3D::crossProduct(cur->normal, prev->tangent);
663 if (header->pointCount == 1)
664 prev->binormal = cur->binormal;
666 prev->binormal = (prev->binormal + cur->binormal).normalized();
670void QQuick3DParticleLineParticle::clearSegment(
int particleIndex)
672 if (m_lineData.isEmpty())
674 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
675 if (header->pointCount) {
676 auto data = m_lineData.begin() + particleIndex * m_segmentCount;
677 std::fill_n(data, m_segmentCount, LineData());
679 header->emitterIndex = -1;
680 header->currentIndex = 0;
681 header->pointCount = 0;
682 header->length = -1.0f;
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)