7#include <QRandomGenerator>
13
14
15
16
17
18
19
20
21
24
25
26
27
30
31
32
33
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
65
66
69
70
71
72
73
74
75
76
77
78
79
80
81
84
85
86
87
88
89
92
93
94
95
96
97
98
99
102
103
104
105
106
107
108
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
129
130
131
132
133
134
135
136
137
140
141
142
143
144
145
146
147
150
151
152
153
154
155
156
157
158
159
162
163
164
165
166
167
168
169
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
197
198
200QQuick3DRandomInstancing::QQuick3DRandomInstancing(QQuick3DObject *parent)
201 : QQuick3DInstancing(parent)
206QQuick3DRandomInstancing::~QQuick3DRandomInstancing()
210void QQuick3DRandomInstancing::setInstanceCount(
int instanceCount)
212 if (instanceCount == m_randomCount)
214 m_randomCount = instanceCount;
215 emit instanceCountChanged();
220void QQuick3DRandomInstancing::setRandomSeed(
int randomSeed)
222 if (m_randomSeed == randomSeed)
225 m_randomSeed = randomSeed;
226 emit randomSeedChanged();
231void QQuick3DRandomInstancing::setPosition(QQuick3DInstanceRange *position)
233 if (m_position == position)
237 disconnect(m_position, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
238 m_position = position;
239 emit positionChanged();
243 connect(m_position, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
244 connect(m_position, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_position) m_position =
nullptr; });
248void QQuick3DRandomInstancing::setScale(QQuick3DInstanceRange *scale)
250 if (m_scale == scale)
254 disconnect(m_scale, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
260 connect(m_scale, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
261 connect(m_scale, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_scale) m_scale =
nullptr; });
265void QQuick3DRandomInstancing::setRotation(QQuick3DInstanceRange *rotation)
267 if (m_rotation == rotation)
271 disconnect(m_rotation, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
272 m_rotation = rotation;
273 emit rotationChanged();
277 connect(m_rotation, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
278 connect(m_rotation, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_rotation) m_rotation =
nullptr; });
282void QQuick3DRandomInstancing::setColor(QQuick3DInstanceRange *color)
284 if (m_color == color)
288 disconnect(m_color, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
294 connect(m_color, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
295 connect(m_color, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_color) m_color =
nullptr; });
300void QQuick3DRandomInstancing::setCustomData(QQuick3DInstanceRange *customData)
302 if (m_customData == customData)
306 disconnect(m_customData, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
307 m_customData = customData;
308 emit customDataChanged();
312 connect(m_customData, &QQuick3DInstanceRange::changed,
this, &QQuick3DRandomInstancing::handleChange);
313 connect(m_customData, &QObject::destroyed,
this, [
this](QObject *obj){
if (obj == m_customData) m_customData =
nullptr; });
317void QQuick3DRandomInstancing::setColorModel(QQuick3DRandomInstancing::ColorModel colorModel)
319 if (m_colorModel == colorModel)
321 m_colorModel = colorModel;
322 emit colorModelChanged();
327void QQuick3DRandomInstancing::handleChange()
333static inline float genRandom(
float from,
float to, QRandomGenerator *rgen)
335 float c = rgen->bounded(1.0);
336 return from + c * (to - from);
339static QVector3D genRandom(
const QVector3D &from,
const QVector3D &to,
bool proportional, QRandomGenerator *rgen)
342 float c = rgen->bounded(1.0);
343 return from + c * (to - from);
345 return { genRandom(from.x(), to.x(), rgen), genRandom(from.y(), to.y(), rgen), genRandom(from.z(), to.z(), rgen) };
348static QVector4D genRandom(
const QVector4D &from,
const QVector4D &to,
bool proportional, QRandomGenerator *rgen)
351 float c = rgen->bounded(1.0);
352 return from + c * (to - from);
354 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) };
357static QColor genRandom(
const QColor &from,
const QColor &to,
bool proportional, QQuick3DRandomInstancing::ColorModel colorModel, QRandomGenerator *rgen)
360 switch (colorModel) {
361 case QQuick3DRandomInstancing::ColorModel::HSL:
362 from.getHslF(&v1[0], &v1[1], &v1[2], &v1[3]);
363 to.getHslF(&v2[0], &v2[1], &v2[2], &v2[3]);
365 case QQuick3DRandomInstancing::ColorModel::HSV:
366 from.getHsvF(&v1[0], &v1[1], &v1[2], &v1[3]);
367 to.getHsvF(&v2[0], &v2[1], &v2[2], &v2[3]);
369 case QQuick3DRandomInstancing::ColorModel::RGB:
371 from.getRgbF(&v1[0], &v1[1], &v1[2], &v1[3]);
372 to.getRgbF(&v2[0], &v2[1], &v2[2], &v2[3]);
375 QVector4D r = genRandom(v1, v2, proportional, rgen);
377 switch (colorModel) {
378 case QQuick3DRandomInstancing::ColorModel::HSL:
379 return QColor::fromHslF(r[0], r[1], r[2], r[3]);
381 case QQuick3DRandomInstancing::ColorModel::HSV:
382 return QColor::fromHsvF(r[0], r[1], r[2], r[3]);
384 case QQuick3DRandomInstancing::ColorModel::RGB:
386 return QColor::fromRgbF(r[0], r[1], r[2], r[3]);
390QByteArray QQuick3DRandomInstancing::getInstanceBuffer(
int *instanceCount)
393 generateInstanceTable();
395 *instanceCount = m_randomCount;
396 return m_instanceData;
405inline bool operator==(
const GridPosition &e1,
const GridPosition &e2)
407 return e1.x == e2.x && e1.y == e2.y && e1.z == e2.z;
410inline size_t qHash(
const GridPosition &key, size_t seed)
412 return qHashMulti(seed, key.x, key.y, key.z);
415class PositionGenerator {
417 void init(QVector3D fromPos, QVector3D toPos,
bool proportional,
bool gridMode, QVector3D gridSize) {
420 m_proportional = proportional;
421 m_gridMode = gridMode;
427 float width = toPos.x() - fromPos.x();
428 float height = toPos.y() - fromPos.y();
429 float depth = toPos.z() - fromPos.z();
430 if (qFuzzyIsNull(width)) {
434 cellWidth = gridSize.x() > 0 ? gridSize.x() : width;
435 nx = width / cellWidth;
437 if (qFuzzyIsNull(height)) {
441 cellHeight = gridSize.y() > 0 ? gridSize.y() : height;
442 ny = height / cellHeight;
444 if (qFuzzyIsNull(depth)) {
448 cellDepth = gridSize.z() > 0 ? gridSize.z() : depth;
449 nz = depth / cellDepth;
454 m_gridSize = {cellWidth, cellHeight, cellDepth};
456 m_remainingAttempts = 1000000;
459 inline GridPosition gridPos(QVector3D pos) {
460 int ix = m_xGrid ?
int(pos.x() / m_gridSize.x()) : 0;
461 int iy = m_yGrid ?
int(pos.y() / m_gridSize.y()) : 0;
462 int iz = m_zGrid ?
int(pos.z() / m_gridSize.z()) : 0;
466 inline bool collision(
const GridPosition &gp) {
467 for (
int x = gp.x - m_xGrid; x <= gp.x + m_xGrid; ++x)
468 for (
int y = gp.y - m_yGrid; y <= gp.y + m_yGrid; ++y)
469 for (
int z = gp.z - m_zGrid; z <= gp.z + m_zGrid; ++z )
470 if (m_occupiedCells.contains({x,y,z}))
475 QVector3D generate(QRandomGenerator *rgen) {
477 while (m_remainingAttempts > 0) {
478 auto pos = genRandom(m_from, m_to, m_proportional, rgen);
479 auto gPos = gridPos(pos);
480 if (!collision(gPos)) {
481 m_occupiedCells.insert(gPos);
484 m_remainingAttempts--;
488 return genRandom(m_from, m_to, m_proportional, rgen);
491 bool isFull()
const {
return m_remainingAttempts <= 0; }
496 QVector3D m_gridSize;
497 QSet<GridPosition> m_occupiedCells;
498 int m_remainingAttempts;
499 bool m_proportional =
false;
500 bool m_gridMode =
false;
501 bool m_xGrid =
false;
502 bool m_yGrid =
false;
503 bool m_zGrid =
false;
508void QQuick3DRandomInstancing::generateInstanceTable()
511 const int count = m_randomCount;
513 QRandomGenerator rgen(m_randomSeed);
514 if (m_randomSeed == -1)
515 rgen.seed(QRandomGenerator::global()->generate());
517 qsizetype tableSize = count *
sizeof(InstanceTableEntry);
518 m_instanceData.resize(tableSize);
520 auto *array =
reinterpret_cast<InstanceTableEntry*>(m_instanceData.data());
522 PositionGenerator posGen;
524 posGen.init(m_position->from().value<QVector3D>(), m_position->to().value<QVector3D>(), m_position->proportional(), m_gridMode, m_gridSpacing);
526 for (
int i = 0; i < count; ++i) {
528 QVector3D scale{1, 1, 1};
529 QVector3D eulerRotation;
530 QColor color(Qt::white);
531 QVector4D customData;
534 pos = posGen.generate(&rgen);
536 scale = genRandom(m_scale->from().value<QVector3D>(), m_scale->to().value<QVector3D>(), m_scale->proportional(), &rgen);
538 eulerRotation = genRandom(m_rotation->from().value<QVector3D>(), m_rotation->to().value<QVector3D>(), m_rotation->proportional(), &rgen);
540 color = genRandom(m_color->from().value<QColor>(), m_color->to().value<QColor>(), m_color->proportional(), m_colorModel, &rgen);
542 customData = genRandom(m_customData->from().value<QVector4D>(), m_customData->to().value<QVector4D>(), m_customData->proportional(), &rgen);
544 if (Q_UNLIKELY(posGen.isFull())) {
545 qWarning() <<
"RandomInstancing: Could not find free cell, truncating instance array" << i;
546 qsizetype newSize = i *
sizeof(InstanceTableEntry);
547 m_instanceData.truncate(newSize);
549 emit instanceCountChanged();
553 array[i] = calculateTableEntry(pos, scale, eulerRotation, color, customData);
557QQuick3DInstanceRange::QQuick3DInstanceRange(QQuick3DObject *parent)
558 : QQuick3DObject(parent)
563void QQuick3DInstanceRange::setFrom(QVariant from)
573void QQuick3DInstanceRange::setTo(QVariant to)
583void QQuick3DInstanceRange::setProportional(
bool proportional)
585 if (m_proportional == proportional)
588 m_proportional = proportional;
589 emit proportionalChanged();
593QVector3D QQuick3DRandomInstancing::gridSpacing()
const
595 return m_gridSpacing;
598void QQuick3DRandomInstancing::setGridSpacing(
const QVector3D &newGridSpacing)
600 if (m_gridSpacing == newGridSpacing)
602 m_gridSpacing = newGridSpacing;
603 emit gridSpacingChanged();
604 m_gridMode = (newGridSpacing.x() > 0 || newGridSpacing.y() > 0 || newGridSpacing.z() > 0) && !(newGridSpacing.x() < 0 || newGridSpacing.y() < 0 || newGridSpacing.z() < 0);
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)