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

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