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}
301
302QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
303{
304 if (m_particle) {
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);
311
312 lineParticle->updateLineBuffer(this, particles);
313
314 m_nodeDirty = false;
315 }
316 return node;
317}
318
319void QQuick3DParticleLineParticle ::reset()
320{
321 QQuick3DParticleSpriteParticle::reset();
322 m_lineData.fill({});
323 m_lineHeaderData.fill({});
324 m_fadeOutData.clear();
325}
326
327void QQuick3DParticleLineParticle::commitParticles(float time)
328{
329 QQuick3DParticleSpriteParticle::commitParticles(time);
330
331 for (auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
332 if (time >= iter->beginTime && time < iter->endTime)
333 iter++;
334 else
335 iter = m_fadeOutData.erase(iter);
336 }
337}
338
339int QQuick3DParticleLineParticle::nextCurrentIndex(const QQuick3DParticleEmitter *emitter)
340{
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++;
348 }
349 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
350 clearSegment(index);
351 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
352 if (m_length > 0.0f)
353 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
354 return index;
355}
356
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)
363{
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);
367 if (update)
368 updateLineSegment(particleIndex);
369}
370
371void QQuick3DParticleLineParticle::resetParticleData(int particleIndex)
372{
373 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
374 if (header->pointCount) {
375 header->currentIndex = 0;
376 header->pointCount = 0;
377 }
378 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
379}
380
381void QQuick3DParticleLineParticle::saveLineSegment(int particleIndex, float time)
382{
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);
394 }
395}
396
397static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)
398{
399 float x = qDegreesToRadians(eulerRotation.x());
400 float y = qDegreesToRadians(eulerRotation.y());
401 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
402 return QVector3D(0, 0, -1);
403 float a = qCos(x);
404 float b = qSin(x);
405 float c = qCos(y);
406 float d = qSin(y);
407 return QVector3D(d, -b * c, a * c);
408}
409
410void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
411{
412 const auto &perEmitter = perEmitterData(updateNode);
413 QSSGRenderParticles *node = static_cast<QSSGRenderParticles *>(spatialNode);
414 if (!node)
415 return;
416
417 int lineCount = 0;
418 for (int i = 0; i < m_lineHeaderData.size(); i++) {
419 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
420 lineCount++;
421 }
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)
426 totalCount++;
427 }
428 } else {
429 totalCount += m_fadeOutData.size();
430 }
431
432 if (node->m_particleBuffer.particleCount() != totalCount)
433 node->m_particleBuffer.resizeLine(totalCount, m_segmentCount + 1);
434
435 if (!totalCount) return;
436
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;
441 int slice = 0;
442 char *dest = node->m_particleBuffer.pointer();
443 QSSGBounds3 bounds;
444
445 const LineDataHeader *header = m_lineHeaderData.constData();
446 const LineData *lineData = m_lineData.constData();
447 const SpriteParticleData *src = m_spriteParticleData.constData();
448
449 auto nextParticle = [](char *&buffer, int &slice, int &sliceParticleIdx, int particlesPerSlice, int sliceStride) -> QSSGLineParticle* {
450 QSSGLineParticle *ret = reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
451 sliceParticleIdx++;
452 if (sliceParticleIdx == particlesPerSlice) {
453 slice++;
454 buffer += sliceStride;
455 sliceParticleIdx = 0;
456 }
457 return ret;
458 };
459
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;
480
481 if (absolute) {
482 particle->length = length0;
483 length0 = 0;
484 }
485
486 if (fill) {
487 if (lineLength > 0.0f) {
488 lengthScale = -1.0f / lineLength;
489 } else {
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;
495 }
496 }
497 bounds.include(sdata.position);
498
499 QSSGLineParticle *prevGood = particle;
500 int segmentIdx = 0;
501 int prevIdx = 0;
502 Q_ASSERT(header.pointCount <= m_segmentCount);
503
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;
526 }
527 totalLength += segmentLength;
528 prevGood = particle;
529 prevIdx = idx;
530 }
531 idx = idx ? (idx - 1) : (segments - 1);
532 }
533 } else {
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;
546 prevGood = particle;
547 prevIdx = idx;
548 }
549 idx = idx ? (idx - 1) : (segments - 1);
550 }
551 }
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);
558 }
559 // Do only for full segment
560 if (prevGood == particle && header.length < 0.0f && segments > 1) {
561 prevGood->position -= tdata[prevIdx].tangent * partialLength;
562 if (!fill)
563 prevGood->length -= partialLength * lengthScale;
564 }
565 };
566
567 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
568 const bool fill = m_texcoordMode == TexcoordMode::Fill;
569 int i = 0;
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);
574 i++;
575 }
576 header++;
577 lineData += segments;
578 src++;
579 }
580
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);
588 }
589 }
590 node->m_particleBuffer.setBounds(bounds);
591}
592
593void QQuick3DParticleLineParticle::handleSegmentCountChanged()
594{
595 markNodesDirty();
596 m_lineData.resize(m_segmentCount * m_maxAmount);
597 m_lineData.fill({});
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;
605 }
606}
607
608void QQuick3DParticleLineParticle::updateLineSegment(int particleIndex)
609{
610 if (m_lineData.isEmpty()) {
611 qWarning () << "Line particle updated before having been initialized";
612 return;
613 }
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);
619
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)
626 return;
627 }
628
629 if (header->pointCount < m_segmentCount)
630 header->pointCount++;
631
632 if (prev)
633 idx = (idx + 1) % m_segmentCount;
634 header->currentIndex = idx;
635 cur = cur + idx;
636
637 cur->color = src.color;
638 cur->size = src.size;
639 cur->normal = qt_normalFromRotation(src.rotation);
640
641 if (prev && m_segmentCount == 1) {
642 QVector3D t = prev->position - src.position;
643 float l = t.length();
644 t.normalize();
645 float minLength = m_lengthDeltaMin;
646 if (header->length >= 0.0f)
647 minLength = header->length;
648 cur->position = src.position + minLength * t;
649 cur->length += l;
650 cur->tangent = t;
651 cur->binormal = QVector3D::crossProduct(cur->normal, t);
652 } else {
653 cur->position = src.position;
654 cur->length = 0.0f;
655 }
656
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;
665 else
666 prev->binormal = (prev->binormal + cur->binormal).normalized();
667 }
668}
669
670void QQuick3DParticleLineParticle::clearSegment(int particleIndex)
671{
672 if (m_lineData.isEmpty())
673 return;
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());
678 }
679 header->emitterIndex = -1;
680 header->currentIndex = 0;
681 header->pointCount = 0;
682 header->length = -1.0f;
683}
684
685QT_END_NAMESPACE
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)