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 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | namespace Qt3DRender { |
22 | namespace Render { |
23 | |
24 | LoadSceneJob::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 | |
33 | void LoadSceneJob::setData(const QByteArray &data) |
34 | { |
35 | m_data = data; |
36 | } |
37 | |
38 | NodeManagers *LoadSceneJob::nodeManagers() const |
39 | { |
40 | return m_managers; |
41 | } |
42 | |
43 | QList<QSceneImporter *> LoadSceneJob::sceneImporters() const |
44 | { |
45 | return m_sceneImporters; |
46 | } |
47 | |
48 | QUrl LoadSceneJob::source() const |
49 | { |
50 | return m_source; |
51 | } |
52 | |
53 | Qt3DCore::QNodeId LoadSceneJob::sceneComponentId() const |
54 | { |
55 | return m_sceneComponent; |
56 | } |
57 | |
58 | void 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 | |
117 | Qt3DCore::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 | |
150 | void 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 | |
174 | QT_END_NAMESPACE |
175 | |