11#include <QtQml/qqmlfile.h>
12#include <QtQuick3D/QQuick3DGeometry>
13#include <QtQuick3D/private/qquick3dmodel_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
50QQuick3DParticleSceneShape::QQuick3DParticleSceneShape(QObject *parent)
51 : QQuick3DParticleAbstractShape(parent)
56QQuick3DParticleSceneShape::~QQuick3DParticleSceneShape()
59 clearModelVertexPositions();
63
64
65
66
67
68
69
70
71
72QQuick3DNode *QQuick3DParticleSceneShape::scene()
const
77void QQuick3DParticleSceneShape::setScene(QQuick3DNode *scene)
82 clearModelVertexPositions();
88
89
90
91
92
93
94QVector3D QQuick3DParticleSceneShape::sceneCenter()
const
99void QQuick3DParticleSceneShape::setSceneCenter(
const QVector3D ¢er)
101 if (m_sceneCenter == center)
104 m_sceneCenter = center;
105 Q_EMIT sceneCenterChanged();
110
111
112
113
114
115
116
117
118
119QVector3D QQuick3DParticleSceneShape::sceneExtents()
const
121 return m_sceneExtents;
124void QQuick3DParticleSceneShape::setSceneExtents(
const QVector3D &extents)
126 if (m_sceneExtents == extents)
129 m_sceneExtents = extents;
130 Q_EMIT sceneExtentsChanged();
135
136
137
138
139
140
141
142
143float QQuick3DParticleSceneShape::shapeResolution()
const
145 return m_shapeResolution;
148void QQuick3DParticleSceneShape::setShapeResolution(
float resolution)
150 if (qFuzzyCompare(resolution, m_shapeResolution))
153 m_shapeResolution = qBound(1.0f, resolution, 100.0f);
154 Q_EMIT sceneExtentsChanged();
159
160
161
162
163
164QQuick3DGeometry *QQuick3DParticleSceneShape::geometry()
const
170
171
172
173
174
175
176QList<QQuick3DNode *> QQuick3DParticleSceneShape::excludedNodes()
const
178 return m_excludeList;
181void QQuick3DParticleSceneShape::setExcludedNodes(
const QList<QQuick3DNode *> &nodes)
183 if (nodes == m_excludeList)
185 m_excludeList = nodes;
186 Q_EMIT excludedNodesChanged();
187 markDirty(
false,
true);
190QVector3D QQuick3DParticleSceneShape::getPosition(
int particleIndex)
192 return randomPositionModel(particleIndex);
195QVector3D QQuick3DParticleSceneShape::getSurfaceNormal(
int particleIndex)
197 if (m_cachedIndex != particleIndex)
198 getPosition(particleIndex);
199 return m_cachedNormal;
202void QQuick3DParticleSceneShape::markDirty(
bool dirty,
bool excludeDirty)
204 for (
auto &model : m_data) {
205 model.dirty |= dirty;
206 model.excludedDirty |= excludeDirty;
211
212
213
214
215
238 void init(
float x,
float z,
float w,
float d,
int ccx,
int ccz,
float y)
254 return QVector3D(
x + 0.5f *
w,
y,
z + 0.5f *
d);
257 void initCells(
Cell *cells,
float x,
float z,
float w,
float d,
int ccx,
int ccz,
float y)
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;
277 void update(
const QVector3D &v1,
const QVector3D &v2,
const QVector3D &v3,
bool append =
true)
282 triangles.append({v1, v2, v3});
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);
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();
313 bool isIn = yOfTriangle(point, v1, v2, v3, py);
317 subcells[idx].triangles.append({v1, v2, v3});
318 }
else if (minx == maxx && minz == maxz) {
321 subcells[idx].triangles.append({v1, v2, v3});
329 for (
const auto &triangle : std::as_const(triangles))
330 update(triangle.p0, triangle.p1, triangle.p2,
false);
335 QList<QVector3D> ret;
337 int pointCount = (
ccx - 1) * (
ccz - 1) * 6;
338 ret.reserve(pointCount);
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;
347 ret.append(
subcells[ca].positions(miny, icp100));
349 QVector3D a, b, c, d;
351 b = subcells[ca + 1].center();
352 c = subcells[cb].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()));
357 if (a.y() > miny && b.y() > miny && c.y() > miny && d.y() > miny) {
424 return (
a < 0.0f &&
b < 0.0f) || (
a > 0.0f &&
b > 0.0f);
441 float den = 1.0f / (
u +
v +
w);
483 static const QMetaMethod geometryChangedSignal = QMetaMethod::fromSignal(&QQuick3DParticleSceneShape::geometryChanged);
484 if (!isSignalConnected(geometryChangedSignal))
489 m_geometry =
new QQuick3DGeometry();
490 QVector3D boxMin, boxMax;
491 if (m_sceneExtents.isNull()) {
492 boxMin = m_sceneMesh->boundsMin;
493 boxMax = m_sceneMesh->boundsMax;
495 boxMin = m_sceneCenter - m_sceneExtents;
496 boxMax = m_sceneCenter + m_sceneExtents;
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();
515 auto models = m_root->findChildren<QQuick3DModel*>();
516 auto rootModel = qobject_cast<QQuick3DModel*>(m_root);
518 models.append(rootModel);
520 for (
const auto &model : std::as_const(models)) {
522 if ((model->geometry() !=
nullptr && model->geometry() == m_geometry))
525 data.sceneTransformConnection = QObject::connect(data.model, &QQuick3DNode::sceneTransformChanged,
this, [&,index](){
526 m_data[index].dirty =
true;
528 data.visibilityConnection = QObject::connect(data.model, &QQuick3DNode::visibleChanged,
this, [&,index](){
529 m_data[index].dirty =
true;
540 calculateModelVertexPositions();
542 float totalArea = 0.0;
543 if (m_sceneData.vertexPositions.isEmpty())
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;
559 m_sceneData.modelTriangleCenter /= positions.size();
561 totalArea += m_sceneData.modelTriangleAreasSum;
563 auto rand = m_system->rand();
564 const float rndWeight = rand->get(particleIndex,
QPRand::Shape1) * totalArea;
566 int index = std::lower_bound(m_sceneData.modelTriangleAreas.begin(), m_sceneData.modelTriangleAreas.end(), rndWeight) - m_sceneData.modelTriangleAreas.begin();
568 const QVector<QVector3D> &positions = m_sceneData.vertexPositions;
570 const QVector3D &v1 = positions[index * 3];
571 const QVector3D &v2 = positions[index * 3 + 1];
572 const QVector3D &v3 = positions[index * 3 + 2];
575 const float aSqrt = qSqrt(a);
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();
582 auto *parent = parentNode();
585 mat.rotate(parent->rotation());
586 m_cachedNormal = mat.mapVector(m_cachedNormal);
587 return mat.mapVector(pos * parent->sceneScale());
591 return QVector3D(0, 0, 0);
596 for (
const auto &model : std::as_const(m_data)) {
597 QObject::disconnect(model.sceneTransformConnection);
598 QObject::disconnect(model.visibilityConnection);
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();
615 if (normal.y() < 0.0f)
617 if (!m_sceneExtents.isNull()) {
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())) {
629 if (v0.x() < boxMin.x() && v1.x() < boxMin.x() && v2.x() < boxMin.x())
631 if (v0.x() > boxMax.x() && v1.x() > boxMax.x() && v2.x() > boxMax.x())
633 if (v0.y() < boxMin.y() && v1.y() < boxMin.y() && v2.y() < boxMin.y())
635 if (v0.y() > boxMax.y() && v1.y() > boxMax.y() && v2.y() > boxMax.y())
637 if (v0.z() < boxMin.z() && v1.z() < boxMin.z() && v2.z() < boxMin.z())
639 if (v0.z() > boxMax.z() && v1.z() > boxMax.z() && v2.z() > boxMax.z())
653 if (!node->visible())
655 node = node->parentNode();
663 for (
const QQuick3DNode *node : std::as_const(m_excludeList)) {
664 QQuick3DNode *m = model;
677 for (
const auto &modelData : std::as_const(m_data)) {
678 update |= modelData.dirty || modelData.excludedDirty;
687 m_sceneMesh =
new QQuick3DParticleSceneShape::SceneNapkinMesh();
688 m_sceneMesh->init(m_sceneExtents, m_sceneCenter, m_shapeResolution / 10.0f);
693 for (
auto &modelData : m_data) {
694 if (modelData.excludedDirty) {
695 modelData.excludedDirty =
false;
696 modelData.excluded = isExcluded(modelData.model);
698 modelData.visible = isVisibleRecursive(modelData.model);
699 if (!modelData.visible || modelData.excluded) {
700 modelData.dirty =
false;
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();
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]);
717 m_sceneData.vertexPositions = m_sceneMesh->positions();
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
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)
static constexpr float c_maximumYDiff
QList< QVector3D > positions(float miny, float icp100)
QList< Triangle > triangles