7#include <QtQuick3DRuntimeRender/private/qssgrenderinstancetable_p.h>
8#include <QtQuick3DUtils/private/qssgutils_p.h>
9#include <QXmlStreamReader>
10#include <QtQml/QQmlFile>
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
37
38
39
40
41
44
45
46
47
48
51
52
53
54
55
56
57
58
59
60
63
64
65
66
67
68
69
70
71
72
75
76
77
78
79
80
81
82
83
85
88
89
90
91
92
93
94
95
96
97
98
101
102
103
104
105
106
107
108
109
110
111
112
113
114
116
117
118
119
120
121
122
123
124
125
126
127
130
131
132
133
134
135
136
137
138
139
140
141
142
143
145
146
147
148
149
150
151
152
153
154
155
156
159
160
161
162
163
164
165
166
167
168
169
172
173
174
175
176
177
179QQuick3DInstancingPrivate::QQuick3DInstancingPrivate()
180 : QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::ModelInstance)
184QQuick3DInstancing::QQuick3DInstancing(QQuick3DObject *parent)
185 : QQuick3DObject(*
new QQuick3DInstancingPrivate, parent)
189QQuick3DInstancing::~QQuick3DInstancing()
194
195
196
197QByteArray QQuick3DInstancing::instanceBuffer(
int *instanceCount)
199 Q_D(QQuick3DInstancing);
200 QByteArray retval = getInstanceBuffer(instanceCount);
201 if (instanceCount && d->m_instanceCountOverride >= 0)
202 *instanceCount = qMin(d->m_instanceCountOverride, *instanceCount);
206int QQuick3DInstancing::instanceCountOverride()
const
208 Q_D(
const QQuick3DInstancing);
209 return d->m_instanceCountOverride;
212bool QQuick3DInstancing::hasTransparency()
const
214 Q_D(
const QQuick3DInstancing);
215 return d->m_hasTransparency;
218bool QQuick3DInstancing::depthSortingEnabled()
const
220 Q_D(
const QQuick3DInstancing);
221 return d->m_depthSortingEnabled;
224QVector3D QQuick3DInstancing::shadowBoundsMinimum()
const
226 Q_D(
const QQuick3DInstancing);
227 return d->m_shadowBoundsMinimum;
230QVector3D QQuick3DInstancing::shadowBoundsMaximum()
const
232 Q_D(
const QQuick3DInstancing);
233 return d->m_shadowBoundsMaximum;
236const QQuick3DInstancing::InstanceTableEntry *QQuick3DInstancing::getInstanceEntry(
int index)
238 const QByteArray data = getInstanceBuffer(
nullptr);
239 if (index >=
int(data.size() /
sizeof(InstanceTableEntry)))
241 return reinterpret_cast<
const QQuick3DInstancing::InstanceTableEntry*>(data.constData()) + index;
244QVector3D QQuick3DInstancing::InstanceTableEntry::getPosition()
const
246 return QVector3D{ row0[3], row1[3], row2[3] };
249QVector3D QQuick3DInstancing::InstanceTableEntry::getScale()
const
251 const QVector3D col0{row0[0], row1[0], row2[0]};
252 const QVector3D col1{row0[1], row1[1], row2[1]};
253 const QVector3D col2{row0[2], row1[2], row2[2]};
254 const float scaleX = col0.length();
255 const float scaleY = col1.length();
256 const float scaleZ = col2.length();
257 return QVector3D(scaleX, scaleY, scaleZ);
260QQuaternion QQuick3DInstancing::InstanceTableEntry::getRotation()
const
262 const QVector3D col0 = QVector3D(row0[0], row1[0], row2[0]).normalized();
263 const QVector3D col1 = QVector3D(row0[1], row1[1], row2[1]).normalized();
264 const QVector3D col2 = QVector3D(row0[2], row1[2], row2[2]).normalized();
266 const float data3x3[3*3] {
267 col0[0], col1[0], col2[0],
268 col0[1], col1[1], col2[1],
269 col0[2], col1[2], col2[2],
271 QMatrix3x3 rot(data3x3);
272 return QQuaternion::fromRotationMatrix(rot).normalized();
275QColor QQuick3DInstancing::InstanceTableEntry::getColor()
const
277 return QColor::fromRgbF(color[0], color[1], color[2], color[3]);
281
282
283
284
285
286
287
289QVector3D QQuick3DInstancing::instancePosition(
int index)
291 auto *entry = getInstanceEntry(index);
295 return QVector3D{ entry->row0[3], entry->row1[3], entry->row2[3] };
299
300
301
302
303
304
305
307QVector3D QQuick3DInstancing::instanceScale(
int index)
309 auto *entry = getInstanceEntry(index);
312 return entry->getScale();
316
317
318
319
320
321
322
324QQuaternion QQuick3DInstancing::instanceRotation(
int index)
326 const auto *entry = getInstanceEntry(index);
329 return entry->getRotation();
333
334
335
336
337
338
339
341QColor QQuick3DInstancing::instanceColor(
int index)
343 const auto *entry = getInstanceEntry(index);
346 return entry->getColor();
350
351
352
353
354
355
356
358QVector4D QQuick3DInstancing::instanceCustomData(
int index)
360 const auto *entry = getInstanceEntry(index);
363 return entry->instanceData;
366void QQuick3DInstancing::setInstanceCountOverride(
int instanceCountOverride)
368 Q_D(QQuick3DInstancing);
369 if (d->m_instanceCountOverride == instanceCountOverride)
372 d->m_instanceCountOverride = instanceCountOverride;
373 d->m_instanceCountOverrideChanged =
true;
374 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
375 emit instanceCountOverrideChanged();
378void QQuick3DInstancing::setHasTransparency(
bool hasTransparency)
380 Q_D(QQuick3DInstancing);
381 if (d->m_hasTransparency == hasTransparency)
384 d->m_hasTransparency = hasTransparency;
385 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
386 emit hasTransparencyChanged();
389void QQuick3DInstancing::setDepthSortingEnabled(
bool enabled)
391 Q_D(QQuick3DInstancing);
392 if (d->m_depthSortingEnabled == enabled)
395 d->m_depthSortingEnabled = enabled;
396 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
397 emit depthSortingEnabledChanged();
400void QQuick3DInstancing::setShadowBoundsMinimum(
const QVector3D &newShadowBoundsMinimum)
402 Q_D(QQuick3DInstancing);
403 if (d->m_shadowBoundsMinimum == newShadowBoundsMinimum)
406 d->m_shadowBoundsMinimum = newShadowBoundsMinimum;
407 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
408 emit shadowBoundsMinimumChanged();
411void QQuick3DInstancing::setShadowBoundsMaximum(
const QVector3D &newShadowBoundsMaximum)
413 Q_D(QQuick3DInstancing);
414 if (d->m_shadowBoundsMinimum == newShadowBoundsMaximum)
417 d->m_shadowBoundsMaximum = newShadowBoundsMaximum;
418 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
419 emit shadowBoundsMaximumChanged();
423
424
425
426
428void QQuick3DInstancing::markDirty()
430 Q_D(QQuick3DInstancing);
431 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
432 d->m_instanceDataChanged =
true;
433 emit instanceTableChanged();
436QSSGRenderGraphObject *QQuick3DInstancing::updateSpatialNode(QSSGRenderGraphObject *node)
438 Q_D(QQuick3DInstancing);
441 node =
new QSSGRenderInstanceTable();
442 emit instanceNodeDirty();
443 d->m_instanceDataChanged =
true;
445 QQuick3DObject::updateSpatialNode(node);
446 auto effectiveInstanceCount = [d]() {
447 if (d->m_instanceCountOverride >= 0)
448 return qMin(d->m_instanceCount, d->m_instanceCountOverride);
449 return d->m_instanceCount;
451 auto *instanceTable =
static_cast<QSSGRenderInstanceTable *>(node);
452 if (d->m_instanceDataChanged) {
453 QByteArray buffer = getInstanceBuffer(&d->m_instanceCount);
454 instanceTable->setData(buffer, effectiveInstanceCount(),
sizeof(InstanceTableEntry));
455 d->m_instanceDataChanged =
false;
456 }
else if (d->m_instanceCountOverrideChanged) {
457 instanceTable->setInstanceCountOverride(effectiveInstanceCount());
459 d->m_instanceCountOverrideChanged =
false;
460 instanceTable->setHasTransparency(d->m_hasTransparency);
461 instanceTable->setDepthSorting(d->m_depthSortingEnabled);
462 instanceTable->setShadowBoundsMinimum(d->m_shadowBoundsMinimum);
463 instanceTable->setShadowBoundsMaximum(d->m_shadowBoundsMaximum);
468 const QVector3D &eulerRotation,
const QColor &color,
469 const QVector4D &customData)
473 xform(0, 0) = scale[0];
474 xform(1, 1) = scale[1];
475 xform(2, 2) = scale[2];
477 QQuaternion quaternion = QQuaternion::fromEulerAngles(eulerRotation);
478 xform = QMatrix4x4(quaternion.toRotationMatrix()) * xform;
480 xform(0, 3) += position[0];
481 xform(1, 3) += position[1];
482 xform(2, 3) += position[2];
484 auto linearColor = QSSGUtils::color::sRGBToLinear(color);
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518QQuick3DInstancing::InstanceTableEntry QQuick3DInstancing::calculateTableEntry(
const QVector3D &position,
const QVector3D &scale,
519 const QVector3D &eulerRotation,
const QColor &color,
const QVector4D &customData)
521 return calculate(position, scale, eulerRotation, color, customData);
525
526
527
528
529
530
531
532
533
534
535
536QQuick3DInstancing::InstanceTableEntry QQuick3DInstancing::calculateTableEntryFromQuaternion(
const QVector3D &position,
const QVector3D &scale,
const QQuaternion &rotation,
const QColor &color,
const QVector4D &customData)
540 xform(0, 0) = scale[0];
541 xform(1, 1) = scale[1];
542 xform(2, 2) = scale[2];
544 xform = QMatrix4x4(rotation.toRotationMatrix()) * xform;
546 xform(0, 3) += position[0];
547 xform(1, 3) += position[1];
548 xform(2, 3) += position[2];
550 auto linearColor = QSSGUtils::color::sRGBToLinear(color);
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
616
617
618
619
620
622QQuick3DInstanceList::QQuick3DInstanceList(QQuick3DObject *parent) : QQuick3DInstancing(parent) {}
624QQuick3DInstanceList::~QQuick3DInstanceList() {}
626QByteArray QQuick3DInstanceList::getInstanceBuffer(
int *instanceCount)
629 generateInstanceData();
631 *instanceCount = m_instances.size();
632 return m_instanceData;
635QQmlListProperty<QQuick3DInstanceListEntry> QQuick3DInstanceList::instances()
638 return QQmlListProperty<QQuick3DInstanceListEntry>(
this,
640 qmlAppendInstanceListEntry,
641 qmlInstanceListEntriesCount,
642 qmlInstanceListEntryAt,
643 qmlClearInstanceListEntries);
647
648
649
650
651
653int QQuick3DInstanceList::instanceCount()
const
655 return m_instances.size();
658void QQuick3DInstanceList::onInstanceDestroyed(QObject *object)
660 if (m_instances.removeAll(object))
661 handleInstanceChange();
664void QQuick3DInstanceList::qmlAppendInstanceListEntry(QQmlListProperty<QQuick3DInstanceListEntry> *list, QQuick3DInstanceListEntry *instance)
666 if (instance ==
nullptr)
668 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
669 self->m_instances.push_back(instance);
671 if (instance->parentItem() ==
nullptr)
672 instance->setParentItem(self);
673 connect(instance, &QQuick3DInstanceListEntry::changed, self, &QQuick3DInstanceList::handleInstanceChange);
674 connect(instance, &QObject::destroyed, self, &QQuick3DInstanceList::onInstanceDestroyed);
675 self->handleInstanceChange();
678QQuick3DInstanceListEntry *QQuick3DInstanceList::qmlInstanceListEntryAt(QQmlListProperty<QQuick3DInstanceListEntry> *list, qsizetype index)
680 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
681 return self->m_instances.at(index);
684qsizetype QQuick3DInstanceList::qmlInstanceListEntriesCount(QQmlListProperty<QQuick3DInstanceListEntry> *list)
686 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
687 return self->m_instances.size();
690void QQuick3DInstanceList::qmlClearInstanceListEntries(QQmlListProperty<QQuick3DInstanceListEntry> *list)
692 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
693 for (
auto *instance : std::as_const(self->m_instances)) {
694 disconnect(instance, &QObject::destroyed, self, &QQuick3DInstanceList::onInstanceDestroyed);
695 disconnect(instance, &QQuick3DInstanceListEntry::changed, self, &QQuick3DInstanceList::handleInstanceChange);
697 self->m_instances.clear();
698 self->handleInstanceChange();
701void QQuick3DInstanceList::handleInstanceChange()
705 emit instanceCountChanged();
708void QQuick3DInstanceList::generateInstanceData()
711 const int count = m_instances.size();
713 qsizetype tableSize = count *
sizeof(InstanceTableEntry);
714 m_instanceData.resize(tableSize);
715 auto *array =
reinterpret_cast<InstanceTableEntry*>(m_instanceData.data());
716 for (
int i = 0; i < count; ++i) {
717 const auto *inst = m_instances.at(i);
718 if (inst->m_useEulerRotation)
719 array[i] = calculateTableEntry(inst->position(), inst->scale(), inst->eulerRotation(), inst->color(), inst->customData());
721 array[i] = calculateTableEntryFromQuaternion(inst->position(), inst->scale(), inst->rotation(), inst->color(), inst->customData());
726
727
728
729
730
731
732
733
734
735
736
737
739QQuick3DInstanceListEntry::QQuick3DInstanceListEntry(QQuick3DObject *parent)
740 : QQuick3DObject(parent)
745
746
747
748
749void QQuick3DInstanceListEntry::setPosition(QVector3D position)
751 if (m_position == position)
754 m_position = position;
755 emit positionChanged();
760
761
762
763
764void QQuick3DInstanceListEntry::setScale(QVector3D scale)
766 if (m_scale == scale)
775
776
777
778
779
780void QQuick3DInstanceListEntry::setEulerRotation(QVector3D eulerRotation)
782 if (m_useEulerRotation && m_eulerRotation == eulerRotation)
784 m_eulerRotation = eulerRotation;
785 m_useEulerRotation =
true;
786 emit eulerRotationChanged();
791
792
793
794
795void QQuick3DInstanceListEntry::setRotation(QQuaternion rotation)
797 if (!m_useEulerRotation && m_rotation == rotation)
800 m_rotation = rotation;
801 m_useEulerRotation =
false;
802 emit rotationChanged();
807
808
809
810
811void QQuick3DInstanceListEntry::setColor(QColor color)
813 if (m_color == color)
822
823
824
825
826
827void QQuick3DInstanceListEntry::setCustomData(QVector4D customData)
829 if (m_customData == customData)
832 m_customData = customData;
833 emit customDataChanged();
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
896
897
898
899
900
901
902
903
906
907
908
909
910
916 char magic[4] = {
'Q',
't',
'I',
'R' };
925
926
927
928
929
930bool QQuick3DFileInstancing::writeInstanceTable(QIODevice *out,
const QByteArray &instanceData,
int instanceCount)
932 QQuick3DInstancingBinaryFileHeader header;
934 header.offset =
sizeof(header);
935 header.count = instanceCount;
937 if (instanceData.size() != qsizetype(header.stride) * instanceCount) {
938 qWarning() <<
"inconsistent data";
944 out->write(
reinterpret_cast<
const char *>(&header),
sizeof(header));
945 out->write(instanceData.constData(), instanceData.size());
950bool QQuick3DFileInstancing::loadFromBinaryFile(
const QString &filename)
952 auto binaryFile = std::make_unique<QFile>(filename);
953 if (!binaryFile->open(QFile::ReadOnly))
956 constexpr auto headerSize =
sizeof(QQuick3DInstancingBinaryFileHeader);
957 const quint64 fileSize = binaryFile->size();
958 if (fileSize < headerSize) {
959 qWarning() <<
"data file too small";
962 const char *data =
reinterpret_cast<
const char *>(binaryFile->map(0, fileSize));
963 const auto *header =
reinterpret_cast<
const QQuick3DInstancingBinaryFileHeader *>(data);
965 if (header->majorVersion > currentMajorVersion) {
966 qWarning() <<
"Version" << header->majorVersion <<
"is too new";
970 if (fileSize != headerSize + header->count * header->stride) {
971 qWarning() <<
"wrong data size";
978 m_dataFile = binaryFile.release();
980 m_instanceData = QByteArray::fromRawData(data + header->offset, header->count * header->stride);
981 m_instanceCount = header->count;
986bool QQuick3DFileInstancing::loadFromXmlFile(
const QString &filename)
989 if (!f.open(QFile::ReadOnly))
993 QXmlStreamReader reader(&f);
997 const auto toVector3D = [](
const QStringView &str) {
999 QTextStream(str.toLocal8Bit()) >> x >> y >> z;
1000 return QVector3D { x, y, z };
1002 const auto toVector4D = [](
const QStringView &str) {
1004 QTextStream(str.toLocal8Bit()) >> x >> y >> z >> w;
1005 return QVector4D { x, y, z, w };
1008 QByteArray instanceData;
1010 while (reader.readNextStartElement()) {
1012 if (reader.name() == QLatin1String(
"InstanceTable")) {
1014 while (reader.readNextStartElement()) {
1015 if (reader.name() == QLatin1String(
"Instance")) {
1016 QColor color = Qt::white;
1018 QVector3D eulerRotation;
1019 QQuaternion quaternion;
1020 bool useQuaternion =
false;
1022 QVector3D scale { 1, 1, 1 };
1023 for (
auto &attr : reader.attributes()) {
1024 if (attr.name() == QLatin1String(
"color")) {
1025 color = QColor::fromString(attr.value());
1026 }
else if (attr.name() == QLatin1String(
"position")) {
1027 position = toVector3D(attr.value());
1028 }
else if (attr.name() == QLatin1String(
"eulerRotation")) {
1029 eulerRotation = toVector3D(attr.value());
1030 }
else if (attr.name() == QLatin1String(
"scale")) {
1031 scale = toVector3D(attr.value());
1032 }
else if (attr.name() == QLatin1String(
"quaternion")) {
1033 quaternion = QQuaternion(toVector4D(attr.value()));
1034 useQuaternion =
true;
1035 }
else if (attr.name() == QLatin1String(
"custom")) {
1036 custom = toVector4D(attr.value());
1039 auto entry = useQuaternion ? calculateTableEntryFromQuaternion(position, scale, quaternion, color, custom)
1040 : calculateTableEntry(position, scale, eulerRotation, color, custom);
1041 instanceData.append(
reinterpret_cast<
const char *>(&entry),
sizeof(entry));
1044 reader.skipCurrentElement();
1047 reader.skipCurrentElement();
1052 m_instanceCount = instances;
1053 m_instanceData = instanceData;
1060int QQuick3DFileInstancing::writeToBinaryFile(QIODevice *out)
1062 bool success = writeInstanceTable(out, m_instanceData, m_instanceCount);
1063 return success ? m_instanceCount : -1;
1066int QQuick3DFileInstancing::instanceCount()
const
1068 return m_instanceCount;
1071bool QQuick3DFileInstancing::loadFromFile(
const QUrl &source)
1073 const QQmlContext *context = qmlContext(
this);
1075 const QString filePath = QQmlFile::urlToLocalFileOrQrc(context ? context->resolvedUrl(source) : source);
1077 if (filePath.endsWith(QStringLiteral(
".bin")))
1078 return loadFromBinaryFile(filePath);
1080 const QString binaryFilePath = filePath + QStringLiteral(
".bin");
1082 int oldCount = m_instanceCount;
1083 bool success = loadFromBinaryFile(binaryFilePath) || loadFromXmlFile(filePath);
1084 if (m_instanceCount != oldCount)
1085 emit instanceCountChanged();
1090QQuick3DFileInstancing::QQuick3DFileInstancing(QQuick3DObject *parent) : QQuick3DInstancing(parent) { }
1092QQuick3DFileInstancing::~QQuick3DFileInstancing()
1097const QUrl &QQuick3DFileInstancing::source()
const
1102void QQuick3DFileInstancing::setSource(
const QUrl &newSource)
1104 if (m_source == newSource)
1106 m_source = newSource;
1110 emit sourceChanged();
1113QByteArray QQuick3DFileInstancing::getInstanceBuffer(
int *instanceCount)
1116 if (!loadFromFile(m_source)) {
1117 qWarning() << Q_FUNC_INFO <<
"could not load" << m_source;
1118 m_instanceData = {};
1119 m_instanceCount = 0;
1125 *instanceCount = m_instanceCount;
1126 return m_instanceData;
1129static_assert(
sizeof(QQuick3DInstancing::InstanceTableEntry) ==
sizeof(QSSGRenderInstanceTableEntry)
1130 &&
alignof(QQuick3DInstancing::InstanceTableEntry) ==
alignof(QSSGRenderInstanceTableEntry),
1131 "QSSGRenderInstanceTableEntry and QQuick3DInstancing::InstanceTableEntry do not match");
static constexpr quint16 currentMajorVersion
\qmltype FileInstancing \inherits Instancing \inqmlmodule QtQuick3D
static QQuick3DInstancing::InstanceTableEntry calculate(const QVector3D &position, const QVector3D &scale, const QVector3D &eulerRotation, const QColor &color, const QVector4D &customData)