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
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 applied to each line. Variation is applied
106 only if the \l length property is also set. The resulting line length clamps to positive
107 values.
108
109 The default value is 0.0.
110*/
111
112float QQuick3DParticleLineParticle::lengthVariation() const
113{
114 return m_lengthVariation;
115}
116
117/*!
118 \qmlproperty real LineParticle3D::lengthDeltaMin
119
120 This property holds the minimum length between segment points. This parameter is
121 ignored if the length parameter is set. The default value is 10.0.
122*/
123float QQuick3DParticleLineParticle::lengthDeltaMin() const
124{
125 return m_lengthDeltaMin;
126}
127
128/*!
129 \qmlproperty int LineParticle3D::eolFadeOutDuration
130
131 This property holds the end-of-life fade-out duration of the line. If set, each line remains
132 in the place it was when the particle reached end of its lifetime, then fades out during this
133 time period. The default value is 0.
134*/
135int QQuick3DParticleLineParticle::eolFadeOutDuration() const
136{
137 return m_eolFadeOutDuration;
138}
139
140/*!
141 \qmlproperty enumeration LineParticle3D::TexcoordMode
142
143 Defines the texture coordinate mode of line particle.
144
145 \value LineParticle3D.Absolute
146 Texture coordinates are specified relative to the world position.
147 \value LineParticle3D.Relative
148 Texture coordinates are specified relative to line first line point.
149 \value LineParticle3D.Fill
150 Texture coordinates are specified such that the texture fills the whole line.
151*/
152
153/*!
154 \qmlproperty TexcoordMode LineParticle3D::texcoordMode
155
156 This property holds the texture coordinate mode of the line.
157*/
158QQuick3DParticleLineParticle::TexcoordMode QQuick3DParticleLineParticle::texcoordMode() const
159{
160 return m_texcoordMode;
161}
162
163void QQuick3DParticleLineParticle::setSegmentCount(int count)
164{
165 count = qMax(1, count);
166 if (m_segmentCount == count)
167 return;
168 m_segmentCount = count;
169 handleSegmentCountChanged();
170 Q_EMIT segmentCountChanged();
171}
172
173void QQuick3DParticleLineParticle::setAlphaFade(float fade)
174{
175 fade = qBound(0.0f, fade, 1.0f);
176 if (qFuzzyCompare(m_alphaFade, fade))
177 return;
178 m_alphaFade = fade;
179 Q_EMIT alphaFadeChanged();
180}
181
182void QQuick3DParticleLineParticle::setScaleMultiplier(float multiplier)
183{
184 multiplier = qBound(0.0f, multiplier, 2.0f);
185 if (qFuzzyCompare(m_scaleMultiplier, multiplier))
186 return;
187 m_scaleMultiplier = multiplier;
188 Q_EMIT scaleMultiplierChanged();
189}
190
191void QQuick3DParticleLineParticle::setTexcoordMultiplier(float multiplier)
192{
193 if (qFuzzyCompare(m_texcoordMultiplier, multiplier))
194 return;
195 m_texcoordMultiplier = multiplier;
196 Q_EMIT texcoordMultiplierChanged();
197}
198
199void QQuick3DParticleLineParticle::setLength(float length)
200{
201 length = length != -1.0f ? qMax(length, 0.0f) : -1.0f;
202 if (qFuzzyCompare(m_length, length))
203 return;
204 m_length = length;
205 Q_EMIT lengthChanged();
206}
207
208void QQuick3DParticleLineParticle::setLengthVariation(float lengthVariation)
209{
210 lengthVariation = qMax(lengthVariation, 0.0f);
211 if (qFuzzyCompare(m_lengthVariation, lengthVariation))
212 return;
213 m_lengthVariation = lengthVariation;
214 Q_EMIT lengthVariationChanged();
215}
216
217void QQuick3DParticleLineParticle::setLengthDeltaMin(float min)
218{
219 min = qMax(min, 0.0f);
220 if (qFuzzyCompare(m_lengthDeltaMin, min))
221 return;
222 m_lengthDeltaMin = min;
223 Q_EMIT lengthDeltaMinChanged();
224}
225
226void QQuick3DParticleLineParticle::setEolFadeOutDuration(int duration)
227{
228 duration = qMax(0, duration);
229 if (duration == m_eolFadeOutDuration)
230 return;
231 m_eolFadeOutDuration = duration;
232 Q_EMIT eolFadeOutDurationChanged();
233}
234
235void QQuick3DParticleLineParticle::setTexcoordMode(TexcoordMode mode)
236{
237 if (mode == m_texcoordMode)
238 return;
239 m_texcoordMode = mode;
240 Q_EMIT texcoordModeChanged();
241}
242
243QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
244{
245 switch (in) {
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;
258 }
259 return QSSGRenderParticles::FeatureLevel::Line;
260}
261
262QSSGRenderGraphObject *QQuick3DParticleLineParticle::updateLineNode(QSSGRenderGraphObject *node)
263{
264 auto particles = static_cast<QSSGRenderParticles *>(node);
265
266 float frames = 1.0f;
267 if (sprite() && spriteSequence())
268 frames = float(spriteSequence()->frameCount());
269
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;
274 else
275 particles->m_texcoordScale = frames / particleScale() * m_texcoordMultiplier;
276 particles->m_featureLevel = lineFeatureLevel(m_featureLevel);
277
278 return particles;
279}
280
281void QQuick3DParticleLineParticle::handleMaxAmountChanged(int amount)
282{
283 if (m_lineData.size() == amount)
284 return;
285
286 m_lineData.resize(m_segmentCount * amount);
287 m_lineHeaderData.resize(amount);
288 QQuick3DParticleSpriteParticle::handleMaxAmountChanged(amount);
289}
290
291void QQuick3DParticleLineParticle::handleSystemChanged(QQuick3DParticleSystem *system)
292{
293 for (PerEmitterData &value : m_perEmitterData) {
294 delete value.particleUpdateNode;
295 value.particleUpdateNode = new LineParticleUpdateNode(system);
296 value.particleUpdateNode->m_particle = this;
297 }
298}
299
300QSSGRenderGraphObject *QQuick3DParticleLineParticle::LineParticleUpdateNode::updateSpatialNode(QSSGRenderGraphObject *node)
301{
302 if (m_particle) {
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);
309
310 lineParticle->updateLineBuffer(this, particles);
311
312 m_nodeDirty = false;
313 }
314 return node;
315}
316
317void QQuick3DParticleLineParticle ::reset()
318{
319 QQuick3DParticleSpriteParticle::reset();
320 m_lineData.fill({});
321 m_lineHeaderData.fill({});
322 m_fadeOutData.clear();
323}
324
325void QQuick3DParticleLineParticle::commitParticles(float time)
326{
327 QQuick3DParticleSpriteParticle::commitParticles(time);
328
329 for (auto iter = m_fadeOutData.begin(); iter != m_fadeOutData.end(); ) {
330 if (time >= iter->beginTime && time < iter->endTime)
331 iter++;
332 else
333 iter = m_fadeOutData.erase(iter);
334 }
335}
336
337int QQuick3DParticleLineParticle::nextCurrentIndex(const QQuick3DParticleEmitter *emitter)
338{
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++;
346 }
347 int index = QQuick3DParticleSpriteParticle::nextCurrentIndex(emitter);
348 clearSegment(index);
349 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
350 if (m_length > 0.0f)
351 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
352 return index;
353}
354
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)
361{
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);
365 if (update)
366 updateLineSegment(particleIndex);
367}
368
369void QQuick3DParticleLineParticle::resetParticleData(int particleIndex)
370{
371 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
372 if (header->pointCount) {
373 header->currentIndex = 0;
374 header->pointCount = 0;
375 }
376 QQuick3DParticleSpriteParticle::resetParticleData(particleIndex);
377}
378
379void QQuick3DParticleLineParticle::saveLineSegment(int particleIndex, float time)
380{
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);
392 }
393}
394
395static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)
396{
397 float x = qDegreesToRadians(eulerRotation.x());
398 float y = qDegreesToRadians(eulerRotation.y());
399 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
400 return QVector3D(0, 0, -1);
401 float a = qCos(x);
402 float b = qSin(x);
403 float c = qCos(y);
404 float d = qSin(y);
405 return QVector3D(d, -b * c, a * c);
406}
407
408void QQuick3DParticleLineParticle::updateLineBuffer(LineParticleUpdateNode *updateNode, QSSGRenderGraphObject *spatialNode)
409{
410 const auto &perEmitter = perEmitterData(updateNode);
411 QSSGRenderParticles *node = static_cast<QSSGRenderParticles *>(spatialNode);
412 if (!node)
413 return;
414
415 int lineCount = 0;
416 for (int i = 0; i < m_lineHeaderData.size(); i++) {
417 if (m_lineHeaderData[i].pointCount && m_lineHeaderData[i].emitterIndex == perEmitter.emitterIndex)
418 lineCount++;
419 }
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)
424 totalCount++;
425 }
426 } else {
427 totalCount += m_fadeOutData.size();
428 }
429
430 if (node->m_particleBuffer.particleCount() != totalCount)
431 node->m_particleBuffer.resizeLine(totalCount, m_segmentCount + 1);
432
433 if (!totalCount) return;
434
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;
439 int slice = 0;
440 char *dest = node->m_particleBuffer.pointer();
441 QSSGBounds3 bounds;
442
443 const LineDataHeader *header = m_lineHeaderData.constData();
444 const LineData *lineData = m_lineData.constData();
445 const SpriteParticleData *src = m_spriteParticleData.constData();
446
447 auto nextParticle = [](char *&buffer, int &slice, int &sliceParticleIdx, int particlesPerSlice, int sliceStride) -> QSSGLineParticle* {
448 QSSGLineParticle *ret = reinterpret_cast<QSSGLineParticle *>(buffer) + sliceParticleIdx;
449 sliceParticleIdx++;
450 if (sliceParticleIdx == particlesPerSlice) {
451 slice++;
452 buffer += sliceStride;
453 sliceParticleIdx = 0;
454 }
455 return ret;
456 };
457
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;
478
479 if (absolute) {
480 particle->length = length0;
481 length0 = 0;
482 }
483
484 if (fill) {
485 if (lineLength > 0.0f) {
486 lengthScale = -1.0f / lineLength;
487 } else {
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;
493 }
494 }
495 bounds.include(sdata.position);
496
497 QSSGLineParticle *prevGood = particle;
498 int segmentIdx = 0;
499 int prevIdx = 0;
500 Q_ASSERT(header.pointCount <= m_segmentCount);
501
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;
524 }
525 totalLength += segmentLength;
526 prevGood = particle;
527 prevIdx = idx;
528 }
529 idx = idx ? (idx - 1) : (segments - 1);
530 }
531 } else {
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;
544 prevGood = particle;
545 prevIdx = idx;
546 }
547 idx = idx ? (idx - 1) : (segments - 1);
548 }
549 }
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);
556 }
557 // Do only for full segment
558 if (prevGood == particle && header.length < 0.0f && segments > 1) {
559 prevGood->position -= tdata[prevIdx].tangent * partialLength;
560 if (!fill)
561 prevGood->length -= partialLength * lengthScale;
562 }
563 };
564
565 const bool absolute = m_texcoordMode == TexcoordMode::Absolute;
566 const bool fill = m_texcoordMode == TexcoordMode::Fill;
567 int i = 0;
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);
572 i++;
573 }
574 header++;
575 lineData += segments;
576 src++;
577 }
578
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);
586 }
587 }
588 node->m_particleBuffer.setBounds(bounds);
589}
590
591void QQuick3DParticleLineParticle::handleSegmentCountChanged()
592{
593 markNodesDirty();
594 m_lineData.resize(m_segmentCount * m_maxAmount);
595 m_lineData.fill({});
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;
603 }
604}
605
606void QQuick3DParticleLineParticle::updateLineSegment(int particleIndex)
607{
608 if (m_lineData.isEmpty()) {
609 qWarning () << "Line particle updated before having been initialized";
610 return;
611 }
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);
617
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)
624 return;
625 }
626
627 if (header->pointCount < m_segmentCount)
628 header->pointCount++;
629
630 if (prev)
631 idx = (idx + 1) % m_segmentCount;
632 header->currentIndex = idx;
633 cur = cur + idx;
634
635 cur->color = src.color;
636 cur->size = src.size;
637 cur->normal = qt_normalFromRotation(src.rotation);
638
639 if (prev && m_segmentCount == 1) {
640 QVector3D t = prev->position - src.position;
641 float l = t.length();
642 t.normalize();
643 float minLength = m_lengthDeltaMin;
644 if (header->length >= 0.0f)
645 minLength = header->length;
646 cur->position = src.position + minLength * t;
647 cur->length += l;
648 cur->tangent = t;
649 cur->binormal = QVector3D::crossProduct(cur->normal, t);
650 } else {
651 cur->position = src.position;
652 cur->length = 0.0f;
653 }
654
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;
663 else
664 prev->binormal = (prev->binormal + cur->binormal).normalized();
665 }
666}
667
668void QQuick3DParticleLineParticle::clearSegment(int particleIndex)
669{
670 if (m_lineData.isEmpty())
671 return;
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());
676 }
677 header->emitterIndex = -1;
678 header->currentIndex = 0;
679 header->pointCount = 0;
680 header->length = -1.0f;
681}
682
683QT_END_NAMESPACE
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)