5#include <QtQuick3DRuntimeRender/private/qssgrenderinstancetable_p.h>
6#include <QtQuick3DUtils/private/qssgutils_p.h>
7#include <QXmlStreamReader>
8#include <QtQml/QQmlFile>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
35
36
37
38
39
42
43
44
45
46
49
50
51
52
53
54
55
56
57
58
61
62
63
64
65
66
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
86
87
88
89
90
91
92
93
94
95
96
99
100
101
102
103
104
105
106
107
108
109
110
113
114
115
116
117
118
119
120
121
122
123
124
127
128
129
130
131
132
133
134
135
136
137
140
141
142
143
144
145
147QQuick3DInstancingPrivate::QQuick3DInstancingPrivate()
148 : QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::ModelInstance)
152QQuick3DInstancing::QQuick3DInstancing(QQuick3DObject *parent)
153 : QQuick3DObject(*
new QQuick3DInstancingPrivate, parent)
157QQuick3DInstancing::~QQuick3DInstancing()
162
163
164
165QByteArray QQuick3DInstancing::instanceBuffer(
int *instanceCount)
167 Q_D(QQuick3DInstancing);
168 QByteArray retval = getInstanceBuffer(instanceCount);
169 if (instanceCount && d->m_instanceCountOverride >= 0)
170 *instanceCount = qMin(d->m_instanceCountOverride, *instanceCount);
174int QQuick3DInstancing::instanceCountOverride()
const
176 Q_D(
const QQuick3DInstancing);
177 return d->m_instanceCountOverride;
180bool QQuick3DInstancing::hasTransparency()
const
182 Q_D(
const QQuick3DInstancing);
183 return d->m_hasTransparency;
186bool QQuick3DInstancing::depthSortingEnabled()
const
188 Q_D(
const QQuick3DInstancing);
189 return d->m_depthSortingEnabled;
192QVector3D QQuick3DInstancing::shadowBoundsMinimum()
const
194 Q_D(
const QQuick3DInstancing);
195 return d->m_shadowBoundsMinimum;
198QVector3D QQuick3DInstancing::shadowBoundsMaximum()
const
200 Q_D(
const QQuick3DInstancing);
201 return d->m_shadowBoundsMaximum;
204const QQuick3DInstancing::InstanceTableEntry *QQuick3DInstancing::getInstanceEntry(
int index)
206 const QByteArray data = getInstanceBuffer(
nullptr);
207 if (index >=
int(data.size() /
sizeof(InstanceTableEntry)))
209 return reinterpret_cast<
const QQuick3DInstancing::InstanceTableEntry*>(data.constData()) + index;
212QVector3D QQuick3DInstancing::InstanceTableEntry::getPosition()
const
214 return QVector3D{ row0[3], row1[3], row2[3] };
217QVector3D QQuick3DInstancing::InstanceTableEntry::getScale()
const
219 const QVector3D col0{row0[0], row1[0], row2[0]};
220 const QVector3D col1{row0[1], row1[1], row2[1]};
221 const QVector3D col2{row0[2], row1[2], row2[2]};
222 const float scaleX = col0.length();
223 const float scaleY = col1.length();
224 const float scaleZ = col2.length();
225 return QVector3D(scaleX, scaleY, scaleZ);
228QQuaternion QQuick3DInstancing::InstanceTableEntry::getRotation()
const
230 const QVector3D col0 = QVector3D(row0[0], row1[0], row2[0]).normalized();
231 const QVector3D col1 = QVector3D(row0[1], row1[1], row2[1]).normalized();
232 const QVector3D col2 = QVector3D(row0[2], row1[2], row2[2]).normalized();
234 const float data3x3[3*3] {
235 col0[0], col1[0], col2[0],
236 col0[1], col1[1], col2[1],
237 col0[2], col1[2], col2[2],
239 QMatrix3x3 rot(data3x3);
240 return QQuaternion::fromRotationMatrix(rot).normalized();
243QColor QQuick3DInstancing::InstanceTableEntry::getColor()
const
245 return QColor::fromRgbF(color[0], color[1], color[2], color[3]);
249
250
251
252
253
254
255
257QVector3D QQuick3DInstancing::instancePosition(
int index)
259 auto *entry = getInstanceEntry(index);
263 return QVector3D{ entry->row0[3], entry->row1[3], entry->row2[3] };
267
268
269
270
271
272
273
275QVector3D QQuick3DInstancing::instanceScale(
int index)
277 auto *entry = getInstanceEntry(index);
280 return entry->getScale();
284
285
286
287
288
289
290
292QQuaternion QQuick3DInstancing::instanceRotation(
int index)
294 const auto *entry = getInstanceEntry(index);
297 return entry->getRotation();
301
302
303
304
305
306
307
309QColor QQuick3DInstancing::instanceColor(
int index)
311 const auto *entry = getInstanceEntry(index);
314 return entry->getColor();
318
319
320
321
322
323
324
326QVector4D QQuick3DInstancing::instanceCustomData(
int index)
328 const auto *entry = getInstanceEntry(index);
331 return entry->instanceData;
334void QQuick3DInstancing::setInstanceCountOverride(
int instanceCountOverride)
336 Q_D(QQuick3DInstancing);
337 if (d->m_instanceCountOverride == instanceCountOverride)
340 d->m_instanceCountOverride = instanceCountOverride;
341 d->m_instanceCountOverrideChanged =
true;
342 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
343 emit instanceCountOverrideChanged();
346void QQuick3DInstancing::setHasTransparency(
bool hasTransparency)
348 Q_D(QQuick3DInstancing);
349 if (d->m_hasTransparency == hasTransparency)
352 d->m_hasTransparency = hasTransparency;
353 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
354 emit hasTransparencyChanged();
357void QQuick3DInstancing::setDepthSortingEnabled(
bool enabled)
359 Q_D(QQuick3DInstancing);
360 if (d->m_depthSortingEnabled == enabled)
363 d->m_depthSortingEnabled = enabled;
364 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
365 emit depthSortingEnabledChanged();
368void QQuick3DInstancing::setShadowBoundsMinimum(
const QVector3D &newShadowBoundsMinimum)
370 Q_D(QQuick3DInstancing);
371 if (d->m_shadowBoundsMinimum == newShadowBoundsMinimum)
374 d->m_shadowBoundsMinimum = newShadowBoundsMinimum;
375 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
376 emit shadowBoundsMinimumChanged();
379void QQuick3DInstancing::setShadowBoundsMaximum(
const QVector3D &newShadowBoundsMaximum)
381 Q_D(QQuick3DInstancing);
382 if (d->m_shadowBoundsMinimum == newShadowBoundsMaximum)
385 d->m_shadowBoundsMaximum = newShadowBoundsMaximum;
386 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
387 emit shadowBoundsMaximumChanged();
391
392
393
394
396void QQuick3DInstancing::markDirty()
398 Q_D(QQuick3DInstancing);
399 d->dirty(QQuick3DObjectPrivate::DirtyType::Content);
400 d->m_instanceDataChanged =
true;
401 emit instanceTableChanged();
404QSSGRenderGraphObject *QQuick3DInstancing::updateSpatialNode(QSSGRenderGraphObject *node)
406 Q_D(QQuick3DInstancing);
409 node =
new QSSGRenderInstanceTable();
410 emit instanceNodeDirty();
411 d->m_instanceDataChanged =
true;
413 QQuick3DObject::updateSpatialNode(node);
414 auto effectiveInstanceCount = [d]() {
415 if (d->m_instanceCountOverride >= 0)
416 return qMin(d->m_instanceCount, d->m_instanceCountOverride);
417 return d->m_instanceCount;
419 auto *instanceTable =
static_cast<QSSGRenderInstanceTable *>(node);
420 if (d->m_instanceDataChanged) {
421 QByteArray buffer = getInstanceBuffer(&d->m_instanceCount);
422 instanceTable->setData(buffer, effectiveInstanceCount(),
sizeof(InstanceTableEntry));
423 d->m_instanceDataChanged =
false;
424 }
else if (d->m_instanceCountOverrideChanged) {
425 instanceTable->setInstanceCountOverride(effectiveInstanceCount());
427 d->m_instanceCountOverrideChanged =
false;
428 instanceTable->setHasTransparency(d->m_hasTransparency);
429 instanceTable->setDepthSorting(d->m_depthSortingEnabled);
430 instanceTable->setShadowBoundsMinimum(d->m_shadowBoundsMinimum);
431 instanceTable->setShadowBoundsMaximum(d->m_shadowBoundsMaximum);
436 const QVector3D &eulerRotation,
const QColor &color,
437 const QVector4D &customData)
441 xform(0, 0) = scale[0];
442 xform(1, 1) = scale[1];
443 xform(2, 2) = scale[2];
445 QQuaternion quaternion = QQuaternion::fromEulerAngles(eulerRotation);
446 xform = QMatrix4x4(quaternion.toRotationMatrix()) * xform;
448 xform(0, 3) += position[0];
449 xform(1, 3) += position[1];
450 xform(2, 3) += position[2];
452 auto linearColor = QSSGUtils::color::sRGBToLinear(color);
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486QQuick3DInstancing::InstanceTableEntry QQuick3DInstancing::calculateTableEntry(
const QVector3D &position,
const QVector3D &scale,
487 const QVector3D &eulerRotation,
const QColor &color,
const QVector4D &customData)
489 return calculate(position, scale, eulerRotation, color, customData);
493
494
495
496
497
498
499
500
501
502
503
504QQuick3DInstancing::InstanceTableEntry QQuick3DInstancing::calculateTableEntryFromQuaternion(
const QVector3D &position,
const QVector3D &scale,
const QQuaternion &rotation,
const QColor &color,
const QVector4D &customData)
508 xform(0, 0) = scale[0];
509 xform(1, 1) = scale[1];
510 xform(2, 2) = scale[2];
512 xform = QMatrix4x4(rotation.toRotationMatrix()) * xform;
514 xform(0, 3) += position[0];
515 xform(1, 3) += position[1];
516 xform(2, 3) += position[2];
518 auto linearColor = QSSGUtils::color::sRGBToLinear(color);
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
584
585
586
587
588
590QQuick3DInstanceList::QQuick3DInstanceList(QQuick3DObject *parent) : QQuick3DInstancing(parent) {}
592QQuick3DInstanceList::~QQuick3DInstanceList() {}
594QByteArray QQuick3DInstanceList::getInstanceBuffer(
int *instanceCount)
597 generateInstanceData();
599 *instanceCount = m_instances.size();
600 return m_instanceData;
603QQmlListProperty<QQuick3DInstanceListEntry> QQuick3DInstanceList::instances()
606 return QQmlListProperty<QQuick3DInstanceListEntry>(
this,
608 qmlAppendInstanceListEntry,
609 qmlInstanceListEntriesCount,
610 qmlInstanceListEntryAt,
611 qmlClearInstanceListEntries);
615
616
617
618
619
621int QQuick3DInstanceList::instanceCount()
const
623 return m_instances.size();
626void QQuick3DInstanceList::onInstanceDestroyed(QObject *object)
628 if (m_instances.removeAll(object))
629 handleInstanceChange();
632void QQuick3DInstanceList::qmlAppendInstanceListEntry(QQmlListProperty<QQuick3DInstanceListEntry> *list, QQuick3DInstanceListEntry *instance)
634 if (instance ==
nullptr)
636 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
637 self->m_instances.push_back(instance);
639 if (instance->parentItem() ==
nullptr)
640 instance->setParentItem(self);
641 connect(instance, &QQuick3DInstanceListEntry::changed, self, &QQuick3DInstanceList::handleInstanceChange);
642 connect(instance, &QObject::destroyed, self, &QQuick3DInstanceList::onInstanceDestroyed);
643 self->handleInstanceChange();
646QQuick3DInstanceListEntry *QQuick3DInstanceList::qmlInstanceListEntryAt(QQmlListProperty<QQuick3DInstanceListEntry> *list, qsizetype index)
648 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
649 return self->m_instances.at(index);
652qsizetype QQuick3DInstanceList::qmlInstanceListEntriesCount(QQmlListProperty<QQuick3DInstanceListEntry> *list)
654 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
655 return self->m_instances.size();
658void QQuick3DInstanceList::qmlClearInstanceListEntries(QQmlListProperty<QQuick3DInstanceListEntry> *list)
660 auto *self =
static_cast<QQuick3DInstanceList *>(list->object);
661 for (
auto *instance : self->m_instances) {
662 disconnect(instance, &QObject::destroyed, self, &QQuick3DInstanceList::onInstanceDestroyed);
663 disconnect(instance, &QQuick3DInstanceListEntry::changed, self, &QQuick3DInstanceList::handleInstanceChange);
665 self->m_instances.clear();
666 self->handleInstanceChange();
669void QQuick3DInstanceList::handleInstanceChange()
673 emit instanceCountChanged();
676void QQuick3DInstanceList::generateInstanceData()
679 const int count = m_instances.size();
681 qsizetype tableSize = count *
sizeof(InstanceTableEntry);
682 m_instanceData.resize(tableSize);
683 auto *array =
reinterpret_cast<InstanceTableEntry*>(m_instanceData.data());
684 for (
int i = 0; i < count; ++i) {
685 const auto *inst = m_instances.at(i);
686 if (inst->m_useEulerRotation)
687 array[i] = calculateTableEntry(inst->position(), inst->scale(), inst->eulerRotation(), inst->color(), inst->customData());
689 array[i] = calculateTableEntryFromQuaternion(inst->position(), inst->scale(), inst->rotation(), inst->color(), inst->customData());
694
695
696
697
698
699
700
701
702
703
704
705
707QQuick3DInstanceListEntry::QQuick3DInstanceListEntry(QQuick3DObject *parent)
708 : QQuick3DObject(parent)
713
714
715
716
717void QQuick3DInstanceListEntry::setPosition(QVector3D position)
719 if (m_position == position)
722 m_position = position;
723 emit positionChanged();
728
729
730
731
732void QQuick3DInstanceListEntry::setScale(QVector3D scale)
734 if (m_scale == scale)
743
744
745
746
747
748void QQuick3DInstanceListEntry::setEulerRotation(QVector3D eulerRotation)
750 if (m_useEulerRotation && m_eulerRotation == eulerRotation)
752 m_eulerRotation = eulerRotation;
753 m_useEulerRotation =
true;
754 emit eulerRotationChanged();
759
760
761
762
763void QQuick3DInstanceListEntry::setRotation(QQuaternion rotation)
765 if (!m_useEulerRotation && m_rotation == rotation)
768 m_rotation = rotation;
769 m_useEulerRotation =
false;
770 emit rotationChanged();
775
776
777
778
779void QQuick3DInstanceListEntry::setColor(QColor color)
781 if (m_color == color)
790
791
792
793
794
795void QQuick3DInstanceListEntry::setCustomData(QVector4D customData)
797 if (m_customData == customData)
800 m_customData = customData;
801 emit customDataChanged();
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
864
865
866
867
868
869
870
871
874
875
876
877
878
884 char magic[4] = {
'Q',
't',
'I',
'R' };
896 header.offset =
sizeof(header);
897 header.count = instanceCount;
899 if (instanceData.size() != qsizetype(header.stride) * instanceCount) {
900 qWarning() <<
"inconsistent data";
906 out->write(
reinterpret_cast<
const char *>(&header),
sizeof(header));
907 out->write(instanceData.constData(), instanceData.size());
912bool QQuick3DFileInstancing::loadFromBinaryFile(
const QString &filename)
914 auto binaryFile = std::make_unique<QFile>(filename);
915 if (!binaryFile->open(QFile::ReadOnly))
918 constexpr auto headerSize =
sizeof(QQuick3DInstancingBinaryFileHeader);
919 const quint64 fileSize = binaryFile->size();
920 if (fileSize < headerSize) {
921 qWarning() <<
"data file too small";
924 const char *data =
reinterpret_cast<
const char *>(binaryFile->map(0, fileSize));
925 const auto *header =
reinterpret_cast<
const QQuick3DInstancingBinaryFileHeader *>(data);
927 if (header->majorVersion > currentMajorVersion) {
928 qWarning() <<
"Version" << header->majorVersion <<
"is too new";
932 if (fileSize != headerSize + header->count * header->stride) {
933 qWarning() <<
"wrong data size";
940 m_dataFile = binaryFile.release();
942 m_instanceData = QByteArray::fromRawData(data + header->offset, header->count * header->stride);
943 m_instanceCount = header->count;
948bool QQuick3DFileInstancing::loadFromXmlFile(
const QString &filename)
951 if (!f.open(QFile::ReadOnly))
955 QXmlStreamReader reader(&f);
959 const auto toVector3D = [](
const QStringView &str) {
961 QTextStream(str.toLocal8Bit()) >> x >> y >> z;
962 return QVector3D { x, y, z };
964 const auto toVector4D = [](
const QStringView &str) {
966 QTextStream(str.toLocal8Bit()) >> x >> y >> z >> w;
967 return QVector4D { x, y, z, w };
970 QByteArray instanceData;
972 while (reader.readNextStartElement()) {
974 if (reader.name() == QLatin1String(
"InstanceTable")) {
976 while (reader.readNextStartElement()) {
977 if (reader.name() == QLatin1String(
"Instance")) {
978 QColor color = Qt::white;
980 QVector3D eulerRotation;
981 QQuaternion quaternion;
982 bool useQuaternion =
false;
984 QVector3D scale { 1, 1, 1 };
985 for (
auto &attr : reader.attributes()) {
986 if (attr.name() == QLatin1String(
"color")) {
987 color = QColor::fromString(attr.value());
988 }
else if (attr.name() == QLatin1String(
"position")) {
989 position = toVector3D(attr.value());
990 }
else if (attr.name() == QLatin1String(
"eulerRotation")) {
991 eulerRotation = toVector3D(attr.value());
992 }
else if (attr.name() == QLatin1String(
"scale")) {
993 scale = toVector3D(attr.value());
994 }
else if (attr.name() == QLatin1String(
"quaternion")) {
995 quaternion = QQuaternion(toVector4D(attr.value()));
996 useQuaternion =
true;
997 }
else if (attr.name() == QLatin1String(
"custom")) {
998 custom = toVector4D(attr.value());
1001 auto entry = useQuaternion ? calculateTableEntryFromQuaternion(position, scale, quaternion, color, custom)
1002 : calculateTableEntry(position, scale, eulerRotation, color, custom);
1003 instanceData.append(
reinterpret_cast<
const char *>(&entry),
sizeof(entry));
1006 reader.skipCurrentElement();
1009 reader.skipCurrentElement();
1014 m_instanceCount = instances;
1015 m_instanceData = instanceData;
1022int QQuick3DFileInstancing::writeToBinaryFile(QIODevice *out)
1024 bool success = writeInstanceTable(out, m_instanceData, m_instanceCount);
1025 return success ? m_instanceCount : -1;
1028int QQuick3DFileInstancing::instanceCount()
const
1030 return m_instanceCount;
1033bool QQuick3DFileInstancing::loadFromFile(
const QUrl &source)
1035 const QQmlContext *context = qmlContext(
this);
1037 const QString filePath = QQmlFile::urlToLocalFileOrQrc(context ? context->resolvedUrl(source) : source);
1039 if (filePath.endsWith(QStringLiteral(
".bin")))
1040 return loadFromBinaryFile(filePath);
1042 const QString binaryFilePath = filePath + QStringLiteral(
".bin");
1044 int oldCount = m_instanceCount;
1045 bool success = loadFromBinaryFile(binaryFilePath) || loadFromXmlFile(filePath);
1046 if (m_instanceCount != oldCount)
1047 emit instanceCountChanged();
1052QQuick3DFileInstancing::QQuick3DFileInstancing(QQuick3DObject *parent) : QQuick3DInstancing(parent) { }
1054QQuick3DFileInstancing::~QQuick3DFileInstancing()
1059const QUrl &QQuick3DFileInstancing::source()
const
1064void QQuick3DFileInstancing::setSource(
const QUrl &newSource)
1066 if (m_source == newSource)
1068 m_source = newSource;
1072 emit sourceChanged();
1075QByteArray QQuick3DFileInstancing::getInstanceBuffer(
int *instanceCount)
1078 if (!loadFromFile(m_source)) {
1079 qWarning() << Q_FUNC_INFO <<
"could not load" << m_source;
1080 m_instanceData = {};
1081 m_instanceCount = 0;
1087 *instanceCount = m_instanceCount;
1088 return m_instanceData;
1091static_assert(
sizeof(QQuick3DInstancing::InstanceTableEntry) ==
sizeof(QSSGRenderInstanceTableEntry)
1092 &&
alignof(QQuick3DInstancing::InstanceTableEntry) ==
alignof(QSSGRenderInstanceTableEntry),
1093 "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)