| 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 | |