8#include <QtQuick3DAssetUtils/private/qssgscenedesc_p.h>
9#include <QtQuick3DAssetUtils/private/qssgqmlutilities_p.h>
10#include <QtQuick3DAssetUtils/private/qssgrtutilities_p.h>
11#include <QtQuick3DAssetImport/private/qssgassetimportmanager_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
13#if QT_CONFIG(mimetype)
14#include <QtCore/qmimedatabase.h>
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
42
43
44
45
46
47
48
49
50
51
52
53
54
55
58
59
60
61
62
63
64
65
66
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
84
85
88
89
90
91
92
93
96
97
98
99
100
101
102
103
106
107
108
109
110
111
112
116QQuick3DRuntimeLoader::QQuick3DRuntimeLoader(QQuick3DNode *parent)
117 : QQuick3DNode(parent)
122QUrl QQuick3DRuntimeLoader::source()
const
127void QQuick3DRuntimeLoader::setSource(
const QUrl &newSource)
129 if (m_source == newSource)
132 const QQmlContext *context = qmlContext(
this);
133 auto resolvedUrl = (context ? context->resolvedUrl(newSource) : newSource);
135 if (m_source == resolvedUrl)
138 m_source = resolvedUrl;
139 emit sourceChanged();
141 if (isComponentComplete())
145void QQuick3DRuntimeLoader::componentComplete()
147 QQuick3DNode::componentComplete();
151QStringList QQuick3DRuntimeLoader::supportedExtensions()
153 static QStringList extensions;
154 if (!extensions.isEmpty())
157 static const QStringList supportedExtensions = { QLatin1StringView(
"obj"),
158 QLatin1StringView(
"gltf"),
159 QLatin1StringView(
"glb")};
161 QSSGAssetImportManager importManager;
162 const auto types = importManager.getImporterPluginInfos();
164 for (
const auto &t : types) {
165 for (
const QString &extension : t.inputExtensions) {
167 if (supportedExtensions.contains(extension) && !extensions.contains(extension))
168 extensions << extension;
174#if QT_CONFIG(mimetype)
175QList<QMimeType> QQuick3DRuntimeLoader::supportedMimeTypes()
177 static QList<QMimeType> mimeTypes;
178 if (!mimeTypes.isEmpty())
181 const QStringList &extensions = supportedExtensions();
184 for (
const auto &ext : extensions) {
186 const QString fileName = QLatin1StringView(
"test.") + ext;
187 mimeTypes << db.mimeTypesForFileName(fileName);
194static void boxBoundsRecursive(
const QQuick3DNode *baseNode,
const QQuick3DNode *node, QQuick3DBounds3 &accBounds)
199 if (
auto *model = qobject_cast<
const QQuick3DModel *>(node)) {
200 auto b = model->bounds();
201 for (
const QVector3D point : b.bounds.toQSSGBoxPoints()) {
202 auto p = model->mapPositionToNode(
const_cast<QQuick3DNode *>(baseNode), point);
203 if (Q_UNLIKELY(accBounds.bounds.isEmpty()))
204 accBounds.bounds = { p, p };
206 accBounds.bounds.include(p);
209 const auto childItems1 = node->childItems();
210 for (
auto *child : childItems1)
211 boxBoundsRecursive(baseNode, qobject_cast<
const QQuick3DNode *>(child), accBounds);
214template<
typename Func>
219 const auto childItems2 = obj->childItems();
220 for (
auto *child : childItems2) {
221 if (
auto *model = qobject_cast<QQuick3DModel *>(child))
223 applyToModels(child, lambda);
227void QQuick3DRuntimeLoader::loadSource()
231 m_objectsByType.clear();
232 const bool hadVariants = !m_materialVariants.isEmpty();
233 m_materialVariants.clear();
234 m_variantModels.clear();
236 emit materialVariantsChanged();
237 QSSGBufferManager::unregisterMeshData(m_assetId);
239 m_status = Status::Empty;
240 m_errorString = QStringLiteral(
"No file selected");
241 if (!m_source.isValid()) {
242 emit statusChanged();
243 emit errorStringChanged();
247 QSSGAssetImportManager importManager;
248 QSSGSceneDesc::Scene scene;
249 QString error(QStringLiteral(
"Unknown error"));
250 auto result = importManager.importFile(m_source, scene, &error);
253 case QSSGAssetImportManager::ImportState::Success:
254 m_errorString = QStringLiteral(
"Success!");
255 m_status = Status::Success;
257 case QSSGAssetImportManager::ImportState::IoError:
258 m_errorString = QStringLiteral(
"IO Error: ") + error;
259 m_status = Status::Error;
261 case QSSGAssetImportManager::ImportState::Unsupported:
262 m_errorString = QStringLiteral(
"Unsupported: ") + error;
263 m_status = Status::Error;
267 if (m_status == Status::Success) {
271 m_root =
new QQuick3DNode(
this);
272 m_root->setObjectName(
"RuntimeLoaderRoot");
273 m_imported = QSSGRuntimeUtils::createScene(*m_root, scene, &m_objects, &m_objectsByType);
274 m_assetId = scene.id;
275 m_boundsDirty =
true;
276 m_instancingChanged = m_instancing !=
nullptr;
279 m_materialVariants = scene.materialVariants;
280 m_variantModels.clear();
281 if (!m_materialVariants.isEmpty()) {
282 const auto materialObjects = [](
const QList<QSSGSceneDesc::Node *> &materials) {
283 QList<QQuick3DMaterial *> result;
284 result.reserve(materials.size());
285 for (
const QSSGSceneDesc::Node *material : materials)
286 result.append(qobject_cast<QQuick3DMaterial *>(material->obj));
289 QList<QSSGSceneDesc::Node *> pending { scene.root };
290 while (!pending.isEmpty()) {
291 QSSGSceneDesc::Node *node = pending.takeLast();
292 pending.append(node->children);
293 if (node->nodeType != QSSGSceneDesc::Node::Type::Model)
295 const auto &model =
static_cast<
const QSSGSceneDesc::Model &>(*node);
296 if (model.variantMaterials.isEmpty())
298 ModelVariantMaterials entry;
299 entry.model = qobject_cast<QQuick3DModel *>(model.obj);
300 entry.defaults = materialObjects(model.defaultMaterials);
301 for (
const auto &variantList : model.variantMaterials)
302 entry.perVariant.append(materialObjects(variantList));
303 m_variantModels.append(entry);
306 emit materialVariantsChanged();
313 emit sourceChanged();
316 emit statusChanged();
317 emit errorStringChanged();
321void QQuick3DRuntimeLoader::updateModels()
323 if (m_instancingChanged) {
324 applyToModels(m_imported, [
this](QQuick3DModel *model) {
325 model->setInstancing(m_instancing);
326 model->setInstanceRoot(m_imported);
328 m_instancingChanged =
false;
330 if (!m_variantModels.isEmpty())
331 applyMaterialVariant();
334void QQuick3DRuntimeLoader::applyMaterialVariant()
336 const qsizetype variantIndex = m_materialVariants.indexOf(m_materialVariant);
337 if (!m_materialVariant.isEmpty() && variantIndex < 0 && !m_materialVariants.isEmpty()) {
338 qWarning() <<
"Asset has no material variant" << m_materialVariant
339 <<
"- available variants:" << m_materialVariants;
341 for (
const ModelVariantMaterials &entry : std::as_const(m_variantModels)) {
344 const QList<QQuick3DMaterial *> &materials = (variantIndex >= 0 && variantIndex < entry.perVariant.size())
345 ? entry.perVariant.at(variantIndex)
347 QQmlListProperty<QQuick3DMaterial> list = entry.model->materials();
349 for (QQuick3DMaterial *material : materials)
350 list.append(&list, material);
355
356
357
358
359
360
361
362
363
364
365QStringList QQuick3DRuntimeLoader::materialVariants()
const
367 return m_materialVariants;
371
372
373
374
375
376
377
378
379
380QString QQuick3DRuntimeLoader::materialVariant()
const
382 return m_materialVariant;
385void QQuick3DRuntimeLoader::setMaterialVariant(
const QString &variant)
387 if (m_materialVariant == variant)
389 m_materialVariant = variant;
390 if (!m_variantModels.isEmpty())
391 applyMaterialVariant();
392 emit materialVariantChanged();
395QQuick3DRuntimeLoader::Status QQuick3DRuntimeLoader::status()
const
400QString QQuick3DRuntimeLoader::errorString()
const
402 return m_errorString;
405QSSGRenderGraphObject *QQuick3DRuntimeLoader::updateSpatialNode(QSSGRenderGraphObject *node)
407 auto *result = QQuick3DNode::updateSpatialNode(node);
409 QMetaObject::invokeMethod(
this, &QQuick3DRuntimeLoader::boundsChanged, Qt::QueuedConnection);
413void QQuick3DRuntimeLoader::calculateBounds()
415 if (!m_imported || !m_boundsDirty)
418 m_bounds.bounds.setEmpty();
419 boxBoundsRecursive(m_imported, m_imported, m_bounds);
420 m_boundsDirty =
false;
423const QQuick3DBounds3 &QQuick3DRuntimeLoader::bounds()
const
426 auto *that =
const_cast<QQuick3DRuntimeLoader *>(
this);
427 that->calculateBounds();
428 return that->m_bounds;
434QQuick3DInstancing *QQuick3DRuntimeLoader::instancing()
const
439void QQuick3DRuntimeLoader::setInstancing(QQuick3DInstancing *newInstancing)
441 if (m_instancing == newInstancing)
444 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DRuntimeLoader::setInstancing,
445 newInstancing, m_instancing);
447 m_instancing = newInstancing;
448 m_instancingChanged =
true;
450 emit instancingChanged();
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
486QQuick3DObject *QQuick3DRuntimeLoader::query(
const QString &name)
const
488 const auto index = name.lastIndexOf(QChar(u'/'));
491 const QString shortName = name.mid(index + 1);
492 const auto range = m_objects.equal_range({shortName, QString()});
493 for (
auto it = range.first; it != range.second; ++it) {
494 if (it.key().path == name)
499 return m_objects.value(QSSGRuntimeObjectNameKey{name, QString()});
504 using Type = QSSGRenderGraphObject::Type;
506 case QQuick3DRuntimeLoader::QueryFilter::Textures:
507 return QSSGRenderGraphObjectUtils::getBaseType(Type::Image2D);
508 case QQuick3DRuntimeLoader::QueryFilter::Materials:
509 return QSSGRenderGraphObjectUtils::getBaseType(Type::PrincipledMaterial);
510 case QQuick3DRuntimeLoader::QueryFilter::Nodes:
511 return QSSGRenderGraphObjectUtils::getBaseType(Type::Node);
512 case QQuick3DRuntimeLoader::QueryFilter::Cameras:
513 return QSSGRenderGraphObjectUtils::getBaseType(Type::OrthographicCamera);
514 case QQuick3DRuntimeLoader::QueryFilter::Lights:
515 return QSSGRenderGraphObjectUtils::getBaseType(Type::DirectionalLight);
516 case QQuick3DRuntimeLoader::QueryFilter::Models:
517 return QSSGRenderGraphObjectUtils::getBaseType(Type::Model);
520 Q_UNREACHABLE_RETURN(QSSGRenderGraphObject::BaseType(0));
524
525
526
527
528
529
530
531
532
533
534
535
536
537
539QList<QQuick3DObject *> QQuick3DRuntimeLoader::queryAll(QueryFilter filter)
const
541 QList<QQuick3DObject *> results;
542 const auto range = m_objectsByType.equal_range(queryFilterToBaseType(filter));
543 for (
auto it = range.first; it != range.second; ++it) {
544 if (
auto *obj = it.value().data())
Combined button and popup list for selecting options.
static void boxBoundsRecursive(const QQuick3DNode *baseNode, const QQuick3DNode *node, QQuick3DBounds3 &accBounds)
static void applyToModels(QQuick3DObject *obj, Func &&lambda)
static QSSGRenderGraphObject::BaseType queryFilterToBaseType(QQuick3DRuntimeLoader::QueryFilter filter)