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
84
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 : 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' };
928 header.offset =
sizeof(header);
929 header.count = instanceCount;
931 if (instanceData.size() != qsizetype(header.stride) * instanceCount) {
932 qWarning() <<
"inconsistent data";
938 out->write(
reinterpret_cast<
const char *>(&header),
sizeof(header));
939 out->write(instanceData.constData(), instanceData.size());
944bool QQuick3DFileInstancing::loadFromBinaryFile(
const QString &filename)
946 auto binaryFile = std::make_unique<QFile>(filename);
947 if (!binaryFile->open(QFile::ReadOnly))
950 constexpr auto headerSize =
sizeof(QQuick3DInstancingBinaryFileHeader);
951 const quint64 fileSize = binaryFile->size();
952 if (fileSize < headerSize) {
953 qWarning() <<
"data file too small";
956 const char *data =
reinterpret_cast<
const char *>(binaryFile->map(0, fileSize));
957 const auto *header =
reinterpret_cast<
const QQuick3DInstancingBinaryFileHeader *>(data);
959 if (header->majorVersion > currentMajorVersion) {
960 qWarning() <<
"Version" << header->majorVersion <<
"is too new";
964 if (fileSize != headerSize + header->count * header->stride) {
965 qWarning() <<
"wrong data size";
972 m_dataFile = binaryFile.release();
974 m_instanceData = QByteArray::fromRawData(data + header->offset, header->count * header->stride);
975 m_instanceCount = header->count;
980bool QQuick3DFileInstancing::loadFromXmlFile(
const QString &filename)
983 if (!f.open(QFile::ReadOnly))
987 QXmlStreamReader reader(&f);
991 const auto toVector3D = [](
const QStringView &str) {
993 QTextStream(str.toLocal8Bit()) >> x >> y >> z;
994 return QVector3D { x, y, z };
996 const auto toVector4D = [](
const QStringView &str) {
998 QTextStream(str.toLocal8Bit()) >> x >> y >> z >> w;
999 return QVector4D { x, y, z, w };
1002 QByteArray instanceData;
1004 while (reader.readNextStartElement()) {
1006 if (reader.name() == QLatin1String(
"InstanceTable")) {
1008 while (reader.readNextStartElement()) {
1009 if (reader.name() == QLatin1String(
"Instance")) {
1010 QColor color = Qt::white;
1012 QVector3D eulerRotation;
1013 QQuaternion quaternion;
1014 bool useQuaternion =
false;
1016 QVector3D scale { 1, 1, 1 };
1017 for (
auto &attr : reader.attributes()) {
1018 if (attr.name() == QLatin1String(
"color")) {
1019 color = QColor::fromString(attr.value());
1020 }
else if (attr.name() == QLatin1String(
"position")) {
1021 position = toVector3D(attr.value());
1022 }
else if (attr.name() == QLatin1String(
"eulerRotation")) {
1023 eulerRotation = toVector3D(attr.value());
1024 }
else if (attr.name() == QLatin1String(
"scale")) {
1025 scale = toVector3D(attr.value());
1026 }
else if (attr.name() == QLatin1String(
"quaternion")) {
1027 quaternion = QQuaternion(toVector4D(attr.value()));
1028 useQuaternion =
true;
1029 }
else if (attr.name() == QLatin1String(
"custom")) {
1030 custom = toVector4D(attr.value());
1033 auto entry = useQuaternion ? calculateTableEntryFromQuaternion(position, scale, quaternion, color, custom)
1034 : calculateTableEntry(position, scale, eulerRotation, color, custom);
1035 instanceData.append(
reinterpret_cast<
const char *>(&entry),
sizeof(entry));
1038 reader.skipCurrentElement();
1041 reader.skipCurrentElement();
1046 m_instanceCount = instances;
1047 m_instanceData = instanceData;
1054int QQuick3DFileInstancing::writeToBinaryFile(QIODevice *out)
1056 bool success = writeInstanceTable(out, m_instanceData, m_instanceCount);
1057 return success ? m_instanceCount : -1;
1060int QQuick3DFileInstancing::instanceCount()
const
1062 return m_instanceCount;
1065bool QQuick3DFileInstancing::loadFromFile(
const QUrl &source)
1067 const QQmlContext *context = qmlContext(
this);
1069 const QString filePath = QQmlFile::urlToLocalFileOrQrc(context ? context->resolvedUrl(source) : source);
1071 if (filePath.endsWith(QStringLiteral(
".bin")))
1072 return loadFromBinaryFile(filePath);
1074 const QString binaryFilePath = filePath + QStringLiteral(
".bin");
1076 int oldCount = m_instanceCount;
1077 bool success = loadFromBinaryFile(binaryFilePath) || loadFromXmlFile(filePath);
1078 if (m_instanceCount != oldCount)
1079 emit instanceCountChanged();
1084QQuick3DFileInstancing::QQuick3DFileInstancing(QQuick3DObject *parent) : QQuick3DInstancing(parent) { }
1086QQuick3DFileInstancing::~QQuick3DFileInstancing()
1091const QUrl &QQuick3DFileInstancing::source()
const
1096void QQuick3DFileInstancing::setSource(
const QUrl &newSource)
1098 if (m_source == newSource)
1100 m_source = newSource;
1104 emit sourceChanged();
1107QByteArray QQuick3DFileInstancing::getInstanceBuffer(
int *instanceCount)
1110 if (!loadFromFile(m_source)) {
1111 qWarning() << Q_FUNC_INFO <<
"could not load" << m_source;
1112 m_instanceData = {};
1113 m_instanceCount = 0;
1119 *instanceCount = m_instanceCount;
1120 return m_instanceData;
1123static_assert(
sizeof(QQuick3DInstancing::InstanceTableEntry) ==
sizeof(QSSGRenderInstanceTableEntry)
1124 &&
alignof(QQuick3DInstancing::InstanceTableEntry) ==
alignof(QSSGRenderInstanceTableEntry),
1125 "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)
static bool writeInstanceTable(QIODevice *out, const QByteArray &instanceData, int instanceCount)