Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
13
33
37
46{
47 return m_segmentCount;
48}
49
60{
61 return m_alphaFade;
62}
63
74{
75 return m_scaleMultiplier;
76}
77
85{
86 return m_texcoordMultiplier;
87}
88
98{
99 return m_length;
100}
101
110{
111 return m_lengthVariation;
112}
113
121{
122 return m_lengthDeltaMin;
123}
124
133{
134 return m_eolFadeOutDuration;
135}
136
159
161{
162 count = qMax(1, count);
163 if (m_segmentCount == count)
164 return;
165 m_segmentCount = count;
166 handleSegmentCountChanged();
168}
169
171{
172 fade = qBound(0.0f, fade, 1.0f);
173 if (qFuzzyCompare(m_alphaFade, fade))
174 return;
175 m_alphaFade = fade;
177}
178
180{
181 multiplier = qBound(0.0f, multiplier, 2.0f);
182 if (qFuzzyCompare(m_scaleMultiplier, multiplier))
183 return;
184 m_scaleMultiplier = multiplier;
186}
187
189{
190 if (qFuzzyCompare(m_texcoordMultiplier, multiplier))
191 return;
192 m_texcoordMultiplier = multiplier;
194}
195
197{
198 length = length != -1.0f ? qMax(length, 0.0f) : -1.0f;
199 if (qFuzzyCompare(m_length, length))
200 return;
201 m_length = length;
203}
204
206{
208 if (qFuzzyCompare(m_lengthVariation, lengthVariation))
209 return;
210 m_lengthVariation = lengthVariation;
212}
213
215{
216 min = qMax(min, 0.0f);
217 if (qFuzzyCompare(m_lengthDeltaMin, min))
218 return;
219 m_lengthDeltaMin = min;
221}
222
224{
225 duration = qMax(0, duration);
226 if (duration == m_eolFadeOutDuration)
227 return;
228 m_eolFadeOutDuration = duration;
230}
231
233{
234 if (mode == m_texcoordMode)
235 return;
236 m_texcoordMode = mode;
238}
239
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(m_featureLevel);
274
275 return particles;
276}
277
279{
280 if (m_lineData.size() == amount)
281 return;
282
283 m_lineData.resize(m_segmentCount * amount);
284 m_lineHeaderData.resize(amount);
286}
287
289{
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 *>(m_particle);
301 node = lineParticle->updateParticleNode(this, node);
302 lineParticle->updateLineNode(node);
304 Q_QUICK3D_PROFILE_ASSIGN_ID_SG(lineParticle, node);
305 auto particles = static_cast<QSSGRenderParticles *>(node);
306
307 lineParticle->updateLineBuffer(this, particles);
308
309 m_nodeDirty = false;
310 }
311 return node;
312}
313
314void QQuick3DParticleLineParticle ::reset()
315{
317 m_lineData.fill({});
318 m_lineHeaderData.fill({});
319 m_fadeOutData.clear();
320}
321
323{
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(iter);
331 }
332}
333
335{
336 if (!m_perEmitterData.contains(emitter)) {
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 }
345 clearSegment(index);
346 m_lineHeaderData[index].emitterIndex = m_perEmitterData[emitter].emitterIndex;
347 if (m_length > 0.0f)
348 m_lineHeaderData[index].length = qMax(0.0f, m_length + m_lengthVariation * (system()->rand()->get(index) - 0.5f));
349 return index;
350}
351
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
367{
368 LineDataHeader *header = m_lineHeaderData.data() + particleIndex;
369 if (header->pointCount) {
370 header->currentIndex = 0;
371 header->pointCount = 0;
372 }
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(particleIndex * m_segmentCount, m_segmentCount);
386 data.emitterIndex = m_spriteParticleData[particleIndex].emitterIndex;
387 m_fadeOutData.emplaceBack(data);
388 clearSegment(particleIndex);
389 }
390}
391
392static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)
393{
394 float x = qDegreesToRadians(eulerRotation.x());
395 float y = qDegreesToRadians(eulerRotation.y());
396 if (qFuzzyIsNull(x) && qFuzzyIsNull(y))
397 return QVector3D(0, 0, -1);
398 float a = qCos(x);
399 float b = qSin(x);
400 float c = qCos(y);
401 float d = qSin(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(totalCount, 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(qt_normalFromRotation(sdata.rotation), 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(totalLength))
489 lengthScale = -1.0f / totalLength;
490 }
491 }
492 bounds.include(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(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(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{
591 m_lineData.resize(m_segmentCount * m_maxAmount);
592 m_lineData.fill({});
593 m_lineHeaderData.resize(m_maxAmount);
594 m_lineHeaderData.fill({});
595 m_fadeOutData.clear();
596 if (!m_spriteParticleData.isEmpty()) {
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(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(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(cur->normal, 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(cur->normal, 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(data, m_segmentCount, LineData());
673 }
674 header->emitterIndex = -1;
675 header->currentIndex = 0;
676 header->pointCount = 0;
677 header->length = -1.0f;
678}
679
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
size_type size() const
Definition qmap.h:267
QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node) override
void handleMaxAmountChanged(int amount) override
void resetParticleData(int particleIndex) override
int nextCurrentIndex(const QQuick3DParticleEmitter *emitter) override
QQuick3DParticleLineParticle(QQuick3DNode *parent=nullptr)
\qmltype LineParticle3D \inherits SpriteParticle3D \inqmlmodule QtQuick3D.Particles3D
void setTexcoordMode(QQuick3DParticleLineParticle::TexcoordMode mode)
void handleSystemChanged(QQuick3DParticleSystem *system) override
void setParticleData(int particleIndex, const QVector3D &position, const QVector3D &rotation, const QVector4D &color, float size, float age, float animationFrame) override
virtual void setParticleData(int particleIndex, const QVector3D &position, const QVector3D &rotation, const QVector4D &color, float size, float age, float animationFrame)
int nextCurrentIndex(const QQuick3DParticleEmitter *emitter) override
virtual void resetParticleData(int particleIndex)
QVector< SpriteParticleData > m_spriteParticleData
QMap< const QQuick3DParticleEmitter *, PerEmitterData > m_perEmitterData
PerEmitterData & perEmitterData(const QQuick3DNode *updateNode)
QQuick3DParticleSpriteSequence * spriteSequence
int currentTime() const
Returns the current time of the system (m_time + m_startTime).
QQuick3DParticleSystem * system() const
Class representing 3D range or axis aligned bounding box.
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
float length() const noexcept
Returns the length of the vector from the origin.
Definition qvectornd.h:690
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
constexpr void setW(float w) noexcept
Sets the w coordinate of this point to the given finite w coordinate.
Definition qvectornd.h:886
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
static QDBusError::ErrorType get(const char *name)
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
#define qWarning
Definition qlogging.h:166
return ret
auto qCos(T v)
Definition qmath.h:60
auto qSin(T v)
Definition qmath.h:54
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLenum mode
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
GLenum GLuint buffer
GLuint color
[2]
GLenum GLenum dst
GLint y
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLint GLenum GLboolean normalized
Definition qopenglext.h:752
GLuint segments
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
QSSGRenderParticles::FeatureLevel lineFeatureLevel(QQuick3DParticleSpriteParticle::FeatureLevel in)
static QVector3D qt_normalFromRotation(const QVector3D &eulerRotation)
#define Q_QUICK3D_PROFILE_ASSIGN_ID_SG(obj, bgnode)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_EMIT
ba fill(true)
void setBounds(const QSSGBounds3 &bounds)
void resizeLine(int particleCount, int segmentCount)
QSSGParticleBuffer m_particleBuffer