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/qcomponentaddedchange.h>
45#include <Qt3DCore/qcomponentremovedchange.h>
46#include <Qt3DCore/qnodecreatedchange.h>
47#include <Qt3DCore/qpropertyupdatedchange.h>
48#include <QtCore/QMetaObject>
49#include <QtCore/QMetaProperty>
50
51#include <Qt3DCore/private/corelogging_p.h>
52#include <Qt3DCore/private/qcomponent_p.h>
53#include <Qt3DCore/private/qscene_p.h>
54
55#include <QQueue>
56
57QT_BEGIN_NAMESPACE
58
59namespace Qt3DCore {
60
61/*!
62 \class Qt3DCore::QEntity
63 \inmodule Qt3DCore
64 \inherits Qt3DCore::QNode
65 \since 5.5
66
67 \brief Qt3DCore::QEntity is a Qt3DCore::QNode subclass that can aggregate several
68 Qt3DCore::QComponent instances that will specify its behavior.
69
70 By itself a Qt3DCore::QEntity is an empty shell. The behavior of a Qt3DCore::QEntity
71 object is defined by the Qt3DCore::QComponent objects it references. Each Qt3D
72 backend aspect will be able to interpret and process an Entity by
73 recognizing which components it is made up of. One aspect may decide to only
74 process entities composed of a single Qt3DCore::QTransform component whilst
75 another may focus on Qt3DInput::QMouseHandler.
76
77 \sa Qt3DCore::QComponent, Qt3DCore::QTransform
78 */
79
80/*!
81 \fn template<typename T> QVector<T *> QEntity::componentsOfType() const
82
83 Returns all the components added to this entity that can be cast to
84 type T or an empty vector if there are no such components.
85*/
86
87/*! \internal */
88QEntityPrivate::QEntityPrivate()
89 : QNodePrivate()
90 , m_parentEntityId()
91{}
92
93/*! \internal */
94QEntityPrivate::~QEntityPrivate()
95{
96}
97
98/*! \internal */
99void QEntityPrivate::removeDestroyedComponent(QComponent *comp)
100{
101 // comp is actually no longer a QComponent, just a QObject
102
103 Q_CHECK_PTR(comp);
104 qCDebug(Nodes) << Q_FUNC_INFO << comp;
105 Q_Q(QEntity);
106
107 if (m_changeArbiter) {
108 const auto componentRemovedChange = QComponentRemovedChangePtr::create(q, comp);
109 notifyObservers(componentRemovedChange);
110 }
111
112 m_components.removeOne(comp);
113
114 // Remove bookkeeping connection
115 unregisterDestructionHelper(comp);
116}
117
118/*!
119 Constructs a new Qt3DCore::QEntity instance with \a parent as parent.
120 */
121QEntity::QEntity(QNode *parent)
122 : QEntity(*new QEntityPrivate, parent) {}
123
124/*! \internal */
125QEntity::QEntity(QEntityPrivate &dd, QNode *parent)
126 : QNode(dd, parent)
127{
128}
129
130QEntity::~QEntity()
131{
132 // remove all component aggregations
133 Q_D(const QEntity);
134 // to avoid hammering m_components by repeated removeComponent()
135 // calls below, move all contents out, so the removeOne() calls in
136 // removeComponent() don't actually remove something:
137 const auto components = std::move(d->m_components);
138 for (QComponent *comp : components)
139 removeComponent(comp);
140}
141
142
143/*!
144 \typedef Qt3DCore::QComponentVector
145 \relates Qt3DCore::QEntity
146
147 List of QComponent pointers.
148 */
149
150/*!
151 Returns the list of Qt3DCore::QComponent instances the entity is referencing.
152 */
153QComponentVector QEntity::components() const
154{
155 Q_D(const QEntity);
156 return d->m_components;
157}
158
159/*!
160 Adds a new reference to the component \a comp.
161
162 \note If the Qt3DCore::QComponent has no parent, the Qt3DCore::QEntity will set
163 itself as its parent thereby taking ownership of the component.
164 */
165void QEntity::addComponent(QComponent *comp)
166{
167 Q_D(QEntity);
168 Q_CHECK_PTR( comp );
169 qCDebug(Nodes) << Q_FUNC_INFO << comp;
170
171 // A Component can only be aggregated once
172 if (d->m_components.count(comp) != 0)
173 return ;
174
175 // We need to add it as a child of the current node if it has been declared inline
176 // Or not previously added as a child of the current node so that
177 // 1) The backend gets notified about it's creation
178 // 2) When the current node is destroyed, it gets destroyed as well
179 if (!comp->parent())
180 comp->setParent(this);
181
182 QNodePrivate::get(comp)->_q_ensureBackendNodeCreated();
183
184 d->m_components.append(comp);
185
186 // Ensures proper bookkeeping
187 d->registerPrivateDestructionHelper(comp, &QEntityPrivate::removeDestroyedComponent);
188
189 if (d->m_changeArbiter) {
190 const auto componentAddedChange = QComponentAddedChangePtr::create(this, comp);
191 d->notifyObservers(componentAddedChange);
192 }
193 static_cast<QComponentPrivate *>(QComponentPrivate::get(comp))->addEntity(this);
194}
195
196/*!
197 Removes the reference to \a comp.
198 */
199void QEntity::removeComponent(QComponent *comp)
200{
201 Q_CHECK_PTR(comp);
202 qCDebug(Nodes) << Q_FUNC_INFO << comp;
203 Q_D(QEntity);
204
205 static_cast<QComponentPrivate *>(QComponentPrivate::get(comp))->removeEntity(this);
206
207 if (d->m_changeArbiter) {
208 const auto componentRemovedChange = QComponentRemovedChangePtr::create(this, comp);
209 d->notifyObservers(componentRemovedChange);
210 }
211
212 d->m_components.removeOne(comp);
213
214 // Remove bookkeeping connection
215 d->unregisterDestructionHelper(comp);
216}
217
218/*!
219 Returns the parent Qt3DCore::QEntity instance of this entity. If the
220 immediate parent isn't a Qt3DCore::QEntity, this function traverses up the
221 scene hierarchy until a parent Qt3DCore::QEntity is found. If no
222 Qt3DCore::QEntity parent can be found, returns null.
223 */
224QEntity *QEntity::parentEntity() const
225{
226 Q_D(const QEntity);
227 QNode *parentNode = QNode::parentNode();
228 QEntity *parentEntity = qobject_cast<QEntity *>(parentNode);
229
230 while (parentEntity == nullptr && parentNode != nullptr) {
231 parentNode = parentNode->parentNode();
232 parentEntity = qobject_cast<QEntity*>(parentNode);
233 }
234 if (!parentEntity) {
235 if (!d->m_parentEntityId.isNull())
236 d->m_parentEntityId = QNodeId();
237 } else {
238 if (d->m_parentEntityId != parentEntity->id())
239 d->m_parentEntityId = parentEntity->id();
240 }
241 return parentEntity;
242}
243
244/*
245 \internal
246
247 Returns the Qt3DCore::QNodeId id of the parent Qt3DCore::QEntity instance of the
248 current Qt3DCore::QEntity object. The QNodeId isNull method will return true if
249 there is no Qt3DCore::QEntity parent of the current Qt3DCore::QEntity in the scene
250 hierarchy.
251 */
252QNodeId QEntityPrivate::parentEntityId() const
253{
254 Q_Q(const QEntity);
255 if (m_parentEntityId.isNull())
256 q->parentEntity();
257 return m_parentEntityId;
258}
259
260QNodeCreatedChangeBasePtr QEntity::createNodeCreationChange() const
261{
262 // connect to the parentChanged signal here rather than constructor because
263 // until now there's no backend node to notify when parent changes
264 connect(this, &QNode::parentChanged, this, &QEntity::onParentChanged);
265
266 auto creationChange = QNodeCreatedChangePtr<QEntityData>::create(this);
267 auto &data = creationChange->data;
268
269 Q_D(const QEntity);
270 data.parentEntityId = parentEntity() ? parentEntity()->id() : Qt3DCore::QNodeId();
271
272 // Find all child entities
273 QQueue<QNode *> queue;
274 queue.append(childNodes().toList());
275 data.childEntityIds.reserve(queue.size());
276 while (!queue.isEmpty()) {
277 auto *child = queue.dequeue();
278 auto *childEntity = qobject_cast<QEntity *>(child);
279 if (childEntity != nullptr)
280 data.childEntityIds.push_back(childEntity->id());
281 else
282 queue.append(child->childNodes().toList());
283 }
284
285 data.componentIdsAndTypes.reserve(d->m_components.size());
286 const QComponentVector &components = d->m_components;
287 for (QComponent *c : components) {
288 const auto idAndType = QNodeIdTypePair(c->id(), QNodePrivate::findStaticMetaObject(c->metaObject()));
289 data.componentIdsAndTypes.push_back(idAndType);
290 }
291
292 return creationChange;
293}
294
295void QEntity::onParentChanged(QObject *)
296{
297 const auto parentID = parentEntity() ? parentEntity()->id() : Qt3DCore::QNodeId();
298 auto parentChange = Qt3DCore::QPropertyUpdatedChangePtr::create(id());
299 parentChange->setPropertyName("parentEntityUpdated");
300 parentChange->setValue(QVariant::fromValue(parentID));
301 const bool blocked = blockNotifications(false);
302 notifyObservers(parentChange);
303 blockNotifications(blocked);
304}
305
306} // namespace Qt3DCore
307
308QT_END_NAMESPACE
309