Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dparticlelineparticle.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
10#include <private/qquick3dobject_p.h>
11
12#include <algorithm>
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \qmltype LineParticle3D
18 \inherits SpriteParticle3D
19 \inqmlmodule QtQuick3D.Particles3D
20 \brief Line particle.
21 \since 6.4
22
23 The LineParticle3D creates line shaped sprite particles.
24
25 The line is created from the path of the particle when it moves.
26 The length of the line is specified either by the \l length parameter
27 or the segment count and minimum delta between points. In latter case the
28 length of the line may vary if the particles speed varies.
29*/
30
31QQuick3DParticleLineParticle::QQuick3DParticleLineParticle(QQuick3DNode *parent)
32 : QQuick3DParticleSpriteParticle(parent)
33{
34}
35
36QQuick3DParticleLineParticle::~QQuick3DParticleLineParticle()
37{
38}
39
40/*!
41 \qmlproperty int LineParticle3D::segmentCount
42
43 This property holds the number of segments in the line. The line is drawn using
44 segment + 1 points, where the additional one comes from the particles current position.
45 The default value is 1.
46*/
47int QQuick3DParticleLineParticle::segmentCount() const
48{
49 return m_segmentCount;
50}
51
52/*!
53 \qmlproperty real LineParticle3D::alphaFade
54
55 This property holds the alpha fade factor of the line. The alphaFade value range is [0, 1].
56 When the value is greater than 0.0, causes the line to fade the further the segment is from the
57 first particle segment. The alpha for a segment is calculated like this:
58 segmentAlpha(s) = (1.0 - alphaFade) ^ s, where s is the segment index.
59 The default value is 0.0.
60*/
61float QQuick3DParticleLineParticle::alphaFade() const
62{
63 return m_alphaFade;
64}
65
66/*!
67 \qmlproperty real LineParticle3D::scaleMultiplier
68
69 This property holds the scale multiplier of the line. The scaleMultiplier value range is [0, 2].
70 The scaleMultiplier modifies the line size for the line segments. If the value is less than 1.0,
71 the line gets smaller the further a segment is from the first segment and if the value is greater
72 than 1.0 the line gets bigger. The size for a segment is calculated like this:
73 size(s) = scaleMultiplier ^ s, where s is the segment index.
74*/
75float QQuick3DParticleLineParticle::scaleMultiplier() const
76{
77 return m_scaleMultiplier;
78}
79
80/*!
81 \qmlproperty real LineParticle3D::texcoordMultiplier
82
83 This property holds the texture coordinate multiplier of the line. This value is factored
84 to the texture coordinate values of the line. The default value is 1.0.
85*/
86float QQuick3DParticleLineParticle::texcoordMultiplier() const
87{
88 return m_texcoordMultiplier;
89}
90
91/*!
92 \qmlproperty real LineParticle3D::length
93
94 This property holds the length of the line. If the value is set, the lines length is limited
95 to the value. In this case the minimum delta of the line is the length divided
96 by the segment count. If the value is not set, the line length varies based on the
97 speed the particle moves as well as segment count and minimum delta. The default value is -1.0.
98*/
99float QQuick3DParticleLineParticle::length() const
100{
101 return m_length;
102}
103
104/*!
105 \qmlproperty real LineParticle3D::lengthVariation
106
107 This property holds the length variation applied to each line. Variation is applied
108 only if the \l length property is also set. The resulting line length clamps to positive
109 values.
110
111 The default value is 0.0.
112*/
113
114float QQuick3DParticleLineParticle::lengthVariation() const
115{
116 return m_lengthVariation;
117}
118
119/*!
120 \qmlproperty real LineParticle3D::lengthDeltaMin
121
122 This property holds the minimum length between segment points. This parameter is
123 ignored if the length parameter is set. The default value is 10.0.
124*/
125float QQuick3DParticleLineParticle::lengthDeltaMin() const
126{
127 return m_lengthDeltaMin;
128}
129
130/*!
131 \qmlproperty int LineParticle3D::eolFadeOutDuration
132
133 This property holds the end-of-life fade-out duration of the line. If set, each line remains
134 in the place it was when the particle reached end of its lifetime, then fades out during this
135 time period. The default value is 0.
136*/
137int QQuick3DParticleLineParticle::eolFadeOutDuration() const
138{
139 return m_eolFadeOutDuration;
140}
141
142/*!
143 \qmlproperty enumeration LineParticle3D::TexcoordMode
144
145 Defines the texture coordinate mode of line particle.
146
147 \value LineParticle3D.Absolute
148 Texture coordinates are specified relative to the world position.
149 \value LineParticle3D.Relative
150 Texture coordinates are specified relative to line first line point.
151 \value LineParticle3D.Fill
152 Texture coordinates are specified such that the texture fills the whole line.
153*/
154
155/*!
156 \qmlproperty TexcoordMode LineParticle3D::texcoordMode
157
158 This property holds the texture coordinate mode of the line.
159*/
160QQuick3DParticleLineParticle::TexcoordMode QQuick3DParticleLineParticle::texcoordMode() const
161{
162 return m_texcoordMode;
163}
164
165void QQuick3DParticleLineParticle::setSegmentCount(int count)
166{
167 count = qMax(1, count);
168 if (m_segmentCount == count)
169 return;
170 m_segmentCount = count;
171 handleSegmentCountChanged();
172 Q_EMIT segmentCountChanged();
173}
174
175void QQuick3DParticleLineParticle::setAlphaFade(float fade)
176{
177 fade = qBound(0.0f, fade, 1.0f);
178 if (qFuzzyCompare(m_alphaFade, fade))
179 return;
180 m_alphaFade = fade;
181 Q_EMIT alphaFadeChanged();
182}
183
184void QQuick3DParticleLineParticle::setScaleMultiplier(float multiplier)
185{
186 multiplier = qBound(0.0f, multiplier, 2.0f);
187 if (qFuzzyCompare(m_scaleMultiplier, multiplier))
188 return;
189 m_scaleMultiplier = multiplier;
190 Q_EMIT scaleMultiplierChanged();
191}
192
193void QQuick3DParticleLineParticle::setTexcoordMultiplier(float multiplier)
194{
195 if (qFuzzyCompare(m_texcoordMultiplier, multiplier))
196 return;
197 m_texcoordMultiplier = multiplier;
198 Q_EMIT texcoordMultiplierChanged();
199}
200
201void QQuick3DParticleLineParticle::setLength(float length)
202{
203 length = length != -1.0f ? qMax(length, 0.0f) : -1.0f;
204 if (qFuzzyCompare(m_length, length))
205 return;
206 m_length = length;
207 Q_EMIT lengthChanged();
208}
209
210void QQuick3DParticleLineParticle::setLengthVariation(float lengthVariation)
211{
212 lengthVariation = qMax(lengthVariation, 0.0f);
213 if (qFuzzyCompare(m_lengthVariation, lengthVariation))
214 return;
215 m_lengthVariation = lengthVariation;
216 Q_EMIT lengthVariationChanged();
217}
218
219void QQuick3DParticleLineParticle::setLengthDeltaMin(float min)
220{
221 min = qMax(min, 0.0f);
222 if (qFuzzyCompare(m_lengthDeltaMin, min))
223 return;
224 m_lengthDeltaMin = min;
225 Q_EMIT lengthDeltaMinChanged();
226}
227
228void QQuick3DParticleLineParticle::setEolFadeOutDuration(int duration)
229{
230 duration = qMax(0, duration);
231 if (duration == m_eolFadeOutDuration)
232 return;
233 m_eolFadeOutDuration = duration;
234 Q_EMIT eolFadeOutDurationChanged();
235}
236
237void QQuick3DParticleLineParticle::setTexcoordMode(TexcoordMode mode)
238{
239 if (mode == m_texcoordMode)
240 return;
241 m_texcoordMode = mode;
242 Q_EMIT texcoordModeChanged();
243}
244
245QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
246{
247 switch (in) {
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;
260 }
261 return QSSGRenderParticles::FeatureLevel::Line;
262}
263
264QSSGRenderGraphObject *QQuick3DParticleLineParticle::updateLineNode(QSSGRenderGraphObject *node)
265{
266 auto particles = static_cast<QSSGRenderParticles *>(node);
267
268 float frames = 1.0f;
269 if (sprite() && spriteSequence())
270 frames = float(spriteSequence()->frameCount());
271
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;
276 else
277 particles->m_texcoordScale = frames / particleScale() * m_texcoordMultiplier;
278 particles->m_featureLevel = lineFeatureLevel(m_featureLevel);
279
280 return particles;
281}
282
283void QQuick3DParticleLineParticle::handleMaxAmountChanged(int amount)
284{
285 if (m_lineData.size() == amount)
286 return;
287
288 m_lineData.resize(m_segmentCount * amount);
289 m_lineHeaderData.resize(amount);
290 QQuick3DParticleSpriteParticle::handleMaxAmountChanged(amount);
291}
292
293void QQuick3DParticleLineParticle::handleSystemChanged(QQuick3DParticleSystem *system)
294{
295 for (PerEmitterData &value : m_perEmitterData) {
296 delete value.particleUpdateNode;
297 value.particleUpdateNode = new LineParticleUpdateNode(system);
298 value.particleUpdateNode->m_particle = this;
299 }
300 updateNodeLayers();
301}
302
303QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
304{
305 if (m_particle) {
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);
312
313 lineParticle->updateLineBuffer(this, particles);
314
315 m_nodeDirty = false;
316 }
317 return node;
318}
319
320void QQuick3DParticleLineParticle ::reset()
321{
322 QQuick3DParticleSpriteParticle::reset();
323 m_lineData.fill({});
324 m_lineHeaderData.fill({});
325 m_fadeOutData.clear();
326}
327
328void QQuick3DParticleLineParticle::commitParticles(float time)
329{
330 QQuick3DParticleSpriteParticle::commitParticles(time);
331
332 for (auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
333 if (time >= iter->beginTime && time < iter->endTime)
334 iter++;
335 else
336 iter = m_fadeOutData.erase(iter);
337 }
338}
339
340int QQuick3DParticleLineParticle::nextCurrentIndex(const QQuick3DParticleEmitter *emitter)
341{
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++;
349 updateNodeLayers();
350 }
351 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
352 clearSegment(index);
353 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
354 if (m_length > 0.0f)
355 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
356 return index;
357}
358
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)
365{
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);
369 if (update)
370 updateLineSegment(particleIndex);
371}
372
373void QQuick3DParticleLineParticle::resetParticleData(int particleIndex)
374{
375 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
376 if (header->pointCount) {
377 header->currentIndex = 0;
378 header->pointCount = 0;
379 }
380 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
381}
382
383void QQuick3DParticleLineParticle::saveLineSegment(int particleIndex, float time)
384{
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);
396 }
397}
398
399static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)
400{
401 float x = qDegreesToRadians(eulerRotation.x());
402 float y = qDegreesToRadians(eulerRotation.y());
403 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
404 return QVector3D(0, 0, -1);
405 float a = qCos(x);
406 float b = qSin(x);
407 float c = qCos(y);
408 float d = qSin(y);
409 return QVector3D(d, -b * c, a * c);
410}
411
412void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
413{
414 const auto &perEmitter = perEmitterData(updateNode);
415 QSSGRenderParticles *node = static_cast<QSSGRenderParticles *>(spatialNode);
416 if (!node)
417 return;
418
419 int lineCount = 0;
420 for (int i = 0; i < m_lineHeaderData.size(); i++) {
421 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
422 lineCount++;
423 }
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)
428 totalCount++;
429 }
430 } else {
431 totalCount += m_fadeOutData.size();
432 }
433
434 if (node->m_particleBuffer.particleCount() != totalCount)
435 node->m_particleBuffer.resizeLine(totalCount, m_segmentCount + 1);
436
437 if (!totalCount) return;
438
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;
443 int slice = 0;
444 char *dest = node->m_particleBuffer.pointer();
445 QSSGBounds3 bounds;
446
447 const LineDataHeader *header = m_lineHeaderData.constData();
448 const LineData *lineData = m_lineData.constData();
449 const SpriteParticleData *src = m_spriteParticleData.constData();
450
451 auto nextParticle = [](char *&buffer, int &slice, int &sliceParticleIdx, int particlesPerSlice, int sliceStride) -> QSSGLineParticle* {
452 QSSGLineParticle *ret = reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
453 sliceParticleIdx++;
454 if (sliceParticleIdx == particlesPerSlice) {
455 slice++;
456 buffer += sliceStride;
457 sliceParticleIdx = 0;
458 }
459 return ret;
460 };
461
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;
482
483 if (absolute) {
484 particle->length = length0;
485 length0 = 0;
486 }
487
488 if (fill) {
489 if (lineLength > 0.0f) {
490 lengthScale = -1.0f / lineLength;
491 } else {
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;
497 }
498 }
499 bounds.include(sdata.position);
500
501 QSSGLineParticle *prevGood = particle;
502 int segmentIdx = 0;
503 int prevIdx = 0;
504 Q_ASSERT(header.pointCount <= m_segmentCount);
505
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;
528 }
529 totalLength += segmentLength;
530 prevGood = particle;
531 prevIdx = idx;
532 }
533 idx = idx ? (idx - 1) : (segments - 1);
534 }
535 } else {
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;
548 prevGood = particle;
549 prevIdx = idx;
550 }
551 idx = idx ? (idx - 1) : (segments - 1);
552 }
553 }
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);
560 }
561 // Do only for full segment
562 if (prevGood == particle && header.length < 0.0f && segments > 1) {
563 prevGood->position -= tdata[prevIdx].tangent * partialLength;
564 if (!fill)
565 prevGood->length -= partialLength * lengthScale;
566 }
567 };
568
569 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
570 const bool fill = m_texcoordMode == TexcoordMode::Fill;
571 int i = 0;
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);
576 i++;
577 }
578 header++;
579 lineData += segments;
580 src++;
581 }
582
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);
590 }
591 }
592 node->m_particleBuffer.setBounds(bounds);
593}
594
595void QQuick3DParticleLineParticle::handleSegmentCountChanged()
596{
597 markNodesDirty();
598 m_lineData.resize(m_segmentCount * m_maxAmount);
599 m_lineData.fill({});
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;
607 }
608}
609
610void QQuick3DParticleLineParticle::updateLineSegment(int particleIndex)
611{
612 if (m_lineData.isEmpty()) {
613 qWarning () << "Line particle updated before having been initialized";
614 return;
615 }
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);
621
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)
628 return;
629 }
630
631 if (header->pointCount < m_segmentCount)
632 header->pointCount++;
633
634 if (prev)
635 idx = (idx + 1) % m_segmentCount;
636 header->currentIndex = idx;
637 cur = cur + idx;
638
639 cur->color = src.color;
640 cur->size = src.size;
641 cur->normal = qt_normalFromRotation(src.rotation);
642
643 if (prev && m_segmentCount == 1) {
644 QVector3D t = prev->position - src.position;
645 float l = t.length();
646 t.normalize();
647 float minLength = m_lengthDeltaMin;
648 if (header->length >= 0.0f)
649 minLength = header->length;
650 cur->position = src.position + minLength * t;
651 cur->length += l;
652 cur->tangent = t;
653 cur->binormal = QVector3D::crossProduct(cur->normal, t);
654 } else {
655 cur->position = src.position;
656 cur->length = 0.0f;
657 }
658
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;
667 else
668 prev->binormal = (prev->binormal + cur->binormal).normalized();
669 }
670}
671
672void QQuick3DParticleLineParticle::clearSegment(int particleIndex)
673{
674 if (m_lineData.isEmpty())
675 return;
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());
680 }
681 header->emitterIndex = -1;
682 header->currentIndex = 0;
683 header->pointCount = 0;
684 header->length = -1.0f;
685}
686
687QT_END_NAMESPACE
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)