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
qquick3dparticlemodelshape.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
7#include <QtCore/qdir.h>
8#include <QtQml/qqmlfile.h>
9#include <QtQuick3D/private/qquick3dmodel_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
11#include <algorithm>
12
14
50
55
64{
65 return m_fill;
66}
67
87{
88 return m_delegate;
89}
90
92{
93 if (m_fill == fill)
94 return;
95
96 m_fill = fill;
98}
99
101{
102 return randomPositionModel(particleIndex);
103}
104
106{
108 if (source.startsWith(QLatin1Char('#'))) {
110 src.prepend(QLatin1String(":/"));
111 }
113 if (src.startsWith(QLatin1String("qrc:/")))
114 src = src.mid(3);
115 QSSGMesh::Mesh mesh;
116 QFileInfo fileInfo = QFileInfo(src);
117 if (fileInfo.exists()) {
118 QFile file(fileInfo.absoluteFilePath());
120 return {};
122 }
123 return mesh;
124}
125
127{
128 if (delegate == m_delegate)
129 return;
130 m_delegate = delegate;
131 clearModelVertexPositions();
132 createModel();
134}
135
136void QQuick3DParticleModelShape::createModel()
137{
138 delete m_model;
139 m_model = nullptr;
140 if (!m_delegate)
141 return;
142 auto *obj = m_delegate->create(m_delegate->creationContext());
143 m_model = qobject_cast<QQuick3DModel *>(obj);
144 if (!m_model)
145 delete obj;
146}
147
148QVector3D QQuick3DParticleModelShape::randomPositionModel(int particleIndex)
149{
150 if (m_model) {
151 calculateModelVertexPositions();
152
153 const QVector<QVector3D> &positions = m_vertexPositions;
154 if (positions.size() > 0) {
155 auto rand = m_system->rand();
156
157 // Calculate model triangle areas so that the random triangle selection can be weighted
158 // by the area. This way particles are uniformly emitted from the whole model.
159 if (m_modelTriangleAreas.size() == 0) {
160 m_modelTriangleAreas.reserve(positions.size() / 3);
161 for (int i = 0; i + 2 < positions.size(); i += 3) {
162 const QVector3D &v1 = positions[i];
163 const QVector3D &v2 = positions[i + 1];
164 const QVector3D &v3 = positions[i + 2];
165 const float area = QVector3D::crossProduct(v1 - v2, v1 - v3).length() * 0.5f;
166 m_modelTriangleAreasSum += area;
167 m_modelTriangleAreas.append(m_modelTriangleAreasSum);
168 m_modelTriangleCenter += v1 + v2 + v3;
169 }
170 m_modelTriangleCenter /= positions.size();
171 }
172
173 const float rndWeight = rand->get(particleIndex, QPRand::Shape1) * m_modelTriangleAreasSum;
174
175 // Use binary search to find the weighted random index
176 int index = std::lower_bound(m_modelTriangleAreas.begin(), m_modelTriangleAreas.end(), rndWeight) - m_modelTriangleAreas.begin();
177
178 const QVector3D &v1 = positions[index * 3];
179 const QVector3D &v2 = positions[index * 3 + 1];
180 const QVector3D &v3 = positions[index * 3 + 2];
181 const float a = rand->get(particleIndex, QPRand::Shape2);
182 const float b = rand->get(particleIndex, QPRand::Shape3);
183 const float aSqrt = qSqrt(a);
184
185 // Calculate a random point from the selected triangle
186 QVector3D pos = (1.0 - aSqrt) * v1 + (aSqrt * (1.0 - b)) * v2 + (b * aSqrt) * v3;
187
188 if (m_fill) {
189 // The model is filled by selecting a random point between a random surface point
190 // and the center of the model. The random point selection is exponentially weighted
191 // towards the surface so that particles aren't clustered in the center.
192 const float uniform = rand->get(particleIndex, QPRand::Shape4);
193 const float lambda = 5.0f;
194 const float alpha = -qLn(1 - (1 - qExp(-lambda)) * uniform) / lambda;
195 pos += (m_modelTriangleCenter - pos) * alpha;
196 }
197
198 auto *parent = parentNode();
199 if (parent) {
200 QMatrix4x4 mat;
201 mat.rotate(parent->rotation() * m_model->rotation());
202 return mat.mapVector(pos * parent->sceneScale() * m_model->scale());
203 }
204 }
205 }
206 return QVector3D(0, 0, 0);
207}
208
209void QQuick3DParticleModelShape::clearModelVertexPositions()
210{
211 m_vertexPositions.clear();
212 m_modelTriangleAreas.clear();
213 m_modelTriangleAreasSum = 0;
214}
215
216void QQuick3DParticleModelShape::calculateModelVertexPositions()
217{
218 if (m_vertexPositions.empty()) {
219 QVector<QVector3D> indicedPositions;
220 QVector<QVector3D> positions;
221
222 if (m_model->geometry()) {
223 QQuick3DGeometry *geometry = m_model->geometry();
224 bool hasIndexBuffer = false;
226 int posOffset = 0;
228 for (int i = 0; i < geometry->attributeCount(); ++i) {
229 auto attribute = geometry->attribute(i);
231 posOffset = attribute.offset;
232 posType = attribute.componentType;
234 hasIndexBuffer = true;
235 indexBufferFormat = attribute.componentType;
236 }
237 }
239 const auto &data = geometry->vertexData();
240 int stride = geometry->stride();
241 for (int i = 0; i < data.size(); i += stride) {
242 float v[3];
243 memcpy(v, data + posOffset + i, sizeof(v));
244 positions.append(QVector3D(v[0], v[1], v[2]));
245 }
246 if (hasIndexBuffer) {
247 const auto &data = geometry->vertexData();
248 int indexSize = 4;
249 if (indexBufferFormat == QQuick3DGeometry::Attribute::U16Type)
250 indexSize = 2;
251 for (int i = 0; i < data.size(); i += indexSize) {
252 qsizetype index = 0;
253 memcpy(&index, data + i, indexSize);
254 if (positions.size() > index)
255 indicedPositions.append(positions[index]);
256 }
257 }
258 }
259 } else {
260 const QQmlContext *context = qmlContext(this);
261 QString src = m_model->source().toString();
262 if (context && !src.startsWith(QLatin1Char('#')))
263 src = QQmlFile::urlToLocalFileOrQrc(context->resolvedUrl(m_model->source()));
265 if (!mesh.isValid())
266 return;
267 if (mesh.drawMode() != QSSGMesh::Mesh::DrawMode::Triangles)
268 return;
269
270 auto entries = mesh.vertexBuffer().entries;
271 int posOffset = 0;
272 int posCount = 0;
273 // Just set 'posType' to something to avoid invalid 'maybe-uninitialized' warning
274 QSSGMesh::Mesh::ComponentType posType = QSSGMesh::Mesh::ComponentType::UnsignedInt8;
275 for (int i = 0; i < entries.size(); ++i) {
276 const char *nameStr = entries[i].name.constData();
277 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
278 posOffset = entries[i].offset;
279 posCount = entries[i].componentCount;
280 posType = entries[i].componentType;
281 break;
282 }
283 }
284 if (posCount == 3 && posType == QSSGMesh::Mesh::ComponentType::Float32) {
285 const auto &data = mesh.vertexBuffer().data;
286 int stride = mesh.vertexBuffer().stride;
287 for (int i = 0; i < data.size(); i += stride) {
288 float v[3];
289 memcpy(v, data + posOffset + i, sizeof(v));
290 positions.append(QVector3D(v[0], v[1], v[2]));
291 }
292 const auto &indexData = mesh.indexBuffer().data;
294 for (int i = 0; i < indexData.size(); i += indexSize) {
295 qsizetype index = 0;
296 memcpy(&index, indexData + i, indexSize);
297 if (positions.size() > index)
298 indicedPositions.append(positions[index]);
299 }
300 }
301 }
302 if (!indicedPositions.empty())
303 m_vertexPositions = indicedPositions;
304 else
305 m_vertexPositions = positions;
306 }
307}
308
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
QString absoluteFilePath() const
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
void rotate(float angle, const QVector3D &vector)
Multiples this matrix by another that rotates coordinates through angle degrees about vector.
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
The QQmlComponent class encapsulates a QML component definition.
QQmlContext * creationContext() const
Returns the QQmlContext the component was created in.
virtual QObject * create(QQmlContext *context=nullptr)
Create an object instance from this component, within the specified context.
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
\qmltype Geometry \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DGeometry
Attribute::Semantic int int stride
Returns the byte stride of the vertex buffer.
int attributeCount() const
Returns the number of attributes defined for this geometry.
Attribute attribute(int index) const
Returns attribute definition number index.
QByteArray vertexData() const
Returns the vertex buffer data set by setVertexData.
QQuick3DGeometry * geometry
QQuaternion rotation
QVector3D scale
QQuick3DParticleModelShape(QObject *parent=nullptr)
\qmltype ParticleModelShape3D \inherits ParticleAbtractShape3D \inqmlmodule QtQuick3D....
void setDelegate(QQmlComponent *delegate)
QVector3D getPosition(int particleIndex) override
static QString primitivePath(const QString &primitive)
bool isValid() const
Definition qssgmesh_p.h:159
static Mesh loadMesh(QIODevice *device, quint32 id=0)
Definition qssgmesh.cpp:581
VertexBuffer vertexBuffer() const
Definition qssgmesh_p.h:140
IndexBuffer indexBuffer() const
Definition qssgmesh_p.h:141
DrawMode drawMode() const
Definition qssgmesh_p.h:161
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
friend constexpr decltype(auto) get(V &&vec) noexcept
Definition qvectornd.h:313
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
Combined button and popup list for selecting options.
static void * context
static const QCssKnownValue positions[NumKnownPositionModes - 1]
EGLOutputLayerEXT EGLint attribute
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
static int area(const QSize &s)
Definition qicon.cpp:153
auto qLn(T v)
Definition qmath.h:168
auto qExp(T v)
Definition qmath.h:174
GLint GLfloat GLfloat GLfloat v2
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint index
[2]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
const void GLsizei GLsizei stride
GLint GLfloat GLfloat v1
GLint GLfloat GLfloat GLfloat GLfloat v3
GLsizei GLsizei GLchar * source
GLhandleARB obj
[2]
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
static QSSGMesh::Mesh loadModelShapeMesh(const QString &source)
QSSGRenderComponentType
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_EMIT
ptrdiff_t qsizetype
Definition qtypes.h:165
QFile file
[0]
ba fill(true)
\inmodule QtCore \reentrant
Definition qchar.h:18
static quint32 byteSizeForComponentType(Mesh::ComponentType componentType)
Definition qssgmesh_p.h:390
static const char * getPositionAttrName()
Definition qssgmesh_p.h:392
QVector< VertexBufferEntry > entries
Definition qssgmesh_p.h:104