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 | |
8 | QT_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 | |
75 | InstanceModel::InstanceModel(QObject *parent) |
76 | : QAbstractListModel(parent) |
77 | { |
78 | } |
79 | |
80 | QVariant 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 | |
111 | int InstanceModel::rowCount(const QModelIndex &) const |
112 | { |
113 | ensureTable(); |
114 | return m_count; |
115 | } |
116 | |
117 | void 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 | |
127 | const 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 | |
134 | void InstanceModel::ensureTable() const |
135 | { |
136 | auto *that = const_cast<InstanceModel*>(this); |
137 | that->m_instanceData = m_instancing->instanceBuffer(instanceCount: &that->m_count); |
138 | } |
139 | |
140 | void 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 | |
182 | InstanceRepeater::InstanceRepeater(QQuick3DNode *parent) |
183 | : QQuick3DRepeater(parent) |
184 | { |
185 | } |
186 | |
187 | QQuick3DInstancing *InstanceRepeater::instancing() const |
188 | { |
189 | return m_model ? m_model->instancing() : nullptr; |
190 | } |
191 | |
192 | void 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 | |
203 | void 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 | |
213 | QT_END_NAMESPACE |
214 | |