Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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:
39 typedef QHash<QByteArray, QVariant> PropertiesMap;
40
41 static PropertyMap *instance();
42
46
47private:
49
50 QHash<QSSGSceneDesc::Node::RuntimeType, PropertiesMap> m_properties;
51
52};
53
55 QString nameCopy = name;
56 if (nameCopy.isEmpty())
57 return QStringLiteral("Presentation");
58
59 nameCopy = sanitizeQmlId(nameCopy);
60
61 if (nameCopy[0].isLower())
62 nameCopy[0] = nameCopy[0].toUpper();
63
64 return nameCopy;
65}
66
68 QString colorString;
69 colorString = QLatin1Char('\"') + color.name(QColor::HexArgb) + QLatin1Char('\"');
70 return colorString;
71}
72
74 switch (variant.typeId()) {
75 case QMetaType::Float: {
76 auto value = variant.toDouble();
77 return QString::number(value);
78 }
79 case QMetaType::QVector2D: {
80 auto value = variant.value<QVector2D>();
81 return QString(QStringLiteral("Qt.vector2d(") + QString::number(double(value.x())) +
82 QStringLiteral(", ") + QString::number(double(value.y())) +
83 QStringLiteral(")"));
84 }
85 case QMetaType::QVector3D: {
86 auto value = variant.value<QVector3D>();
87 return QString(QStringLiteral("Qt.vector3d(") + QString::number(double(value.x())) +
88 QStringLiteral(", ") + QString::number(double(value.y())) +
89 QStringLiteral(", ") + QString::number(double(value.z())) +
90 QStringLiteral(")"));
91 }
92 case QMetaType::QVector4D: {
93 auto value = variant.value<QVector4D>();
94 return QString(QStringLiteral("Qt.vector4d(") + QString::number(double(value.x())) +
95 QStringLiteral(", ") + QString::number(double(value.y())) +
96 QStringLiteral(", ") + QString::number(double(value.z())) +
97 QStringLiteral(", ") + QString::number(double(value.w())) +
98 QStringLiteral(")"));
99 }
100 case QMetaType::QColor: {
101 auto value = variant.value<QColor>();
102 return colorToQml(value);
103 }
104 case QMetaType::QQuaternion: {
105 auto value = variant.value<QQuaternion>();
106 return QString(QStringLiteral("Qt.quaternion(") + QString::number(double(value.scalar())) +
107 QStringLiteral(", ") + QString::number(double(value.x())) +
108 QStringLiteral(", ") + QString::number(double(value.y())) +
109 QStringLiteral(", ") + QString::number(double(value.z())) +
110 QStringLiteral(")"));
111 }
112 default:
113 return variant.toString();
114 }
115}
116
118{
119 QString idCopy = id;
120 // If the id starts with a number...
121 if (!idCopy.isEmpty() && idCopy.at(0).isNumber())
122 idCopy.prepend(QStringLiteral("node"));
123
124 // sometimes first letter is a # (don't replace with underscore)
125 if (idCopy.startsWith(QChar::fromLatin1('#')))
126 idCopy.remove(0, 1);
127
128 // Replace all the characters other than ascii letters, numbers or underscore to underscores.
129 static QRegularExpression regExp(QStringLiteral("\\W"));
130 idCopy.replace(regExp, QStringLiteral("_"));
131
132 // first letter of id can not be upper case
133 // to make it look nicer, lower-case the initial run of all-upper-case characters
134 if (!idCopy.isEmpty() && idCopy[0].isUpper()) {
135
136 int i = 0;
137 int len = idCopy.length();
138 while (i < len && idCopy[i].isUpper()) {
139 idCopy[i] = idCopy[i].toLower();
140 ++i;
141 }
142 }
143
144 // ### qml keywords as names
145 static QSet<QByteArray> keywords {
146 "x",
147 "y",
148 "as",
149 "do",
150 "if",
151 "in",
152 "on",
153 "of",
154 "for",
155 "get",
156 "int",
157 "let",
158 "new",
159 "set",
160 "try",
161 "var",
162 "top",
163 "byte",
164 "case",
165 "char",
166 "else",
167 "num",
168 "from",
169 "goto",
170 "null",
171 "this",
172 "true",
173 "void",
174 "with",
175 "clip",
176 "item",
177 "flow",
178 "font",
179 "text",
180 "left",
181 "data",
182 "alias",
183 "break",
184 "state",
185 "scale",
186 "color",
187 "right",
188 "catch",
189 "class",
190 "const",
191 "false",
192 "float",
193 "layer", // Design Studio doesn't like "layer" as an id
194 "short",
195 "super",
196 "throw",
197 "while",
198 "yield",
199 "border",
200 "source",
201 "delete",
202 "double",
203 "export",
204 "import",
205 "native",
206 "public",
207 "pragma",
208 "return",
209 "signal",
210 "static",
211 "switch",
212 "throws",
213 "bottom",
214 "parent",
215 "typeof",
216 "boolean",
217 "opacity",
218 "enabled",
219 "anchors",
220 "padding",
221 "default",
222 "extends",
223 "finally",
224 "package",
225 "private",
226 "abstract",
227 "continue",
228 "debugger",
229 "function",
230 "property",
231 "readonly",
232 "children",
233 "volatile",
234 "interface",
235 "protected",
236 "transient",
237 "implements",
238 "instanceof",
239 "synchronized"
240 };
241 if (keywords.contains(idCopy.toUtf8())) {
242 idCopy += QStringLiteral("_");
243 }
244
245 // We may have removed all the characters by now
246 if (idCopy.isEmpty())
247 idCopy = QStringLiteral("node");
248
249 return idCopy;
250}
251
252QString sanitizeQmlSourcePath(const QString &source, bool removeParentDirectory)
253{
254 QString sourceCopy = source;
255
256 if (removeParentDirectory)
257 sourceCopy = QSSGQmlUtilities::stripParentDirectory(sourceCopy);
258
259 sourceCopy.replace(QChar::fromLatin1('\\'), QChar::fromLatin1('/'));
260
261 // must be surrounded in quotes
262 return QString(QStringLiteral("\"") + sourceCopy + QStringLiteral("\""));
263}
264
266{
267 static PropertyMap p;
268 return &p;
269}
270
275
277{
279
280 if (m_properties.contains(type)) {
281 auto properties = m_properties[type];
282 value = properties.value(property);
283 }
284
285 return value;
286}
287
289{
290 bool isTheSame = value == getDefaultValue(type, property);
291 return isTheSame;
292}
293
295 PropertyMap::PropertiesMap propertiesMap;
296 auto metaObject = object->metaObject();
297 for (auto i = 0; i < metaObject->propertyCount(); ++i) {
298 auto property = metaObject->property(i);
299 const auto name = property.name();
300 const auto value = property.read(object);
301 propertiesMap.insert(name, value);
302 }
303 return propertiesMap;
304}
305
306PropertyMap::PropertyMap()
307{
308 // Create a table containing the default values for each property for each supported type
309 {
310 QQuick3DNode node;
311 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Node, getObjectPropertiesMap(&node));
312 }
313 {
314 QQuick3DPrincipledMaterial principledMaterial;
315 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PrincipledMaterial, getObjectPropertiesMap(&principledMaterial));
316 }
317 {
318 QQuick3DSpecularGlossyMaterial specularGlossyMaterial;
319 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::SpecularGlossyMaterial, getObjectPropertiesMap(&specularGlossyMaterial));
320 }
321 {
322 QQuick3DCustomMaterial customMaterial;
323 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::CustomMaterial, getObjectPropertiesMap(&customMaterial));
324 }
325 {
327 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Image2D, getObjectPropertiesMap(&texture));
328 }
329 {
330 QQuick3DCubeMapTexture cubeMapTexture;
331 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::ImageCube, getObjectPropertiesMap(&cubeMapTexture));
332 }
333 {
334 QQuick3DTextureData textureData;
335 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::TextureData, getObjectPropertiesMap(&textureData));
336 }
337 {
339 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Model, getObjectPropertiesMap(&model));
340 }
341 {
342 QQuick3DOrthographicCamera orthographicCamera;
343 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::OrthographicCamera, getObjectPropertiesMap(&orthographicCamera));
344 }
345 {
346 QQuick3DPerspectiveCamera perspectiveCamera;
347 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PerspectiveCamera, getObjectPropertiesMap(&perspectiveCamera));
348 }
349 {
350 QQuick3DDirectionalLight directionalLight;
351 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::DirectionalLight, getObjectPropertiesMap(&directionalLight));
352 }
353 {
354 QQuick3DPointLight pointLight;
355 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PointLight, getObjectPropertiesMap(&pointLight));
356 }
357 {
358 QQuick3DSpotLight spotLight;
359 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::SpotLight, getObjectPropertiesMap(&spotLight));
360 }
361 {
362 QQuick3DSkeleton skeleton;
363 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Skeleton, getObjectPropertiesMap(&skeleton));
364 }
365 {
366 QQuick3DJoint joint;
367 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Joint, getObjectPropertiesMap(&joint));
368 }
369 {
370 QQuick3DSkin skin;
371 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Skin, getObjectPropertiesMap(&skin));
372 }
373 {
374 QQuick3DMorphTarget morphTarget;
375 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::MorphTarget, getObjectPropertiesMap(&morphTarget));
376 }
377}
378
396
397template<QSSGSceneDesc::Material::RuntimeType T>
398const char *qmlElementName() { static_assert(!std::is_same_v<decltype(T), decltype(T)>, "Unknown type"); return nullptr; }
399template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::Node>() { return "Node"; }
400
401template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::SpecularGlossyMaterial>() { return "SpecularGlossyMaterial"; }
402template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::PrincipledMaterial>() { return "PrincipledMaterial"; }
403template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::CustomMaterial>() { return "CustomMaterial"; }
404template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::OrthographicCamera>() { return "OrthographicCamera"; }
405template<> const char *qmlElementName<QSSGSceneDesc::Material::RuntimeType::PerspectiveCamera>() { return "PerspectiveCamera"; }
406
407template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::Model>() { return "Model"; }
408
409template<> const char *qmlElementName<QSSGSceneDesc::Texture::RuntimeType::Image2D>() { return "Texture"; }
410template<> const char *qmlElementName<QSSGSceneDesc::Texture::RuntimeType::ImageCube>() { return "CubeMapTexture"; }
411template<> const char *qmlElementName<QSSGSceneDesc::Texture::RuntimeType::TextureData>() { return "TextureData"; }
412
413template<> const char *qmlElementName<QSSGSceneDesc::Camera::RuntimeType::DirectionalLight>() { return "DirectionalLight"; }
414template<> const char *qmlElementName<QSSGSceneDesc::Camera::RuntimeType::SpotLight>() { return "SpotLight"; }
415template<> const char *qmlElementName<QSSGSceneDesc::Camera::RuntimeType::PointLight>() { return "PointLight"; }
416
417template<> const char *qmlElementName<QSSGSceneDesc::Joint::RuntimeType::Joint>() { return "Joint"; }
418template<> const char *qmlElementName<QSSGSceneDesc::Skeleton::RuntimeType::Skeleton>() { return "Skeleton"; }
419template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::Skin>() { return "Skin"; }
420template<> const char *qmlElementName<QSSGSceneDesc::Node::RuntimeType::MorphTarget>() { return "MorphTarget"; }
421
422static const char *getQmlElementName(const QSSGSceneDesc::Node &node)
423{
424 using RuntimeType = QSSGSceneDesc::Node::RuntimeType;
425 switch (node.runtimeType) {
426 case RuntimeType::Node:
427 return qmlElementName<RuntimeType::Node>();
428 case RuntimeType::PrincipledMaterial:
429 return qmlElementName<RuntimeType::PrincipledMaterial>();
430 case RuntimeType::SpecularGlossyMaterial:
431 return qmlElementName<RuntimeType::SpecularGlossyMaterial>();
432 case RuntimeType::CustomMaterial:
433 return qmlElementName<RuntimeType::CustomMaterial>();
434 case RuntimeType::Image2D:
435 return qmlElementName<RuntimeType::Image2D>();
436 case RuntimeType::ImageCube:
437 return qmlElementName<RuntimeType::ImageCube>();
438 case RuntimeType::TextureData:
439 return qmlElementName<RuntimeType::TextureData>();
440 case RuntimeType::Model:
441 return qmlElementName<RuntimeType::Model>();
442 case RuntimeType::OrthographicCamera:
443 return qmlElementName<RuntimeType::OrthographicCamera>();
444 case RuntimeType::PerspectiveCamera:
445 return qmlElementName<RuntimeType::PerspectiveCamera>();
446 case RuntimeType::DirectionalLight:
447 return qmlElementName<RuntimeType::DirectionalLight>();
448 case RuntimeType::PointLight:
449 return qmlElementName<RuntimeType::PointLight>();
450 case RuntimeType::SpotLight:
451 return qmlElementName<RuntimeType::SpotLight>();
452 case RuntimeType::Skeleton:
453 return qmlElementName<RuntimeType::Skeleton>();
454 case RuntimeType::Joint:
455 return qmlElementName<RuntimeType::Joint>();
456 case RuntimeType::Skin:
457 return qmlElementName<RuntimeType::Skin>();
458 case RuntimeType::MorphTarget:
459 return qmlElementName<RuntimeType::MorphTarget>();
460 default:
461 return "UNKNOWN_TYPE";
462 }
463}
464
488
489static constexpr QByteArrayView qml_basic_types[] {
490 "bool",
491 "double",
492 "int",
493 "list",
494 "real",
495 "string",
496 "url",
497 "var",
498 "color",
499 "date",
500 "font",
501 "matrix4x4",
502 "point",
503 "quaternion",
504 "rect",
505 "size",
506 "vector2d",
507 "vector3d",
508 "vector4d"
509};
510
511static_assert(std::size(qml_basic_types) == QMLBasicType::Unknown_Count, "Missing type?");
512
514{
515 switch (mt.id()) {
516 case QMetaType::Bool:
518 case QMetaType::Char:
519 case QMetaType::SChar:
520 case QMetaType::UChar:
521 case QMetaType::Char16:
522 case QMetaType::Char32:
523 case QMetaType::QChar:
524 case QMetaType::Short:
525 case QMetaType::UShort:
526 case QMetaType::Int:
527 case QMetaType::UInt:
528 case QMetaType::Long:
529 case QMetaType::ULong:
530 case QMetaType::LongLong:
531 case QMetaType::ULongLong:
533 case QMetaType::Float:
534 case QMetaType::Double:
536 case QMetaType::QByteArray:
537 case QMetaType::QString:
539 case QMetaType::QDate:
540 case QMetaType::QTime:
541 case QMetaType::QDateTime:
543 case QMetaType::QUrl:
545 case QMetaType::QRect:
546 case QMetaType::QRectF:
548 case QMetaType::QSize:
549 case QMetaType::QSizeF:
551 case QMetaType::QPoint:
552 case QMetaType::QPointF:
556 case QMetaType::QColor:
558 case QMetaType::QMatrix4x4:
560 case QMetaType::QVector2D:
562 case QMetaType::QVector3D:
564 case QMetaType::QVector4D:
566 case QMetaType::QQuaternion:
568 case QMetaType::QFont:
570 default:
572 }
573}
574
575using NodeNameMap = QHash<const QSSGSceneDesc::Node *, QString>;
576Q_GLOBAL_STATIC(NodeNameMap, g_nodeNameMap)
579// Normally g_idMap will contain all the ids but in some cases
580// (like Animation, not Node) the ids will just be stored
581// to avoid conflict.
582// Now, Animations will be processed after all the Nodes,
583// For Nodes, it is not used.
586
588{
589 static constexpr const char *typeNames[] = {
590 "", // Transform
591 "_camera",
592 "", // Model
593 "_texture",
594 "_material",
595 "_light",
596 "_mesh",
597 "_skin",
598 "_skeleton",
599 "_joint",
600 "_morphtarget",
601 "_unknown"
602 };
603 constexpr uint nameCount = sizeof(typeNames)/sizeof(const char*);
604 const bool nodeHasName = (node.name.size() > 0);
605 uint nameIdx = qMin(uint(node.nodeType), nameCount);
606 QString name = nodeHasName ? QString::fromUtf8(node.name + typeNames[nameIdx]) : QString::fromLatin1(getQmlElementName(node));
608
609 // Make sure we return a unique id.
610 if (const auto it = g_nodeNameMap->constFind(&node); it != g_nodeNameMap->constEnd())
611 return *it;
612
613 quint64 id = node.id;
614 int attempts = 1000;
615 QString candidate = sanitizedName;
616 do {
617 if (const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
618 g_idMap->insert(candidate, &node);
619 g_nodeNameMap->insert(&node, candidate);
620 return candidate;
621 }
622
623 candidate = QStringLiteral("%1%2").arg(sanitizedName).arg(id++);
624 } while (--attempts);
625
626 return candidate;
627}
628
630{
631 QString name = !inName.isEmpty() ? QString::fromUtf8(inName + "_timeline") : "timeline0"_L1;
633
634 int attempts = 1000;
635 quint16 id = 0;
636 QString candidate = sanitizedName;
637 do {
638 if (const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
639 if (const auto oIt = g_idOthers->constFind(candidate); oIt == g_idOthers->constEnd()) {
640 g_idOthers->insert(candidate);
641 return candidate;
642 }
643 }
644
645 candidate = QStringLiteral("%1%2").arg(sanitizedName).arg(++id);
646 } while (--attempts);
647
648 return candidate;
649}
650
652 QString sourceCopy = filePath;
653 while (sourceCopy.startsWith(QChar::fromLatin1('.')) || sourceCopy.startsWith(QChar::fromLatin1('/')) || sourceCopy.startsWith(QChar::fromLatin1('\\')))
654 sourceCopy.remove(0, 1);
655 return sourceCopy;
656}
657
658static const char *blockBegin() { return " {\n"; }
659static const char *blockEnd() { return "}\n"; }
660static const char *comment() { return "// "; }
661static const char *indent() { return " "; }
662
670
672{
673 QString str;
674 for (quint8 i = 0; i < output.indent; i += QSSGQmlScopedIndent::QSSG_INDENT)
676 return str;
677}
678
680{
681 for (quint8 i = 0; i < output.indent; i += QSSGQmlScopedIndent::QSSG_INDENT)
682 output.stream << indent();
683 return output.stream;
684}
685
686static const char *blockBegin(OutputContext &output)
687{
688 ++output.scopeDepth;
689 return blockBegin();
690}
691
692static const char *blockEnd(OutputContext &output)
693{
694 output.scopeDepth = qMax(0, output.scopeDepth - 1);
695 return blockEnd();
696}
697
698static void writeImportHeader(OutputContext &output, bool hasAnimation = false)
699{
700 output.stream << "import QtQuick\n"
701 << "import QtQuick3D\n\n";
702 if (hasAnimation)
703 output.stream << "import QtQuick.Timeline\n\n";
704}
705
706static QString toQuotedString(const QString &text) { return QStringLiteral("\"%1\"").arg(text); }
707
708static inline QString getMeshFolder() { return QStringLiteral("meshes/"); }
709static inline QString getMeshExtension() { return QStringLiteral(".mesh"); }
710
712{
713 const auto meshFolder = getMeshFolder();
714 const auto extension = getMeshExtension();
715
716 return QString(meshFolder + name + extension);
717}
718
719static inline QString getTextureFolder() { return QStringLiteral("maps/"); }
720
721static inline QString getAnimationFolder() { return QStringLiteral("animations/"); }
722static inline QString getAnimationExtension() { return QStringLiteral(".qad"); }
724{
725 const auto animationFolder = getAnimationFolder();
726 const auto extension = getAnimationExtension();
727 return QString(animationFolder + id + QStringLiteral("_")
728 + property + QStringLiteral("_")
730}
731
733{
734 return var.toString();
735}
736
738{
739 switch (var.metaType().id()) {
740 case QMetaType::QVector2D: {
741 const auto vec2 = qvariant_cast<QVector2D>(var);
742 return QLatin1String("Qt.vector2d(") + QString::number(vec2.x()) + QLatin1String(", ") + QString::number(vec2.y()) + QLatin1Char(')');
743 }
744 case QMetaType::QVector3D: {
745 const auto vec3 = qvariant_cast<QVector3D>(var);
746 return QLatin1String("Qt.vector3d(") + QString::number(vec3.x()) + QLatin1String(", ")
747 + QString::number(vec3.y()) + QLatin1String(", ")
748 + QString::number(vec3.z()) + QLatin1Char(')');
749 }
750 case QMetaType::QVector4D: {
751 const auto vec4 = qvariant_cast<QVector4D>(var);
752 return QLatin1String("Qt.vector4d(") + QString::number(vec4.x()) + QLatin1String(", ")
753 + QString::number(vec4.y()) + QLatin1String(", ")
754 + QString::number(vec4.z()) + QLatin1String(", ")
755 + QString::number(vec4.w()) + QLatin1Char(')');
756 }
757 case QMetaType::QColor: {
758 const auto color = qvariant_cast<QColor>(var);
759 return colorToQml(color);
760 }
761 case QMetaType::QQuaternion: {
762 const auto &quat = qvariant_cast<QQuaternion>(var);
763 return QLatin1String("Qt.quaternion(") + QString::number(quat.scalar()) + QLatin1String(", ")
764 + QString::number(quat.x()) + QLatin1String(", ")
765 + QString::number(quat.y()) + QLatin1String(", ")
766 + QString::number(quat.z()) + QLatin1Char(')');
767 }
768 case QMetaType::QMatrix4x4: {
769 const auto mat44 = qvariant_cast<QMatrix4x4>(var);
770 return QLatin1String("Qt.matrix4x4(")
771 + QString::number(mat44(0, 0)) + u", " + QString::number(mat44(0, 1)) + u", " + QString::number(mat44(0, 2)) + u", " + QString::number(mat44(0, 3)) + u", "
772 + QString::number(mat44(1, 0)) + u", " + QString::number(mat44(1, 1)) + u", " + QString::number(mat44(1, 2)) + u", " + QString::number(mat44(1, 3)) + u", "
773 + QString::number(mat44(2, 0)) + u", " + QString::number(mat44(2, 1)) + u", " + QString::number(mat44(2, 2)) + u", " + QString::number(mat44(2, 3)) + u", "
774 + QString::number(mat44(3, 0)) + u", " + QString::number(mat44(3, 1)) + u", " + QString::number(mat44(3, 2)) + u", " + QString::number(mat44(3, 3)) + u')';
775 }
776 case QMetaType::Float:
777 case QMetaType::Double:
778 case QMetaType::Int:
779 case QMetaType::Char:
780 case QMetaType::Long:
781 case QMetaType::LongLong:
782 case QMetaType::ULong:
783 case QMetaType::ULongLong:
784 case QMetaType::Bool:
785 return var.toString();
786 case QMetaType::QUrl: // QUrl needs special handling. Return empty string to trigger that.
787 default:
788 break;
789 }
790
791 return QString();
792}
793
807
808static std::pair<QString, QString> meshAssetName(const QSSGSceneDesc::Scene &scene, const QSSGSceneDesc::Mesh &meshNode, const QDir &outdir)
809{
810 // Returns {name, notValidReason}
811
812 const auto meshFolder = getMeshFolder();
813 const auto meshId = QSSGQmlUtilities::getIdForNode(meshNode);
814 const auto meshSourceName = QSSGQmlUtilities::getMeshSourceName(meshId);
815 Q_ASSERT(scene.meshStorage.size() > meshNode.idx);
816 const auto &mesh = scene.meshStorage.at(meshNode.idx);
817
818 // If a mesh folder does not exist, then create one
819 if (!outdir.exists(meshFolder) && !outdir.mkdir(meshFolder)) {
820 qDebug() << "Failed to create meshes folder at" << outdir;
821 return {}; // Error out
822 }
823
824 const QString path = outdir.path() + QDir::separator() + meshSourceName;
825 QFile file(path);
827 return {QString(), QStringLiteral("Failed to find mesh at ") + path};
828 }
829
830 if (mesh.save(&file) == 0) {
831 return {};
832 }
833
834 return {meshSourceName, QString()};
835};
836
837static std::pair<QString, QString> copyTextureAsset(const QUrl &texturePath, OutputContext &output)
838{
839 // Returns {path, notValidReason}
840
841 // TODO: Use QUrl::resolved() instead of manual string manipulation
842 QString assetPath = output.outdir.isAbsolutePath(texturePath.path()) ? texturePath.toString() : texturePath.path();
843 QFileInfo fi(assetPath);
844 if (fi.isRelative() && !output.sourceDir.isEmpty()) {
845 fi = QFileInfo(output.sourceDir + QChar(u'/') + assetPath);
846 }
847 if (!fi.exists()) {
848 indent(output) << comment() << "Source texture path expected: " << getTextureFolder() + texturePath.fileName() << "\n";
849 return {QString(), QStringLiteral("Failed to find texture at ") + assetPath};
850 }
851
852 const auto mapsFolder = getTextureFolder();
853 // If a maps folder does not exist, then create one
854 if (!output.outdir.exists(mapsFolder) && !output.outdir.mkdir(mapsFolder)) {
855 qDebug() << "Failed to create maps folder at" << output.outdir;
856 return {}; // Error out
857 }
858
859 const QString relpath = mapsFolder + fi.fileName();
860 const auto newfilepath = QString(output.outdir.canonicalPath() + QDir::separator() + relpath);
861 if (!QFile::exists(newfilepath) && !QFile::copy(fi.canonicalFilePath(), newfilepath)) {
862 qDebug() << "Failed to copy file from" << fi.canonicalFilePath() << "to" << newfilepath;
863 return {};
864 }
865
866 return {relpath, QString()};
867};
868
870{
871 static const QRegularExpression re(QLatin1String("^Qt.[a-z0-9]*\\(([0-9.e\\+\\-, ]*)\\)"));
872 Q_ASSERT(re.isValid());
873
874 switch (mt.id()) {
875 case QMetaType::QVector2D: {
877 if (match.hasMatch()) {
878 const auto comp = match.captured(1).split(QLatin1Char(','));
879 if (comp.size() == 2) {
880 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
881 QLatin1String(".y: ") + comp.at(1).trimmed() };
882 }
883 }
884 break;
885 }
886 case QMetaType::QVector3D: {
888 if (match.hasMatch()) {
889 const auto comp = match.captured(1).split(QLatin1Char(','));
890 if (comp.size() == 3) {
891 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
892 QLatin1String(".y: ") + comp.at(1).trimmed(),
893 QLatin1String(".z: ") + comp.at(2).trimmed() };
894 }
895 }
896 break;
897 }
898 case QMetaType::QVector4D: {
900 if (match.hasMatch()) {
901 const auto comp = match.captured(1).split(QLatin1Char(','));
902 if (comp.size() == 4) {
903 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
904 QLatin1String(".y: ") + comp.at(1).trimmed(),
905 QLatin1String(".z: ") + comp.at(2).trimmed(),
906 QLatin1String(".w: ") + comp.at(3).trimmed() };
907 }
908 }
909 break;
910 }
911 case QMetaType::QQuaternion: {
913 if (match.hasMatch()) {
914 const auto comp = match.captured(1).split(QLatin1Char(','));
915 if (comp.size() == 4) {
916 return { QLatin1String(".x: ") + comp.at(0).trimmed(),
917 QLatin1String(".y: ") + comp.at(1).trimmed(),
918 QLatin1String(".z: ") + comp.at(2).trimmed(),
919 QLatin1String(".scalar: ") + comp.at(3).trimmed() };
920 }
921 }
922 break;
923 }
924 default:
925 break;
926 }
927
928 return { value };
929}
930
932{
933 // Workaround for DS
934 if (mt.id() != QMetaType::QQuaternion)
935 return expandComponents(value, mt);
936
937 return { value };
938}
939
948
950{
952 if (property.value.isNull()) {
953 result.ok = false;
954 result.notValidReason = QStringLiteral("Property value is null");
955 return result;
956 }
957
958 const QVariant &value = property.value;
959 result.name = QString::fromUtf8(property.name);
960 result.isDynamicProperty = property.type == QSSGSceneDesc::Property::Type::Dynamic;
961
962 // Built-in types
963 QString valueAsString = builtinQmlType(value);
964 if (valueAsString.size() > 0) {
965 result.value = valueAsString;
966 result.ok = true;
967 } else if (value.metaType().flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) {
968 static const auto qmlEnumString = [](const QLatin1String &element, const QString &enumString) {
969 return QStringLiteral("%1.%2").arg(element).arg(enumString);
970 };
972 QString enumValue = asString(value);
973 if (enumValue.size() > 0) {
974 result.value = qmlEnumString(qmlElementName, enumValue);
975 result.ok = true;
976 }
977 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Flag>()) {
979 if (element.size() > 0) {
980 const auto flag = qvariant_cast<QSSGSceneDesc::Flag>(value);
981 QByteArray keysString = flag.me.valueToKeys(int(flag.value));
982 if (keysString.size() > 0) {
983 keysString.prepend(element + '.');
984 QByteArray replacement(" | " + element + '.');
985 keysString.replace('|', replacement);
986 result.value = QString::fromLatin1(keysString);
987 result.ok = true;
988 }
989 }
990 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::NodeList *>()) {
991 const auto *list = qvariant_cast<QSSGSceneDesc::NodeList *>(value);
992 if (list->count > 0) {
993 const QString indentStr = indentString(output);
994 QSSGQmlScopedIndent scopedIndent(output);
995 const QString listIndentStr = indentString(output);
996
997 QString str;
998 str.append(u"[\n");
999
1000 for (int i = 0, end = list->count; i != end; ++i) {
1001 if (i != 0)
1002 str.append(u",\n");
1003 str.append(listIndentStr);
1004 str.append(getIdForNode(*(list->head[i])));
1005 }
1006
1007 str.append(u'\n' + indentStr + u']');
1008
1009 result.value = str;
1010 result.ok = true;
1011 }
1012 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::ListView *>()) {
1013 const auto &list = *qvariant_cast<QSSGSceneDesc::ListView *>(value);
1014 if (list.count > 0) {
1015 const QString indentStr = indentString(output);
1016 QSSGQmlScopedIndent scopedIndent(output);
1017 const QString listIndentStr = indentString(output);
1018
1019 QString str;
1020 str.append(u"[\n");
1021
1022 char *vptr = reinterpret_cast<char *>(list.data);
1023 auto size = list.mt.sizeOf();
1024
1025 for (int i = 0, end = list.count; i != end; ++i) {
1026 if (i != 0)
1027 str.append(u",\n");
1028
1029 const QVariant var{list.mt, reinterpret_cast<void *>(vptr + (size * i))};
1030 QString valueString = builtinQmlType(var);
1031 if (valueString.isEmpty())
1032 valueString = asString(var);
1033
1034 str.append(listIndentStr);
1035 str.append(valueString);
1036 }
1037
1038 str.append(u'\n' + indentStr + u']');
1039
1040 result.value = str;
1041 result.ok = true;
1042 }
1043 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Node *>()) {
1044 if (const auto node = qvariant_cast<QSSGSceneDesc::Node *>(value)) {
1045 // If this assert is triggerd it likely means that the node never got added
1046 // to the scene tree (see: addNode()) or that it's a type not handled as a resource, see:
1047 // writeQmlForResources()
1048 Q_ASSERT(node->id != 0);
1049 // The 'TextureData' node will have its data written out and become
1050 // a source url.
1051
1052 if (node->runtimeType == QSSGSceneDesc::Node::RuntimeType::TextureData) {
1053 result.name = QStringLiteral("source");
1054 result.value = getIdForNode(*node->scene->root) + QLatin1Char('.') + getIdForNode(*node);
1055 } else {
1056 result.value = getIdForNode(*node);
1057 }
1058 result.ok = true;
1059 }
1060 } else if (value.metaType() == QMetaType::fromType<QSSGSceneDesc::Mesh *>()) {
1061 if (const auto meshNode = qvariant_cast<const QSSGSceneDesc::Mesh *>(value)) {
1062 Q_ASSERT(meshNode->nodeType == QSSGSceneDesc::Node::Type::Mesh);
1063 Q_ASSERT(meshNode->scene);
1064 const auto &scene = *meshNode->scene;
1065 const auto& [meshSourceName, notValidReason] = meshAssetName(scene, *meshNode, output.outdir);
1066 result.notValidReason = notValidReason;
1067 if (!meshSourceName.isEmpty()) {
1068 result.value = toQuotedString(meshSourceName);
1069 result.ok = true;
1070 }
1071 }
1072 } else if (value.metaType() == QMetaType::fromType<QUrl>()) {
1073 if (const auto url = qvariant_cast<QUrl>(value); !url.isEmpty()) {
1074 // We need to adjust source url(s) as those should contain the canonical path
1075 QString path;
1076 if (QSSGRenderGraphObject::isTexture(target.runtimeType)) {
1077 const auto& [relpath, notValidReason] = copyTextureAsset(url, output);
1078 result.notValidReason = notValidReason;
1079 if (!relpath.isEmpty()) {
1080 path = relpath;
1081 }
1082 } else
1083 path = url.path();
1084
1085 if (!path.isEmpty()) {
1086 result.value = toQuotedString(path);
1087 result.ok = true;
1088 }
1089 }
1090 } else if (target.runtimeType == QSSGSceneDesc::Material::RuntimeType::CustomMaterial) {
1091 // Workaround the TextureInput item that wraps textures for the Custom material.
1092 if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Texture *>()) {
1093 if (const auto texture = qvariant_cast<QSSGSceneDesc::Texture *>(value)) {
1095 result.value = QLatin1String("TextureInput { texture: ") +
1097 result.ok = true;
1098 }
1099 }
1100 } else if (value.metaType() == QMetaType::fromType<QString>()) {
1101 // Plain strings in the scenedesc should map to QML string values
1102 result.value = toQuotedString(value.toString());
1103 result.ok = true;
1104 } else {
1105 result.notValidReason = QStringLiteral("Unsupported value type: ") + QString::fromUtf8(value.metaType().name());
1106 qWarning() << result.notValidReason;
1107 result.ok = false;
1108 }
1109
1112 ? expandComponentsPartially(result.value, value.metaType())
1113 : expandComponents(result.value, value.metaType());
1114 }
1115
1116 return result;
1117}
1118
1120{
1121 QSSGQmlScopedIndent scopedIndent(output);
1122
1123 indent(output) << u"id: "_s << getIdForNode(node) << u'\n';
1124
1125 // Set Object Name if one exists
1126 if (node.name.size()) {
1127 const QString objectName = QString::fromLocal8Bit(node.name);
1128 if (!objectName.startsWith(u'*'))
1129 indent(output) << u"objectName: \""_s << node.name << u"\"\n"_s;
1130 }
1131
1132 const auto &properties = node.properties;
1133 auto it = properties.begin();
1134 const auto end = properties.end();
1135 for (; it != end; ++it) {
1136 const auto &property = *it;
1137
1139 if (result.ok) {
1140 if (result.isDynamicProperty) {
1141 indent(output) << "property " << typeName(property->value.metaType()).toByteArray() << ' ' << result.name << u": "_s << result.value << u'\n';
1142 } else if (!QSSGQmlUtilities::PropertyMap::instance()->isDefaultValue(node.runtimeType, property->name, property->value)) {
1143 if (result.expandedProperties.size() > 1) {
1144 for (const auto &va : result.expandedProperties)
1145 indent(output) << result.name << va << u'\n';
1146 } else {
1147 indent(output) << result.name << u": "_s << result.value << u'\n';
1148 }
1149 }
1150 } else if (!result.isDynamicProperty) {
1151 QString message = u"Skipped property: "_s + QString::fromUtf8(property->name);
1152 if (!result.notValidReason.isEmpty())
1153 message.append(u", reason: "_s + result.notValidReason);
1154 qDebug() << message;
1155 indent(output) << comment() << message + u'\n';
1156 }
1157 }
1158}
1159
1161{
1162 using namespace QSSGSceneDesc;
1163 Q_ASSERT(transform.nodeType == QSSGSceneDesc::Node::Type::Transform && transform.runtimeType == QSSGSceneDesc::Node::RuntimeType::Node);
1166}
1167
1169{
1170 using namespace QSSGSceneDesc;
1171 Q_ASSERT(material.nodeType == QSSGSceneDesc::Model::Type::Material);
1172 if (material.runtimeType == QSSGSceneDesc::Model::RuntimeType::SpecularGlossyMaterial) {
1173 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1174 } else if (material.runtimeType == Model::RuntimeType::PrincipledMaterial) {
1175 indent(output) << qmlElementName<Material::RuntimeType::PrincipledMaterial>() << blockBegin(output);
1176 } else if (material.runtimeType == Material::RuntimeType::CustomMaterial) {
1177 indent(output) << qmlElementName<Material::RuntimeType::CustomMaterial>() << blockBegin(output);
1178 } else if (material.runtimeType == Material::RuntimeType::SpecularGlossyMaterial) {
1179 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1180 } else {
1181 Q_UNREACHABLE();
1182 }
1183
1184 writeNodeProperties(material, output);
1185}
1186
1188{
1189 using namespace QSSGSceneDesc;
1190 Q_ASSERT(model.nodeType == Node::Type::Model);
1193}
1194
1196{
1197 using namespace QSSGSceneDesc;
1198 Q_ASSERT(camera.nodeType == Node::Type::Camera);
1199 if (camera.runtimeType == Camera::RuntimeType::PerspectiveCamera)
1200 indent(output) << qmlElementName<Camera::RuntimeType::PerspectiveCamera>() << blockBegin(output);
1201 else if (camera.runtimeType == Camera::RuntimeType::OrthographicCamera)
1202 indent(output) << qmlElementName<Camera::RuntimeType::OrthographicCamera>() << blockBegin(output);
1203 else
1204 Q_UNREACHABLE();
1206}
1207
1209{
1210 using namespace QSSGSceneDesc;
1211 Q_ASSERT(texture.nodeType == Node::Type::Texture && QSSGRenderGraphObject::isTexture(texture.runtimeType));
1212 if (texture.runtimeType == Texture::RuntimeType::Image2D)
1213 indent(output) << qmlElementName<Texture::RuntimeType::Image2D>() << blockBegin(output);
1214 else if (texture.runtimeType == Texture::RuntimeType::ImageCube)
1215 indent(output) << qmlElementName<Texture::RuntimeType::ImageCube>() << blockBegin(output);
1217}
1218
1220{
1221 using namespace QSSGSceneDesc;
1222 Q_ASSERT(skin.nodeType == Node::Type::Skin && skin.runtimeType == Node::RuntimeType::Skin);
1223 indent(output) << qmlElementName<Node::RuntimeType::Skin>() << blockBegin(output);
1225}
1226
1228{
1229 using namespace QSSGSceneDesc;
1230 Q_ASSERT(morphTarget.nodeType == Node::Type::MorphTarget);
1232 writeNodeProperties(morphTarget, output);
1233}
1234
1236{
1237 const auto textureFolder = getTextureFolder();
1238
1239 const auto sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
1240 const auto ext = (fmt.length() != 3) ? u".png"_s
1241 : u"."_s + fmt;
1242
1243 return QString(textureFolder + sanitizedName + ext);
1244}
1245
1246static QString outputTextureAsset(const QSSGSceneDesc::TextureData &textureData, const QDir &outdir)
1247{
1248 if (textureData.data.isEmpty())
1249 return QString();
1250
1251 const auto mapsFolder = getTextureFolder();
1252 const auto id = getIdForNode(textureData);
1253 const QString textureSourceName = getTextureSourceName(id, QString::fromUtf8(textureData.fmt));
1254
1255 const bool isCompressed = ((textureData.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
1256
1257 // If a maps folder does not exist, then create one
1258 if (!outdir.exists(mapsFolder) && !outdir.mkdir(mapsFolder))
1259 return QString(); // Error out
1260
1261 const auto imagePath = QString(outdir.path() + QDir::separator() + textureSourceName);
1262
1263 if (isCompressed) {
1264 QFile file(imagePath);
1266 file.write(textureData.data);
1267 file.close();
1268 } else {
1269 const auto &texData = textureData.data;
1270 const auto &size = textureData.sz;
1271 QImage image;
1272 image = QImage(reinterpret_cast<const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
1273 if (!image.save(imagePath))
1274 return QString();
1275 }
1276
1277 return textureSourceName;
1278}
1279
1281{
1282 using namespace QSSGSceneDesc;
1283 Q_ASSERT(textureData.nodeType == Node::Type::Texture && textureData.runtimeType == Node::RuntimeType::TextureData);
1284
1285 QString textureSourcePath = outputTextureAsset(textureData, output.outdir);
1286
1287 static const auto writeProperty = [](const QString &type, const QString &name, const QString &value) {
1288 return QString::fromLatin1("property %1 %2: %3").arg(type, name, value);
1289 };
1290
1291 if (!textureSourcePath.isEmpty()) {
1292 const auto type = QLatin1String("url");
1293 const auto name = getIdForNode(textureData);
1294
1295 indent(output) << writeProperty(type, name, toQuotedString(textureSourcePath)) << '\n';
1296 }
1297}
1298
1300{
1301 using namespace QSSGSceneDesc;
1302 Q_ASSERT(light.nodeType == Node::Type::Light);
1303 if (light.runtimeType == Light::RuntimeType::DirectionalLight)
1304 indent(output) << qmlElementName<Light::RuntimeType::DirectionalLight>() << blockBegin(output);
1305 else if (light.runtimeType == Light::RuntimeType::SpotLight)
1306 indent(output) << qmlElementName<Light::RuntimeType::SpotLight>() << blockBegin(output);
1307 else if (light.runtimeType == Light::RuntimeType::PointLight)
1308 indent(output) << qmlElementName<Light::RuntimeType::PointLight>() << blockBegin(output);
1309 else
1310 Q_UNREACHABLE();
1312}
1313
1315{
1316 using namespace QSSGSceneDesc;
1317 Q_ASSERT(skeleton.nodeType == Node::Type::Skeleton && skeleton.runtimeType == Node::RuntimeType::Skeleton);
1318 indent(output) << qmlElementName<Node::RuntimeType::Skeleton>() << blockBegin(output);
1319 writeNodeProperties(skeleton, output);
1320}
1321
1323{
1324 using namespace QSSGSceneDesc;
1325 Q_ASSERT(joint.nodeType == Node::Type::Joint && joint.runtimeType == Node::RuntimeType::Joint);
1326 indent(output) << qmlElementName<Node::RuntimeType::Joint>() << blockBegin(output);
1328}
1329
1331{
1332 using namespace QSSGSceneDesc;
1334 Q_ASSERT(QSSGRenderGraphObject::isResource(node.runtimeType) || node.nodeType == Node::Type::Mesh || node.nodeType == Node::Type::Skeleton);
1335
1336 const bool processNode = !node.properties.isEmpty() || (output.type == OutputContext::Resource);
1337 if (processNode) {
1338 QSSGQmlScopedIndent scopedIndent(output);
1339 switch (node.nodeType) {
1340 case Node::Type::Skin:
1341 writeQml(static_cast<const Skin &>(node), output);
1342 break;
1343 case Node::Type::MorphTarget:
1344 writeQml(static_cast<const MorphTarget &>(node), output);
1345 break;
1346 case Node::Type::Skeleton:
1347 writeQml(static_cast<const Skeleton &>(node), output);
1348 break;
1349 case Node::Type::Texture:
1350 if (node.runtimeType == Node::RuntimeType::Image2D)
1351 writeQml(static_cast<const Texture &>(node), output);
1352 else if (node.runtimeType == Node::RuntimeType::ImageCube)
1353 writeQml(static_cast<const Texture &>(node), output);
1354 else if (node.runtimeType == Node::RuntimeType::TextureData)
1355 writeQml(static_cast<const TextureData &>(node), output);
1356 else
1357 Q_UNREACHABLE();
1358 break;
1359 case Node::Type::Material:
1360 writeQml(static_cast<const Material &>(node), output);
1361 break;
1362 case Node::Type::Mesh:
1363 // Only handled as a property (see: valueToQml())
1364 break;
1365 default:
1366 qWarning("Unhandled resource type \'%d\'?", int(node.runtimeType));
1367 break;
1368 }
1369 }
1370
1371 // Do something more convenient if this starts expending to more types...
1372 // NOTE: The TextureData type is written out as a url property...
1373 const bool skipBlockEnd = (node.runtimeType == Node::RuntimeType::TextureData || node.nodeType == Node::Type::Mesh);
1374 if (!skipBlockEnd && processNode && output.scopeDepth != 0) {
1375 QSSGQmlScopedIndent scopedIndent(output);
1377 }
1378}
1379
1381{
1382 using namespace QSSGSceneDesc;
1383
1384 const bool processNode = !(node.properties.isEmpty() && node.children.isEmpty())
1385 || (output.type == OutputContext::Resource);
1386 if (processNode) {
1387 QSSGQmlScopedIndent scopedIndent(output);
1388 switch (node.nodeType) {
1389 case Node::Type::Skeleton:
1390 writeQml(static_cast<const Skeleton &>(node), output);
1391 break;
1392 case Node::Type::Joint:
1393 writeQml(static_cast<const Joint &>(node), output);
1394 break;
1395 case Node::Type::Light:
1396 writeQml(static_cast<const Light &>(node), output);
1397 break;
1398 case Node::Type::Transform:
1399 writeQml(node, output);
1400 break;
1401 case Node::Type::Camera:
1402 writeQml(static_cast<const Camera &>(node), output);
1403 break;
1404 case Node::Type::Model:
1405 writeQml(static_cast<const Model &>(node), output);
1406 break;
1407 default:
1408 break;
1409 }
1410 }
1411
1412 for (const auto &cld : node.children) {
1413 if (!QSSGRenderGraphObject::isResource(cld->runtimeType) && output.type == OutputContext::NodeTree) {
1414 QSSGQmlScopedIndent scopedIndent(output);
1415 writeQmlForNode(*cld, output);
1416 }
1417 }
1418
1419 // Do something more convenient if this starts expending to more types...
1420 // NOTE: The TextureData type is written out as a url property...
1421 const bool skipBlockEnd = (node.runtimeType == Node::RuntimeType::TextureData || node.nodeType == Node::Type::Mesh);
1422 if (!skipBlockEnd && processNode && output.scopeDepth != 0) {
1423 QSSGQmlScopedIndent scopedIndent(output);
1425 }
1426}
1427
1429{
1430 auto sortedResources = resources;
1431 std::sort(sortedResources.begin(), sortedResources.end(), [](const QSSGSceneDesc::Node *a, const QSSGSceneDesc::Node *b) {
1432 using RType = QSSGSceneDesc::Node::RuntimeType;
1433 if (a->runtimeType == RType::TextureData && b->runtimeType != RType::TextureData)
1434 return true;
1435 if (a->runtimeType == RType::ImageCube && (b->runtimeType != RType::TextureData && b->runtimeType != RType::ImageCube))
1436 return true;
1437 if (a->runtimeType == RType::Image2D && (b->runtimeType != RType::TextureData && b->runtimeType != RType::Image2D))
1438 return true;
1439
1440 return false;
1441 });
1442 for (const auto &res : std::as_const(sortedResources))
1444}
1445
1447{
1448#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1449 QCborStreamWriter writer(&keyframeData);
1450 // Start root array
1451 writer.startArray();
1452 // header name
1453 writer.append("QTimelineKeyframes");
1454 // file version. Increase this if the format changes.
1455 const int keyframesDataVersion = 1;
1456 writer.append(keyframesDataVersion);
1457 writer.append(int(channel.keys.at(0)->getValueQMetaType()));
1458
1459 // Start Keyframes array
1460 writer.startArray();
1461 quint8 compEnd = quint8(channel.keys.at(0)->getValueType());
1462 bool isQuaternion = false;
1464 isQuaternion = true;
1465 compEnd = 3;
1466 } else {
1467 compEnd++;
1468 }
1469 for (const auto &key : channel.keys) {
1470 writer.append(key->time);
1471 // Easing always linear
1472 writer.append(QEasingCurve::Linear);
1473 if (isQuaternion)
1474 writer.append(key->value[3]);
1475 for (quint8 i = 0; i < compEnd; ++i)
1476 writer.append(key->value[i]);
1477 }
1478 // End Keyframes array
1479 writer.endArray();
1480 // End root array
1481 writer.endArray();
1482#else
1484 Q_UNUSED(keyframeData)
1485#endif // QT_QUICK3D_ENABLE_RT_ANIMATIONS
1486}
1487
1488QPair<QString, QString> writeQmlForAnimation(const QSSGSceneDesc::Animation &anim, qsizetype index, OutputContext &output, bool useBinaryKeyframes = true, bool generateTimelineAnimations = true)
1489{
1490 indent(output) << "Timeline {\n";
1491
1492 QSSGQmlScopedIndent scopedIndent(output);
1493 // The duration property of the TimelineAnimation is an int...
1494 const int duration = qCeil(anim.length);
1495 // Use the same name for objectName and id
1496 const QString animationId = getIdForAnimation(anim.name);
1497 indent(output) << "id: " << animationId << "\n";
1498 QString animationName = animationId;
1499 if (!anim.name.isEmpty())
1500 animationName = QString::fromLocal8Bit(anim.name);
1501 indent(output) << "objectName: \"" << animationName << "\"\n";
1502 indent(output) << "property real framesPerSecond: " << anim.framesPerSecond << "\n";
1503 indent(output) << "startFrame: 0\n";
1504 indent(output) << "endFrame: " << duration << "\n";
1505 indent(output) << "currentFrame: 0\n";
1506 // Only generate the TimelineAnimation component here if requested
1507 // enabled is only set to true up front if we expect to autoplay
1508 // the generated TimelineAnimation
1509 if (generateTimelineAnimations) {
1510 indent(output) << "enabled: true\n";
1511 indent(output) << "animations: TimelineAnimation {\n";
1512 {
1513 QSSGQmlScopedIndent scopedIndent(output);
1514 indent(output) << "duration: " << duration << "\n";
1515 indent(output) << "from: 0\n";
1516 indent(output) << "to: " << duration << "\n";
1517 indent(output) << "running: true\n";
1518 indent(output) << "loops: Animation.Infinite\n";
1519 }
1521 }
1522
1523 for (const auto &channel : anim.channels) {
1524 QString id = getIdForNode(*channel->target);
1525 QString propertyName = asString(channel->targetProperty);
1526
1527 indent(output) << "KeyframeGroup {\n";
1528 {
1529 QSSGQmlScopedIndent scopedIndent(output);
1530 indent(output) << "target: " << id << "\n";
1531 indent(output) << "property: " << toQuotedString(propertyName) << "\n";
1532 if (useBinaryKeyframes && channel->keys.size() != 1) {
1533 const auto animFolder = getAnimationFolder();
1534 const auto animSourceName = getAnimationSourceName(id, propertyName, index);
1535 if (!output.outdir.exists(animFolder) && !output.outdir.mkdir(animFolder)) {
1536 // Make a warning
1537 continue;
1538 }
1539 QFile file(output.outdir.path() + QDir::separator() + animSourceName);
1541 continue;
1542 QByteArray keyframeData;
1543 // It is possible to store this keyframeData but we have to consider
1544 // all the cases including runtime only or writeQml only.
1545 // For now, we will generate it for each case.
1546 generateKeyframeData(*channel, keyframeData);
1547 file.write(keyframeData);
1548 file.close();
1549 indent(output) << "keyframeSource: " << toQuotedString(animSourceName) << "\n";
1550 } else {
1551 Q_ASSERT(!channel->keys.isEmpty());
1552 for (const auto &key : channel->keys) {
1553 indent(output) << "Keyframe {\n";
1554 {
1555 QSSGQmlScopedIndent scopedIndent(output);
1556 indent(output) << "frame: " << key->time << "\n";
1557 indent(output) << "value: " << variantToQml(key->getValue()) << "\n";
1558 }
1560 }
1561 }
1562 }
1564 }
1565 return {animationName, animationId};
1566}
1567
1568void writeQml(const QSSGSceneDesc::Scene &scene, QTextStream &stream, const QDir &outdir, const QJsonObject &optionsObject)
1569{
1570 static const auto checkBooleanOption = [](const QLatin1String &optionName, const QJsonObject &options, bool defaultValue = false) {
1571 const auto it = options.constFind(optionName);
1572 const auto end = options.constEnd();
1574 if (it != end) {
1575 if (it->isObject())
1576 value = it->toObject().value(QLatin1String("value"));
1577 else
1578 value = it.value();
1579 }
1580 return value.toBool(defaultValue);
1581 };
1582
1583 auto root = scene.root;
1584 Q_ASSERT(root);
1585
1586 QJsonObject options = optionsObject;
1587
1588 if (auto it = options.constFind(QLatin1String("options")), end = options.constEnd(); it != end)
1589 options = it->toObject();
1590
1591 quint8 outputOptions{ OutputContext::Options::None };
1592 if (checkBooleanOption(QLatin1String("expandValueComponents"), options))
1594
1595 // Workaround for design studio type components
1596 if (checkBooleanOption(QLatin1String("designStudioWorkarounds"), options))
1598
1599 const bool useBinaryKeyframes = checkBooleanOption("useBinaryKeyframes"_L1, options);
1600 const bool generateTimelineAnimations = !checkBooleanOption("manualAnimations"_L1, options);
1601
1602 OutputContext output { stream, outdir, scene.sourceDir, 0, OutputContext::Header, outputOptions };
1603
1604 writeImportHeader(output, scene.animations.count() > 0);
1605
1607 writeQml(*root, output); // Block scope will be left open!
1608 stream << "\n";
1609 stream << indent() << "// Resources\n";
1611 writeQmlForResources(scene.resources, output);
1613 stream << "\n";
1614 stream << indent() << "// Nodes:\n";
1615 for (const auto &cld : root->children)
1616 writeQmlForNode(*cld, output);
1617
1618 // animations
1619 qsizetype animId = 0;
1620 stream << "\n";
1621 stream << indent() << "// Animations:\n";
1622 QList<QPair<QString, QString>> animationMap;
1623 for (const auto &cld : scene.animations) {
1624 QSSGQmlScopedIndent scopedIndent(output);
1625 auto mapValues = writeQmlForAnimation(*cld, animId++, output, useBinaryKeyframes, generateTimelineAnimations);
1626 animationMap.append(mapValues);
1628 }
1629
1630 if (!generateTimelineAnimations) {
1631 // Expose a map of timelines
1632 stream << "\n";
1633 stream << indent() << "// An exported mapping of Timelines (--manualAnimations)\n";
1634 stream << indent() << "property var timelineMap: {\n";
1635 QSSGQmlScopedIndent scopedIndent(output);
1636 for (const auto &mapValues : animationMap) {
1637 QSSGQmlScopedIndent scopedIndent(output);
1638 indent(output) << "\"" << mapValues.first << "\": " << mapValues.second << ",\n";
1639 }
1641 stream << indent() << "// A simple list of Timelines (--manualAnimations)\n";
1642 stream << indent() << "property var timelineList: [\n";
1643 for (const auto &mapValues : animationMap) {
1644 QSSGQmlScopedIndent scopedIndent(output);
1645 indent(output) << mapValues.second << ",\n";
1646 }
1647 indent(output) << "]\n";
1648 }
1649
1650
1651 // close the root
1653}
1654
1655void createTimelineAnimation(const QSSGSceneDesc::Animation &anim, QObject *parent, bool isEnabled, bool useBinaryKeyframes)
1656{
1657#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1658 auto timeline = new QQuickTimeline(parent);
1659 auto timelineKeyframeGroup = timeline->keyframeGroups();
1660 for (const auto &channel : anim.channels) {
1661 auto keyframeGroup = new QQuickKeyframeGroup(timeline);
1662 keyframeGroup->setTargetObject(channel->target->obj);
1663 keyframeGroup->setProperty(asString(channel->targetProperty));
1664
1665 Q_ASSERT(!channel->keys.isEmpty());
1666 if (useBinaryKeyframes) {
1667 QByteArray keyframeData;
1668 generateKeyframeData(*channel, keyframeData);
1669
1670 keyframeGroup->setKeyframeData(keyframeData);
1671 } else {
1672 auto keyframes = keyframeGroup->keyframes();
1673 for (const auto &key : channel->keys) {
1674 auto keyframe = new QQuickKeyframe(keyframeGroup);
1675 keyframe->setFrame(key->time);
1676 keyframe->setValue(key->getValue());
1677 keyframes.append(&keyframes, keyframe);
1678 }
1679 }
1680 (qobject_cast<QQmlParserStatus *>(keyframeGroup))->componentComplete();
1681 timelineKeyframeGroup.append(&timelineKeyframeGroup, keyframeGroup);
1682 }
1683 timeline->setEndFrame(anim.length);
1684 timeline->setEnabled(isEnabled);
1685
1686 auto timelineAnimation = new QQuickTimelineAnimation(timeline);
1687 timelineAnimation->setObjectName(anim.name);
1688 timelineAnimation->setDuration(int(anim.length));
1689 timelineAnimation->setFrom(0.0f);
1690 timelineAnimation->setTo(anim.length);
1691 timelineAnimation->setLoops(QQuickTimelineAnimation::Infinite);
1692 timelineAnimation->setTargetObject(timeline);
1693
1694 (qobject_cast<QQmlParserStatus *>(timeline))->componentComplete();
1695
1696 timelineAnimation->setRunning(true);
1697#else // QT_QUICK3D_ENABLE_RT_ANIMATIONS
1698 Q_UNUSED(anim)
1699 Q_UNUSED(parent)
1700 Q_UNUSED(isEnabled)
1701 Q_UNUSED(useBinaryKeyframes)
1702#endif // QT_QUICK3D_ENABLE_RT_ANIMATIONS
1703}
1704
1706{
1707 using namespace QSSGSceneDesc;
1708
1709 QSSG_ASSERT(node.scene != nullptr, return);
1710
1711 if (node.runtimeType == Material::RuntimeType::CustomMaterial) {
1712 QString sourceDir = node.scene->sourceDir;
1713 OutputContext output { stream, outDir, sourceDir, 0, OutputContext::Resource };
1715 writeQml(static_cast<const Material &>(node), output);
1716 // Resources, if any, are written out as properties on the component
1717 const auto &resources = node.scene->resources;
1718 writeQmlForResources(resources, output);
1720 } else {
1721 Q_UNREACHABLE(); // Only implemented for Custom material at this point.
1722 }
1723}
1724
1725}
1726
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:136
\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:280
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
\inmodule QtCore\reentrant
\inmodule QtCore
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:20
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:1526
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:209
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
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
bool copy(const QString &newName)
Copies the file named fileName() to newName.
Definition qfile.cpp:765
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
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:25
constexpr QLatin1Char at(qsizetype i) const
bool isEmpty() const noexcept
Definition qlist.h:401
qsizetype count() const noexcept
Definition qlist.h:398
pointer data()
Definition qlist.h:431
\inmodule QtCore
Definition qmetatype.h:341
int id(int=0) const
Definition qmetatype.h:475
@ IsUnsignedEnumeration
Definition qmetatype.h:411
@ IsEnumeration
Definition qmetatype.h:407
friend class QVariant
Definition qmetatype.h:796
\inmodule QtCore
Definition qobject.h:103
The QQuaternion class represents a quaternion consisting of a vector and scalar.
\qmltype TextureData \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DTextureData
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
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
static constexpr bool isResource(Type type) noexcept
static constexpr bool isTexture(Type type) noexcept
const_iterator constEnd() const noexcept
Definition qset.h:143
const_iterator constFind(const T &value) const
Definition qset.h:161
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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:5455
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
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:8084
QString & append(QChar c)
Definition qstring.cpp:3252
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:3466
QString & prepend(QChar c)
Definition qstring.h:478
\inmodule QtCore
\inmodule QtCore
Definition qurl.h:94
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2468
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
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:340
QMetaType metaType() const
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:878
constexpr float w() const noexcept
Returns the w coordinate of this point.
Definition qvectornd.h:881
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:879
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:880
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)
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 >()
QHash< const QSSGSceneDesc::Node *, QString > NodeNameMap
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)
QHash< QString, const QSSGSceneDesc::Node * > UniqueIdMap
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)
QPair< QString, QString > writeQmlForAnimation(const QSSGSceneDesc::Animation &anim, qsizetype index, OutputContext &output, bool useBinaryKeyframes=true, bool generateTimelineAnimations=true)
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)
QString getMeshSourceName(const QString &name)
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 >()
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:164
#define qWarning
Definition qlogging.h:166
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]
GLuint color
[2]
GLenum type
GLenum target
GLenum GLuint texture
GLuint GLsizei const GLchar * message
GLuint name
GLsizei GLsizei GLchar * source
GLuint GLenum GLenum transform
GLuint res
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
#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:32
unsigned short quint16
Definition qtypes.h:48
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
unsigned char quint8
Definition qtypes.h:46
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]
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:18
QSSGRenderGraphObject::Type RuntimeType
ResourceNodes resources
Definition moc.h:23