Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qssgqmlutilities.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5#include "qssgscenedesc_p.h"
6
7#include <QVector2D>
8#include <QVector3D>
9#include <QVector4D>
10#include <QQuaternion>
11#include <QDebug>
12#include <QRegularExpression>
13#include <QtCore/qdir.h>
14#include <QtCore/qfile.h>
15#include <QtCore/qbuffer.h>
16
17#include <QtGui/qimage.h>
18#include <QtGui/qimagereader.h>
19
20#include <QtQuick3DUtils/private/qssgmesh_p.h>
21#include <QtQuick3DUtils/private/qssgassert_p.h>
22
23#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
24
25#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
26#include <QtCore/QCborStreamWriter>
27#include <QtQuickTimeline/private/qquicktimeline_p.h>
28#endif // QT_QUICK3D_ENABLE_RT_ANIMATIONS
29
31
32using namespace Qt::StringLiterals;
33
35
37{
38public:
40
41 static PropertyMap *instance();
42
46
47private:
49
51
52};
53
55{
56 QString tabs;
57 for (int i = 0; i < n; ++i)
58 tabs += QLatin1String(" ");
59 return tabs;
60}
61
63 QString nameCopy = name;
64 if (nameCopy.isEmpty())
65 return QStringLiteral("Presentation");
66
67 nameCopy = sanitizeQmlId(nameCopy);
68
69 if (nameCopy[0].isLower())
70 nameCopy[0] = nameCopy[0].toUpper();
71
72 return nameCopy;
73}
74
76 QString colorString;
77 colorString = QLatin1Char('\"') + color.name(QColor::HexArgb) + QLatin1Char('\"');
78 return colorString;
79}
80
82 switch (variant.typeId()) {
83 case QMetaType::Float: {
84 auto value = variant.toDouble();
85 return QString::number(value);
86 }
87 case QMetaType::QVector2D: {
88 auto value = variant.value<QVector2D>();
89 return QString(QStringLiteral("Qt.vector2d(") + QString::number(double(value.x())) +
90 QStringLiteral(", ") + QString::number(double(value.y())) +
91 QStringLiteral(")"));
92 }
93 case QMetaType::QVector3D: {
94 auto value = variant.value<QVector3D>();
95 return QString(QStringLiteral("Qt.vector3d(") + QString::number(double(value.x())) +
96 QStringLiteral(", ") + QString::number(double(value.y())) +
97 QStringLiteral(", ") + QString::number(double(value.z())) +
98 QStringLiteral(")"));
99 }
100 case QMetaType::QVector4D: {
101 auto value = variant.value<QVector4D>();
102 return QString(QStringLiteral("Qt.vector4d(") + QString::number(double(value.x())) +
103 QStringLiteral(", ") + QString::number(double(value.y())) +
104 QStringLiteral(", ") + QString::number(double(value.z())) +
105 QStringLiteral(", ") + QString::number(double(value.w())) +
106 QStringLiteral(")"));
107 }
108 case QMetaType::QColor: {
109 auto value = variant.value<QColor>();
110 return colorToQml(value);
111 }
112 case QMetaType::QQuaternion: {
113 auto value = variant.value<QQuaternion>();
114 return QString(QStringLiteral("Qt.quaternion(") + QString::number(double(value.scalar())) +
115 QStringLiteral(", ") + QString::number(double(value.x())) +
116 QStringLiteral(", ") + QString::number(double(value.y())) +
117 QStringLiteral(", ") + QString::number(double(value.z())) +
118 QStringLiteral(")"));
119 }
120 default:
121 return variant.toString();
122 }
123}
124
126{
127 QString idCopy = id;
128 // If the id starts with a number...
129 if (!idCopy.isEmpty() && idCopy.at(0).isNumber())
130 idCopy.prepend(QStringLiteral("node"));
131
132 // sometimes first letter is a # (don't replace with underscore)
133 if (idCopy.startsWith(QChar::fromLatin1('#')))
134 idCopy.remove(0, 1);
135
136 // Replace all the characters other than ascii letters, numbers or underscore to underscores.
137 static QRegularExpression regExp(QStringLiteral("\\W"));
138 idCopy.replace(regExp, QStringLiteral("_"));
139
140 // first letter of id can not be upper case
141 // to make it look nicer, lower-case the initial run of all-upper-case characters
142 if (!idCopy.isEmpty() && idCopy[0].isUpper()) {
143
144 int i = 0;
145 int len = idCopy.length();
146 while (i < len && idCopy[i].isUpper()) {
147 idCopy[i] = idCopy[i].toLower();
148 ++i;
149 }
150 }
151
152 // ### qml keywords as names
154 "x",
155 "y",
156 "as",
157 "do",
158 "if",
159 "in",
160 "on",
161 "of",
162 "for",
163 "get",
164 "int",
165 "let",
166 "new",
167 "set",
168 "try",
169 "var",
170 "top",
171 "byte",
172 "case",
173 "char",
174 "else",
175 "num",
176 "from",
177 "goto",
178 "null",
179 "this",
180 "true",
181 "void",
182 "with",
183 "clip",
184 "item",
185 "flow",
186 "font",
187 "text",
188 "left",
189 "data",
190 "alias",
191 "break",
192 "state",
193 "scale",
194 "color",
195 "right",
196 "catch",
197 "class",
198 "const",
199 "false",
200 "float",
201 "layer", // Design Studio doesn't like "layer" as an id
202 "short",
203 "super",
204 "throw",
205 "while",
206 "yield",
207 "border",
208 "source",
209 "delete",
210 "double",
211 "export",
212 "import",
213 "native",
214 "public",
215 "pragma",
216 "return",
217 "signal",
218 "static",
219 "switch",
220 "throws",
221 "bottom",
222 "parent",
223 "typeof",
224 "boolean",
225 "opacity",
226 "enabled",
227 "anchors",
228 "padding",
229 "default",
230 "extends",
231 "finally",
232 "package",
233 "private",
234 "abstract",
235 "continue",
236 "debugger",
237 "function",
238 "property",
239 "readonly",
240 "children",
241 "volatile",
242 "interface",
243 "protected",
244 "transient",
245 "implements",
246 "instanceof",
247 "synchronized"
248 };
249 if (keywords.contains(idCopy.toUtf8())) {
250 idCopy += QStringLiteral("_");
251 }
252
253 // We may have removed all the characters by now
254 if (idCopy.isEmpty())
255 idCopy = QStringLiteral("node");
256
257 return idCopy;
258}
259
260QString sanitizeQmlSourcePath(const QString &source, bool removeParentDirectory)
261{
262 QString sourceCopy = source;
263
264 if (removeParentDirectory)
265 sourceCopy = QSSGQmlUtilities::stripParentDirectory(sourceCopy);
266
267 sourceCopy.replace(QChar::fromLatin1('\\'), QChar::fromLatin1('/'));
268
269 // must be surrounded in quotes
270 return QString(QStringLiteral("\"") + sourceCopy + QStringLiteral("\""));
271}
272
274{
275 static PropertyMap p;
276 return &p;
277}
278
280{
281 return m_properties[type];
282}
283
285{
287
288 if (m_properties.contains(type)) {
289 auto properties = m_properties[type];
290 value = properties.value(property);
291 }
292
293 return value;
294}
295
297{
298 bool isTheSame = value == getDefaultValue(type, property);
299 return isTheSame;
300}
301
303 PropertyMap::PropertiesMap propertiesMap;
304 auto metaObject = object->metaObject();
305 for (auto i = 0; i < metaObject->propertyCount(); ++i) {
306 auto property = metaObject->property(i);
307 const auto name = property.name();
308 const auto value = property.read(object);
309 propertiesMap.insert(name, value);
310 }
311 return propertiesMap;
312}
313
314PropertyMap::PropertyMap()
315{
316 // Create a table containing the default values for each property for each supported type
317 {
318 QQuick3DNode node;
319 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Node, getObjectPropertiesMap(&node));
320 }
321 {
322 QQuick3DPrincipledMaterial principledMaterial;
323 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PrincipledMaterial, getObjectPropertiesMap(&principledMaterial));
324 }
325 {
326 QQuick3DSpecularGlossyMaterial specularGlossyMaterial;
327 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::SpecularGlossyMaterial, getObjectPropertiesMap(&specularGlossyMaterial));
328 }
329 {
330 QQuick3DCustomMaterial customMaterial;
331 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::CustomMaterial, getObjectPropertiesMap(&customMaterial));
332 }
333 {
335 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Image2D, getObjectPropertiesMap(&texture));
336 }
337 {
338 QQuick3DCubeMapTexture cubeMapTexture;
339 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::ImageCube, getObjectPropertiesMap(&cubeMapTexture));
340 }
341 {
342 QQuick3DTextureData textureData;
343 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::TextureData, getObjectPropertiesMap(&textureData));
344 }
345 {
347 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Model, getObjectPropertiesMap(&model));
348 }
349 {
350 QQuick3DOrthographicCamera orthographicCamera;
351 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::OrthographicCamera, getObjectPropertiesMap(&orthographicCamera));
352 }
353 {
354 QQuick3DPerspectiveCamera perspectiveCamera;
355 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PerspectiveCamera, getObjectPropertiesMap(&perspectiveCamera));
356 }
357 {
358 QQuick3DDirectionalLight directionalLight;
359 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::DirectionalLight, getObjectPropertiesMap(&directionalLight));
360 }
361 {
362 QQuick3DPointLight pointLight;
363 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PointLight, getObjectPropertiesMap(&pointLight));
364 }
365 {
366 QQuick3DSpotLight spotLight;
367 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::SpotLight, getObjectPropertiesMap(&spotLight));
368 }
369 {
370 QQuick3DSkeleton skeleton;
371 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Skeleton, getObjectPropertiesMap(&skeleton));
372 }
373 {
374 QQuick3DJoint joint;
375 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Joint, getObjectPropertiesMap(&joint));
376 }
377 {
378 QQuick3DSkin skin;
379 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Skin, getObjectPropertiesMap(&skin));
380 }
381 {
382 QQuick3DMorphTarget morphTarget;
383 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::MorphTarget, getObjectPropertiesMap(&morphTarget));
384 }
385}
386
388{
391 {
395 };
403};
404
405template<QSSGSceneDesc::Material::RuntimeType T>
406const char *qmlElementName() { static_assert(!std::is_same_v<decltype(T), decltype(T)>, "Unknown type"); return nullptr; }
407template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::Node>() { return "Node"; }
408
409template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::SpecularGlossyMaterial>() { return "SpecularGlossyMaterial"; }
410template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::PrincipledMaterial>() { return "PrincipledMaterial"; }
411template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::CustomMaterial>() { return "CustomMaterial"; }
412template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::OrthographicCamera>() { return "OrthographicCamera"; }
413template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::PerspectiveCamera>() { return "PerspectiveCamera"; }
414
415template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::Model>() { return "Model"; }
416
417template<> const char *qmlElementName<QSSGSceneDesc::Texture::RuntimeType::Image2D>() { return "Texture"; }
418template<> const char *qmlElementName<QSSGSceneDesc::Texture::RuntimeType::ImageCube>() { return "CubeMapTexture"; }
419template<> const char *qmlElementName<QSSGSceneDesc::Texture::RuntimeType::TextureData>() { return "TextureData"; }
420
421template<> const char *qmlElementName<QSSGSceneDesc::Camera::RuntimeType::DirectionalLight>() { return "DirectionalLight"; }
422template<> const char *qmlElementName<QSSGSceneDesc::Camera::RuntimeType::SpotLight>() { return "SpotLight"; }
423template<> const char *qmlElementName<QSSGSceneDesc::Camera::RuntimeType::PointLight>() { return "PointLight"; }
424
425template<> const char *qmlElementName<QSSGSceneDesc::Joint::RuntimeType::Joint>() { return "Joint"; }
426template<> const char *qmlElementName<QSSGSceneDesc::Skeleton::RuntimeType::Skeleton>() { return "Skeleton"; }
427template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::Skin>() { return "Skin"; }
428template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::MorphTarget>() { return "MorphTarget"; }
429
430static const char *getQmlElementName(const QSSGSceneDesc::Node &node)
431{
432 using RuntimeType = QSSGSceneDesc::Node::RuntimeType;
433 switch (node.runtimeType) {
434 case RuntimeType::Node:
435 return qmlElementName<RuntimeType::Node>();
436 case RuntimeType::PrincipledMaterial:
437 return qmlElementName<RuntimeType::PrincipledMaterial>();
438 case RuntimeType::SpecularGlossyMaterial:
439 return qmlElementName<RuntimeType::SpecularGlossyMaterial>();
440 case RuntimeType::CustomMaterial:
441 return qmlElementName<RuntimeType::CustomMaterial>();
442 case RuntimeType::Image2D:
443 return qmlElementName<RuntimeType::Image2D>();
444 case RuntimeType::ImageCube:
445 return qmlElementName<RuntimeType::ImageCube>();
446 case RuntimeType::TextureData:
447 return qmlElementName<RuntimeType::TextureData>();
448 case RuntimeType::Model:
449 return qmlElementName<RuntimeType::Model>();
450 case RuntimeType::OrthographicCamera:
451 return qmlElementName<RuntimeType::OrthographicCamera>();
452 case RuntimeType::PerspectiveCamera:
453 return qmlElementName<RuntimeType::PerspectiveCamera>();
454 case RuntimeType::DirectionalLight:
455 return qmlElementName<RuntimeType::DirectionalLight>();
456 case RuntimeType::PointLight:
457 return qmlElementName<RuntimeType::PointLight>();
458 case RuntimeType::SpotLight:
459 return qmlElementName<RuntimeType::SpotLight>();
460 case RuntimeType::Skeleton:
461 return qmlElementName<RuntimeType::Skeleton>();
462 case RuntimeType::Joint:
463 return qmlElementName<RuntimeType::Joint>();
464 case RuntimeType::Skin:
465 return qmlElementName<RuntimeType::Skin>();
466 case RuntimeType::MorphTarget:
467 return qmlElementName<RuntimeType::MorphTarget>();
468 default:
469 return "UNKNOWN_TYPE";
470 }
471}
472
474{
496
497static constexpr QByteArrayView qml_basic_types[] {
498 "bool",
499 "double",
500 "int",
501 "list",
502 "real",
503 "string",
504 "url",
505 "var",
506 "color",
507 "date",
508 "font",
509 "matrix4x4",
510 "point",
511 "quaternion",
512 "rect",
513 "size",
514 "vector2d",
515 "vector3d",
516 "vector4d"
517};
518
519static_assert(std::size(qml_basic_types) == QMLBasicType::Unknown_Count, "Missing type?");
520
522{
523 switch (mt.id()) {
524 case QMetaType::Bool:
526 case QMetaType::Char:
527 case QMetaType::SChar:
528 case QMetaType::UChar:
529 case QMetaType::Char16:
530 case QMetaType::Char32:
531 case QMetaType::QChar:
532 case QMetaType::Short:
533 case QMetaType::UShort:
534 case QMetaType::Int:
535 case QMetaType::UInt:
536 case QMetaType::Long:
537 case QMetaType::ULong:
538 case QMetaType::LongLong:
539 case QMetaType::ULongLong:
541 case QMetaType::Float:
542 case QMetaType::Double:
544 case QMetaType::QByteArray:
545 case QMetaType::QString:
547 case QMetaType::QDate:
548 case QMetaType::QTime:
549 case QMetaType::QDateTime:
551 case QMetaType::QUrl:
553 case QMetaType::QRect:
554 case QMetaType::QRectF:
556 case QMetaType::QSize:
557 case QMetaType::QSizeF:
559 case QMetaType::QPoint:
560 case QMetaType::QPointF:
564 case QMetaType::QColor:
566 case QMetaType::QMatrix4x4:
568 case QMetaType::QVector2D:
570 case QMetaType::QVector3D:
572 case QMetaType::QVector4D:
574 case QMetaType::QQuaternion:
576 case QMetaType::QFont:
578 default:
580 }
581}
582
584Q_GLOBAL_STATIC(NodeNameMap, g_nodeNameMap)
587// Normally g_idMap will contain all the ids but in some cases
588// (like Animation, not Node) the ids will just be stored
589// to avoid conflict.
590// Now, Animations will be processed after all the Nodes,
591// For Nodes, it is not used.
594
596{
597 static constexpr const char *typeNames[] = {
598 "", // Transform
599 "_camera",
600 "", // Model
601 "_texture",
602 "_material",
603 "_light",
604 "_mesh",
605 "_skin",
606 "_skeleton",
607 "_joint",
608 "_morphtarget",
609 "_unknown"
610 };
611 constexpr uint nameCount = sizeof(typeNames)/sizeof(const char*);
612 const bool nodeHasName = (node.name.size() > 0);
613 uint nameIdx = qMin(uint(node.nodeType), nameCount);
614 QString name = nodeHasName ? QString::fromUtf8(node.name + typeNames[nameIdx]) : QString::fromLatin1(getQmlElementName(node));
616
617 // Make sure we return a unique id.
618 if (const auto it = g_nodeNameMap->constFind(&node); it != g_nodeNameMap->constEnd())
619 return *it;
620
621 quint64 id = node.id;
622 int attempts = 1000;
623 do {
624 if (const auto it = g_idMap->constFind(sanitizedName); it == g_idMap->constEnd()) {
625 g_idMap->insert(sanitizedName, &node);
626 g_nodeNameMap->insert(&node, sanitizedName);
627 return sanitizedName;
628 }
629
630 sanitizedName = QStringLiteral("%1%2").arg(sanitizedName).arg(id++);
631 } while (--attempts);
632
633 return sanitizedName;
634}
635
637{
638 QString name = !inName.isEmpty() ? QString::fromUtf8(inName + "_timeline") : "timeline0"_L1;
640
641 int attempts = 1000;
642 quint16 id = 0;
643 do {
644 if (const auto it = g_idMap->constFind(sanitizedName); it == g_idMap->constEnd()) {
645 if (const auto oIt = g_idOthers->constFind(sanitizedName); oIt == g_idOthers->constEnd()) {
646 g_idOthers->insert(sanitizedName);
647 return sanitizedName;
648 }
649 }
650
651 sanitizedName = QStringLiteral("%1%2").arg(sanitizedName).arg(++id);
652 } while (--attempts);
653
654 return sanitizedName;
655}
656
658 QString sourceCopy = filePath;
659 while (sourceCopy.startsWith(QChar::fromLatin1('.')) || sourceCopy.startsWith(QChar::fromLatin1('/')) || sourceCopy.startsWith(QChar::fromLatin1('\\')))
660 sourceCopy.remove(0, 1);
661 return sourceCopy;
662}
663
664static const char *blockBegin() { return " {\n"; }
665static const char *blockEnd() { return "}\n"; }
666static const char *comment() { return "// "; }
667static const char *indent() { return " "; }
668
670{
671 enum : quint8 { QSSG_INDENT = 4 };
675};
676
678{
679 QString str;
680 for (quint8 i = 0; i < output.indent; i += QSSGQmlScopedIndent::QSSG_INDENT)
682 return str;
683}
684
686{
687 for (quint8 i = 0; i < output.indent; i += QSSGQmlScopedIndent::QSSG_INDENT)
688 output.stream << indent();
689 return output.stream;
690}
691
692static const char *blockBegin(OutputContext &output)
693{
694 ++output.scopeDepth;
695 return blockBegin();
696}
697
698static const char *blockEnd(OutputContext &output)
699{
700 output.scopeDepth = qMax(0, output.scopeDepth - 1);
701 return blockEnd();
702}
703
704static void writeImportHeader(OutputContext &output, bool hasAnimation = false)
705{
706 output.stream << "import QtQuick\n"
707 << "import QtQuick3D\n\n";
708 if (hasAnimation)
709 output.stream << "import QtQuick.Timeline\n\n";
710}
711
712static QString toQuotedString(const QString &text) { return QStringLiteral("\"%1\"").arg(text); }
713
714static inline QString getMeshFolder() { return QStringLiteral("meshes/"); }
715static inline QString getMeshExtension() { return QStringLiteral(".mesh"); }
716
718{
719 const auto meshFolder = getMeshFolder();
720 const auto extension = getMeshExtension();
721
722 const auto sanitizedName = QSSGQmlUtilities::sanitizeQmlId(QString::fromUtf8(name));
723 return QString(meshFolder + sanitizedName + extension);
724}
725
726static inline QString getTextureFolder() { return QStringLiteral("maps/"); }
727
728static inline QString getAnimationFolder() { return QStringLiteral("animations/"); }
729static inline QString getAnimationExtension() { return QStringLiteral(".qad"); }
731{
732 const auto animationFolder = getAnimationFolder();
733 const auto extension = getAnimationExtension();
734 return QString(animationFolder + id + QStringLiteral("_")
735 + property + QStringLiteral("_")
737}
738
740{
741 return var.toString();
742}
743
745{
746 switch (var.metaType().id()) {
747 case QMetaType::QVector2D: {
748 const auto vec2 = qvariant_cast<QVector2D>(var);
749 return QLatin1String("Qt.vector2d(") + QString::number(vec2.x()) + QLatin1String(", ") + QString::number(vec2.y()) + QLatin1Char(')');
750 }
751 case QMetaType::QVector3D: {
752 const auto vec3 = qvariant_cast<QVector3D>(var);
753 return QLatin1String("Qt.vector3d(") + QString::number(vec3.x()) + QLatin1String(", ")
754 + QString::number(vec3.y()) + QLatin1String(", ")
755 + QString::number(vec3.z()) + QLatin1Char(')');
756 }
757 case QMetaType::QVector4D: {
758 const auto vec4 = qvariant_cast<QVector4D>(var);
759 return QLatin1String("Qt.vector4d(") + QString::number(vec4.x()) + QLatin1String(", ")
760 + QString::number(vec4.y()) + QLatin1String(", ")
761 + QString::number(vec4.z()) + QLatin1String(", ")
762 + QString::number(vec4.w()) + QLatin1Char(')');
763 }
764 case QMetaType::QColor: {
765 const auto color = qvariant_cast<QColor>(var);
766 return colorToQml(color);
767 }
768 case QMetaType::QQuaternion: {
769 const auto &quat = qvariant_cast<QQuaternion>(var);
770 return QLatin1String("Qt.quaternion(") + QString::number(quat.scalar()) + QLatin1String(", ")
771 + QString::number(quat.x()) + QLatin1String(", ")
772 + QString::number(quat.y()) + QLatin1String(", ")
773 + QString::number(quat.z()) + QLatin1Char(')');
774 }
775 case QMetaType::QMatrix4x4: {
776 const auto mat44 = qvariant_cast<QMatrix4x4>(var);
777 return QLatin1String("Qt.matrix4x4(")
778 + QString::number(mat44(0, 0)) + u", " + QString::number(mat44(0, 1)) + u", " + QString::number(mat44(0, 2)) + u", " + QString::number(mat44(0, 3)) + u", "
779 + QString::number(mat44(1, 0)) + u", " + QString::number(mat44(1, 1)) + u", " + QString::number(mat44(1, 2)) + u", " + QString::number(mat44(1, 3)) + u", "
780 + QString::number(mat44(2, 0)) + u", " + QString::number(mat44(2, 1)) + u", " + QString::number(mat44(2, 2)) + u", " + QString::number(mat44(2, 3)) + u", "
781 + QString::number(mat44(3, 0)) + u", " + QString::number(mat44(3, 1)) + u", " + QString::number(mat44(3, 2)) + u", " + QString::number(mat44(3, 3)) + u')';
782 }
783 case QMetaType::Float:
784 case QMetaType::Double:
785 case QMetaType::Int:
786 case QMetaType::Char:
787 case QMetaType::Long:
788 case QMetaType::LongLong:
789 case QMetaType::ULong:
790 case QMetaType::ULongLong:
791 case QMetaType::Bool:
792 return var.toString();
793 case QMetaType::QUrl: // QUrl needs special handling. Return empty string to trigger that.
794 default:
795 break;
796 }
797
798 return QString();
799}
800
802{
804 return QStringLiteral("position");
806 return QStringLiteral("rotation");
808 return QStringLiteral("scale");
810 return QStringLiteral("weight");
811
812 return QStringLiteral("unknown");
813}
814
815static std::pair<QString, QString> meshAssetName(const QSSGSceneDesc::Scene &scene, const QSSGSceneDesc::Mesh &meshNode, const QDir &outdir)
816{
817 // Returns {name, notValidReason}
818
819 const auto meshFolder = getMeshFolder();
820 const auto meshSourceName = QSSGQmlUtilities::getMeshSourceName(meshNode.name);
821 Q_ASSERT(scene.meshStorage.size() > meshNode.idx);
822 const auto &mesh = scene.meshStorage.at(meshNode.idx);
823
824 // If a mesh folder does not exist, then create one
825 if (!outdir.exists(meshFolder) && !outdir.mkdir(meshFolder)) {
826 qDebug() << "Failed to create meshes folder at" << outdir;
827 return {}; // Error out
828 }
829
830 const QString path = outdir.path() + QDir::separator() + meshSourceName;
831 QFile file(path);
833 return {QString(), QStringLiteral("Failed to find mesh at ") + path};
834 }
835
836 if (mesh.save(&file) == 0) {
837 return {};
838 }
839
840 return {meshSourceName, QString()};
841};
842
843static std::pair<QString, QString> copyTextureAsset(const QUrl &texturePath, OutputContext &output)
844{
845 // Returns {path, notValidReason}
846
847 // TODO: Use QUrl::resolved() instead of manual string manipulation
848 QString assetPath = output.outdir.isAbsolutePath(texturePath.path()) ? texturePath.toString() : texturePath.path();
849 QFileInfo fi(assetPath);
850 if (fi.isRelative() && !output.sourceDir.isEmpty()) {
851 fi = QFileInfo(output.sourceDir + QChar(u'/') + assetPath);
852 }
853 if (!fi.exists()) {
854 indent(output) << comment() << "Source texture path expected: " << getTextureFolder() + texturePath.fileName() << "\n";
855 return {QString(), QStringLiteral("Failed to find texture at ") + assetPath};
856 }
857
858 const auto mapsFolder = getTextureFolder();
859 // If a maps folder does not exist, then create one
860 if (!output.outdir.exists(mapsFolder) && !output.outdir.mkdir(mapsFolder)) {
861 qDebug() << "Failed to create maps folder at" << output.outdir;
862 return {}; // Error out
863 }
864
865 const QString relpath = mapsFolder + fi.fileName();
866 const auto newfilepath = QString(output.outdir.canonicalPath() + QDir::separator() + relpath);
867 if (!QFile::exists(newfilepath) && !QFile::copy(fi.canonicalFilePath(), newfilepath)) {
868 qDebug() << "Failed to copy file from" << fi.canonicalFilePath() << "to" << newfilepath;
869 return {};
870 }
871
872 return {relpath, QString()};
873};
874
876{
877 static const QRegularExpression re(QLatin1String("^Qt.[a-z0-9]*\\(([0-9.e\\+\\-, ]*)\\)"));
878 Q_ASSERT(re.isValid());
879
880 switch (mt.id()) {
881 case QMetaType::QVector2D: {
883 if (match.hasMatch()) {
884 const auto comp = match.captured(1).split(QLatin1Char(','));
885 if (comp.size() == 2) {
886 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
887 QLatin1String(".y: ") + comp.at(1).trimmed() };
888 }
889 }
890 break;
891 }
892 case QMetaType::QVector3D: {
894 if (match.hasMatch()) {
895 const auto comp = match.captured(1).split(QLatin1Char(','));
896 if (comp.size() == 3) {
897 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
898 QLatin1String(".y: ") + comp.at(1).trimmed(),
899 QLatin1String(".z: ") + comp.at(2).trimmed() };
900 }
901 }
902 break;
903 }
904 case QMetaType::QVector4D: {
906 if (match.hasMatch()) {
907 const auto comp = match.captured(1).split(QLatin1Char(','));
908 if (comp.size() == 4) {
909 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
910 QLatin1String(".y: ") + comp.at(1).trimmed(),
911 QLatin1String(".z: ") + comp.at(2).trimmed(),
912 QLatin1String(".w: ") + comp.at(3).trimmed() };
913 }
914 }
915 break;
916 }
917 case QMetaType::QQuaternion: {
919 if (match.hasMatch()) {
920 const auto comp = match.captured(1).split(QLatin1Char(','));
921 if (comp.size() == 4) {
922 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
923 QLatin1String(".y: ") + comp.at(1).trimmed(),
924 QLatin1String(".z: ") + comp.at(2).trimmed(),
925 QLatin1String(".scalar: ") + comp.at(3).trimmed() };
926 }
927 }
928 break;
929 }
930 default:
931 break;
932 }
933
934 return { value };
935}
936
938{
939 // Workaround for DS
940 if (mt.id() != QMetaType::QQuaternion)
941 return expandComponents(value, mt);
942
943 return { value };
944}
945
947 bool ok = false;
951 bool isDynamicProperty = false;
953};
954
956{
958 if (property.value.isNull()) {
959 result.ok = false;
960 result.notValidReason = QStringLiteral("Property value is null");
961 return result;
962 }
963
964 const QVariant &value = property.value;
965 result.name = QString::fromUtf8(property.name);
966 result.isDynamicProperty = property.type == QSSGSceneDesc::Property::Type::Dynamic;
967
968 // Built-in types
969 QString valueAsString = builtinQmlType(value);
970 if (valueAsString.size() > 0) {
971 result.value = valueAsString;
972 result.ok = true;
973 } else if (value.metaType().flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) {
974 static const auto qmlEnumString = [](const QLatin1String &element, const QString &enumString) {
975 return QStringLiteral("%1.%2").arg(element).arg(enumString);
976 };
978 QString enumValue = asString(value);
979 if (enumValue.size() > 0) {
980 result.value = qmlEnumString(qmlElementName, enumValue);
981 result.ok = true;
982 }
983 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Flag>()) {
985 if (element.size() > 0) {
986 const auto flag = qvariant_cast<QSSGSceneDesc::Flag>(value);
987 QByteArray keysString = flag.me.valueToKeys(int(flag.value));
988 if (keysString.size() > 0) {
989 keysString.prepend(element + '.');
990 QByteArray replacement(" | " + element + '.');
991 keysString.replace('|', replacement);
992 result.value = QString::fromLatin1(keysString);
993 result.ok = true;
994 }
995 }
996 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::NodeList *>()) {
997 const auto *list = qvariant_cast<QSSGSceneDesc::NodeList *>(value);
998 if (list->count > 0) {
999 const QString indentStr = indentString(output);
1000 QSSGQmlScopedIndent scopedIndent(output);
1001 const QString listIndentStr = indentString(output);
1002
1003 QString str;
1004 str.append(u"[\n");
1005
1006 for (int i = 0, end = list->count; i != end; ++i) {
1007 if (i != 0)
1008 str.append(u",\n");
1009 str.append(listIndentStr);
1010 str.append(getIdForNode(*(list->head[i])));
1011 }
1012
1013 str.append(u'\n' + indentStr + u']');
1014
1015 result.value = str;
1016 result.ok = true;
1017 }
1018 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::ListView *>()) {
1019 const auto &list = *qvariant_cast<QSSGSceneDesc::ListView *>(value);
1020 if (list.count > 0) {
1021 const QString indentStr = indentString(output);
1022 QSSGQmlScopedIndent scopedIndent(output);
1023 const QString listIndentStr = indentString(output);
1024
1025 QString str;
1026 str.append(u"[\n");
1027
1028 char *vptr = reinterpret_cast<char *>(list.data);
1029 auto size = list.mt.sizeOf();
1030
1031 for (int i = 0, end = list.count; i != end; ++i) {
1032 if (i != 0)
1033 str.append(u",\n");
1034
1035 const QVariant var{list.mt, reinterpret_cast<void *>(vptr + (size * i))};
1036 QString valueString = builtinQmlType(var);
1037 if (valueString.isEmpty())
1038 valueString = asString(var);
1039
1040 str.append(listIndentStr);
1041 str.append(valueString);
1042 }
1043
1044 str.append(u'\n' + indentStr + u']');
1045
1046 result.value = str;
1047 result.ok = true;
1048 }
1049 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Node *>()) {
1050 if (const auto node = qvariant_cast<QSSGSceneDesc::Node *>(value)) {
1051 // If this assert is triggerd it likely means that the node never got added
1052 // to the scene tree (see: addNode()) or that it's a type not handled as a resource, see:
1053 // writeQmlForResources()
1054 Q_ASSERT(node->id != 0);
1055 // The 'TextureData' node will have its data written out and become
1056 // a source url.
1057
1058 if (node->runtimeType == QSSGSceneDesc::Node::RuntimeType::TextureData) {
1059 result.name = QStringLiteral("source");
1060 result.value = getIdForNode(*node->scene->root) + QLatin1Char('.') + getIdForNode(*node);
1061 } else {
1062 result.value = getIdForNode(*node);
1063 }
1064 result.ok = true;
1065 }
1066 } else if (value.metaType() == QMetaType::fromType<QSSGSceneDesc::Mesh *>()) {
1067 if (const auto meshNode = qvariant_cast<const QSSGSceneDesc::Mesh *>(value)) {
1068 Q_ASSERT(meshNode->nodeType == QSSGSceneDesc::Node::Type::Mesh);
1069 Q_ASSERT(meshNode->scene);
1070 const auto &scene = *meshNode->scene;
1071 const auto& [meshSourceName, notValidReason] = meshAssetName(scene, *meshNode, output.outdir);
1072 result.notValidReason = notValidReason;
1073 if (!meshSourceName.isEmpty()) {
1074 result.value = toQuotedString(meshSourceName);
1075 result.ok = true;
1076 }
1077 }
1078 } else if (value.metaType() == QMetaType::fromType<QUrl>()) {
1079 if (const auto url = qvariant_cast<QUrl>(value); !url.isEmpty()) {
1080 // We need to adjust source url(s) as those should contain the canonical path
1081 QString path;
1082 if (QSSGRenderGraphObject::isTexture(target.runtimeType)) {
1083 const auto& [relpath, notValidReason] = copyTextureAsset(url, output);
1084 result.notValidReason = notValidReason;
1085 if (!relpath.isEmpty()) {
1086 path = relpath;
1087 }
1088 } else
1089 path = url.path();
1090
1091 if (!path.isEmpty()) {
1092 result.value = toQuotedString(path);
1093 result.ok = true;
1094 }
1095 }
1096 } else if (target.runtimeType == QSSGSceneDesc::Material::RuntimeType::CustomMaterial) {
1097 // Workaround the TextureInput item that wraps textures for the Custom material.
1098 if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Texture *>()) {
1099 if (const auto texture = qvariant_cast<QSSGSceneDesc::Texture *>(value)) {
1101 result.value = QLatin1String("TextureInput { texture: ") +
1103 result.ok = true;
1104 }
1105 }
1106 } else if (value.metaType() == QMetaType::fromType<QString>()) {
1107 // Plain strings in the scenedesc should map to QML string values
1108 result.value = toQuotedString(value.toString());
1109 result.ok = true;
1110 } else {
1111 result.notValidReason = QStringLiteral("Unsupported value type: ") + QString::fromUtf8(value.metaType().name());
1112 qWarning() << result.notValidReason;
1113 result.ok = false;
1114 }
1115
1118 ? expandComponentsPartially(result.value, value.metaType())
1119 : expandComponents(result.value, value.metaType());
1120 }
1121
1122 return result;
1123}
1124
1126{
1127 QSSGQmlScopedIndent scopedIndent(output);
1128
1129 indent(output) << u"id: "_s << getIdForNode(node) << u'\n';
1130
1131 // Set Object Name if one exists
1132 if (node.name.size()) {
1133 const QString objectName = QString::fromLocal8Bit(node.name);
1134 if (!objectName.startsWith(u'*'))
1135 indent(output) << u"objectName: \""_s << node.name << u"\"\n"_s;
1136 }
1137
1138 const auto &properties = node.properties;
1139 auto it = properties.begin();
1140 const auto end = properties.end();
1141 for (; it != end; ++it) {
1142 const auto &property = *it;
1143
1145 if (result.ok) {
1146 if (result.isDynamicProperty) {
1147 indent(output) << "property " << typeName(property->value.metaType()).toByteArray() << ' ' << result.name << u": "_s << result.value << u'\n';
1148 } else if (!QSSGQmlUtilities::PropertyMap::instance()->isDefaultValue(node.runtimeType, property->name, property->value)) {
1149 if (result.expandedProperties.size() > 1) {
1150 for (const auto &va : result.expandedProperties)
1151 indent(output) << result.name << va << u'\n';
1152 } else {
1153 indent(output) << result.name << u": "_s << result.value << u'\n';
1154 }
1155 }
1156 } else if (!result.isDynamicProperty) {
1157 QString message = u"Skipped property: "_s + QString::fromUtf8(property->name);
1158 if (!result.notValidReason.isEmpty())
1159 message.append(u", reason: "_s + result.notValidReason);
1160 qDebug() << message;
1161 indent(output) << comment() << message + u'\n';
1162 }
1163 }
1164}
1165
1167{
1168 using namespace QSSGSceneDesc;
1169 Q_ASSERT(transform.nodeType == QSSGSceneDesc::Node::Type::Transform && transform.runtimeType == QSSGSceneDesc::Node::RuntimeType::Node);
1172}
1173
1175{
1176 using namespace QSSGSceneDesc;
1177 Q_ASSERT(material.nodeType == QSSGSceneDesc::Model::Type::Material);
1178 if (material.runtimeType == QSSGSceneDesc::Model::RuntimeType::SpecularGlossyMaterial) {
1179 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1180 } else if (material.runtimeType == Model::RuntimeType::PrincipledMaterial) {
1181 indent(output) << qmlElementName<Material::RuntimeType::PrincipledMaterial>() << blockBegin(output);
1182 } else if (material.runtimeType == Material::RuntimeType::CustomMaterial) {
1183 indent(output) << qmlElementName<Material::RuntimeType::CustomMaterial>() << blockBegin(output);
1184 } else if (material.runtimeType == Material::RuntimeType::SpecularGlossyMaterial) {
1185 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1186 } else {
1187 Q_UNREACHABLE();
1188 }
1189
1190 writeNodeProperties(material, output);
1191}
1192
1194{
1195 using namespace QSSGSceneDesc;
1196 Q_ASSERT(model.nodeType == Node::Type::Model);
1199}
1200
1202{
1203 using namespace QSSGSceneDesc;
1204 Q_ASSERT(camera.nodeType == Node::Type::Camera);
1205 if (camera.runtimeType == Camera::RuntimeType::PerspectiveCamera)
1206 indent(output) << qmlElementName<Camera::RuntimeType::PerspectiveCamera>() << blockBegin(output);
1207 else if (camera.runtimeType == Camera::RuntimeType::OrthographicCamera)
1208 indent(output) << qmlElementName<Camera::RuntimeType::OrthographicCamera>() << blockBegin(output);
1209 else
1210 Q_UNREACHABLE();
1212}
1213
1215{
1216 using namespace QSSGSceneDesc;
1217 Q_ASSERT(texture.nodeType == Node::Type::Texture && QSSGRenderGraphObject::isTexture(texture.runtimeType));
1218 if (texture.runtimeType == Texture::RuntimeType::Image2D)
1219 indent(output) << qmlElementName<Texture::RuntimeType::Image2D>() << blockBegin(output);
1220 else if (texture.runtimeType == Texture::RuntimeType::ImageCube)
1221 indent(output) << qmlElementName<Texture::RuntimeType::ImageCube>() << blockBegin(output);
1223}
1224
1226{
1227 using namespace QSSGSceneDesc;
1228 Q_ASSERT(skin.nodeType == Node::Type::Skin && skin.runtimeType == Node::RuntimeType::Skin);
1229 indent(output) << qmlElementName<Node::RuntimeType::Skin>() << blockBegin(output);
1231}
1232
1234{
1235 using namespace QSSGSceneDesc;
1236 Q_ASSERT(morphTarget.nodeType == Node::Type::MorphTarget);
1238 writeNodeProperties(morphTarget, output);
1239}
1240
1242{
1243 const auto textureFolder = getTextureFolder();
1244
1245 const auto sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
1246 const auto ext = (fmt.length() != 3) ? u".png"_s
1247 : u"."_s + fmt;
1248
1249 return QString(textureFolder + sanitizedName + ext);
1250}
1251
1252static QString outputTextureAsset(const QSSGSceneDesc::TextureData &textureData, const QDir &outdir)
1253{
1254 if (textureData.data.isEmpty())
1255 return QString();
1256
1257 const auto mapsFolder = getTextureFolder();
1258 const auto id = getIdForNode(textureData);
1259 const QString textureSourceName = getTextureSourceName(id, QString::fromUtf8(textureData.fmt));
1260
1261 const bool isCompressed = ((textureData.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
1262
1263 // If a maps folder does not exist, then create one
1264 if (!outdir.exists(mapsFolder) && !outdir.mkdir(mapsFolder))
1265 return QString(); // Error out
1266
1267 const auto imagePath = QString(outdir.path() + QDir::separator() + textureSourceName);
1268
1269 if (isCompressed) {
1270 QFile file(imagePath);
1272 file.write(textureData.data);
1273 file.close();
1274 } else {
1275 const auto &texData = textureData.data;
1276 const auto &size = textureData.sz;
1277 QImage image;
1278 image = QImage(reinterpret_cast<const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
1279 if (!image.save(imagePath))
1280 return QString();
1281 }
1282
1283 return textureSourceName;
1284}
1285
1287{
1288 using namespace QSSGSceneDesc;
1289 Q_ASSERT(textureData.nodeType == Node::Type::Texture && textureData.runtimeType == Node::RuntimeType::TextureData);
1290
1291 QString textureSourcePath = outputTextureAsset(textureData, output.outdir);
1292
1293 static const auto writeProperty = [](const QString &type, const QString &name, const QString &value) {
1294 return QString::fromLatin1("property %1 %2: %3").arg(type, name, value);
1295 };
1296
1297 if (!textureSourcePath.isEmpty()) {
1298 const auto type = QLatin1String("url");
1299 const auto name = getIdForNode(textureData);
1300
1301 indent(output) << writeProperty(type, name, toQuotedString(textureSourcePath)) << '\n';
1302 }
1303}
1304
1306{
1307 using namespace QSSGSceneDesc;
1308 Q_ASSERT(light.nodeType == Node::Type::Light);
1309 if (light.runtimeType == Light::RuntimeType::DirectionalLight)
1310 indent(output) << qmlElementName<Light::RuntimeType::DirectionalLight>() << blockBegin(output);
1311 else if (light.runtimeType == Light::RuntimeType::SpotLight)
1312 indent(output) << qmlElementName<Light::RuntimeType::SpotLight>() << blockBegin(output);
1313 else if (light.runtimeType == Light::RuntimeType::PointLight)
1314 indent(output) << qmlElementName<Light::RuntimeType::PointLight>() << blockBegin(output);
1315 else
1316 Q_UNREACHABLE();
1318}
1319
1321{
1322 using namespace QSSGSceneDesc;
1323 Q_ASSERT(skeleton.nodeType == Node::Type::Skeleton && skeleton.runtimeType == Node::RuntimeType::Skeleton);
1324 indent(output) << qmlElementName<Node::RuntimeType::Skeleton>() << blockBegin(output);
1325 writeNodeProperties(skeleton, output);
1326}
1327
1329{
1330 using namespace QSSGSceneDesc;
1331 Q_ASSERT(joint.nodeType == Node::Type::Joint && joint.runtimeType == Node::RuntimeType::Joint);
1332 indent(output) << qmlElementName<Node::RuntimeType::Joint>() << blockBegin(output);
1334}
1335
1337{
1338 using namespace QSSGSceneDesc;
1340 Q_ASSERT(QSSGRenderGraphObject::isResource(node.runtimeType) || node.nodeType == Node::Type::Mesh || node.nodeType == Node::Type::Skeleton);
1341
1342 const bool processNode = !node.properties.isEmpty() || (output.type == OutputContext::Resource);
1343 if (processNode) {
1344 QSSGQmlScopedIndent scopedIndent(output);
1345 switch (node.nodeType) {
1346 case Node::Type::Skin:
1347 writeQml(static_cast<const Skin &>(node), output);
1348 break;
1349 case Node::Type::MorphTarget:
1350 writeQml(static_cast<const MorphTarget &>(node), output);
1351 break;
1352 case Node::Type::Skeleton:
1353 writeQml(static_cast<const Skeleton &>(node), output);
1354 break;
1355 case Node::Type::Texture:
1356 if (node.runtimeType == Node::RuntimeType::Image2D)
1357 writeQml(static_cast<const Texture &>(node), output);
1358 else if (node.runtimeType == Node::RuntimeType::ImageCube)
1359 writeQml(static_cast<const Texture &>(node), output);
1360 else if (node.runtimeType == Node::RuntimeType::TextureData)
1361 writeQml(static_cast<const TextureData &>(node), output);
1362 else
1363 Q_UNREACHABLE();
1364 break;
1365 case Node::Type::Material:
1366 writeQml(static_cast<const Material &>(node), output);
1367 break;
1368 case Node::Type::Mesh:
1369 // Only handled as a property (see: valueToQml())
1370 break;
1371 default:
1372 qWarning("Unhandled resource type \'%d\'?", int(node.runtimeType));
1373 break;
1374 }
1375 }
1376
1377 // Do something more convenient if this starts expending to more types...
1378 // NOTE: The TextureData type is written out as a url property...
1379 const bool skipBlockEnd = (node.runtimeType == Node::RuntimeType::TextureData || node.nodeType == Node::Type::Mesh);
1380 if (!skipBlockEnd && processNode && output.scopeDepth != 0) {
1381 QSSGQmlScopedIndent scopedIndent(output);
1383 }
1384}
1385
1387{
1388 using namespace QSSGSceneDesc;
1389
1390 const bool processNode = !(node.properties.isEmpty() && node.children.isEmpty())
1391 || (output.type == OutputContext::Resource);
1392 if (processNode) {
1393 QSSGQmlScopedIndent scopedIndent(output);
1394 switch (node.nodeType) {
1395 case Node::Type::Skeleton:
1396 writeQml(static_cast<const Skeleton &>(node), output);
1397 break;
1398 case Node::Type::Joint:
1399 writeQml(static_cast<const Joint &>(node), output);
1400 break;
1401 case Node::Type::Light:
1402 writeQml(static_cast<const Light &>(node), output);
1403 break;
1404 case Node::Type::Transform:
1405 writeQml(node, output);
1406 break;
1407 case Node::Type::Camera:
1408 writeQml(static_cast<const Camera &>(node), output);
1409 break;
1410 case Node::Type::Model:
1411 writeQml(static_cast<const Model &>(node), output);
1412 break;
1413 default:
1414 break;
1415 }
1416 }
1417
1418 for (const auto &cld : node.children) {
1419 if (!QSSGRenderGraphObject::isResource(cld->runtimeType) && output.type == OutputContext::NodeTree) {
1420 QSSGQmlScopedIndent scopedIndent(output);
1421 writeQmlForNode(*cld, output);
1422 }
1423 }
1424
1425 // Do something more convenient if this starts expending to more types...
1426 // NOTE: The TextureData type is written out as a url property...
1427 const bool skipBlockEnd = (node.runtimeType == Node::RuntimeType::TextureData || node.nodeType == Node::Type::Mesh);
1428 if (!skipBlockEnd && processNode && output.scopeDepth != 0) {
1429 QSSGQmlScopedIndent scopedIndent(output);
1431 }
1432}
1433
1435{
1436 auto sortedResources = resources;
1437 std::sort(sortedResources.begin(), sortedResources.end(), [](const QSSGSceneDesc::Node *a, const QSSGSceneDesc::Node *b) {
1438 using RType = QSSGSceneDesc::Node::RuntimeType;
1439 if (a->runtimeType == RType::TextureData && b->runtimeType != RType::TextureData)
1440 return true;
1441 if (a->runtimeType == RType::ImageCube && (b->runtimeType != RType::TextureData && b->runtimeType != RType::ImageCube))
1442 return true;
1443 if (a->runtimeType == RType::Image2D && (b->runtimeType != RType::TextureData && b->runtimeType != RType::Image2D))
1444 return true;
1445
1446 return false;
1447 });
1448 for (const auto &res : std::as_const(sortedResources))
1450}
1451
1453{
1454#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1455 QCborStreamWriter writer(&keyframeData);
1456 // Start root array
1457 writer.startArray();
1458 // header name
1459 writer.append("QTimelineKeyframes");
1460 // file version. Increase this if the format changes.
1461 const int keyframesDataVersion = 1;
1462 writer.append(keyframesDataVersion);
1463 writer.append(int(channel.keys.at(0)->getValueQMetaType()));
1464
1465 // Start Keyframes array
1466 writer.startArray();
1467 quint8 compEnd = quint8(channel.keys.at(0)->getValueType());
1468 bool isQuaternion = false;
1470 isQuaternion = true;
1471 compEnd = 3;
1472 } else {
1473 compEnd++;
1474 }
1475 for (const auto &key : channel.keys) {
1476 writer.append(key->time);
1477 // Easing always linear
1479 if (isQuaternion)
1480 writer.append(key->value[3]);
1481 for (quint8 i = 0; i < compEnd; ++i)
1482 writer.append(key->value[i]);
1483 }
1484 // End Keyframes array
1485 writer.endArray();
1486 // End root array
1487 writer.endArray();
1488#else
1490 Q_UNUSED(keyframeData)
1491#endif // QT_QUICK3D_ENABLE_RT_ANIMATIONS
1492}
1493
1494void writeQmlForAnimation(const QSSGSceneDesc::Animation &anim, qsizetype index, OutputContext &output, bool useBinaryKeyframes = true)
1495{
1496 indent(output) << "Timeline {\n";
1497
1498 QSSGQmlScopedIndent scopedIndent(output);
1499 // The duration property of the TimelineAnimation is an int...
1500 const int duration = qCeil(anim.length);
1501 // Use the same name for objectName and id
1502 const QString objectName = getIdForAnimation(anim.name);
1503 indent(output) << "id: " << objectName << "\n";
1504 indent(output) << "objectName: \"" << objectName << "\"\n";
1505 indent(output) << "property real framesPerSecond: " << anim.framesPerSecond << "\n";
1506 indent(output) << "startFrame: 0\n";
1507 indent(output) << "endFrame: " << duration << "\n";
1508 indent(output) << "currentFrame: 0\n";
1509 indent(output) << "enabled: true\n";
1510 indent(output) << "animations: TimelineAnimation {\n";
1511 {
1512 QSSGQmlScopedIndent scopedIndent(output);
1513 indent(output) << "duration: " << duration << "\n";
1514 indent(output) << "from: 0\n";
1515 indent(output) << "to: " << duration << "\n";
1516 indent(output) << "running: true\n";
1517 indent(output) << "loops: Animation.Infinite\n";
1518 }
1520
1521 for (const auto &channel : anim.channels) {
1522 QString id = getIdForNode(*channel->target);
1523 QString propertyName = asString(channel->targetProperty);
1524
1525 indent(output) << "KeyframeGroup {\n";
1526 {
1527 QSSGQmlScopedIndent scopedIndent(output);
1528 indent(output) << "target: " << id << "\n";
1529 indent(output) << "property: " << toQuotedString(propertyName) << "\n";
1530 if (useBinaryKeyframes && channel->keys.size() != 1) {
1531 const auto animFolder = getAnimationFolder();
1532 const auto animSourceName = getAnimationSourceName(id, propertyName, index);
1533 if (!output.outdir.exists(animFolder) && !output.outdir.mkdir(animFolder)) {
1534 // Make a warning
1535 continue;
1536 }
1537 QFile file(output.outdir.path() + QDir::separator() + animSourceName);
1539 continue;
1540 QByteArray keyframeData;
1541 // It is possible to store this keyframeData but we have to consider
1542 // all the cases including runtime only or writeQml only.
1543 // For now, we will generate it for each case.
1544 generateKeyframeData(*channel, keyframeData);
1545 file.write(keyframeData);
1546 file.close();
1547 indent(output) << "keyframeSource: " << toQuotedString(animSourceName) << "\n";
1548 } else {
1549 Q_ASSERT(!channel->keys.isEmpty());
1550 for (const auto &key : channel->keys) {
1551 indent(output) << "Keyframe {\n";
1552 {
1553 QSSGQmlScopedIndent scopedIndent(output);
1554 indent(output) << "frame: " << key->time << "\n";
1555 indent(output) << "value: " << variantToQml(key->getValue()) << "\n";
1556 }
1558 }
1559 }
1560 }
1562 }
1563}
1564
1565void writeQml(const QSSGSceneDesc::Scene &scene, QTextStream &stream, const QDir &outdir, const QJsonObject &optionsObject)
1566{
1567 static const auto checkBooleanOption = [](const QLatin1String &optionName, const QJsonObject &options, bool defaultValue = false) {
1568 const auto it = options.constFind(optionName);
1569 const auto end = options.constEnd();
1571 if (it != end) {
1572 if (it->isObject())
1573 value = it->toObject().value(QLatin1String("value"));
1574 else
1575 value = it.value();
1576 }
1577 return value.toBool(defaultValue);
1578 };
1579
1580 auto root = scene.root;
1581 Q_ASSERT(root);
1582
1583 QJsonObject options = optionsObject;
1584
1585 if (auto it = options.constFind(QLatin1String("options")), end = options.constEnd(); it != end)
1586 options = it->toObject();
1587
1588 quint8 outputOptions{ OutputContext::Options::None };
1589 if (checkBooleanOption(QLatin1String("expandValueComponents"), options))
1591
1592 // Workaround for design studio type components
1593 if (checkBooleanOption(QLatin1String("designStudioWorkarounds"), options))
1595
1596 const bool useBinaryKeyframes = checkBooleanOption("useBinaryKeyframes"_L1, options);
1597
1598 OutputContext output { stream, outdir, scene.sourceDir, 0, OutputContext::Header, outputOptions };
1599
1600 writeImportHeader(output, scene.animations.count() > 0);
1601
1603 writeQml(*root, output); // Block scope will be left open!
1604 stream << "\n";
1605 stream << indent() << "// Resources\n";
1607 writeQmlForResources(scene.resources, output);
1609 stream << "\n";
1610 stream << indent() << "// Nodes:\n";
1611 for (const auto &cld : root->children)
1612 writeQmlForNode(*cld, output);
1613
1614 // animations
1615 qsizetype animId = 0;
1616 stream << "\n";
1617 stream << indent() << "// Animations:\n";
1618 for (const auto &cld : scene.animations) {
1619 QSSGQmlScopedIndent scopedIndent(output);
1620 writeQmlForAnimation(*cld, animId++, output, useBinaryKeyframes);
1622 }
1623
1624 // close the root
1626}
1627
1628void createTimelineAnimation(const QSSGSceneDesc::Animation &anim, QObject *parent, bool isEnabled, bool useBinaryKeyframes)
1629{
1630#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1631 auto timeline = new QQuickTimeline(parent);
1632 auto timelineKeyframeGroup = timeline->keyframeGroups();
1633 for (const auto &channel : anim.channels) {
1634 auto keyframeGroup = new QQuickKeyframeGroup(timeline);
1635 keyframeGroup->setTargetObject(channel->target->obj);
1636 keyframeGroup->setProperty(asString(channel->targetProperty));
1637
1638 Q_ASSERT(!channel->keys.isEmpty());
1639 if (useBinaryKeyframes) {
1640 QByteArray keyframeData;
1641 generateKeyframeData(*channel, keyframeData);
1642
1643 keyframeGroup->setKeyframeData(keyframeData);
1644 } else {
1645 auto keyframes = keyframeGroup->keyframes();
1646 for (const auto &key : channel->keys) {
1647 auto keyframe = new QQuickKeyframe(keyframeGroup);
1648 keyframe->setFrame(key->time);
1649 keyframe->setValue(key->getValue());
1650 keyframes.append(&keyframes, keyframe);
1651 }
1652 }
1653 (qobject_cast<QQmlParserStatus *>(keyframeGroup))->componentComplete();
1654 timelineKeyframeGroup.append(&timelineKeyframeGroup, keyframeGroup);
1655 }
1656 timeline->setEndFrame(anim.length);
1657 timeline->setEnabled(isEnabled);
1658
1659 auto timelineAnimation = new QQuickTimelineAnimation(timeline);
1660 timelineAnimation->setObjectName(anim.name);
1661 timelineAnimation->setDuration(int(anim.length));
1662 timelineAnimation->setFrom(0.0f);
1663 timelineAnimation->setTo(anim.length);
1664 timelineAnimation->setLoops(QQuickTimelineAnimation::Infinite);
1665 timelineAnimation->setTargetObject(timeline);
1666
1667 (qobject_cast<QQmlParserStatus *>(timeline))->componentComplete();
1668
1669 timelineAnimation->setRunning(true);
1670#else // QT_QUICK3D_ENABLE_RT_ANIMATIONS
1671 Q_UNUSED(anim)
1673 Q_UNUSED(isEnabled)
1674 Q_UNUSED(useBinaryKeyframes)
1675#endif // QT_QUICK3D_ENABLE_RT_ANIMATIONS
1676}
1677
1679{
1680 using namespace QSSGSceneDesc;
1681
1682 QSSG_ASSERT(node.scene != nullptr, return);
1683
1684 if (node.runtimeType == Material::RuntimeType::CustomMaterial) {
1685 QString sourceDir = node.scene->sourceDir;
1686 OutputContext output { stream, outDir, sourceDir, 0, OutputContext::Resource };
1688 writeQml(static_cast<const Material &>(node), output);
1689 // Resources, if any, are written out as properties on the component
1690 const auto &resources = node.scene->resources;
1691 writeQmlForResources(resources, output);
1693 } else {
1694 Q_UNREACHABLE(); // Only implemented for Custom material at this point.
1695 }
1696}
1697
1698}
1699
static void processNode(const SceneInfo &sceneInfo, const aiNode &source, QSSGSceneDesc::Node &parent, const NodeMap &nodeMap, AnimationNodeMap &animationNodes)
static bool checkBooleanOption(const QString &optionName, const QJsonObject &options)
IOBluetoothL2CAPChannel * channel
Definition lalr.h:137
\inmodule QtCore
Definition qbytearray.h:57
QByteArray & prepend(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:216
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
QByteArray & replace(qsizetype index, qsizetype len, const char *s, qsizetype alen)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:275
\inmodule QtCore\reentrant
void startArray()
Starts a CBOR Array with indeterminate length in the CBOR stream.
void append(quint64 u)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool endArray()
Terminates the array started by either overload of startArray() and returns true if the correct numbe...
\inmodule QtCore
Definition qchar.h:48
static constexpr QChar fromLatin1(char c) noexcept
Converts the Latin-1 character c to its equivalent QChar.
Definition qchar.h:461
constexpr bool isNumber() const noexcept
Returns true if the character is a number (Number_* categories, not just 0-9); otherwise returns fals...
Definition qchar.h:471
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
@ HexArgb
Definition qcolor.h:36
\inmodule QtCore
Definition qdir.h:19
bool mkdir(const QString &dirName) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qdir.cpp:1527
QString path() const
Returns the path.
Definition qdir.cpp:653
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
Definition qdir.h:206
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qdir.cpp:1715
void close() override
Calls QFileDevice::flush() and closes the file.
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString fileName() const
Returns the name of the file, excluding the path.
QString canonicalFilePath() const
Returns the canonical path including the file name, i.e.
bool isRelative() const
Returns true if the file path is relative, otherwise returns false (i.e.
bool exists() const
Returns true if the file exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:744
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
\inmodule QtCore
Definition qhash.h:818
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
\inmodule QtGui
Definition qimage.h:37
@ Format_RGBA8888
Definition qimage.h:59
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
\inmodule QtCore\reentrant
Definition qjsonvalue.h:24
constexpr QLatin1Char at(qsizetype i) const
bool isEmpty() const noexcept
Definition qlist.h:390
qsizetype count() const noexcept
Definition qlist.h:387
pointer data()
Definition qlist.h:414
\inmodule QtCore
Definition qmetatype.h:320
int id(int=0) const
Definition qmetatype.h:454
@ IsUnsignedEnumeration
Definition qmetatype.h:390
@ IsEnumeration
Definition qmetatype.h:386
friend class QVariant
Definition qmetatype.h:775
\inmodule QtCore
Definition qobject.h:90
The QQuaternion class represents a quaternion consisting of a vector and scalar.
Definition qquaternion.h:21
\qmltype TextureData \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DTextureData
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
bool isValid() const
Returns true if the regular expression is a valid regular expression (that is, it contains no syntax ...
QRegularExpressionMatch match(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to match the regular expression against the given subject string, starting at the position o...
bool isDefaultValue(QSSGSceneDesc::Node::RuntimeType type, const char *property, const QVariant &value)
PropertiesMap propertiesForType(QSSGSceneDesc::Node::RuntimeType type)
static PropertyMap * instance()
QVariant getDefaultValue(QSSGSceneDesc::Node::RuntimeType type, const char *property)
QHash< QByteArray, QVariant > PropertiesMap
Definition qset.h:18
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isUpper() const
Returns true if the string is uppercase, that is, it's identical to its toUpper() folding.
Definition qstring.cpp:5403
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString toLower() const &
Definition qstring.h:368
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString & append(QChar c)
Definition qstring.cpp:3227
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
QByteArray toUtf8() const &
Definition qstring.h:563
QString & prepend(QChar c)
Definition qstring.h:411
QString toUpper() const &
Definition qstring.h:372
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
\inmodule QtCore
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2494
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1888
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2465
\inmodule QtCore
Definition qvariant.h:64
T value() const &
Definition qvariant.h:511
double toDouble(bool *ok=nullptr) const
Returns the variant as a double if the variant has userType() \l QMetaType::Double,...
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
int typeId() const
Returns the storage type of the value stored in the variant.
Definition qvariant.h:337
QMetaType metaType() const
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
void extension()
[6]
Definition dialogs.cpp:230
QCamera * camera
Definition camera.cpp:19
QString str
[2]
QString text
QSet< QString >::iterator it
static const struct @480 keywords[]
const char * qmlElementName< QSSGSceneDesc::Camera::RuntimeType::PointLight >()
static const char * blockEnd()
QString builtinQmlType(const QVariant &var)
QString colorToQml(const QColor &color)
static void generateKeyframeData(const QSSGSceneDesc::Animation::Channel &channel, QByteArray &keyframeData)
QString getMeshSourceName(const QByteArrayView &name)
static QString getIdForNode(const QSSGSceneDesc::Node &node)
const char * qmlElementName< QSSGSceneDesc::Material::RuntimeType::PrincipledMaterial >()
const char * qmlElementName< QSSGSceneDesc::Node::RuntimeType::Node >()
QString asString(const QVariant &var)
const char * qmlElementName< QSSGSceneDesc::Material::RuntimeType::PerspectiveCamera >()
const char * qmlElementName< QSSGSceneDesc::Material::RuntimeType::CustomMaterial >()
const char * qmlElementName< QSSGSceneDesc::Node::RuntimeType::Skin >()
QString variantToQml(const QVariant &variant)
static QString getTextureFolder()
const char * qmlElementName< QSSGSceneDesc::Camera::RuntimeType::SpotLight >()
static PropertyMap::PropertiesMap getObjectPropertiesMap(QObject *object)
static QString getAnimationFolder()
const char * qmlElementName< QSSGSceneDesc::Texture::RuntimeType::TextureData >()
static QString getAnimationExtension()
static ValueToQmlResult valueToQml(const QSSGSceneDesc::Node &target, const QSSGSceneDesc::Property &property, OutputContext &output)
QString insertTabs(int n)
static QString getMeshExtension()
static const char * blockBegin()
QString stripParentDirectory(const QString &filePath)
static void writeQml(const QSSGSceneDesc::Node &transform, OutputContext &output)
static QStringList expandComponents(const QString &value, QMetaType mt)
void writeQmlForResources(const QSSGSceneDesc::Scene::ResourceNodes &resources, OutputContext &output)
const char * qmlElementName()
void writeQmlComponent(const QSSGSceneDesc::Node &node, QTextStream &stream, const QDir &outDir)
const char * qmlElementName< QSSGSceneDesc::Node::RuntimeType::Model >()
void createTimelineAnimation(const QSSGSceneDesc::Animation &anim, QObject *parent, bool isEnabled, bool useBinaryKeyframes)
static QString outputTextureAsset(const QSSGSceneDesc::TextureData &textureData, const QDir &outdir)
static const char * comment()
static QString toQuotedString(const QString &text)
QString sanitizeQmlId(const QString &id)
static QString getIdForAnimation(const QByteArray &inName)
static QString getMeshFolder()
static void writeImportHeader(OutputContext &output, bool hasAnimation=false)
static QStringList expandComponentsPartially(const QString &value, QMetaType mt)
static void writeNodeProperties(const QSSGSceneDesc::Node &node, OutputContext &output)
static std::pair< QString, QString > copyTextureAsset(const QUrl &texturePath, OutputContext &output)
static std::pair< QString, QString > meshAssetName(const QSSGSceneDesc::Scene &scene, const QSSGSceneDesc::Mesh &meshNode, const QDir &outdir)
static const char * getQmlElementName(const QSSGSceneDesc::Node &node)
const char * qmlElementName< QSSGSceneDesc::Skeleton::RuntimeType::Skeleton >()
static const char * typeNames[]
static const char * indent()
QString getAnimationSourceName(const QString &id, const QString &property, qsizetype index)
const char * qmlElementName< QSSGSceneDesc::Joint::RuntimeType::Joint >()
static constexpr QByteArrayView qml_basic_types[]
const char * qmlElementName< QSSGSceneDesc::Texture::RuntimeType::Image2D >()
static void writeQmlForResourceNode(const QSSGSceneDesc::Node &node, OutputContext &output)
QString qmlComponentName(const QString &name)
const char * qmlElementName< QSSGSceneDesc::Material::RuntimeType::SpecularGlossyMaterial >()
QString getTextureSourceName(const QString &name, const QString &fmt)
const char * qmlElementName< QSSGSceneDesc::Node::RuntimeType::MorphTarget >()
void writeQmlForAnimation(const QSSGSceneDesc::Animation &anim, qsizetype index, OutputContext &output, bool useBinaryKeyframes=true)
const char * qmlElementName< QSSGSceneDesc::Material::RuntimeType::OrthographicCamera >()
const char * qmlElementName< QSSGSceneDesc::Camera::RuntimeType::DirectionalLight >()
const char * qmlElementName< QSSGSceneDesc::Texture::RuntimeType::ImageCube >()
static QString indentString(OutputContext &output)
QString sanitizeQmlSourcePath(const QString &source, bool removeParentDirectory)
static void writeQmlForNode(const QSSGSceneDesc::Node &node, OutputContext &output)
Combined button and popup list for selecting options.
Definition image.cpp:4
static const QCssKnownValue properties[NumProperties - 1]
static int writeProperty(QObject *obj, const QByteArray &property_name, QVariant value, int propFlags=QDBusConnection::ExportAllProperties)
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
int qCeil(T v)
Definition qmath.h:36
const char * typeName
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLuint id
[7]
GLenum type
GLenum target
GLenum GLuint texture
GLuint GLsizei const GLchar * message
GLuint name
GLfloat n
GLsizei GLsizei GLchar * source
GLuint GLenum GLenum transform
GLuint res
GLenum GLsizei len
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_ASSERT(cond, action)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
unsigned char uchar
Definition qtypes.h:27
unsigned short quint16
Definition qtypes.h:43
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
unsigned char quint8
Definition qtypes.h:41
QVideoFrameFormat::PixelFormat fmt
QT_BEGIN_NAMESPACE typedef uchar * output
const char property[13]
Definition qwizard.cpp:101
QSqlQueryModel * model
[16]
QList< int > list
[14]
QFile file
[0]
QFileInfo fi("c:/temp/foo")
[newstuff]
QTextStream out(stdout)
[7]
QUrl url("example.com")
[constructor-url-reference]
obj metaObject() -> className()
QVariant variant
[1]
QGraphicsScene scene
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
static Q_REQUIRED_RESULT constexpr bool isTexture(Type type) Q_DECL_NOTHROW
static Q_REQUIRED_RESULT constexpr bool isResource(Type type) Q_DECL_NOTHROW
QSSGRenderGraphObject::Type RuntimeType
ResourceNodes resources
Definition moc.h:24
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent