1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "instancerepeater_p.h"
5#include <math.h>
6#include <QMatrix4x4>
7
8QT_BEGIN_NAMESPACE
9
10
11/*!
12 \qmltype InstanceModel
13 \inherits Object3D
14 \inqmlmodule QtQuick3D.Helpers
15 \since 6.4
16 \brief Defines a data model based on an instance table.
17
18 The InstanceModel QML type is a data model that provides access to the elements of an \l Instancing table.
19
20 The following roles are available:
21 \table
22 \header
23 \li Role name
24 \li Description
25 \row
26 \li \c modelPosition
27 \li The position of the instance as a \l vector3d
28 \row
29 \li \c modelRotation
30 \li The rotation of the instance as a \l quaternion
31 \row
32 \li \c modelScale
33 \li The scale of the instance as a \l vector3d
34 \row
35 \li \c modelColor
36 \li The \l color of the instance
37 \row
38 \li \c modelData
39 \li The custom data of the instance as a \l vector4d
40 \endtable
41
42 \sa InstanceRepeater
43*/
44
45
46/*
47 \table
48 \header
49 \li Qt Core Feature
50 \li Brief Description
51 \row
52 \li \l {Signal and Slots}
53 \li Signals and slots are used for communication
54 between objects.
55 \row
56 \li \l {Layout Management}
57 \li The Qt layout system provides a simple
58 and powerful way of specifying the layout
59 of child widgets.
60 \row
61 \li \l {Drag and Drop}
62 \li Drag and drop provides a simple visual
63 mechanism which users can use to transfer
64 information between and within applications.
65 \endtable
66
67 */
68
69/*!
70 \qmlproperty Instancing InstanceModel::instancingTable
71
72 This property specifies the underlying instance table of the model.
73*/
74
75InstanceModel::InstanceModel(QObject *parent)
76 : QAbstractListModel(parent)
77{
78}
79
80QVariant InstanceModel::data(const QModelIndex &index, int role) const
81{
82 if (!index.isValid())
83 return QVariant();
84 ensureTable();
85
86
87 int idx = index.row();
88
89 if (idx >= m_count) {
90 qWarning(msg: "That's not supposed to happen...");
91 return QVariant();
92 }
93
94 auto *instanceData = reinterpret_cast<const QQuick3DInstancing::InstanceTableEntry*>(m_instanceData.data()) + idx;
95
96 switch (role) {
97 case ColorRole:
98 return instanceData->getColor();
99 case PositionRole:
100 return instanceData->getPosition();
101 case RotationRole:
102 return instanceData->getRotation();
103 case ScaleRole:
104 return instanceData->getScale();
105 case CustomDataRole:
106 return instanceData->instanceData;
107 }
108 return QVariant();
109}
110
111int InstanceModel::rowCount(const QModelIndex &) const
112{
113 ensureTable();
114 return m_count;
115}
116
117void InstanceModel::setInstancing(QQuick3DInstancing *instancing)
118{
119 if (m_instancing == instancing)
120 return;
121 QObject::disconnect(m_tableConnection);
122 m_instancing = instancing;
123 m_tableConnection = QObject::connect(sender: instancing, signal: &QQuick3DInstancing::instanceTableChanged, context: this, slot: &InstanceModel::reset);
124 emit instancingChanged();
125}
126
127const QQuick3DInstancing::InstanceTableEntry *InstanceModel::instanceData(int index) const
128{
129 if (index >= m_count)
130 return nullptr;
131 return reinterpret_cast<const QQuick3DInstancing::InstanceTableEntry*>(m_instanceData.constData()) + index;
132}
133
134void InstanceModel::ensureTable() const
135{
136 auto *that = const_cast<InstanceModel*>(this);
137 that->m_instanceData = m_instancing->instanceBuffer(instanceCount: &that->m_count);
138}
139
140void InstanceModel::reset()
141{
142 m_instanceData.clear();
143}
144
145/*!
146 \qmltype InstanceRepeater
147 \inherits Repeater3D
148 \inqmlmodule QtQuick3D.Helpers
149 \since 6.4
150 \brief Instantiates components based on an instance table.
151
152 The InstanceRepeater type is used to create a number of objects based on an
153 \l Instancing table. It is a \l Repeater3D subtype that takes an Instancing table instead
154 of a data model, and automatically applies \c position, \c scale, and \c rotation.
155
156 One use case is to implement \l {View3D::pick}{picking} by creating invisible dummy objects
157 that match the rendered instances. To improve performance, the dummy objects can be created with a
158 simpler geometry than the instanced models.
159
160 For example:
161 \qml
162 InstanceRepeater {
163 instancingTable: myInstanceTable
164 Model {
165 source: "#Cube"
166 pickable: true
167 property int instanceIndex: index // expose the index, so we can identify the instance
168 opacity: 0
169 }
170 }
171 \endqml
172
173 \sa InstanceModel
174*/
175
176/*!
177 \qmlproperty Instancing InstanceRepeater::instancingTable
178
179 This property specifies the instance table used by the repeater.
180*/
181
182InstanceRepeater::InstanceRepeater(QQuick3DNode *parent)
183 : QQuick3DRepeater(parent)
184{
185}
186
187QQuick3DInstancing *InstanceRepeater::instancing() const
188{
189 return m_model ? m_model->instancing() : nullptr;
190}
191
192void InstanceRepeater::setInstancing(QQuick3DInstancing *instancing)
193{
194 if (m_model && m_model->instancing() == instancing)
195 return;
196 if (!m_model)
197 m_model = new InstanceModel(this);
198 m_model->setInstancing(instancing);
199 setModel(QVariant::fromValue(value: m_model));
200 emit instancingChanged();
201}
202
203void InstanceRepeater::initDelegate(int index, QQuick3DNode *node)
204{
205 Q_ASSERT(m_model);
206 auto *entry = m_model->instanceData(index);
207 Q_ASSERT(entry);
208 node->setPosition(entry->getPosition());
209 node->setScale(entry->getScale());
210 node->setRotation(entry->getRotation());
211}
212
213QT_END_NAMESPACE
214

source code of qtquick3d/src/helpers/instancerepeater.cpp