5#include <QRandomGenerator>
11
12
13
14
15
16
17
18
19
22
23
24
25
28
29
30
31
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
67
68
69
70
71
72
73
74
75
76
77
78
79
82
83
84
85
86
87
90
91
92
93
94
95
96
97
100
101
102
103
104
105
106
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
127
128
129
130
131
132
133
134
135
138
139
140
141
142
143
144
145
148
149
150
151
152
153
154
155
156
157
160
161
162
163
164
165
166
167
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
204QQuick3DRandomInstancing::~QQuick3DRandomInstancing()
210 if (instanceCount == m_randomCount)
212 m_randomCount = instanceCount;
213 emit instanceCountChanged();
220 if (m_randomSeed == randomSeed)
223 m_randomSeed = randomSeed;
224 emit randomSeedChanged();
231 if (m_position == position)
235 disconnect(m_position, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
236 m_position = position;
237 emit positionChanged();
241 connect(m_position, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
242 connect(m_position, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_position) m_position =
nullptr; });
248 if (m_scale == scale)
252 disconnect(m_scale, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
258 connect(m_scale, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
259 connect(m_scale, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_scale) m_scale =
nullptr; });
265 if (m_rotation == rotation)
269 disconnect(m_rotation, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
270 m_rotation = rotation;
271 emit rotationChanged();
275 connect(m_rotation, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
276 connect(m_rotation, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_rotation) m_rotation =
nullptr; });
282 if (m_color == color)
286 disconnect(m_color, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
292 connect(m_color, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
293 connect(m_color, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_color) m_color =
nullptr; });
300 if (m_customData == customData)
304 disconnect(m_customData, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
305 m_customData = customData;
306 emit customDataChanged();
310 connect(m_customData, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
311 connect(m_customData, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_customData) m_customData =
nullptr; });
317 if (m_colorModel == colorModel)
319 m_colorModel = colorModel;
320 emit colorModelChanged();
331static inline float genRandom(
float from,
float to, QRandomGenerator *rgen)
333 float c = rgen->bounded(1.0);
334 return from + c * (to - from);
337static QVector3D genRandom(
const QVector3D &from,
const QVector3D &to,
bool proportional, QRandomGenerator *rgen)
340 float c = rgen->bounded(1.0);
341 return from + c * (to - from);
343 return { genRandom(from.x(), to.x(), rgen), genRandom(from.y(), to.y(), rgen), genRandom(from.z(), to.z(), rgen) };
346static QVector4D genRandom(
const QVector4D &from,
const QVector4D &to,
bool proportional, QRandomGenerator *rgen)
349 float c = rgen->bounded(1.0);
350 return from + c * (to - from);
352 return { genRandom(from.x(), to.x(), rgen), genRandom(from.y(), to.y(), rgen), genRandom(from.z(), to.z(), rgen), genRandom(from.w(), to.w(), rgen) };
355static QColor genRandom(
const QColor &from,
const QColor &to,
bool proportional, QQuick3DRandomInstancing::ColorModel colorModel, QRandomGenerator *rgen)
358 switch (colorModel) {
359 case QQuick3DRandomInstancing::ColorModel::HSL:
360 from.getHslF(&v1[0], &v1[1], &v1[2], &v1[3]);
361 to.getHslF(&v2[0], &v2[1], &v2[2], &v2[3]);
363 case QQuick3DRandomInstancing::ColorModel::HSV:
364 from.getHsvF(&v1[0], &v1[1], &v1[2], &v1[3]);
365 to.getHsvF(&v2[0], &v2[1], &v2[2], &v2[3]);
367 case QQuick3DRandomInstancing::ColorModel::RGB:
369 from.getRgbF(&v1[0], &v1[1], &v1[2], &v1[3]);
370 to.getRgbF(&v2[0], &v2[1], &v2[2], &v2[3]);
373 QVector4D r = genRandom(v1, v2, proportional, rgen);
375 switch (colorModel) {
376 case QQuick3DRandomInstancing::ColorModel::HSL:
377 return QColor::fromHslF(r[0], r[1], r[2], r[3]);
379 case QQuick3DRandomInstancing::ColorModel::HSV:
380 return QColor::fromHsvF(r[0], r[1], r[2], r[3]);
382 case QQuick3DRandomInstancing::ColorModel::RGB:
384 return QColor::fromRgbF(r[0], r[1], r[2], r[3]);
391 generateInstanceTable();
393 *instanceCount = m_randomCount;
394 return m_instanceData;
403inline bool operator==(
const GridPosition &e1,
const GridPosition &e2)
405 return e1.x == e2.x && e1.y == e2.y && e1.z == e2.z;
408inline size_t qHash(
const GridPosition &key, size_t seed)
410 return qHashMulti(seed, key.x, key.y, key.z);
413class PositionGenerator {
415 void init(QVector3D fromPos, QVector3D toPos,
bool proportional,
bool gridMode, QVector3D gridSize) {
418 m_proportional = proportional;
419 m_gridMode = gridMode;
425 float width = toPos.x() - fromPos.x();
426 float height = toPos.y() - fromPos.y();
427 float depth = toPos.z() - fromPos.z();
428 if (qFuzzyIsNull(width)) {
432 cellWidth = gridSize.x() > 0 ? gridSize.x() : width;
433 nx = width / cellWidth;
435 if (qFuzzyIsNull(height)) {
439 cellHeight = gridSize.y() > 0 ? gridSize.y() : height;
440 ny = height / cellHeight;
442 if (qFuzzyIsNull(depth)) {
446 cellDepth = gridSize.z() > 0 ? gridSize.z() : depth;
447 nz = depth / cellDepth;
452 m_gridSize = {cellWidth, cellHeight, cellDepth};
454 m_remainingAttempts = 1000000;
457 inline GridPosition gridPos(QVector3D pos) {
458 int ix = m_xGrid ?
int(pos.x() / m_gridSize.x()) : 0;
459 int iy = m_yGrid ?
int(pos.y() / m_gridSize.y()) : 0;
460 int iz = m_zGrid ?
int(pos.z() / m_gridSize.z()) : 0;
464 inline bool collision(
const GridPosition &gp) {
465 for (
int x = gp.x - m_xGrid; x <= gp.x + m_xGrid; ++x)
466 for (
int y = gp.y - m_yGrid; y <= gp.y + m_yGrid; ++y)
467 for (
int z = gp.z - m_zGrid; z <= gp.z + m_zGrid; ++z )
468 if (m_occupiedCells.contains({x,y,z}))
473 QVector3D generate(QRandomGenerator *rgen) {
475 while (m_remainingAttempts > 0) {
476 auto pos = genRandom(m_from, m_to, m_proportional, rgen);
477 auto gPos = gridPos(pos);
478 if (!collision(gPos)) {
479 m_occupiedCells.insert(gPos);
482 m_remainingAttempts--;
486 return genRandom(m_from, m_to, m_proportional, rgen);
489 bool isFull()
const {
return m_remainingAttempts <= 0; }
494 QVector3D m_gridSize;
495 QSet<GridPosition> m_occupiedCells;
496 int m_remainingAttempts;
497 bool m_proportional =
false;
498 bool m_gridMode =
false;
499 bool m_xGrid =
false;
500 bool m_yGrid =
false;
501 bool m_zGrid =
false;
509 const int count = m_randomCount;
511 QRandomGenerator rgen(m_randomSeed);
512 if (m_randomSeed == -1)
513 rgen.seed(QRandomGenerator::global()->generate());
515 qsizetype tableSize = count *
sizeof(InstanceTableEntry);
516 m_instanceData.resize(tableSize);
518 auto *array =
reinterpret_cast<InstanceTableEntry*>(m_instanceData.data());
520 PositionGenerator posGen;
522 posGen.init(m_position->from().value<QVector3D>(), m_position->to().value<QVector3D>(), m_position->proportional(), m_gridMode, m_gridSpacing);
524 for (
int i = 0; i < count; ++i) {
526 QVector3D scale{1, 1, 1};
527 QVector3D eulerRotation;
528 QColor color(Qt::white);
529 QVector4D customData;
532 pos = posGen.generate(&rgen);
534 scale = genRandom(m_scale->from().value<QVector3D>(), m_scale->to().value<QVector3D>(), m_scale->proportional(), &rgen);
536 eulerRotation = genRandom(m_rotation->from().value<QVector3D>(), m_rotation->to().value<QVector3D>(), m_rotation->proportional(), &rgen);
538 color = genRandom(m_color->from().value<QColor>(), m_color->to().value<QColor>(), m_color->proportional(), m_colorModel, &rgen);
540 customData = genRandom(m_customData->from().value<QVector4D>(), m_customData->to().value<QVector4D>(), m_customData->proportional(), &rgen);
542 if (Q_UNLIKELY(posGen.isFull())) {
543 qWarning() <<
"RandomInstancing: Could not find free cell, truncating instance array" << i;
544 qsizetype newSize = i *
sizeof(InstanceTableEntry);
545 m_instanceData.truncate(newSize);
547 emit instanceCountChanged();
551 array[i] = calculateTableEntry(pos, scale, eulerRotation, color, customData);
555QQuick3DInstanceRange::QQuick3DInstanceRange(QQuick3DObject *parent)
556 : QQuick3DObject(parent)
561void QQuick3DInstanceRange::setFrom(QVariant from)
571void QQuick3DInstanceRange::setTo(QVariant to)
581void QQuick3DInstanceRange::setProportional(
bool proportional)
583 if (m_proportional == proportional)
586 m_proportional = proportional;
587 emit proportionalChanged();
593 return m_gridSpacing;
598 if (m_gridSpacing == newGridSpacing)
600 m_gridSpacing = newGridSpacing;
601 emit gridSpacingChanged();
602 m_gridMode = (newGridSpacing.x() > 0 || newGridSpacing.y() > 0 || newGridSpacing.z() > 0) && !(newGridSpacing.x() < 0 || newGridSpacing.y() < 0 || newGridSpacing.z() < 0);
\qmltype Object3D \inqmlmodule QtQuick3D \nativetype QQuick3DObject \inherits QtObject
void setRandomSeed(int randomSeed)
void setGridSpacing(const QVector3D &newGridSpacing)
void setColor(QQuick3DInstanceRange *color)
QVector3D gridSpacing() const
void setCustomData(QQuick3DInstanceRange *customData)
QByteArray getInstanceBuffer(int *instanceCount) override
Implement this function to return the contents of the instance table.
void setRotation(QQuick3DInstanceRange *rotation)
void setPosition(QQuick3DInstanceRange *position)
void setScale(QQuick3DInstanceRange *scale)
Combined button and popup list for selecting options.
static QVector3D genRandom(const QVector3D &from, const QVector3D &to, bool proportional, QRandomGenerator *rgen)
static float genRandom(float from, float to, QRandomGenerator *rgen)
static QColor genRandom(const QColor &from, const QColor &to, bool proportional, QQuick3DRandomInstancing::ColorModel colorModel, QRandomGenerator *rgen)