| 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 | \qmltype InstanceModel |
| 12 | \inherits Object3D |
| 13 | \inqmlmodule QtQuick3D.Helpers |
| 14 | \since 6.4 |
| 15 | \brief Defines a data model based on an instance table. |
| 16 | |
| 17 | The InstanceModel QML type is a data model that provides access to the elements of an \l Instancing table. |
| 18 | |
| 19 | The following roles are available: |
| 20 | \table |
| 21 | \header |
| 22 | \li Role name |
| 23 | \li Description |
| 24 | \row |
| 25 | \li \c modelPosition |
| 26 | \li The position of the instance as a \l vector3d |
| 27 | \row |
| 28 | \li \c modelRotation |
| 29 | \li The rotation of the instance as a \l quaternion |
| 30 | \row |
| 31 | \li \c modelScale |
| 32 | \li The scale of the instance as a \l vector3d |
| 33 | \row |
| 34 | \li \c modelColor |
| 35 | \li The \l color of the instance |
| 36 | \row |
| 37 | \li \c modelData |
| 38 | \li The custom data of the instance as a \l vector4d |
| 39 | \endtable |
| 40 | |
| 41 | \sa InstanceRepeater |
| 42 | */ |
| 43 | |
| 44 | /*! |
| 45 | \qmlproperty Instancing InstanceModel::instancingTable |
| 46 | |
| 47 | This property specifies the underlying instance table of the model. |
| 48 | */ |
| 49 | |
| 50 | InstanceModel::InstanceModel(QObject *parent) |
| 51 | : QAbstractListModel(parent) |
| 52 | { |
| 53 | } |
| 54 | |
| 55 | QVariant InstanceModel::data(const QModelIndex &index, int role) const |
| 56 | { |
| 57 | if (!index.isValid()) |
| 58 | return QVariant(); |
| 59 | ensureTable(); |
| 60 | |
| 61 | int idx = index.row(); |
| 62 | |
| 63 | if (idx >= m_count) { |
| 64 | qWarning(msg: "InstanceModel: index out of range" ); |
| 65 | return QVariant(); |
| 66 | } |
| 67 | |
| 68 | auto *instanceData = reinterpret_cast<const QQuick3DInstancing::InstanceTableEntry*>(m_instanceData.data()) + idx; |
| 69 | |
| 70 | switch (role) { |
| 71 | case ColorRole: |
| 72 | return instanceData->getColor(); |
| 73 | case PositionRole: |
| 74 | return instanceData->getPosition(); |
| 75 | case RotationRole: |
| 76 | return instanceData->getRotation(); |
| 77 | case ScaleRole: |
| 78 | return instanceData->getScale(); |
| 79 | case CustomDataRole: |
| 80 | return instanceData->instanceData; |
| 81 | } |
| 82 | return QVariant(); |
| 83 | } |
| 84 | |
| 85 | int InstanceModel::rowCount(const QModelIndex &) const |
| 86 | { |
| 87 | ensureTable(); |
| 88 | return m_count; |
| 89 | } |
| 90 | |
| 91 | void InstanceModel::setInstancing(QQuick3DInstancing *instancing) |
| 92 | { |
| 93 | if (m_instancing == instancing) |
| 94 | return; |
| 95 | QObject::disconnect(m_tableConnection); |
| 96 | m_instancing = instancing; |
| 97 | m_tableConnection = QObject::connect(sender: instancing, signal: &QQuick3DInstancing::instanceTableChanged, context: this, slot: &InstanceModel::reset); |
| 98 | emit instancingChanged(); |
| 99 | } |
| 100 | |
| 101 | const QQuick3DInstancing::InstanceTableEntry *InstanceModel::instanceData(int index) const |
| 102 | { |
| 103 | if (index >= m_count) |
| 104 | return nullptr; |
| 105 | return reinterpret_cast<const QQuick3DInstancing::InstanceTableEntry*>(m_instanceData.constData()) + index; |
| 106 | } |
| 107 | |
| 108 | void InstanceModel::ensureTable() const |
| 109 | { |
| 110 | auto *that = const_cast<InstanceModel*>(this); |
| 111 | that->m_instanceData = m_instancing->instanceBuffer(instanceCount: &that->m_count); |
| 112 | } |
| 113 | |
| 114 | void InstanceModel::reset() |
| 115 | { |
| 116 | beginResetModel(); |
| 117 | m_instanceData.clear(); |
| 118 | endResetModel(); |
| 119 | } |
| 120 | |
| 121 | /*! |
| 122 | \qmltype InstanceRepeater |
| 123 | \inherits Repeater3D |
| 124 | \inqmlmodule QtQuick3D.Helpers |
| 125 | \since 6.4 |
| 126 | \brief Instantiates components based on an instance table. |
| 127 | |
| 128 | The InstanceRepeater type is used to create a number of objects based on an |
| 129 | \l Instancing table. It is a \l Repeater3D subtype that takes an Instancing table instead |
| 130 | of a data model, and automatically applies \c position, \c scale, and \c rotation. |
| 131 | |
| 132 | One use case is to implement \l {View3D::pick}{picking} by creating invisible dummy objects |
| 133 | that match the rendered instances. To improve performance, the dummy objects can be created with a |
| 134 | simpler geometry than the instanced models. |
| 135 | |
| 136 | For example: |
| 137 | \qml |
| 138 | InstanceRepeater { |
| 139 | instancingTable: myInstanceTable |
| 140 | Model { |
| 141 | source: "#Cube" |
| 142 | pickable: true |
| 143 | property int instanceIndex: index // expose the index, so we can identify the instance |
| 144 | opacity: 0 |
| 145 | } |
| 146 | } |
| 147 | \endqml |
| 148 | |
| 149 | \sa InstanceModel |
| 150 | */ |
| 151 | |
| 152 | /*! |
| 153 | \qmlproperty Instancing InstanceRepeater::instancingTable |
| 154 | |
| 155 | This property specifies the instance table used by the repeater. |
| 156 | */ |
| 157 | |
| 158 | InstanceRepeater::InstanceRepeater(QQuick3DNode *parent) |
| 159 | : QQuick3DRepeater(parent) |
| 160 | { |
| 161 | } |
| 162 | |
| 163 | QQuick3DInstancing *InstanceRepeater::instancing() const |
| 164 | { |
| 165 | return m_model ? m_model->instancing() : nullptr; |
| 166 | } |
| 167 | |
| 168 | void InstanceRepeater::setInstancing(QQuick3DInstancing *instancing) |
| 169 | { |
| 170 | if (m_model && m_model->instancing() == instancing) |
| 171 | return; |
| 172 | if (!m_model) |
| 173 | m_model = new InstanceModel(this); |
| 174 | m_model->setInstancing(instancing); |
| 175 | setModel(QVariant::fromValue(value: m_model)); |
| 176 | emit instancingChanged(); |
| 177 | } |
| 178 | |
| 179 | void InstanceRepeater::initDelegate(int index, QQuick3DNode *node) |
| 180 | { |
| 181 | Q_ASSERT(m_model); |
| 182 | auto *entry = m_model->instanceData(index); |
| 183 | Q_ASSERT(entry); |
| 184 | node->setPosition(entry->getPosition()); |
| 185 | node->setScale(entry->getScale()); |
| 186 | node->setRotation(entry->getRotation()); |
| 187 | } |
| 188 | |
| 189 | QT_END_NAMESPACE |
| 190 | |