1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dparticlelineparticle_p.h"
5#include "qquick3dparticlesystem_p.h"
6#include "qquick3dparticlerandomizer_p.h"
7#include "qquick3dparticleutils_p.h"
8#include <private/qquick3dobject_p.h>
9
10#include <algorithm>
11
12QT_BEGIN_NAMESPACE
13
14/*!
15 \qmltype LineParticle3D
16 \inherits SpriteParticle3D
17 \inqmlmodule QtQuick3D.Particles3D
18 \brief Line particle.
19 \since 6.4
20
21 The LineParticle3D creates line shaped sprite particles.
22
23 The line is created from the path of the particle when it moves.
24 The length of the line is specified either by the \l length parameter
25 or the segment count and minimum delta between points. In latter case the
26 length of the line may vary if the particles speed varies.
27*/
28
29QQuick3DParticleLineParticle::QQuick3DParticleLineParticle(QQuick3DNode *parent)
30 : QQuick3DParticleSpriteParticle(parent)
31{
32}
33
34QQuick3DParticleLineParticle::~QQuick3DParticleLineParticle()
35{
36}
37
38/*!
39 \qmlproperty int LineParticle3D::segmentCount
40
41 This property holds the number of segments in the line. The line is drawn using
42 segment + 1 points, where the additional one comes from the particles current position.
43 The default value is 1.
44*/
45int QQuick3DParticleLineParticle::segmentCount() const
46{
47 return m_segmentCount;
48}
49
50/*!
51 \qmlproperty real LineParticle3D::alphaFade
52
53 This property holds the alpha fade factor of the line. The alphaFade value range is [0, 1].
54 When the value is greater than 0.0, causes the line to fade the further the segment is from the
55 first particle segment. The alpha for a segment is calculated like this:
56 segmentAlpha(s) = (1.0 - alphaFade) ^ s, where s is the segment index.
57 The default value is 0.0.
58*/
59float QQuick3DParticleLineParticle::alphaFade() const
60{
61 return m_alphaFade;
62}
63
64/*!
65 \qmlproperty real LineParticle3D::scaleMultiplier
66
67 This property holds the scale multiplier of the line. The scaleMultiplier value range is [0, 2].
68 The scaleMultiplier modifies the line size for the line segments. If the value is less than 1.0,
69 the line gets smaller the further a segment is from the first segment and if the value is greater
70 than 1.0 the line gets bigger. The size for a segment is calculated like this:
71 size(s) = scaleMultiplier ^ s, where s is the segment index.
72*/
73float QQuick3DParticleLineParticle::scaleMultiplier() const
74{
75 return m_scaleMultiplier;
76}
77
78/*!
79 \qmlproperty real LineParticle3D::texcoordMultiplier
80
81 This property holds the texture coordinate multiplier of the line. This value is factored
82 to the texture coordinate values of the line. The default value is 1.0.
83*/
84float QQuick3DParticleLineParticle::texcoordMultiplier() const
85{
86 return m_texcoordMultiplier;
87}
88
89/*!
90 \qmlproperty real LineParticle3D::length
91
92 This property holds the length of the line. If the value is set, the lines length is limited
93 to the value. In this case the minimum delta of the line is the length divided
94 by the segment count. If the value is not set, the line length varies based on the
95 speed the particle moves as well as segment count and minimum delta. The default value is -1.0.
96*/
97float QQuick3DParticleLineParticle::length() const
98{
99 return m_length;
100}
101
102/*!
103 \qmlproperty real LineParticle3D::lengthVariation
104
105 This property holds the length variation of the line. This parameter is not used if
106 the length parameter has not been set. When the length is set, this parameter can be used to
107 vary the length of each line. The default value is 0.0.
108*/
109float QQuick3DParticleLineParticle::lengthVariation() const
110{
111 return m_lengthVariation;
112}
113
114/*!
115 \qmlproperty real LineParticle3D::lengthDeltaMin
116
117 This property holds the minimum length between segment points. This parameter is
118 ignored if the length parameter is set. The default value is 10.0.
119*/
120float QQuick3DParticleLineParticle::lengthDeltaMin() const
121{
122 return m_lengthDeltaMin;
123}
124
125/*!
126 \qmlproperty int LineParticle3D::eolFadeOutDuration
127
128 This property holds the end-of-life fade-out duration of the line. If set, each line remains
129 in the place it was when the particle reached end of its lifetime, then fades out during this
130 time period. The default value is 0.
131*/
132int QQuick3DParticleLineParticle::eolFadeOutDuration() const
133{
134 return m_eolFadeOutDuration;
135}
136
137/*!
138 \qmlproperty enumeration LineParticle3D::TexcoordMode
139
140 Defines the texture coordinate mode of line particle.
141
142 \value LineParticle3D.Absolute
143 Texture coordinates are specified relative to the world position.
144 \value LineParticle3D.Relative
145 Texture coordinates are specified relative to line first line point.
146 \value LineParticle3D.Fill
147 Texture coordinates are specified such that the texture fills the whole line.
148*/
149
150/*!
151 \qmlproperty TexcoordMode LineParticle3D::texcoordMode
152
153 This property holds the texture coordinate mode of the line.
154*/
155QQuick3DParticleLineParticle::TexcoordMode QQuick3DParticleLineParticle::texcoordMode() const
156{
157 return m_texcoordMode;
158}
159
160void QQuick3DParticleLineParticle::setSegmentCount(int count)
161{
162 count = qMax(a: 1, b: count);
163 if (m_segmentCount == count)
164 return;
165 m_segmentCount = count;
166 handleSegmentCountChanged();
167 Q_EMIT segmentCountChanged();
168}
169
170void QQuick3DParticleLineParticle::setAlphaFade(float fade)
171{
172 fade = qBound(min: 0.0f, val: fade, max: 1.0f);
173 if (qFuzzyCompare(p1: m_alphaFade, p2: fade))
174 return;
175 m_alphaFade = fade;
176 Q_EMIT alphaFadeChanged();
177}
178
179void QQuick3DParticleLineParticle::setScaleMultiplier(float multiplier)
180{
181 multiplier = qBound(min: 0.0f, val: multiplier, max: 2.0f);
182 if (qFuzzyCompare(p1: m_scaleMultiplier, p2: multiplier))
183 return;
184 m_scaleMultiplier = multiplier;
185 Q_EMIT scaleMultiplierChanged();
186}
187
188void QQuick3DParticleLineParticle::setTexcoordMultiplier(float multiplier)
189{
190 if (qFuzzyCompare(p1: m_texcoordMultiplier, p2: multiplier))
191 return;
192 m_texcoordMultiplier = multiplier;
193 Q_EMIT texcoordMultiplierChanged();
194}
195
196void QQuick3DParticleLineParticle::setLength(float length)
197{
198 length = length != -1.0f ? qMax(a: length, b: 0.0f) : -1.0f;
199 if (qFuzzyCompare(p1: m_length, p2: length))
200 return;
201 m_length = length;
202 Q_EMIT lengthChanged();
203}
204
205void QQuick3DParticleLineParticle::setLengthVariation(float lengthVariation)
206{
207 lengthVariation = qMax(a: lengthVariation, b: 0.0f);
208 if (qFuzzyCompare(p1: m_lengthVariation, p2: lengthVariation))
209 return;
210 m_lengthVariation = lengthVariation;
211 Q_EMIT lengthVariationChanged();
212}
213
214void QQuick3DParticleLineParticle::setLengthDeltaMin(float min)
215{
216 min = qMax(a: min, b: 0.0f);
217 if (qFuzzyCompare(p1: m_lengthDeltaMin, p2: min))
218 return;
219 m_lengthDeltaMin = min;
220 Q_EMIT lengthDeltaMinChanged();
221}
222
223void QQuick3DParticleLineParticle::setEolFadeOutDuration(int duration)
224{
225 duration = qMax(a: 0, b: duration);
226 if (duration == m_eolFadeOutDuration)
227 return;
228 m_eolFadeOutDuration = duration;
229 Q_EMIT eolFadeOutDurationChanged();
230}
231
232void QQuick3DParticleLineParticle::setTexcoordMode(TexcoordMode mode)
233{
234 if (mode == m_texcoordMode)
235 return;
236 m_texcoordMode = mode;
237 Q_EMIT texcoordModeChanged();
238}
239
240QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
241{
242 switch (in) {
243 case QQuick3DParticleSpriteParticle::FeatureLevel::Simple:
244 return QSSGRenderParticles::FeatureLevel::Line;
245 case QQuick3DParticleSpriteParticle::FeatureLevel::Mapped:
246 return QSSGRenderParticles::FeatureLevel::LineMapped;
247 case QQuick3DParticleSpriteParticle::FeatureLevel::Animated:
248 return QSSGRenderParticles::FeatureLevel::LineAnimated;
249 case QQuick3DParticleSpriteParticle::FeatureLevel::SimpleVLight:
250 return QSSGRenderParticles::FeatureLevel::LineVLight;
251 case QQuick3DParticleSpriteParticle::FeatureLevel::MappedVLight:
252 return QSSGRenderParticles::FeatureLevel::LineMappedVLight;
253 case QQuick3DParticleSpriteParticle::FeatureLevel::AnimatedVLight:
254 return QSSGRenderParticles::FeatureLevel::LineAnimatedVLight;
255 }
256 return QSSGRenderParticles::FeatureLevel::Line;
257}
258
259QSSGRenderGraphObject *QQuick3DParticleLineParticle::updateLineNode(QSSGRenderGraphObject *node)
260{
261 auto particles = static_cast<QSSGRenderParticles *>(node);
262
263 float frames = 1.0f;
264 if (sprite() && spriteSequence())
265 frames = float(spriteSequence()->frameCount());
266
267 particles->m_sizeModifier = m_scaleMultiplier;
268 particles->m_alphaFade = 1.0f - m_alphaFade;
269 if (m_texcoordMode == TexcoordMode::Fill)
270 particles->m_texcoordScale = frames * m_texcoordMultiplier;
271 else
272 particles->m_texcoordScale = frames / particleScale() * m_texcoordMultiplier;
273 particles->m_featureLevel = lineFeatureLevel(in: m_featureLevel);
274
275 return particles;
276}
277
278void QQuick3DParticleLineParticle::handleMaxAmountChanged(int amount)
279{
280 if (m_lineData.size() == amount)
281 return;
282
283 m_lineData.resize(size: m_segmentCount * amount);
284 m_lineHeaderData.resize(size: amount);
285 QQuick3DParticleSpriteParticle::handleMaxAmountChanged(amount);
286}
287
288void QQuick3DParticleLineParticle::handleSystemChanged(QQuick3DParticleSystem *system)
289{
290 for (PerEmitterData &value : m_perEmitterData) {
291 delete value.particleUpdateNode;
292 value.particleUpdateNode = new LineParticleUpdateNode(system);
293 value.particleUpdateNode->m_particle = this;
294 }
295}
296
297QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
298{
299 if (m_particle) {
300 QQuick3DParticleLineParticle *lineParticle = qobject_cast<QQuick3DParticleLineParticle *>(object: m_particle);
301 node = lineParticle->updateParticleNode(updateNode: this, node);
302 lineParticle->updateLineNode(node);
303 QQuick3DNode::updateSpatialNode(node);
304 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(lineParticle, node);
305 auto particles = static_cast<QSSGRenderParticles *>(node);
306
307 lineParticle->updateLineBuffer(updateNode: this, node: particles);
308
309 m_nodeDirty = false;
310 }
311 return node;
312}
313
314void QQuick3DParticleLineParticle ::reset()
315{
316 QQuick3DParticleSpriteParticle::reset();
317 m_lineData.fill(t: {});
318 m_lineHeaderData.fill(t: {});
319 m_fadeOutData.clear();
320}
321
322void QQuick3DParticleLineParticle::commitParticles(float time)
323{
324 QQuick3DParticleSpriteParticle::commitParticles(time);
325
326 for (auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
327 if (time >= iter->beginTime && time < iter->endTime)
328 iter++;
329 else
330 iter = m_fadeOutData.erase(pos: iter);
331 }
332}
333
334int QQuick3DParticleLineParticle::nextCurrentIndex(const QQuick3DParticleEmitter *emitter)
335{
336 if (!m_perEmitterData.contains(key: emitter)) {
337 m_perEmitterData.insert(key: emitter, value: PerEmitterData());
338 auto &perEmitter = m_perEmitterData[emitter];
339 perEmitter.particleUpdateNode = new LineParticleUpdateNode(system());
340 perEmitter.emitter = emitter;
341 perEmitter.particleUpdateNode->m_particle = this;
342 perEmitter.emitterIndex = m_nextEmitterIndex++;
343 }
344 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
345 clearSegment(particleIndex: index);
346 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
347 if (m_length > 0.0f)
348 m_lineHeaderData[index].length = qMax(a: 0.0f, b: m_length + m_lengthVariation * (system()->rand()->get(particleIndex: index) - 0.5f));
349 return index;
350}
351
352void QQuick3DParticleLineParticle::setParticleData(int particleIndex,
353 const QVector3D &position,
354 const QVector3D &rotation,
355 const QVector4D &color,
356 float size, float age,
357 float animationFrame)
358{
359 auto &dst = m_spriteParticleData[particleIndex];
360 bool update = size > 0.0f || dst.size > 0.0f;
361 QQuick3DParticleSpriteParticle::setParticleData(particleIndex, position, rotation, color, size, age, animationFrame);
362 if (update)
363 updateLineSegment(particleIndex);
364}
365
366void QQuick3DParticleLineParticle::resetParticleData(int particleIndex)
367{
368 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
369 if (header->pointCount) {
370 header->currentIndex = 0;
371 header->pointCount = 0;
372 }
373 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
374}
375
376void QQuick3DParticleLineParticle::saveLineSegment(int particleIndex, float time)
377{
378 if (m_eolFadeOutDuration > 0 && m_lineHeaderData[particleIndex].pointCount > 0) {
379 FadeOutLineData data;
380 data.endPoint = m_spriteParticleData[particleIndex];
381 data.beginTime = time;
382 data.endTime = time + m_eolFadeOutDuration * 0.001f;
383 data.timeFactor = 1000.0f / ((float)m_eolFadeOutDuration);
384 data.header = m_lineHeaderData[particleIndex];
385 data.lineData = m_lineData.mid(pos: particleIndex * m_segmentCount, len: m_segmentCount);
386 data.emitterIndex = m_spriteParticleData[particleIndex].emitterIndex;
387 m_fadeOutData.emplaceBack(args&: data);
388 clearSegment(particleIndex);
389 }
390}
391
392static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)
393{
394 float x = qDegreesToRadians(degrees: eulerRotation.x());
395 float y = qDegreesToRadians(degrees: eulerRotation.y());
396 if (qFuzzyIsNull(f: x) && qFuzzyIsNull(f: y))
397 return QVector3D(0, 0, -1);
398 float a = qCos(v: x);
399 float b = qSin(v: x);
400 float c = qCos(v: y);
401 float d = qSin(v: y);
402 return QVector3D(d, -b * c, a * c);
403}
404
405void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
406{
407 const auto &perEmitter = perEmitterData(updateNode);
408 QSSGRenderParticles *node = static_cast<QSSGRenderParticles *>(spatialNode);
409 if (!node)
410 return;
411
412 int lineCount = 0;
413 for (int i = 0; i < m_lineHeaderData.size(); i++) {
414 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
415 lineCount++;
416 }
417 int totalCount = lineCount;
418 if (m_perEmitterData.size() > 1) {
419 for (int i = 0; i < m_fadeOutData.size(); i++) {
420 if (m_fadeOutData[i].emitterIndex == perEmitter.emitterIndex)
421 totalCount++;
422 }
423 } else {
424 totalCount += m_fadeOutData.size();
425 }
426
427 if (node->m_particleBuffer.particleCount() != totalCount)
428 node->m_particleBuffer.resizeLine(particleCount: totalCount, segmentCount: m_segmentCount + 1);
429
430 if (!totalCount) return;
431
432 const int segments = m_segmentCount;
433 const int particlesPerSlice = node->m_particleBuffer.particlesPerSlice();
434 const int sliceStride = node->m_particleBuffer.sliceStride();
435 int sliceParticleIdx = 0;
436 int slice = 0;
437 char *dest = node->m_particleBuffer.pointer();
438 QSSGBounds3 bounds;
439
440 const LineDataHeader *header = m_lineHeaderData.constData();
441 const LineData *lineData = m_lineData.constData();
442 const SpriteParticleData *src = m_spriteParticleData.constData();
443
444 auto nextParticle = [](char *&buffer, int &slice, int &sliceParticleIdx, int particlesPerSlice, int sliceStride) -> QSSGLineParticle* {
445 QSSGLineParticle *ret = reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
446 sliceParticleIdx++;
447 if (sliceParticleIdx == particlesPerSlice) {
448 slice++;
449 buffer += sliceStride;
450 sliceParticleIdx = 0;
451 }
452 return ret;
453 };
454
455 auto genLine = [&](const SpriteParticleData &sdata, const LineDataHeader &header, const LineData *tdata,
456 QSSGBounds3 &bounds, int segments, float particleScale, float alpha,
457 char *&buffer, int &slice, int &sliceParticleIdx, int particlesPerSlice, int sliceStride, bool absolute, bool fill) {
458 QSSGLineParticle *particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
459 int idx = header.currentIndex;
460 particle->color = sdata.color;
461 particle->color.setW(sdata.color.w() * alpha);
462 QVector3D tangent = (tdata[idx].position - sdata.position).normalized();
463 QVector3D binormal = QVector3D::crossProduct(v1: qt_normalFromRotation(eulerRotation: sdata.rotation), v2: tangent);
464 particle->binormal = binormal;
465 particle->position = sdata.position;
466 particle->age = sdata.age;
467 particle->animationFrame = sdata.animationFrame;
468 particle->size = sdata.size * particleScale;
469 float partialLength = (tdata[idx].position - sdata.position).length();
470 float length0 = tdata[idx].length + partialLength;
471 particle->length = 0.0f;
472 float lineLength = header.length;
473 int lastIdx = (idx + 1 + segments - header.pointCount) % segments;
474 float lengthScale = -1.0f;
475
476 if (absolute) {
477 particle->length = length0;
478 length0 = 0;
479 }
480
481 if (fill) {
482 if (lineLength > 0.0f) {
483 lengthScale = -1.0f / lineLength;
484 } else {
485 float totalLength = tdata[idx].length - tdata[lastIdx].length;
486 if (header.pointCount < segments)
487 totalLength += partialLength;
488 if (!qFuzzyIsNull(f: totalLength))
489 lengthScale = -1.0f / totalLength;
490 }
491 }
492 bounds.include(v: sdata.position);
493
494 QSSGLineParticle *prevGood = particle;
495 int segmentIdx = 0;
496 int prevIdx = 0;
497 Q_ASSERT(header.pointCount <= m_segmentCount);
498
499 if (header.length >= 0.0f) {
500 float totalLength = 0;
501 float prevLength = tdata[idx].length + partialLength;
502 for (segmentIdx = 0; segmentIdx < header.pointCount && totalLength < header.length; segmentIdx++) {
503 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
504 particle->size = tdata[idx].size * particleScale;
505 if (particle->size > 0.0f) {
506 bounds.include(v: tdata[idx].position);
507 particle->color = tdata[idx].color;
508 particle->color.setW(tdata[idx].color.w() * alpha);
509 particle->binormal = tdata[idx].binormal;
510 particle->position = tdata[idx].position;
511 particle->animationFrame = sdata.animationFrame;
512 particle->age = sdata.age;
513 particle->length = (length0 - tdata[idx].length) * lengthScale;
514 float segmentLength = prevLength - tdata[idx].length;
515 prevLength = tdata[idx].length;
516 if (totalLength + segmentLength > header.length) {
517 float diff = totalLength + segmentLength - header.length;
518 particle->position -= tdata[idx].tangent * diff;
519 particle->length -= diff * lengthScale;
520 segmentLength -= diff;
521 }
522 totalLength += segmentLength;
523 prevGood = particle;
524 prevIdx = idx;
525 }
526 idx = idx ? (idx - 1) : (segments - 1);
527 }
528 } else {
529 for (segmentIdx = 0; segmentIdx < header.pointCount; segmentIdx++) {
530 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
531 particle->size = tdata[idx].size * particleScale;
532 if (particle->size > 0.0f) {
533 bounds.include(v: tdata[idx].position);
534 particle->color = tdata[idx].color;
535 particle->color.setW(tdata[idx].color.w() * alpha);
536 particle->binormal = tdata[idx].binormal;
537 particle->position = tdata[idx].position;
538 particle->animationFrame = sdata.animationFrame;
539 particle->age = sdata.age;
540 particle->length = (length0 - tdata[idx].length) * lengthScale;
541 prevGood = particle;
542 prevIdx = idx;
543 }
544 idx = idx ? (idx - 1) : (segments - 1);
545 }
546 }
547 for (;segmentIdx < segments; segmentIdx++) {
548 particle = nextParticle(buffer, slice, sliceParticleIdx, particlesPerSlice, sliceStride);
549 *particle = *prevGood;
550 particle->size = 0.0f;
551 particle->length = 0.0f;
552 idx = idx ? (idx - 1) : (segments - 1);
553 }
554 // Do only for full segment
555 if (prevGood == particle && header.length < 0.0f && segments > 1) {
556 prevGood->position -= tdata[prevIdx].tangent * partialLength;
557 if (!fill)
558 prevGood->length -= partialLength * lengthScale;
559 }
560 };
561
562 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
563 const bool fill = m_texcoordMode == TexcoordMode::Fill;
564 int i = 0;
565 while (i < lineCount) {
566 if (header->pointCount && header->emitterIndex == perEmitter.emitterIndex) {
567 genLine(*src, *header, lineData, bounds, segments, particleScale(), 1.0f, dest,
568 slice, sliceParticleIdx, particlesPerSlice, sliceStride, absolute, fill);
569 i++;
570 }
571 header++;
572 lineData += segments;
573 src++;
574 }
575
576 float time = system()->currentTime() * 0.001f;
577 for (const FadeOutLineData &fdata : m_fadeOutData) {
578 if (fdata.emitterIndex == perEmitter.emitterIndex) {
579 float factor = 1.0f - (time - fdata.beginTime) * fdata.timeFactor;
580 genLine(fdata.endPoint, fdata.header, fdata.lineData.data(), bounds, segments,
581 particleScale(), factor, dest, slice, sliceParticleIdx, particlesPerSlice,
582 sliceStride, absolute, fill);
583 }
584 }
585 node->m_particleBuffer.setBounds(bounds);
586}
587
588void QQuick3DParticleLineParticle::handleSegmentCountChanged()
589{
590 markNodesDirty();
591 m_lineData.resize(size: m_segmentCount * m_maxAmount);
592 m_lineData.fill(t: {});
593 m_lineHeaderData.resize(size: m_maxAmount);
594 m_lineHeaderData.fill(t: {});
595 m_fadeOutData.clear();
596 if (!m_spriteParticleData.isEmpty()) {
597 auto count = qMin(a: m_maxAmount, b: m_spriteParticleData.size());
598 for (int i = 0; i < count; i++)
599 m_lineHeaderData[i].emitterIndex = m_spriteParticleData[i].emitterIndex;
600 }
601}
602
603void QQuick3DParticleLineParticle::updateLineSegment(int particleIndex)
604{
605 if (m_lineData.isEmpty()) {
606 qWarning () << "Line particle updated before having been initialized";
607 return;
608 }
609 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
610 int idx = header->currentIndex;
611 LineData *cur = m_lineData.data() + particleIndex * m_segmentCount;
612 LineData *prev = header->pointCount ? cur + idx : nullptr;
613 const SpriteParticleData &src = m_spriteParticleData.at(i: particleIndex);
614
615 if (prev && m_segmentCount > 1) {
616 float length = (prev->position - src.position).length();
617 float minLength = m_lengthDeltaMin;
618 if (header->length >= 0.0f)
619 minLength = header->length / (m_segmentCount - 1);
620 if (length < minLength)
621 return;
622 }
623
624 if (header->pointCount < m_segmentCount)
625 header->pointCount++;
626
627 if (prev)
628 idx = (idx + 1) % m_segmentCount;
629 header->currentIndex = idx;
630 cur = cur + idx;
631
632 cur->color = src.color;
633 cur->size = src.size;
634 cur->normal = qt_normalFromRotation(eulerRotation: src.rotation);
635
636 if (prev && m_segmentCount == 1) {
637 QVector3D t = prev->position - src.position;
638 float l = t.length();
639 t.normalize();
640 float minLength = m_lengthDeltaMin;
641 if (header->length >= 0.0f)
642 minLength = header->length;
643 cur->position = src.position + minLength * t;
644 cur->length += l;
645 cur->tangent = t;
646 cur->binormal = QVector3D::crossProduct(v1: cur->normal, v2: t);
647 } else {
648 cur->position = src.position;
649 cur->length = 0.0f;
650 }
651
652 if (prev && prev != cur) {
653 prev->tangent = prev->position - src.position;
654 cur->length = prev->tangent.length();
655 prev->tangent /= cur->length;
656 cur->length += prev->length;
657 cur->binormal = QVector3D::crossProduct(v1: cur->normal, v2: prev->tangent);
658 if (header->pointCount == 1)
659 prev->binormal = cur->binormal;
660 else
661 prev->binormal = (prev->binormal + cur->binormal).normalized();
662 }
663}
664
665void QQuick3DParticleLineParticle::clearSegment(int particleIndex)
666{
667 if (m_lineData.isEmpty())
668 return;
669 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
670 if (header->pointCount) {
671 auto data = m_lineData.begin() + particleIndex * m_segmentCount;
672 std::fill_n(first: data, n: m_segmentCount, value: LineData());
673 }
674 header->emitterIndex = -1;
675 header->currentIndex = 0;
676 header->pointCount = 0;
677 header->length = -1.0f;
678}
679
680QT_END_NAMESPACE
681

source code of qtquick3d/src/quick3dparticles/qquick3dparticlelineparticle.cpp