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 "qsceneloader.h"
5#include "qsceneloader_p.h"
6#include <Qt3DCore/private/qscene_p.h>
7
8#include <Qt3DCore/qentity.h>
9#include <Qt3DCore/qtransform.h>
10#include <Qt3DRender/qgeometryrenderer.h>
11#include <Qt3DRender/qmaterial.h>
12#include <Qt3DRender/qabstractlight.h>
13#include <Qt3DRender/qcameralens.h>
14
15#include <private/renderlogging_p.h>
16
17#include <QThread>
18
19QT_BEGIN_NAMESPACE
20
21using namespace Qt3DCore;
22
23namespace Qt3DRender {
24
25/*!
26 \class Qt3DRender::QSceneLoader
27 \inmodule Qt3DRender
28 \since 5.7
29 \ingroup io
30
31 \brief Provides the facility to load an existing Scene.
32
33 Given a 3D source file, the Qt3DRender::QSceneLoader will try to parse it and
34 build a tree of Qt3DCore::QEntity objects with proper Qt3DRender::QGeometryRenderer,
35 Qt3DCore::QTransform and Qt3DRender::QMaterial components.
36
37 The loader will try to determine the best material to be used based on the properties
38 of the model file. If you wish to use a custom material, you will have to traverse
39 the tree and replace the default associated materials with yours.
40
41 As the name implies, Qt3DRender::QSceneLoader loads a complete scene subtree.
42 If you wish to load a single piece of geometry, you should rather use
43 the Qt3DRender::QMesh instead.
44
45 Qt3DRender::QSceneLoader internally relies on the use of plugins to support a
46 wide variety of 3D file formats. \l
47 {http://assimp.sourceforge.net/main_features_formats.html}{Here} is a list of formats
48 that are supported by Qt3D.
49
50 \note this component shouldn't be shared among several Qt3DCore::QEntity instances.
51 Undefined behavior will result.
52
53 \sa Qt3DRender::QMesh
54 \sa Qt3DRender::QGeometryRenderer
55 */
56
57/*!
58 \qmltype SceneLoader
59 \inqmlmodule Qt3D.Render
60 \instantiates Qt3DRender::QSceneLoader
61 \inherits Component
62 \since 5.7
63 \brief Provides the facility to load an existing Scene.
64
65 Given a 3D source file, the SceneLoader will try to parse it and build a
66 tree of Entity objects with proper GeometryRenderer, Transform and Material
67 components.
68
69 The loader will try to determine the best material to be used based on the
70 properties of the model file. If you wish to use a custom material, you
71 will have to traverse the tree and replace the default associated materials
72 with yours.
73
74 As the name implies, SceneLoader loads a complete scene subtree. If you
75 wish to load a single piece of geometry, you should rather use the
76 Mesh instead.
77
78 SceneLoader internally relies on the use of plugins to support a wide
79 variety of 3D file formats. \l
80 {http://assimp.sourceforge.net/main_features_formats.html}{Here} is a list of
81 formats that are supported by Qt3D.
82
83 \note this component shouldn't be shared among several Entity instances.
84 Undefined behavior will result.
85
86 \sa Mesh
87 \sa GeometryRenderer
88 */
89
90/*!
91 \enum QSceneLoader::Status
92
93 This enum identifies the state of loading
94 \value None The Qt3DRender::QSceneLoader hasn't been used yet.
95 \value Loading The Qt3DRender::QSceneLoader is currently loading the scene file.
96 \value Ready The Qt3DRender::QSceneLoader successfully loaded the scene file.
97 \value Error The Qt3DRender::QSceneLoader encountered an error while loading the scene file.
98 */
99
100/*!
101 \enum QSceneLoader::ComponentType
102
103 This enum specifies a component type.
104 \value UnknownComponent Unknown component type
105 \value GeometryRendererComponent Qt3DRender::QGeometryRenderer component
106 \value TransformComponent Qt3DCore::QTransform component
107 \value MaterialComponent Qt3DRender::QMaterial component
108 \value LightComponent Qt3DRender::QAbstractLight component
109 \value CameraLensComponent Qt3DRender::QCameraLens component
110 */
111
112/*!
113 \qmlproperty url SceneLoader::source
114
115 Holds the url to the source to be loaded.
116 */
117
118/*!
119 \qmlproperty enumeration SceneLoader::status
120
121 Holds the status of scene loading.
122 \list
123 \li SceneLoader.None
124 \li SceneLoader.Loading
125 \li SceneLoader.Ready
126 \li SceneLoader.Error
127 \endlist
128 \sa Qt3DRender::QSceneLoader::Status
129 \readonly
130 */
131
132/*!
133 \property QSceneLoader::source
134
135 Holds the url to the source to be loaded.
136 */
137
138/*!
139 \property QSceneLoader::status
140
141 Holds the status of scene loading.
142 \list
143 \li SceneLoader.None
144 \li SceneLoader.Loading
145 \li SceneLoader.Ready
146 \li SceneLoader.Error
147 \endlist
148 \sa Qt3DRender::QSceneLoader::Status
149 */
150
151/*! \internal */
152QSceneLoaderPrivate::QSceneLoaderPrivate()
153 : QComponentPrivate()
154 , m_status(QSceneLoader::None)
155 , m_subTreeRoot(nullptr)
156{
157 m_shareable = false;
158}
159
160void QSceneLoaderPrivate::populateEntityMap(QEntity *parentEntity)
161{
162 // Topmost parent entity is not considered part of the scene as that is typically
163 // an unnamed entity inserted by importer.
164 const QNodeVector childNodes = parentEntity->childNodes();
165 for (auto childNode : childNodes) {
166 auto childEntity = qobject_cast<QEntity *>(object: childNode);
167 if (childEntity) {
168 m_entityMap.insert(key: childEntity->objectName(), value: childEntity);
169 populateEntityMap(parentEntity: childEntity);
170 }
171 }
172}
173
174void QSceneLoaderPrivate::setStatus(QSceneLoader::Status status)
175{
176 if (m_status != status) {
177 Q_Q(QSceneLoader);
178 m_status = status;
179 const bool wasBlocked = q->blockNotifications(block: true);
180 emit q->statusChanged(status);
181 q->blockNotifications(block: wasBlocked);
182 }
183}
184
185void QSceneLoaderPrivate::setSceneRoot(QEntity *root)
186{
187 // If we already have a scene sub tree, delete it
188 if (m_subTreeRoot) {
189 delete m_subTreeRoot;
190 m_subTreeRoot = nullptr;
191 }
192
193 // If we have successfully loaded a scene, graft it in
194 if (root) {
195 // Get the entity to which this component is attached
196 const Qt3DCore::QNodeIdVector entities = m_scene->entitiesForComponent(id: m_id);
197 Q_ASSERT(entities.size() == 1);
198 Qt3DCore::QNodeId parentEntityId = entities.first();
199 QEntity *parentEntity = qobject_cast<QEntity *>(object: m_scene->lookupNode(id: parentEntityId));
200 root->setParent(parentEntity);
201 m_subTreeRoot = root;
202 populateEntityMap(parentEntity: m_subTreeRoot);
203 }
204}
205
206/*!
207 The constructor creates an instance with the specified \a parent.
208 */
209QSceneLoader::QSceneLoader(QNode *parent)
210 : Qt3DCore::QComponent(*new QSceneLoaderPrivate, parent)
211{
212}
213
214/*! \internal */
215QSceneLoader::~QSceneLoader()
216{
217}
218
219/*! \internal */
220QSceneLoader::QSceneLoader(QSceneLoaderPrivate &dd, QNode *parent)
221 : Qt3DCore::QComponent(dd, parent)
222{
223}
224
225QUrl QSceneLoader::source() const
226{
227 Q_D(const QSceneLoader);
228 return d->m_source;
229}
230
231void QSceneLoader::setSource(const QUrl &arg)
232{
233 Q_D(QSceneLoader);
234 if (d->m_source != arg) {
235 d->m_entityMap.clear();
236 d->m_source = arg;
237 emit sourceChanged(source: arg);
238 }
239}
240
241QSceneLoader::Status QSceneLoader::status() const
242{
243 Q_D(const QSceneLoader);
244 return d->m_status;
245}
246
247/*!
248 \qmlmethod Entity SceneLoader::entity(string entityName)
249 Returns a loaded entity with the \c objectName matching the \a entityName parameter.
250 If multiple entities have the same name, it is undefined which one of them is returned, but it
251 will always be the same one.
252*/
253/*!
254 Returns a loaded entity with an \c objectName matching the \a entityName parameter.
255 If multiple entities have the same name, it is undefined which one of them is returned, but it
256 will always be the same one.
257*/
258QEntity *QSceneLoader::entity(const QString &entityName) const
259{
260 Q_D(const QSceneLoader);
261 return d->m_entityMap.value(key: entityName);
262}
263
264/*!
265 \qmlmethod list SceneLoader::entityNames()
266 Returns a list of the \c objectNames of the loaded entities.
267*/
268/*!
269 Returns a list of the \c objectNames of the loaded entities.
270*/
271QStringList QSceneLoader::entityNames() const
272{
273 Q_D(const QSceneLoader);
274 return d->m_entityMap.keys();
275}
276
277/*!
278 \qmlmethod Entity SceneLoader::component(string entityName, enumeration componentType)
279 Returns a component matching \a componentType of a loaded entity with an \e objectName matching
280 the \a entityName.
281 If the entity has multiple matching components, the first match in the component list of
282 the entity is returned.
283 If there is no match, an undefined item is returned.
284 \list
285 \li SceneLoader.UnknownComponent Unknown component type
286 \li SceneLoader.GeometryRendererComponent Qt3DRender::QGeometryRenderer component
287 \li SceneLoader.TransformComponent Qt3DCore::QTransform component
288 \li SceneLoader.MaterialComponent Qt3DRender::QMaterial component
289 \li SceneLoader.LightComponent Qt3DRender::QAbstractLight component
290 \li SceneLoader.CameraLensComponent Qt3DRender::QCameraLens component
291 \endlist
292 \sa Qt3DRender::QSceneLoader::ComponentType
293*/
294/*!
295 Returns a component matching \a componentType of a loaded entity with an objectName matching
296 the \a entityName.
297 If the entity has multiple matching components, the first match in the component list of
298 the entity is returned.
299 If there is no match, a null pointer is returned.
300*/
301QComponent *QSceneLoader::component(const QString &entityName,
302 QSceneLoader::ComponentType componentType) const
303{
304 QEntity *e = entity(entityName);
305 if (!e)
306 return nullptr;
307 const QComponentVector components = e->components();
308 for (auto component : components) {
309 switch (componentType) {
310 case GeometryRendererComponent:
311 if (qobject_cast<Qt3DRender::QGeometryRenderer *>(object: component))
312 return component;
313 break;
314 case TransformComponent:
315 if (qobject_cast<Qt3DCore::QTransform *>(object: component))
316 return component;
317 break;
318 case MaterialComponent:
319 if (qobject_cast<Qt3DRender::QMaterial *>(object: component))
320 return component;
321 break;
322 case LightComponent:
323 if (qobject_cast<Qt3DRender::QAbstractLight *>(object: component))
324 return component;
325 break;
326 case CameraLensComponent:
327 if (qobject_cast<Qt3DRender::QCameraLens *>(object: component))
328 return component;
329 break;
330 default:
331 break;
332 }
333 }
334 return nullptr;
335}
336
337} // namespace Qt3DRender
338
339QT_END_NAMESPACE
340
341#include "moc_qsceneloader.cpp"
342

source code of qt3d/src/render/io/qsceneloader.cpp