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
qquick3dparticlecustomshape.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
9#include <QtQml/qqmlcontext.h>
10#include <QtQml/qqmlfile.h>
11#include <QtCore/qfile.h>
12
14
15/*!
16 \qmltype ParticleCustomShape3D
17 \inherits ParticleAbstractShape3D
18 \inqmlmodule QtQuick3D.Particles3D
19 \brief Loads custom particle shapes for emitters and affectors.
20 \since 6.3
21
22 The ParticleCustomShape3D element can be used to load custom particle shapes.
23
24 For example, to emit particles from positions defined in heart.cbor:
25
26 \qml
27 ParticleEmitter3D {
28 shape: ParticleCustomShape3D {
29 source: "heart.cbor"
30 }
31 ...
32 }
33 \endqml
34
35 The format of CBOR shape files is following:
36 \badcode
37 [
38 "QQ3D_SHAPE", // string
39 version, // integer
40 [
41 posX, // float
42 posY, // float
43 posZ, // float
44 posX, // float
45 ...
46 ]
47 ]
48 \endcode
49
50 To assist in generating these shape files you can use the shapegen tool.
51*/
52
53QQuick3DParticleCustomShape::QQuick3DParticleCustomShape(QObject *parent)
54 : QQuick3DParticleAbstractShape(parent)
55{
56}
57
58/*!
59 \qmlproperty url ParticleCustomShape3D::source
60
61 This property holds the location of the shape file.
62
63 \warning \a source is expected to contain trusted content. Application
64 developers are advised to carefully consider the potential implications
65 before passing in user-provided source files that are not part of the
66 application.
67*/
68
69QUrl QQuick3DParticleCustomShape::source() const
70{
71 return m_source;
72}
73
74/*!
75 \qmlproperty bool ParticleCustomShape3D::randomizeData
76
77 This property holds whether the particles are used in random order instead
78 of in the order they are specified in the source.
79
80 The default value is \c false.
81*/
82bool QQuick3DParticleCustomShape::randomizeData() const
83{
84 return m_random;
85}
86
87void QQuick3DParticleCustomShape::setSource(const QUrl &source)
88{
89 if (m_source == source)
90 return;
91
92 m_source = source;
93
94 loadFromSource();
95 Q_EMIT sourceChanged();
96}
97
98void QQuick3DParticleCustomShape::setRandomizeData(bool random)
99{
100 if (m_random == random)
101 return;
102
103 m_random = random;
104 if (m_random)
105 m_randomizeDirty = true;
106 Q_EMIT randomizeDataChanged();
107}
108
109void QQuick3DParticleCustomShape::loadFromSource()
110{
111 m_positions.clear();
112
113 // Get path to file
114 const QQmlContext *context = qmlContext(this);
115 QString dataFilePath = QQmlFile::urlToLocalFileOrQrc(context ? context->resolvedUrl(m_source) : m_source);
116
117 QFile dataFile(dataFilePath);
118 if (!dataFile.open(QIODevice::ReadOnly)) {
119 // Invalid file
120 qWarning() << "Unable to open file:" << dataFilePath;
121 return;
122 }
123 QCborStreamReader reader(&dataFile);
124
125 // Check that file is proper CBOR and get the version
126 int version = QQuick3DParticleShapeDataUtils::readShapeHeader(reader);
127
128 if (version == -1) {
129 // Invalid file
130 qWarning() << "Invalid shape data version:" << version;
131 return;
132 }
133
134 // Start positions array
135 reader.enterContainer();
136
137 m_center = QVector3D();
138 while (reader.lastError() == QCborError::NoError && reader.hasNext()) {
139 QVector3D pos = QQuick3DParticleShapeDataUtils::readValue(reader, QMetaType::QVector3D).value<QVector3D>();
140 m_positions.append(pos);
141 m_center += pos;
142 }
143 if (!m_positions.isEmpty())
144 m_center *= 1.0f / float(m_positions.size());
145
146 // Leave positions array
147 reader.leaveContainer();
148
149 // Leave root array
150 reader.leaveContainer();
151
152 if (m_random)
153 m_randomizeDirty = true;
154}
155
156void QQuick3DParticleCustomShape::doRandomizeData()
157{
158 if (!m_system || m_positions.isEmpty())
159 return;
160
161 auto rand = m_system->rand();
162 int seed = rand->get(0, QPRand::Shape1) * float(INT_MAX);
163 std::shuffle(m_positions.begin(), m_positions.end(), std::default_random_engine(seed));
164
165 m_randomizeDirty = false;
166}
167
168QVector3D QQuick3DParticleCustomShape::getPosition(int particleIndex)
169{
170 auto *parent = parentNode();
171 if (!parent || m_positions.isEmpty())
172 return QVector3D();
173
174 if (m_randomizeDirty)
175 doRandomizeData();
176
177 int index = particleIndex % m_positions.size();
178 return m_positions.at(index) * parent->scale();
179}
180
181QVector3D QQuick3DParticleCustomShape::getSurfaceNormal(int particleIndex)
182{
183 auto *parent = parentNode();
184 if (!parent || m_positions.isEmpty())
185 return QVector3D();
186
187 if (m_randomizeDirty)
188 doRandomizeData();
189
190 int index = particleIndex % m_positions.size();
191 return (m_positions.at(index) - m_center).normalized();
192}
193
194QT_END_NAMESPACE