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

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