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 "qabstractaspect.h"
41#include "qabstractaspect_p.h"
42
43#include <Qt3DCore/qentity.h>
44#include <Qt3DCore/qpropertyupdatedchange.h>
45
46#include <Qt3DCore/private/corelogging_p.h>
47#include <Qt3DCore/private/qaspectjobmanager_p.h>
48#include <Qt3DCore/private/qaspectmanager_p.h>
49#include <Qt3DCore/private/qchangearbiter_p.h>
50#include <Qt3DCore/private/qnodevisitor_p.h>
51#include <Qt3DCore/private/qscene_p.h>
52
53QT_BEGIN_NAMESPACE
54
55namespace Qt3DCore {
56
57QAbstractAspectPrivate::QAbstractAspectPrivate()
58 : QObjectPrivate()
59 , m_root(nullptr)
60 , m_rootId()
61 , m_aspectManager(nullptr)
62 , m_jobManager(nullptr)
63 , m_arbiter(nullptr)
64{
65}
66
67QAbstractAspectPrivate::~QAbstractAspectPrivate()
68{
69}
70
71QAbstractAspectPrivate *QAbstractAspectPrivate::get(QAbstractAspect *aspect)
72{
73 return aspect->d_func();
74}
75
76/*!
77 * \internal
78 * Called in the context of the main thread
79 */
80void QAbstractAspectPrivate::onEngineAboutToShutdown()
81{
82}
83
84/*! \internal */
85void QAbstractAspectPrivate::unregisterBackendType(const QMetaObject &mo)
86{
87 m_backendCreatorFunctors.remove(&mo);
88}
89
90/*!
91 * \class Qt3DCore::QAbstractAspect
92 * \inheaderfile Qt3DCore/QAbstractAspect
93 * \inherits QObject
94 * \inmodule Qt3DCore
95 * \brief QAbstractAspect is the base class for aspects that provide a vertical slice of behavior.
96 */
97
98/*!
99 * \fn void Qt3DCore::QAbstractAspect::registerBackendType(const Qt3DCore::QBackendNodeMapperPtr &functor)
100 * Registers backend with \a functor.
101 */
102
103/*!
104 * \macro QT3D_REGISTER_ASPECT(name, AspectType)
105 * \relates Qt3DCore::QAbstractAspect
106 *
107 * Convenience macro for registering \a AspectType for instantiation by the
108 * currently set Qt3DCore::QAspectFactory. This makes it possible to create an
109 * instance of \a AspectType in the aspect thread by later passing \a name to
110 * Qt3DCore::QAspectEngine::registerAspect(const QString &name).
111 *
112 * \note It is also possible to register a new aspect without using this macro
113 * by instead using Qt3DCore::QAspectEngine::registerAspect(QAbstractAspect *aspect)
114 * which will handle moving a previously created aspect instance to the aspect
115 * thread context.
116 *
117 * KDAB has published a few articles about writing custom Qt3D aspects
118 * \l {https://www.kdab.com/writing-custom-qt-3d-aspect/}{on their blog}. These
119 * provide an excellent starting point if you wish to learn more about it.
120 */
121
122
123/*!
124 * Constructs a new QAbstractAspect with \a parent
125 */
126QAbstractAspect::QAbstractAspect(QObject *parent)
127 : QAbstractAspect(*new QAbstractAspectPrivate, parent) {}
128
129/*!
130 * \typedef Qt3DCore::QAspectJobPtr
131 * \relates Qt3DCore::QAbstractAspect
132 *
133 * A shared pointer for QAspectJob.
134 */
135
136/*!
137 * \typedef Qt3DCore::QBackendNodeMapperPtr
138 * \relates Qt3DCore::QAbstractAspect
139 *
140 * A shared pointer for QBackendNodeMapper.
141 */
142
143/*!
144 * \internal
145 */
146QAbstractAspect::QAbstractAspect(QAbstractAspectPrivate &dd, QObject *parent)
147 : QObject(dd, parent)
148{
149}
150
151/*!
152 \internal
153*/
154QAbstractAspect::~QAbstractAspect()
155{
156}
157
158/*!
159 * \return root entity node id.
160 */
161QNodeId QAbstractAspect::rootEntityId() const Q_DECL_NOEXCEPT
162{
163 Q_D(const QAbstractAspect);
164 return d->m_rootId;
165}
166
167/*!
168 * Registers backend with \a obj and \a functor.
169 */
170void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor)
171{
172 Q_D(QAbstractAspect);
173 d->m_backendCreatorFunctors.insert(&obj, functor);
174}
175
176void QAbstractAspect::unregisterBackendType(const QMetaObject &obj)
177{
178 Q_D(QAbstractAspect);
179 d->m_backendCreatorFunctors.remove(&obj);
180}
181
182void QAbstractAspectPrivate::sceneNodeAdded(QSceneChangePtr &change)
183{
184 QNodeCreatedChangeBasePtr creationChange = qSharedPointerCast<QNodeCreatedChangeBase>(change);
185 createBackendNode(creationChange);
186}
187
188void QAbstractAspectPrivate::sceneNodeRemoved(QSceneChangePtr &change)
189{
190 QNodeDestroyedChangePtr destructionChange = qSharedPointerCast<QNodeDestroyedChange>(change);
191 clearBackendNode(destructionChange);
192}
193
194QVariant QAbstractAspect::executeCommand(const QStringList &args)
195{
196 Q_UNUSED(args);
197 return QVariant();
198}
199
200QVector<QAspectJobPtr> QAbstractAspect::jobsToExecute(qint64 time)
201{
202 Q_UNUSED(time);
203 return QVector<QAspectJobPtr>();
204}
205
206QBackendNode *QAbstractAspectPrivate::createBackendNode(const QNodeCreatedChangeBasePtr &change) const
207{
208 const QMetaObject *metaObj = change->metaObject();
209 QBackendNodeMapperPtr backendNodeMapper;
210 while (metaObj != nullptr && backendNodeMapper.isNull()) {
211 backendNodeMapper = m_backendCreatorFunctors.value(metaObj);
212 metaObj = metaObj->superClass();
213 }
214
215 if (!backendNodeMapper)
216 return nullptr;
217
218 QBackendNode *backend = backendNodeMapper->get(change->subjectId());
219 if (backend != nullptr)
220 return backend;
221 backend = backendNodeMapper->create(change);
222
223 if (!backend)
224 return nullptr;
225
226 // TODO: Find some place else to do all of this function from the arbiter
227 backend->setPeerId(change->subjectId());
228
229 // Backend could be null if the user decides that his functor should only
230 // perform some action when encountering a given type of item but doesn't need to
231 // return a QBackendNode pointer.
232
233 QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(backend);
234 backendPriv->setEnabled(change->isNodeEnabled());
235
236 // TO DO: Find a way to specify the changes to observe
237 // Register backendNode with QChangeArbiter
238 if (m_arbiter != nullptr) { // Unit tests may not have the arbiter registered
239 qCDebug(Nodes) << q_func()->objectName() << "Creating backend node for node id"
240 << change->subjectId() << "of type" << change->metaObject()->className();
241 m_arbiter->registerObserver(backendPriv, backend->peerId(), AllChanges);
242 if (backend->mode() == QBackendNode::ReadWrite)
243 m_arbiter->scene()->addObservable(backendPriv, backend->peerId());
244 }
245
246 backend->initializeFromPeer(change);
247
248 return backend;
249}
250
251void QAbstractAspectPrivate::clearBackendNode(const QNodeDestroyedChangePtr &change) const
252{
253 // Each QNodeDestroyedChange may contain info about a whole sub-tree of nodes that
254 // are being destroyed. Iterate over them and process each in turn
255 const auto subTree = change->subtreeIdsAndTypes();
256 for (const auto &idAndType : subTree) {
257 const QMetaObject *metaObj = idAndType.type;
258 QBackendNodeMapperPtr backendNodeMapper;
259
260 // Find backend node mapper for this type
261 while (metaObj != nullptr && backendNodeMapper.isNull()) {
262 backendNodeMapper = m_backendCreatorFunctors.value(metaObj);
263 metaObj = metaObj->superClass();
264 }
265
266 if (!backendNodeMapper)
267 continue;
268
269 // Request the mapper to destroy the corresponding backend node
270 QBackendNode *backend = backendNodeMapper->get(idAndType.id);
271 if (backend) {
272 qCDebug(Nodes) << q_func()->objectName() << "Deleting backend node for node id"
273 << idAndType.id << "of type" << idAndType.type->className();
274 QBackendNodePrivate *backendPriv = QBackendNodePrivate::get(backend);
275 m_arbiter->unregisterObserver(backendPriv, backend->peerId());
276 if (backend->mode() == QBackendNode::ReadWrite)
277 m_arbiter->scene()->removeObservable(backendPriv, backend->peerId());
278 backendNodeMapper->destroy(idAndType.id);
279 }
280 }
281}
282
283void QAbstractAspectPrivate::setRootAndCreateNodes(QEntity *rootObject, const QVector<QNodeCreatedChangeBasePtr> &changes)
284{
285 qCDebug(Aspects) << Q_FUNC_INFO << "rootObject =" << rootObject;
286 if (rootObject == m_root)
287 return;
288
289 m_root = rootObject;
290 m_rootId = rootObject->id();
291
292 for (const auto &change : changes)
293 createBackendNode(change);
294}
295
296QServiceLocator *QAbstractAspectPrivate::services() const
297{
298 return m_aspectManager ? m_aspectManager->serviceLocator() : nullptr;
299}
300
301QAbstractAspectJobManager *QAbstractAspectPrivate::jobManager() const
302{
303 return m_jobManager;
304}
305
306QVector<QAspectJobPtr> QAbstractAspectPrivate::jobsToExecute(qint64 time)
307{
308 Q_Q(QAbstractAspect);
309 auto res = q->jobsToExecute(time);
310
311 {
312 QMutexLocker lock(&m_singleShotMutex);
313 res << m_singleShotJobs;
314 m_singleShotJobs.clear();
315 }
316
317 return res;
318}
319
320/*!
321 * Called in the context of the aspect thread once the aspect has been registered.
322 * This provides an opportunity for the aspect to do any initialization tasks that
323 * require to be in the aspect thread context such as creating QObject subclasses that
324 * must have affinity with this thread.
325 *
326 * \sa onUnregistered
327 */
328void QAbstractAspect::onRegistered()
329{
330}
331
332/*!
333 * Called in the context of the aspect thread during unregistration
334 * of the aspect. This gives the aspect a chance to do any final pieces of
335 * cleanup that it would not do when just changing to a new scene.
336 *
337 * \sa onRegistered
338 */
339void QAbstractAspect::onUnregistered()
340{
341}
342
343/*!
344 *
345 * Called in the QAspectThread context
346 */
347void QAbstractAspect::onEngineStartup()
348{
349}
350
351/*!
352 *
353 * Called in the QAspectThread context
354 */
355void QAbstractAspect::onEngineShutdown()
356{
357}
358
359void QAbstractAspect::scheduleSingleShotJob(const Qt3DCore::QAspectJobPtr &job)
360{
361 Q_D(QAbstractAspect);
362 QMutexLocker lock(&d->m_singleShotMutex);
363 d->m_singleShotJobs.push_back(job);
364}
365
366namespace Debug {
367
368AsynchronousCommandReply::AsynchronousCommandReply(const QString &commandName, QObject *parent)
369 : QObject(parent)
370 , m_commandName(commandName)
371 , m_finished(false)
372{
373}
374
375void AsynchronousCommandReply::setFinished(bool replyFinished)
376{
377 m_finished = replyFinished;
378 if (m_finished)
379 emit finished(this);
380}
381
382void AsynchronousCommandReply::setData(const QByteArray &data)
383{
384 m_data = data;
385}
386
387} // Debug
388
389
390} // of namespace Qt3DCore
391
392QT_END_NAMESPACE
393