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 "loadscenejob_p.h"
41#include <private/nodemanagers_p.h>
42#include <private/scenemanager_p.h>
43#include <QCoreApplication>
44#include <Qt3DCore/qentity.h>
45#include <Qt3DCore/private/qaspectmanager_p.h>
46#include <Qt3DRender/private/job_common_p.h>
47#include <Qt3DRender/private/qsceneimporter_p.h>
48#include <Qt3DRender/private/qurlhelper_p.h>
49#include <Qt3DRender/qsceneloader.h>
50#include <Qt3DRender/private/qsceneloader_p.h>
51#include <Qt3DRender/private/renderlogging_p.h>
52#include <QFileInfo>
53#include <QMimeDatabase>
54
55QT_BEGIN_NAMESPACE
56
57namespace Qt3DRender {
58namespace Render {
59
60LoadSceneJob::LoadSceneJob(const QUrl &source, Qt3DCore::QNodeId sceneComponent)
61 : QAspectJob(*new LoadSceneJobPrivate(this))
62 , m_source(source)
63 , m_sceneComponent(sceneComponent)
64 , m_managers(nullptr)
65{
66 SET_JOB_RUN_STAT_TYPE(this, JobTypes::LoadScene, 0)
67}
68
69void LoadSceneJob::setData(const QByteArray &data)
70{
71 m_data = data;
72}
73
74NodeManagers *LoadSceneJob::nodeManagers() const
75{
76 return m_managers;
77}
78
79QList<QSceneImporter *> LoadSceneJob::sceneImporters() const
80{
81 return m_sceneImporters;
82}
83
84QUrl LoadSceneJob::source() const
85{
86 return m_source;
87}
88
89Qt3DCore::QNodeId LoadSceneJob::sceneComponentId() const
90{
91 return m_sceneComponent;
92}
93
94void LoadSceneJob::run()
95{
96 // Iterate scene IO handlers until we find one that can handle this file type
97 Qt3DCore::QEntity *sceneSubTree = nullptr;
98 Scene *scene = m_managers->sceneManager()->lookupResource(id: m_sceneComponent);
99 Q_ASSERT(scene);
100
101 // Reset status
102 QSceneLoader::Status finalStatus = QSceneLoader::None;
103
104 // Perform the loading only if the source wasn't explicitly set to empty
105 if (!m_source.isEmpty()) {
106 finalStatus = QSceneLoader::Error;
107
108 if (m_data.isEmpty()) {
109 const QString path = QUrlHelper::urlToLocalFileOrQrc(url: m_source);
110 const QFileInfo finfo(path);
111 qCDebug(SceneLoaders) << Q_FUNC_INFO << "Attempting to load" << finfo.filePath();
112 if (finfo.exists()) {
113 const QStringList extensions(finfo.suffix());
114 sceneSubTree = tryLoadScene(finalStatus,
115 extensions,
116 importerSetupFunc: [this] (QSceneImporter *importer) {
117 importer->setSource(m_source);
118 });
119 } else {
120 qCWarning(SceneLoaders) << Q_FUNC_INFO << finfo.filePath() << "doesn't exist";
121 }
122 } else {
123 QStringList extensions;
124 QMimeDatabase db;
125 const QMimeType mtype = db.mimeTypeForData(data: m_data);
126
127 if (mtype.isValid())
128 extensions = mtype.suffixes();
129 else
130 qCWarning(SceneLoaders) << Q_FUNC_INFO << "Invalid mime type" << mtype;
131
132 const QString basePath = m_source.adjusted(options: QUrl::RemoveFilename).toString();
133
134 sceneSubTree = tryLoadScene(finalStatus,
135 extensions,
136 importerSetupFunc: [this, basePath] (QSceneImporter *importer) {
137 importer->setData(data: m_data, basePath);
138 });
139 }
140 }
141
142 Q_D(LoadSceneJob);
143 d->m_sceneSubtree = sceneSubTree;
144 d->m_status = finalStatus;
145
146 if (d->m_sceneSubtree) {
147 // Move scene sub tree to the application thread so that it can be grafted in.
148 const auto appThread = QCoreApplication::instance()->thread();
149 d->m_sceneSubtree->moveToThread(thread: appThread);
150 }
151}
152
153Qt3DCore::QEntity *LoadSceneJob::tryLoadScene(QSceneLoader::Status &finalStatus,
154 const QStringList &extensions,
155 const std::function<void (QSceneImporter *)> &importerSetupFunc)
156{
157 Qt3DCore::QEntity *sceneSubTree = nullptr;
158 bool foundSuitableLoggerPlugin = false;
159
160 for (QSceneImporter *sceneImporter : qAsConst(t&: m_sceneImporters)) {
161 if (!sceneImporter->areFileTypesSupported(extensions))
162 continue;
163
164 foundSuitableLoggerPlugin = true;
165
166 // Set source file or data on importer
167 importerSetupFunc(sceneImporter);
168
169 // File type is supported, try to load it
170 sceneSubTree = sceneImporter->scene();
171 if (sceneSubTree != nullptr) {
172 // Successfully built a subtree
173 finalStatus = QSceneLoader::Ready;
174 break;
175 }
176
177 qCWarning(SceneLoaders) << Q_FUNC_INFO << "Failed to import" << m_source << "with errors" << sceneImporter->errors();
178 }
179
180 if (!foundSuitableLoggerPlugin)
181 qCWarning(SceneLoaders) << Q_FUNC_INFO << "Found no suitable importer plugin for" << m_source;
182
183 return sceneSubTree;
184}
185
186void LoadSceneJobPrivate::postFrame(Qt3DCore::QAspectManager *manager)
187{
188 Q_Q(LoadSceneJob);
189 QSceneLoader *node =
190 qobject_cast<QSceneLoader *>(object: manager->lookupNode(id: q->sceneComponentId()));
191 if (!node)
192 return;
193 Qt3DRender::QSceneLoaderPrivate *dNode =
194 static_cast<decltype(dNode)>(Qt3DCore::QNodePrivate::get(q: node));
195
196 // If the sceneSubTree is null it will trigger the frontend to unload
197 // any subtree it may hold
198 // Set clone of sceneTree in sceneComponent. This will move the sceneSubTree
199 // to the QCoreApplication thread which is where the frontend object tree lives.
200 dNode->setSceneRoot(m_sceneSubtree);
201
202 // Note: the status is set after the subtree so that bindinds depending on the status
203 // in the frontend will be consistent
204 dNode->setStatus(m_status);
205}
206
207} // namespace Render
208} // namespace Qt3DRender
209
210QT_END_NAMESPACE
211

source code of qt3d/src/render/jobs/loadscenejob.cpp