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

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