1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qentity.h"
41#include "qentity_p.h"
42
43#include <Qt3DCore/qcomponent.h>
44#include <Qt3DCore/qnodecreatedchange.h>
45#include <QtCore/QMetaObject>
46#include <QtCore/QMetaProperty>
47
48#include <Qt3DCore/private/corelogging_p.h>
49#include <Qt3DCore/private/qcomponent_p.h>
50#include <Qt3DCore/private/qscene_p.h>
51
52#include <QQueue>
53
54QT_BEGIN_NAMESPACE
55
56namespace {
57
58QString dumpNode(const Qt3DCore::QEntity *n) {
59 auto formatNode = [](const Qt3DCore::QNode *n) {
60 QString res = QString(QLatin1String("%1{%2}"))
61 .arg(a: QLatin1String(n->metaObject()->className()))
62 .arg(a: n->id().id());
63 if (!n->objectName().isEmpty())
64 res += QString(QLatin1String(" (%1)")).arg(a: n->objectName());
65 if (!n->isEnabled())
66 res += QLatin1String(" [D]");
67 return res;
68 };
69
70 QString res = formatNode(n);
71 const auto &components = n->components();
72 if (components.size()) {
73 QStringList componentNames;
74 for (const auto &c : components)
75 componentNames += formatNode(c);
76 res += QString(QLatin1String(" [ %1 ]")).arg(a: componentNames.join(sep: QLatin1String(", ")));
77 }
78
79 return res;
80}
81
82QStringList dumpSG(const Qt3DCore::QNode *n, int level = 0)
83{
84 QStringList reply;
85 const auto *entity = qobject_cast<const Qt3DCore::QEntity *>(object: n);
86 if (entity != nullptr) {
87 QString res = dumpNode(n: entity);
88 reply += res.rightJustified(width: res.length() + level * 2, fill: ' ');
89 level++;
90 }
91
92 const auto children = n->childNodes();
93 for (auto *child: children)
94 reply += dumpSG(n: child, level);
95
96 return reply;
97}
98
99}
100
101namespace Qt3DCore {
102
103/*!
104 \class Qt3DCore::QEntity
105 \inmodule Qt3DCore
106 \inherits Qt3DCore::QNode
107 \since 5.5
108
109 \brief Qt3DCore::QEntity is a Qt3DCore::QNode subclass that can aggregate several
110 Qt3DCore::QComponent instances that will specify its behavior.
111
112 By itself a Qt3DCore::QEntity is an empty shell. The behavior of a Qt3DCore::QEntity
113 object is defined by the Qt3DCore::QComponent objects it references. Each Qt3D
114 backend aspect will be able to interpret and process an Entity by
115 recognizing which components it is made up of. One aspect may decide to only
116 process entities composed of a single Qt3DCore::QTransform component whilst
117 another may focus on Qt3DInput::QMouseHandler.
118
119 \sa Qt3DCore::QComponent, Qt3DCore::QTransform
120 */
121
122/*!
123 \fn template<typename T> QVector<T *> Qt3DCore::QEntity::componentsOfType() const
124
125 Returns all the components added to this entity that can be cast to
126 type T or an empty vector if there are no such components.
127*/
128
129/*! \internal */
130QEntityPrivate::QEntityPrivate()
131 : QNodePrivate()
132 , m_parentEntityId()
133{}
134
135/*! \internal */
136QEntityPrivate::~QEntityPrivate()
137{
138}
139
140/*! \internal */
141void QEntityPrivate::removeDestroyedComponent(QComponent *comp)
142{
143 // comp is actually no longer a QComponent, just a QObject
144
145 Q_CHECK_PTR(comp);
146 qCDebug(Nodes) << Q_FUNC_INFO << comp;
147
148 updateNode(node: comp, property: nullptr, change: ComponentRemoved);
149 m_components.removeOne(t: comp);
150
151 // Remove bookkeeping connection
152 unregisterDestructionHelper(node: comp);
153}
154
155/*!
156 Constructs a new Qt3DCore::QEntity instance with \a parent as parent.
157 */
158QEntity::QEntity(QNode *parent)
159 : QEntity(*new QEntityPrivate, parent) {}
160
161/*! \internal */
162QEntity::QEntity(QEntityPrivate &dd, QNode *parent)
163 : QNode(dd, parent)
164{
165 connect(sender: this, signal: &QNode::parentChanged, receiver: this, slot: &QEntity::onParentChanged);
166}
167
168QEntity::~QEntity()
169{
170 // remove all component aggregations
171 Q_D(const QEntity);
172 // to avoid hammering m_components by repeated removeComponent()
173 // calls below, move all contents out, so the removeOne() calls in
174 // removeComponent() don't actually remove something:
175 const auto components = std::move(d->m_components);
176 for (QComponent *comp : components)
177 removeComponent(comp);
178}
179
180
181/*!
182 \typedef Qt3DCore::QComponentVector
183 \relates Qt3DCore::QEntity
184
185 List of QComponent pointers.
186 */
187
188/*!
189 Returns the list of Qt3DCore::QComponent instances the entity is referencing.
190 */
191QComponentVector QEntity::components() const
192{
193 Q_D(const QEntity);
194 return d->m_components;
195}
196
197/*!
198 Adds a new reference to the component \a comp.
199
200 \note If the Qt3DCore::QComponent has no parent, the Qt3DCore::QEntity will set
201 itself as its parent thereby taking ownership of the component.
202 */
203void QEntity::addComponent(QComponent *comp)
204{
205 Q_D(QEntity);
206 Q_CHECK_PTR( comp );
207 qCDebug(Nodes) << Q_FUNC_INFO << comp;
208
209 // A Component can only be aggregated once
210 if (d->m_components.count(t: comp) != 0)
211 return ;
212
213 // We need to add it as a child of the current node if it has been declared inline
214 // Or not previously added as a child of the current node so that
215 // 1) The backend gets notified about it's creation
216 // 2) When the current node is destroyed, it gets destroyed as well
217 if (!comp->parent())
218 comp->setParent(this);
219
220 QNodePrivate::get(q: comp)->_q_ensureBackendNodeCreated();
221
222 d->m_components.append(t: comp);
223
224 // Ensures proper bookkeeping
225 d->registerPrivateDestructionHelper(node: comp, func: &QEntityPrivate::removeDestroyedComponent);
226
227 d->updateNode(node: comp, property: nullptr, change: ComponentAdded);
228 static_cast<QComponentPrivate *>(QComponentPrivate::get(q: comp))->addEntity(entity: this);
229}
230
231/*!
232 Removes the reference to \a comp.
233 */
234void QEntity::removeComponent(QComponent *comp)
235{
236 Q_CHECK_PTR(comp);
237 qCDebug(Nodes) << Q_FUNC_INFO << comp;
238 Q_D(QEntity);
239
240 static_cast<QComponentPrivate *>(QComponentPrivate::get(q: comp))->removeEntity(entity: this);
241
242 d->updateNode(node: comp, property: nullptr, change: ComponentRemoved);
243
244 d->m_components.removeOne(t: comp);
245
246 // Remove bookkeeping connection
247 d->unregisterDestructionHelper(node: comp);
248}
249
250/*!
251 Returns the parent Qt3DCore::QEntity instance of this entity. If the
252 immediate parent isn't a Qt3DCore::QEntity, this function traverses up the
253 scene hierarchy until a parent Qt3DCore::QEntity is found. If no
254 Qt3DCore::QEntity parent can be found, returns null.
255 */
256QEntity *QEntity::parentEntity() const
257{
258 Q_D(const QEntity);
259 QNode *parentNode = QNode::parentNode();
260 QEntity *parentEntity = qobject_cast<QEntity *>(object: parentNode);
261
262 while (parentEntity == nullptr && parentNode != nullptr) {
263 parentNode = parentNode->parentNode();
264 parentEntity = qobject_cast<QEntity*>(object: parentNode);
265 }
266 if (!parentEntity) {
267 if (!d->m_parentEntityId.isNull())
268 d->m_parentEntityId = QNodeId();
269 } else {
270 if (d->m_parentEntityId != parentEntity->id())
271 d->m_parentEntityId = parentEntity->id();
272 }
273 return parentEntity;
274}
275
276/*
277 \internal
278
279 Returns the Qt3DCore::QNodeId id of the parent Qt3DCore::QEntity instance of the
280 current Qt3DCore::QEntity object. The QNodeId isNull method will return true if
281 there is no Qt3DCore::QEntity parent of the current Qt3DCore::QEntity in the scene
282 hierarchy.
283 */
284QNodeId QEntityPrivate::parentEntityId() const
285{
286 Q_Q(const QEntity);
287 if (m_parentEntityId.isNull())
288 q->parentEntity();
289 return m_parentEntityId;
290}
291
292QString QEntityPrivate::dumpSceneGraph() const
293{
294 Q_Q(const QEntity);
295 return dumpSG(n: q).join(sep: '\n');
296}
297
298QNodeCreatedChangeBasePtr QEntity::createNodeCreationChange() const
299{
300 auto creationChange = QNodeCreatedChangePtr<QEntityData>::create(arguments: this);
301 auto &data = creationChange->data;
302
303 Q_D(const QEntity);
304 data.parentEntityId = parentEntity() ? parentEntity()->id() : Qt3DCore::QNodeId();
305
306 // Find all child entities
307 QQueue<QNode *> queue;
308 queue.append(t: childNodes().toList());
309 data.childEntityIds.reserve(asize: queue.size());
310 while (!queue.isEmpty()) {
311 auto *child = queue.dequeue();
312 auto *childEntity = qobject_cast<QEntity *>(object: child);
313 if (childEntity != nullptr)
314 data.childEntityIds.push_back(t: childEntity->id());
315 else
316 queue.append(t: child->childNodes().toList());
317 }
318
319 data.componentIdsAndTypes.reserve(asize: d->m_components.size());
320 const QComponentVector &components = d->m_components;
321 for (QComponent *c : components) {
322 const auto idAndType = QNodeIdTypePair(c->id(), QNodePrivate::findStaticMetaObject(metaObject: c->metaObject()));
323 data.componentIdsAndTypes.push_back(t: idAndType);
324 }
325
326 return creationChange;
327}
328
329void QEntity::onParentChanged(QObject *)
330{
331 Q_D(QEntity);
332 if (!d->m_hasBackendNode)
333 return;
334
335 d->update();
336}
337
338} // namespace Qt3DCore
339
340QT_END_NAMESPACE
341

source code of qt3d/src/core/nodes/qentity.cpp