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

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