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
qquick3dparticlesceneshape.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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
11#include <QtQml/qqmlfile.h>
12#include <QtQuick3D/QQuick3DGeometry>
13#include <QtQuick3D/private/qquick3dmodel_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
15#include <algorithm>
16
17QT_BEGIN_NAMESPACE
18
19/*!
20 \qmltype ParticleSceneShape3D
21 \inherits ParticleAbstractShape3D
22 \inqmlmodule QtQuick3D.Particles3D
23 \brief Offers particle shape from a scene for emitters and affectors.
24 \since 6.11
25
26 The ParticleSceneShape3D element can be used to get particle shape from a 3D scene.
27 The shape envelopes the scene into single shape based on the models in a scene.
28
29 For example,
30
31 \qml
32 ParticleEmitter3D {
33 id: emitter
34 particle: particle
35
36 ...
37
38 shape: ParticleSceneShape3D {
39 id: sceneShape
40 scene: sceneRoot
41 sceneCenter: node.scenePosition
42 sceneExtents: Qt.vector3d(1000, 500, 1000)
43 }
44
45 ...
46 }
47 \endqml
48*/
49
50QQuick3DParticleSceneShape::QQuick3DParticleSceneShape(QObject *parent)
51 : QQuick3DParticleAbstractShape(parent)
52{
53
54}
55
56QQuick3DParticleSceneShape::~QQuick3DParticleSceneShape()
57{
58 delete m_geometry;
59 clearModelVertexPositions();
60}
61
62/*!
63 \qmlproperty Node ParticleSceneShape3D::scene
64
65 This property holds the root node of the scene. The scene shape is dynamically
66 calculated based on the child models of the scene. If the scene changes,
67 the shape is recalculated. If a model is not visible in the scene, it won't be
68 included in the shape. Only the node visibility is checked by the implementation.
69 If the material is fully transparent or the node gets filtered out, the shape
70 will still contain those nodes unless they are manyally excluded.
71*/
72QQuick3DNode *QQuick3DParticleSceneShape::scene() const
73{
74 return m_scene;
75}
76
77void QQuick3DParticleSceneShape::setScene(QQuick3DNode *scene)
78{
79 if (m_scene == scene)
80 return;
81 m_scene = scene;
82 clearModelVertexPositions();
83 if (scene)
84 createShapeData();
85}
86
87/*!
88 \qmlproperty Node ParticleSceneShape3D::sceneCenter
89
90 This property holds the center point where the shape is calculated.
91
92 The default value is \c Qt.vector3(0, 0, 0).
93*/
94QVector3D QQuick3DParticleSceneShape::sceneCenter() const
95{
96 return m_sceneCenter;
97}
98
99void QQuick3DParticleSceneShape::setSceneCenter(const QVector3D &center)
100{
101 if (m_sceneCenter == center)
102 return;
103
104 m_sceneCenter = center;
105 Q_EMIT sceneCenterChanged();
106 markDirty();
107}
108
109/*!
110 \qmlproperty Node ParticleSceneShape3D::sceneExtents
111
112 This property holds the extents for which the shape is calculated.
113 The extents are added to the scene center and any polygon outside
114 this volume are left out of the shape. If this value is empty, all
115 polygons are included.
116
117 The default value is \c Qt.vector3(0, 0, 0).
118*/
119QVector3D QQuick3DParticleSceneShape::sceneExtents() const
120{
121 return m_sceneExtents;
122}
123
124void QQuick3DParticleSceneShape::setSceneExtents(const QVector3D &extents)
125{
126 if (m_sceneExtents == extents)
127 return;
128
129 m_sceneExtents = extents;
130 Q_EMIT sceneExtentsChanged();
131 markDirty();
132}
133
134/*!
135 \qmlproperty real ParticleSceneShape3D::shapeResolution
136
137 This property holds the resolution of the shape. The higher the value
138 the more accurately the shape resembles individual models.
139
140 The minimum value is 1.0, the maximum value is 100.0;
141 \default 10.0;
142*/
143float QQuick3DParticleSceneShape::shapeResolution() const
144{
145 return m_shapeResolution;
146}
147
148void QQuick3DParticleSceneShape::setShapeResolution(float resolution)
149{
150 if (qFuzzyCompare(resolution, m_shapeResolution))
151 return;
152
153 m_shapeResolution = qBound(1.0f, resolution, 100.0f);
154 Q_EMIT sceneExtentsChanged();
155 markDirty();
156}
157
158/*!
159 \qmlproperty Node ParticleSceneShape3D::geometry
160
161 This property holds the geometry of the generated shape. It can be used to render
162 the shape to visualize it.
163*/
164QQuick3DGeometry *QQuick3DParticleSceneShape::geometry() const
165{
166 return m_geometry;
167}
168
169/*!
170 \qmlproperty list<Node> ParticleSceneShape3D::excludedNodes
171
172 This property holds the list of nodes that are excluded when building
173 the shape. Model is excluded from the shape if it is one of the nodes
174 or their child node.
175*/
176QList<QQuick3DNode *> QQuick3DParticleSceneShape::excludedNodes() const
177{
178 return m_excludeList;
179}
180
181void QQuick3DParticleSceneShape::setExcludedNodes(const QList<QQuick3DNode *> &nodes)
182{
183 if (nodes == m_excludeList)
184 return;
185 m_excludeList = nodes;
186 Q_EMIT excludedNodesChanged();
187 markDirty(false, true);
188}
189
190QVector3D QQuick3DParticleSceneShape::getPosition(int particleIndex)
191{
192 return randomPositionModel(particleIndex);
193}
194
195QVector3D QQuick3DParticleSceneShape::getSurfaceNormal(int particleIndex)
196{
197 if (m_cachedIndex != particleIndex)
198 getPosition(particleIndex);
199 return m_cachedNormal;
200}
201
202void QQuick3DParticleSceneShape::markDirty(bool dirty, bool excludeDirty)
203{
204 for (auto &model : m_data) {
205 model.dirty |= dirty;
206 model.excludedDirty |= excludeDirty;
207 }
208}
209
210/*
211 SceneNapkinMesh is a grid mesh that is placed on top of the scene
212 following the scene geometry. If neighbouring cells have large
213 enough height difference, they are not connected and the shape
214 has a hole in that position.
215 */
217{
218 struct Triangle
219 {
221 };
222
223 struct Cell
224 {
225 float x, z;
226 float w, d;
227 float y;
229 int ccx, ccz;
230 Cell *subcells = nullptr;
231 static constexpr float c_maximumYDiff = 40.0f;
232
233 ~Cell() {
234 delete [] subcells;
235 }
237
238 void init(float x, float z, float w, float d, int ccx, int ccz, float y)
239 {
240 this->ccx = ccx;
241 this->ccz = ccz;
242 this->x = x;
243 this->z = z;
244 this->w = w;
245 this->d = d;
246 this->y = y;
247 subcellCount = ccz * ccx;
249 initCells(subcells, x, z, w, d, ccx, ccz, y);
250 }
251
253 {
254 return QVector3D(x + 0.5f * w, y, z + 0.5f * d);
255 }
256
257 void initCells(Cell *cells, float x, float z, float w, float d, int ccx, int ccz, float y)
258 {
259 float wccx = w / float(ccx - 1);
260 float dccz = d / float(ccz - 1);
261 for (int i = 0; i < ccx; i++) {
262 for (int j = 0; j < ccz; j++) {
263 int idx = i * ccz + j;
264 cells[idx].x = x + i * wccx;
265 cells[idx].z = z + j * dccz;
266 cells[idx].w = wccx;
267 cells[idx].d = dccz;
268 cells[idx].subcellCount = 0;
269 cells[idx].subcells = nullptr;
270 cells[idx].y = y;
271 cells[idx].ccx = 0;
272 cells[idx].ccz = 0;
273 }
274 }
275 }
276
277 void update(const QVector3D &v1, const QVector3D &v2, const QVector3D &v3, bool append = true)
278 {
279 if (subcellCount == 0) {
280 // Extents was empty so simply collect the triangles
281 if (append)
282 triangles.append({v1, v2, v3});
283 return;
284 }
285 // First get the rectangle this triangle forms ...
286 float wccx = w / float(ccx - 1);
287 float dccz = d / float(ccz - 1);
288 int cx1 = (v1.x() - x) / wccx;
289 int cz1 = (v1.z() - z) / dccz;
290 int cx2 = (v2.x() - x) / wccx;
291 int cz2 = (v2.z() - z) / dccz;
292 int cx3 = (v3.x() - x) / wccx;
293 int cz3 = (v3.z() - z) / dccz;
294 cx1 = qBound(0, cx1, ccx - 1);
295 cx2 = qBound(0, cx2, ccx - 1);
296 cx3 = qBound(0, cx3, ccx - 1);
297 cz1 = qBound(0, cz1, ccz - 1);
298 cz2 = qBound(0, cz2, ccz - 1);
299 cz3 = qBound(0, cz3, ccz - 1);
300 int minx = qMin(qMin(cx1, cx2), cx3);
301 int maxx = qMax(qMax(cx1, cx2), cx3);
302 int minz = qMin(qMin(cz1, cz2), cz3);
303 int maxz = qMax(qMax(cz1, cz2), cz3);
304
305 // ... then test all cells inside this rectangle if they are inside the triangle.
306 // If they are, adjust the height of the cell to the height of the triangle.
307 for (int i = minx; i <= maxx; i++) {
308 for (int j = minz; j <= maxz; j++) {
309 int idx = i * ccz + j;
310 QVector3D point = subcells[idx].center();
311 point.setY(this->y);
312 float py;
313 bool isIn = yOfTriangle(point, v1, v2, v3, py);
314 if (isIn) {
315 subcells[idx].y = qMax(subcells[idx].y, py);
316 if (append)
317 subcells[idx].triangles.append({v1, v2, v3});
318 } else if (minx == maxx && minz == maxz) { // Triangle is smaller than a cell so update anyway
319 subcells[idx].y = qMax(subcells[idx].y, qMax(v1.y(),qMax(v2.y(), v3.y())));
320 if (append)
321 subcells[idx].triangles.append({v1, v2, v3});
322 }
323 }
324 }
325 }
326
328 {
329 for (const auto &triangle : std::as_const(triangles))
330 update(triangle.p0, triangle.p1, triangle.p2, false);
331 }
332
333 QList<QVector3D> positions(float miny, float icp100)
334 {
335 QList<QVector3D> ret;
336 Q_ASSERT(subcells);
337 int pointCount = (ccx - 1) * (ccz - 1) * 6;
338 ret.reserve(pointCount);
339
340 // Form the mesh grid from the cells
341 for (int i = 0; i < ccx - 1; i++) {
342 for (int j = 0; j < ccz - 1; j++) {
343 int ca = i * ccz + j;
344 int cb = (i + 1) * ccz + j;
345 auto &sc = subcells[ca];
346 if (sc.subcells) {
347 ret.append(subcells[ca].positions(miny, icp100));
348 } else {
349 QVector3D a, b, c, d;
350 a = subcells[ca].center();
351 b = subcells[ca + 1].center();
352 c = subcells[cb].center();
353 d = subcells[cb + 1].center();
354 float ydiff1 = qMax(qMax(qAbs(a.y() - b.y()), qAbs(a.y() - c.y())), qAbs(b.y() - c.y()));
355 float ydiff2 = qMax(qMax(qAbs(d.y() - b.y()), qAbs(d.y() - c.y())), qAbs(b.y() - c.y()));
356 // Only add grid cell if the y is greated than the ground
357 if (a.y() > miny && b.y() > miny && c.y() > miny && d.y() > miny) {
358 // Do not add triangle if the height difference to neighbour is too big.
359 if (ydiff1 < c_maximumYDiff) {
360 ret.append(a);
361 ret.append(b);
362 ret.append(c);
363 }
364 if (ydiff2 < c_maximumYDiff) {
365 ret.append(c);
366 ret.append(b);
367 ret.append(d);
368 }
369 }
370 }
371 }
372 }
373 return ret;
374 }
375 };
376
378 float width = 0;
379 float depth = 0;
380 float x = 0;
381 float z = 0;
382 float icp100;
386
389
391 {
392 ext = extents;
393 cent = center;
395 x = center.x()-extents.x();
396 z = center.z()-extents.z();
397 if (extents.isNull()) {
398 // If the extents is empty, we must collect all the triangles
399 // and calculate the bounds while collecting them
400 constexpr auto maxVal = std::numeric_limits<float>().max();
401 constexpr auto minVal = std::numeric_limits<float>().min();
404 return;
405 }
406 width = extents.x() * 2.0f;
407 depth = extents.z() * 2.0f;
408 float dwp = 100.0f / icp100;
409
410 int ccx = width / dwp + 1;
411 int ccz = depth / dwp + 1;
412
414 }
415
416 // Calculate y from top-down inside triangle [p0, p1, p2] based on point p, which
417 // must be inside the triangle.
419 {
420 const auto scalarTriple = [](QVector3D a, QVector3D b, QVector3D c) {
422 };
423 const auto sameSign = [](float a, float b) {
424 return (a < 0.0f && b < 0.0f) || (a > 0.0f && b > 0.0f);
425 };
426
427 // Vectors from point to triangle vertices
428 QVector3D pp0 = p0 - p;
429 QVector3D pp1 = p1 - p;
430 QVector3D pp2 = p2 - p;
431
432 // Compute the barycentric coordinates (u, v, w) using parallelepiped volumes.
434 float u = QVector3D::dotProduct(pp1, m);
435 float v = -QVector3D::dotProduct(pp0, m);
436 if (!qFuzzyIsNull(u) && !qFuzzyIsNull(v) && !sameSign(u, v))
437 return false;
438 float w = scalarTriple(QVector3D(0, 1, 0), pp1, pp0);
439 if (!qFuzzyIsNull(w) && !sameSign(u, w))
440 return false;
441 float den = 1.0f / (u + v + w);
442 u *= den;
443 v *= den;
444 w *= den;
445 y = p0.y() * u + p1.y() * v + p2.y() * w;
446 return true;
447 }
448 void update(const QVector3D &v1, const QVector3D &v2, const QVector3D &v3)
449 {
450 if (ext.isNull()) {
451 // Update bounds if extents is empty
452 boundsMax.setX(qMax(boundsMax.x(), qMax(v1.x(), qMax(v2.x(), v3.x()))));
453 boundsMax.setY(qMax(boundsMax.y(), qMax(v1.y(), qMax(v2.y(), v3.y()))));
454 boundsMax.setZ(qMax(boundsMax.z(), qMax(v1.z(), qMax(v2.z(), v3.z()))));
455
456 boundsMin.setX(qMin(boundsMin.x(), qMin(v1.x(), qMin(v2.x(), v3.x()))));
457 boundsMin.setY(qMin(boundsMin.y(), qMin(v1.y(), qMin(v2.y(), v3.y()))));
458 boundsMin.setZ(qMin(boundsMin.z(), qMin(v1.z(), qMin(v2.z(), v3.z()))));
459 }
461 }
463 {
464 float miny = cent.y() - ext.y();
465 if (ext.isNull()) {
466 // If extends was empty, rootCell simply collected all triangles
467 // Now it needs to update subcells
469 return {};
474 miny = center.y() - extents.y();
475 }
476 return rootCell.positions(miny, icp100);
477 }
478};
479
480void QQuick3DParticleSceneShape::updateGeometry()
481{
482 // Only update geometry if the geometry is used
483 static const QMetaMethod geometryChangedSignal = QMetaMethod::fromSignal(&QQuick3DParticleSceneShape::geometryChanged);
484 if (!isSignalConnected(geometryChangedSignal))
485 return;
486
487 if (m_geometry)
488 delete m_geometry;
489 m_geometry = new QQuick3DGeometry();
490 QVector3D boxMin, boxMax;
491 if (m_sceneExtents.isNull()) {
492 boxMin = m_sceneMesh->boundsMin;
493 boxMax = m_sceneMesh->boundsMax;
494 } else {
495 boxMin = m_sceneCenter - m_sceneExtents;
496 boxMax = m_sceneCenter + m_sceneExtents;
497 }
498 m_geometry->setBounds(boxMin, boxMax);
499 QByteArray data((char *)m_sceneData.vertexPositions.data(), m_sceneData.vertexPositions.size() * sizeof(QVector3D));
500 m_geometry->setVertexData(data);
501 m_geometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
502 m_geometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type);
503 m_geometry->setStride(sizeof(float) * 3);
504 emit geometryChanged();
505}
506
507void QQuick3DParticleSceneShape::createShapeData()
508{
509 if (!m_scene)
510 return;
511
512 m_root = m_scene;
513
514 if (m_root) {
515 auto models = m_root->findChildren<QQuick3DModel*>();
516 auto rootModel = qobject_cast<QQuick3DModel*>(m_root);
517 if (rootModel)
518 models.append(rootModel);
519 int index = 0;
520 for (const auto &model : std::as_const(models)) {
521 ShapeData data;
522 if ((model->geometry() != nullptr && model->geometry() == m_geometry))
523 continue;
524 data.model = model;
525 data.sceneTransformConnection = QObject::connect(data.model, &QQuick3DNode::sceneTransformChanged, this, [&,index](){
526 m_data[index].dirty = true;
527 });
528 data.visibilityConnection = QObject::connect(data.model, &QQuick3DNode::visibleChanged, this, [&,index](){
529 m_data[index].dirty = true;
530 });
531 m_data.append(data);
532 index++;
533 }
534 }
535}
536
537QVector3D QQuick3DParticleSceneShape::randomPositionModel(int particleIndex)
538{
539 if (m_root) {
540 calculateModelVertexPositions();
541
542 float totalArea = 0.0;
543 if (m_sceneData.vertexPositions.isEmpty())
544 return {};
545 if (m_sceneData.modelTriangleAreas.size() == 0) {
546 const auto & positions = m_sceneData.vertexPositions;
547 m_sceneData.modelTriangleAreas.reserve(positions.size() / 3);
548 m_sceneData.modelTriangleAreasSum = 0.0f;
549 m_sceneData.modelTriangleCenter = {};
550 for (int i = 0; i + 2 < positions.size(); i += 3) {
551 const QVector3D &v1 = positions[i];
552 const QVector3D &v2 = positions[i + 1];
553 const QVector3D &v3 = positions[i + 2];
554 const float area = QVector3D::crossProduct(v1 - v2, v1 - v3).length() * 0.5f;
555 m_sceneData.modelTriangleAreasSum += area;
556 m_sceneData.modelTriangleAreas.append(m_sceneData.modelTriangleAreasSum);
557 m_sceneData.modelTriangleCenter += v1 + v2 + v3;
558 }
559 m_sceneData.modelTriangleCenter /= positions.size();
560 }
561 totalArea += m_sceneData.modelTriangleAreasSum;
562
563 auto rand = m_system->rand();
564 const float rndWeight = rand->get(particleIndex, QPRand::Shape1) * totalArea;
565 // Use binary search to find the weighted random index
566 int index = std::lower_bound(m_sceneData.modelTriangleAreas.begin(), m_sceneData.modelTriangleAreas.end(), rndWeight) - m_sceneData.modelTriangleAreas.begin();
567
568 const QVector<QVector3D> &positions = m_sceneData.vertexPositions;
569
570 const QVector3D &v1 = positions[index * 3];
571 const QVector3D &v2 = positions[index * 3 + 1];
572 const QVector3D &v3 = positions[index * 3 + 2];
573 const float a = rand->get(particleIndex, QPRand::Shape2);
574 const float b = rand->get(particleIndex, QPRand::Shape3);
575 const float aSqrt = qSqrt(a);
576
577 // Calculate a random point from the selected triangle
578 QVector3D pos = (1.0 - aSqrt) * v1 + (aSqrt * (1.0 - b)) * v2 + (b * aSqrt) * v3;
579 m_cachedIndex = particleIndex;
580 m_cachedNormal = QVector3D::crossProduct(v2 - v1, v3 - v2).normalized();
581
582 auto *parent = parentNode();
583 if (parent) {
584 QMatrix4x4 mat;
585 mat.rotate(parent->rotation());
586 m_cachedNormal = mat.mapVector(m_cachedNormal);
587 return mat.mapVector(pos * parent->sceneScale());
588 }
589
590 }
591 return QVector3D(0, 0, 0);
592}
593
594void QQuick3DParticleSceneShape::clearModelVertexPositions()
595{
596 for (const auto &model : std::as_const(m_data)) {
597 QObject::disconnect(model.sceneTransformConnection);
598 QObject::disconnect(model.visibilityConnection);
599 }
600 m_data.clear();
601}
602
603QList<QVector3D> QQuick3DParticleSceneShape::excludeTriangles(const QList<QVector3D> &list)
604{
605 QList<QVector3D> ret;
606 QVector3D boxMin, boxMax;
607 boxMin = m_sceneCenter - m_sceneExtents;
608 boxMax = m_sceneCenter + m_sceneExtents;
609 for (int i = 0; i < list.size(); i+=3) {
610 QVector3D v0 = list[i];
611 QVector3D v1 = list[i + 1];
612 QVector3D v2 = list[i + 2];
613 QVector3D normal = QVector3D::crossProduct(v1 - v0, v2 - v1).normalized();
614 // The normal must point upwards
615 if (normal.y() < 0.0f)
616 continue;
617 if (!m_sceneExtents.isNull()) {
618 // Check if all v0-v2 are outside scene extents and don't add them if they are
619 if (!(qAbs(v0.x() - m_sceneCenter.x()) < m_sceneExtents.x() &&
620 qAbs(v0.y() - m_sceneCenter.y()) < m_sceneExtents.y() &&
621 qAbs(v0.z() - m_sceneCenter.z()) < m_sceneExtents.z() &&
622 qAbs(v1.x() - m_sceneCenter.x()) < m_sceneExtents.x() &&
623 qAbs(v1.y() - m_sceneCenter.y()) < m_sceneExtents.y() &&
624 qAbs(v1.z() - m_sceneCenter.z()) < m_sceneExtents.z() &&
625 qAbs(v2.x() - m_sceneCenter.x()) < m_sceneExtents.x() &&
626 qAbs(v2.y() - m_sceneCenter.y()) < m_sceneExtents.y() &&
627 qAbs(v2.z() - m_sceneCenter.z()) < m_sceneExtents.z())) {
628 // If so make sure they are not crossing the extents
629 if (v0.x() < boxMin.x() && v1.x() < boxMin.x() && v2.x() < boxMin.x())
630 continue;
631 if (v0.x() > boxMax.x() && v1.x() > boxMax.x() && v2.x() > boxMax.x())
632 continue;
633 if (v0.y() < boxMin.y() && v1.y() < boxMin.y() && v2.y() < boxMin.y())
634 continue;
635 if (v0.y() > boxMax.y() && v1.y() > boxMax.y() && v2.y() > boxMax.y())
636 continue;
637 if (v0.z() < boxMin.z() && v1.z() < boxMin.z() && v2.z() < boxMin.z())
638 continue;
639 if (v0.z() > boxMax.z() && v1.z() > boxMax.z() && v2.z() > boxMax.z())
640 continue;
641 }
642 }
643 ret.append(v0);
644 ret.append(v1);
645 ret.append(v2);
646 }
647 return ret;
648}
649
650bool QQuick3DParticleSceneShape::isVisibleRecursive(QQuick3DNode *node)
651{
652 while (node) {
653 if (!node->visible())
654 return false;
655 node = node->parentNode();
656 }
657 return true;
658}
659
660bool QQuick3DParticleSceneShape::isExcluded(QQuick3DModel *model)
661{
662 // Check if model parent is in the excluded list
663 for (const QQuick3DNode *node : std::as_const(m_excludeList)) {
664 QQuick3DNode *m = model;
665 while (m) {
666 if (m == node)
667 return true;
668 m = m->parentNode();
669 }
670 }
671 return false;
672}
673
674void QQuick3DParticleSceneShape::calculateModelVertexPositions()
675{
676 bool update = false;
677 for (const auto &modelData : std::as_const(m_data)) {
678 update |= modelData.dirty || modelData.excludedDirty;
679 if (update)
680 break;
681 }
682 if (update) {
683 if (m_sceneMesh) {
684 delete m_sceneMesh;
685 m_sceneData.clear();
686 }
687 m_sceneMesh = new QQuick3DParticleSceneShape::SceneNapkinMesh();
688 m_sceneMesh->init(m_sceneExtents, m_sceneCenter, m_shapeResolution / 10.0f);
689 } else {
690 return;
691 }
692
693 for (auto &modelData : m_data) {
694 if (modelData.excludedDirty) {
695 modelData.excludedDirty = false;
696 modelData.excluded = isExcluded(modelData.model);
697 }
698 modelData.visible = isVisibleRecursive(modelData.model);
699 if (!modelData.visible || modelData.excluded) {
700 modelData.dirty = false;
701 continue;
702 }
703 if (modelData.dirty || modelData.vertexPositions.empty()) {
704 const QMatrix4x4 mat = modelData.model->sceneTransform();
705 modelData.vertexPositions = excludeTriangles(positionsFromModel(modelData.model, &mat, qmlContext(this)));
706 modelData.hasVertices = !modelData.vertexPositions.isEmpty();
707 modelData.modelTriangleAreas.clear();
708 }
709 modelData.dirty = false;
710 if (modelData.hasVertices && modelData.visible) {
711 int count = modelData.vertexPositions.size();
712 for (int k = 0; k < count; k+=3)
713 m_sceneMesh->update(modelData.vertexPositions[k], modelData.vertexPositions[k+1], modelData.vertexPositions[k+2]);
714 }
715 }
716 if (update) {
717 m_sceneData.vertexPositions = m_sceneMesh->positions();
718 updateGeometry();
719 }
720}
721
722QT_END_NAMESPACE
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:27
void init(float x, float z, float w, float d, int ccx, int ccz, float y)
void initCells(Cell *cells, float x, float z, float w, float d, int ccx, int ccz, float y)
void update(const QVector3D &v1, const QVector3D &v2, const QVector3D &v3, bool append=true)
QList< QVector3D > positions(float miny, float icp100)