1 | // Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qentity.h" |
5 | #include "qentity_p.h" |
6 | |
7 | #include <Qt3DCore/qcomponent.h> |
8 | #include <QtCore/QMetaObject> |
9 | #include <QtCore/QMetaProperty> |
10 | |
11 | #include <Qt3DCore/private/corelogging_p.h> |
12 | #include <Qt3DCore/private/qcomponent_p.h> |
13 | #include <Qt3DCore/private/qscene_p.h> |
14 | |
15 | #include <QQueue> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | namespace { |
20 | |
21 | QString dumpNode(const Qt3DCore::QEntity *n) { |
22 | auto formatNode = [](const Qt3DCore::QNode *n) { |
23 | QString res = QString(QLatin1String("%1{%2}" )) |
24 | .arg(a: QLatin1String(n->metaObject()->className())) |
25 | .arg(a: n->id().id()); |
26 | if (!n->objectName().isEmpty()) |
27 | res += QString(QLatin1String(" (%1)" )).arg(a: n->objectName()); |
28 | if (!n->isEnabled()) |
29 | res += QLatin1String(" [D]" ); |
30 | return res; |
31 | }; |
32 | |
33 | QString res = formatNode(n); |
34 | const auto &components = n->components(); |
35 | if (components.size()) { |
36 | QStringList componentNames; |
37 | for (const auto &c : components) |
38 | componentNames += formatNode(c); |
39 | res += QString(QLatin1String(" [ %1 ]" )).arg(a: componentNames.join(sep: QLatin1String(", " ))); |
40 | } |
41 | |
42 | return res; |
43 | } |
44 | |
45 | QStringList dumpSG(const Qt3DCore::QNode *n, int level = 0) |
46 | { |
47 | QStringList reply; |
48 | const auto *entity = qobject_cast<const Qt3DCore::QEntity *>(object: n); |
49 | if (entity != nullptr) { |
50 | QString res = dumpNode(n: entity); |
51 | reply += res.rightJustified(width: res.size() + level * 2, fill: QLatin1Char(' ')); |
52 | level++; |
53 | } |
54 | |
55 | const auto children = n->childNodes(); |
56 | for (auto *child: children) |
57 | reply += dumpSG(n: child, level); |
58 | |
59 | return reply; |
60 | } |
61 | |
62 | } |
63 | |
64 | namespace Qt3DCore { |
65 | |
66 | /*! |
67 | \class Qt3DCore::QEntity |
68 | \inmodule Qt3DCore |
69 | \inherits Qt3DCore::QNode |
70 | \since 5.5 |
71 | |
72 | \brief Qt3DCore::QEntity is a Qt3DCore::QNode subclass that can aggregate several |
73 | Qt3DCore::QComponent instances that will specify its behavior. |
74 | |
75 | By itself a Qt3DCore::QEntity is an empty shell. The behavior of a Qt3DCore::QEntity |
76 | object is defined by the Qt3DCore::QComponent objects it references. Each Qt3D |
77 | backend aspect will be able to interpret and process an Entity by |
78 | recognizing which components it is made up of. One aspect may decide to only |
79 | process entities composed of a single Qt3DCore::QTransform component whilst |
80 | another may focus on Qt3DInput::QMouseHandler. |
81 | |
82 | \sa Qt3DCore::QComponent, Qt3DCore::QTransform |
83 | */ |
84 | |
85 | /*! |
86 | \fn template<typename T> QList<T *> Qt3DCore::QEntity::componentsOfType() const |
87 | |
88 | Returns all the components added to this entity that can be cast to |
89 | type T or an empty vector if there are no such components. |
90 | */ |
91 | |
92 | /*! \internal */ |
93 | QEntityPrivate::QEntityPrivate() |
94 | : QNodePrivate() |
95 | , m_parentEntityId() |
96 | , m_dirty(false) |
97 | {} |
98 | |
99 | /*! \internal */ |
100 | QEntityPrivate::~QEntityPrivate() |
101 | { |
102 | } |
103 | |
104 | /*! \internal */ |
105 | QEntityPrivate *QEntityPrivate::get(QEntity *q) |
106 | { |
107 | return q->d_func(); |
108 | } |
109 | |
110 | /*! \internal */ |
111 | void QEntityPrivate::removeDestroyedComponent(QComponent *comp) |
112 | { |
113 | // comp is actually no longer a QComponent, just a QObject |
114 | |
115 | Q_CHECK_PTR(comp); |
116 | qCDebug(Nodes) << Q_FUNC_INFO << comp; |
117 | |
118 | updateComponentRelationShip(component: comp, change: ComponentRelationshipChange::Removed); |
119 | m_components.removeOne(t: comp); |
120 | m_dirty = true; |
121 | |
122 | // Remove bookkeeping connection |
123 | unregisterDestructionHelper(node: comp); |
124 | } |
125 | |
126 | /*! |
127 | Constructs a new Qt3DCore::QEntity instance with \a parent as parent. |
128 | */ |
129 | QEntity::QEntity(QNode *parent) |
130 | : QEntity(*new QEntityPrivate, parent) {} |
131 | |
132 | /*! \internal */ |
133 | QEntity::QEntity(QEntityPrivate &dd, QNode *parent) |
134 | : QNode(dd, parent) |
135 | { |
136 | connect(sender: this, signal: &QNode::parentChanged, context: this, slot: &QEntity::onParentChanged); |
137 | } |
138 | |
139 | QEntity::~QEntity() |
140 | { |
141 | // remove all component aggregations |
142 | Q_D(const QEntity); |
143 | // to avoid hammering m_components by repeated removeComponent() |
144 | // calls below, move all contents out, so the removeOne() calls in |
145 | // removeComponent() don't actually remove something: |
146 | const auto components = std::move(d->m_components); |
147 | for (QComponent *comp : components) |
148 | removeComponent(comp); |
149 | } |
150 | |
151 | |
152 | /*! |
153 | \typedef Qt3DCore::QComponentVector |
154 | \relates Qt3DCore::QEntity |
155 | |
156 | List of QComponent pointers. |
157 | */ |
158 | |
159 | /*! |
160 | Returns the list of Qt3DCore::QComponent instances the entity is referencing. |
161 | */ |
162 | QComponentVector QEntity::components() const |
163 | { |
164 | Q_D(const QEntity); |
165 | return d->m_components; |
166 | } |
167 | |
168 | /*! |
169 | Adds a new reference to the component \a comp. |
170 | |
171 | \note If the Qt3DCore::QComponent has no parent, the Qt3DCore::QEntity will set |
172 | itself as its parent thereby taking ownership of the component. |
173 | */ |
174 | void QEntity::addComponent(QComponent *comp) |
175 | { |
176 | Q_D(QEntity); |
177 | Q_CHECK_PTR( comp ); |
178 | qCDebug(Nodes) << Q_FUNC_INFO << comp; |
179 | |
180 | // A Component can only be aggregated once |
181 | if (d->m_components.count(t: comp) != 0) |
182 | return ; |
183 | |
184 | // We need to add it as a child of the current node if it has been declared inline |
185 | // Or not previously added as a child of the current node so that |
186 | // 1) The backend gets notified about it's creation |
187 | // 2) When the current node is destroyed, it gets destroyed as well |
188 | if (!comp->parent()) |
189 | comp->setParent(this); |
190 | |
191 | QNodePrivate::get(q: comp)->_q_ensureBackendNodeCreated(); |
192 | |
193 | d->m_components.append(t: comp); |
194 | d->m_dirty = true; |
195 | |
196 | // Ensures proper bookkeeping |
197 | d->registerPrivateDestructionHelper(node: comp, func: &QEntityPrivate::removeDestroyedComponent); |
198 | |
199 | d->updateComponentRelationShip(component: comp, change: ComponentRelationshipChange::Added); |
200 | static_cast<QComponentPrivate *>(QComponentPrivate::get(q: comp))->addEntity(entity: this); |
201 | } |
202 | |
203 | /*! |
204 | Removes the reference to \a comp. |
205 | */ |
206 | void QEntity::removeComponent(QComponent *comp) |
207 | { |
208 | Q_CHECK_PTR(comp); |
209 | qCDebug(Nodes) << Q_FUNC_INFO << comp; |
210 | Q_D(QEntity); |
211 | |
212 | static_cast<QComponentPrivate *>(QComponentPrivate::get(q: comp))->removeEntity(entity: this); |
213 | |
214 | d->updateComponentRelationShip(component: comp, change: ComponentRelationshipChange::Removed); |
215 | |
216 | d->m_components.removeOne(t: comp); |
217 | d->m_dirty = true; |
218 | |
219 | // Remove bookkeeping connection |
220 | d->unregisterDestructionHelper(node: comp); |
221 | } |
222 | |
223 | /*! |
224 | Returns the parent Qt3DCore::QEntity instance of this entity. If the |
225 | immediate parent isn't a Qt3DCore::QEntity, this function traverses up the |
226 | scene hierarchy until a parent Qt3DCore::QEntity is found. If no |
227 | Qt3DCore::QEntity parent can be found, returns null. |
228 | */ |
229 | QEntity *QEntity::parentEntity() const |
230 | { |
231 | Q_D(const QEntity); |
232 | QNode *parentNode = QNode::parentNode(); |
233 | QEntity *parentEntity = qobject_cast<QEntity *>(object: parentNode); |
234 | |
235 | while (parentEntity == nullptr && parentNode != nullptr) { |
236 | parentNode = parentNode->parentNode(); |
237 | parentEntity = qobject_cast<QEntity*>(object: parentNode); |
238 | } |
239 | if (!parentEntity) { |
240 | if (!d->m_parentEntityId.isNull()) |
241 | d->m_parentEntityId = QNodeId(); |
242 | } else { |
243 | if (d->m_parentEntityId != parentEntity->id()) |
244 | d->m_parentEntityId = parentEntity->id(); |
245 | } |
246 | return parentEntity; |
247 | } |
248 | |
249 | /* |
250 | \internal |
251 | |
252 | Returns the Qt3DCore::QNodeId id of the parent Qt3DCore::QEntity instance of the |
253 | current Qt3DCore::QEntity object. The QNodeId isNull method will return true if |
254 | there is no Qt3DCore::QEntity parent of the current Qt3DCore::QEntity in the scene |
255 | hierarchy. |
256 | */ |
257 | QNodeId QEntityPrivate::parentEntityId() const |
258 | { |
259 | Q_Q(const QEntity); |
260 | if (m_parentEntityId.isNull()) |
261 | q->parentEntity(); |
262 | return m_parentEntityId; |
263 | } |
264 | |
265 | QString QEntityPrivate::dumpSceneGraph() const |
266 | { |
267 | Q_Q(const QEntity); |
268 | return dumpSG(n: q).join(sep: QLatin1Char('\n')); |
269 | } |
270 | |
271 | void QEntityPrivate::updateComponentRelationShip(QComponent *component, ComponentRelationshipChange::RelationShip change) |
272 | { |
273 | if (m_changeArbiter) { |
274 | // Ensure node has its postConstructorInit called if we reach this |
275 | // point, we could otherwise endup referencing a node that has yet |
276 | // to be created in the backend |
277 | QNodePrivate::get(q: component)->_q_ensureBackendNodeCreated(); |
278 | |
279 | Q_Q(QEntity); |
280 | m_changeArbiter->addDirtyEntityComponentNodes(entity: q, component, change); |
281 | } |
282 | } |
283 | |
284 | void QEntity::onParentChanged(QObject *) |
285 | { |
286 | Q_D(QEntity); |
287 | if (!d->m_hasBackendNode) |
288 | return; |
289 | |
290 | d->update(); |
291 | } |
292 | |
293 | } // namespace Qt3DCore |
294 | |
295 | QT_END_NAMESPACE |
296 | |
297 | #include "moc_qentity.cpp" |
298 | |