14#include <QRegularExpression>
15#include <QtCore/qdir.h>
16#include <QtCore/qfile.h>
17#include <QtCore/qbuffer.h>
19#include <QtGui/qimage.h>
20#include <QtGui/qimagereader.h>
22#include <QtQuick3DUtils/private/qssgmesh_p.h>
23#include <QtQuick3DUtils/private/qssgassert_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
27#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
28#include <QtCore/QCborStreamWriter>
29#include <QtQuickTimeline/private/qquicktimeline_p.h>
34using namespace Qt::StringLiterals;
46 QVariant
getDefaultValue(QSSGSceneDesc::Node::RuntimeType type,
const char *property);
47 bool isDefaultValue(QSSGSceneDesc::Node::RuntimeType type,
const char *property,
const QVariant &value);
52 QHash<QSSGSceneDesc::Node::RuntimeType, PropertiesMap> m_properties;
57 QString nameCopy = name;
58 if (nameCopy.isEmpty())
59 return QStringLiteral(
"Presentation");
61 nameCopy = sanitizeQmlId(nameCopy);
63 if (nameCopy[0].isLower())
64 nameCopy[0] = nameCopy[0].toUpper();
71 colorString = QLatin1Char(
'\"') + color.name(QColor::HexArgb) + QLatin1Char(
'\"');
76 switch (variant.typeId()) {
77 case QMetaType::Float: {
78 auto value = variant.toDouble();
79 return QString::number(value);
81 case QMetaType::QVector2D: {
82 auto value = variant.value<QVector2D>();
83 return QString(QStringLiteral(
"Qt.vector2d(") + QString::number(
double(value.x())) +
84 QStringLiteral(
", ") + QString::number(
double(value.y())) +
87 case QMetaType::QVector3D: {
88 auto value = variant.value<QVector3D>();
89 return QString(QStringLiteral(
"Qt.vector3d(") + QString::number(
double(value.x())) +
90 QStringLiteral(
", ") + QString::number(
double(value.y())) +
91 QStringLiteral(
", ") + QString::number(
double(value.z())) +
94 case QMetaType::QVector4D: {
95 auto value = variant.value<QVector4D>();
96 return QString(QStringLiteral(
"Qt.vector4d(") + QString::number(
double(value.x())) +
97 QStringLiteral(
", ") + QString::number(
double(value.y())) +
98 QStringLiteral(
", ") + QString::number(
double(value.z())) +
99 QStringLiteral(
", ") + QString::number(
double(value.w())) +
100 QStringLiteral(
")"));
102 case QMetaType::QColor: {
103 auto value = variant.value<QColor>();
104 return colorToQml(value);
106 case QMetaType::QQuaternion: {
107 auto value = variant.value<QQuaternion>();
108 return QString(QStringLiteral(
"Qt.quaternion(") + QString::number(
double(value.scalar())) +
109 QStringLiteral(
", ") + QString::number(
double(value.x())) +
110 QStringLiteral(
", ") + QString::number(
double(value.y())) +
111 QStringLiteral(
", ") + QString::number(
double(value.z())) +
112 QStringLiteral(
")"));
115 return variant.toString();
123 if (!idCopy.isEmpty() && idCopy.at(0).isNumber())
124 idCopy.prepend(QStringLiteral(
"node"));
127 if (idCopy.startsWith(QChar::fromLatin1(
'#')))
131 static QRegularExpression regExp(QStringLiteral(
"\\W"));
132 idCopy.replace(regExp, QStringLiteral(
"_"));
136 if (!idCopy.isEmpty() && idCopy[0].isUpper()) {
139 int len = idCopy.length();
140 while (i < len && idCopy[i].isUpper()) {
141 idCopy[i] = idCopy[i].toLower();
147 static QSet<QByteArray> keywords {
243 if (keywords.contains(idCopy.toUtf8())) {
244 idCopy += QStringLiteral(
"_");
248 if (idCopy.isEmpty())
249 idCopy = QStringLiteral(
"node");
256 QString sourceCopy = source;
258 if (removeParentDirectory)
259 sourceCopy = QSSGQmlUtilities::stripParentDirectory(sourceCopy);
261 sourceCopy.replace(QChar::fromLatin1(
'\\'), QChar::fromLatin1(
'/'));
264 return QString(QStringLiteral(
"\"") + sourceCopy + QStringLiteral(
"\""));
298 auto metaObject = object->metaObject();
299 for (
auto i = 0; i < metaObject->propertyCount(); ++i) {
300 auto property = metaObject->property(i);
301 const auto name = property.name();
302 const auto value = property.read(object);
303 propertiesMap.insert(name, value);
305 return propertiesMap;
313 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Node, getObjectPropertiesMap(&node));
316 QQuick3DPrincipledMaterial principledMaterial;
317 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PrincipledMaterial, getObjectPropertiesMap(&principledMaterial));
320 QQuick3DSpecularGlossyMaterial specularGlossyMaterial;
321 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::SpecularGlossyMaterial, getObjectPropertiesMap(&specularGlossyMaterial));
324 QQuick3DCustomMaterial customMaterial;
325 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::CustomMaterial, getObjectPropertiesMap(&customMaterial));
328 QQuick3DTexture texture;
329 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Image2D, getObjectPropertiesMap(&texture));
332 QQuick3DCubeMapTexture cubeMapTexture;
333 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::ImageCube, getObjectPropertiesMap(&cubeMapTexture));
336 QQuick3DTextureData textureData;
337 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::TextureData, getObjectPropertiesMap(&textureData));
341 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Model, getObjectPropertiesMap(&model));
344 QQuick3DOrthographicCamera orthographicCamera;
345 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::OrthographicCamera, getObjectPropertiesMap(&orthographicCamera));
348 QQuick3DPerspectiveCamera perspectiveCamera;
349 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PerspectiveCamera, getObjectPropertiesMap(&perspectiveCamera));
352 QQuick3DDirectionalLight directionalLight;
353 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::DirectionalLight, getObjectPropertiesMap(&directionalLight));
356 QQuick3DPointLight pointLight;
357 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::PointLight, getObjectPropertiesMap(&pointLight));
360 QQuick3DSpotLight spotLight;
361 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::SpotLight, getObjectPropertiesMap(&spotLight));
364 QQuick3DSkeleton skeleton;
365 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Skeleton, getObjectPropertiesMap(&skeleton));
369 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Joint, getObjectPropertiesMap(&joint));
373 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::Skin, getObjectPropertiesMap(&skin));
376 QQuick3DMorphTarget morphTarget;
377 m_properties.insert(QSSGSceneDesc::Node::RuntimeType::MorphTarget, getObjectPropertiesMap(&morphTarget));
399template<QSSGSceneDesc::Material::RuntimeType T>
400const char *
qmlElementName() {
static_assert(!
std::is_same_v<
decltype(T),
decltype(T)>,
"Unknown type");
return nullptr; }
427 using RuntimeType = QSSGSceneDesc::Node::RuntimeType;
428 switch (node.runtimeType) {
429 case RuntimeType::Node:
430 return qmlElementName<RuntimeType::Node>();
431 case RuntimeType::PrincipledMaterial:
432 return qmlElementName<RuntimeType::PrincipledMaterial>();
433 case RuntimeType::SpecularGlossyMaterial:
434 return qmlElementName<RuntimeType::SpecularGlossyMaterial>();
435 case RuntimeType::CustomMaterial:
436 return qmlElementName<RuntimeType::CustomMaterial>();
437 case RuntimeType::Image2D:
438 return qmlElementName<RuntimeType::Image2D>();
439 case RuntimeType::ImageCube:
440 return qmlElementName<RuntimeType::ImageCube>();
441 case RuntimeType::TextureData:
442 return qmlElementName<RuntimeType::TextureData>();
443 case RuntimeType::Model:
444 return qmlElementName<RuntimeType::Model>();
445 case RuntimeType::OrthographicCamera:
446 return qmlElementName<RuntimeType::OrthographicCamera>();
447 case RuntimeType::PerspectiveCamera:
448 return qmlElementName<RuntimeType::PerspectiveCamera>();
449 case RuntimeType::DirectionalLight:
450 return qmlElementName<RuntimeType::DirectionalLight>();
451 case RuntimeType::PointLight:
452 return qmlElementName<RuntimeType::PointLight>();
453 case RuntimeType::SpotLight:
454 return qmlElementName<RuntimeType::SpotLight>();
455 case RuntimeType::Skeleton:
456 return qmlElementName<RuntimeType::Skeleton>();
457 case RuntimeType::Joint:
458 return qmlElementName<RuntimeType::Joint>();
459 case RuntimeType::Skin:
460 return qmlElementName<RuntimeType::Skin>();
461 case RuntimeType::MorphTarget:
462 return qmlElementName<RuntimeType::MorphTarget>();
463 case RuntimeType::ModelInstance:
464 return qmlElementName<RuntimeType::ModelInstance>();
466 return "UNKNOWN_TYPE";
521 case QMetaType::Bool:
523 case QMetaType::Char:
524 case QMetaType::SChar:
525 case QMetaType::UChar:
526 case QMetaType::Char16:
527 case QMetaType::Char32:
528 case QMetaType::QChar:
529 case QMetaType::Short:
530 case QMetaType::UShort:
532 case QMetaType::UInt:
533 case QMetaType::Long:
534 case QMetaType::ULong:
535 case QMetaType::LongLong:
536 case QMetaType::ULongLong:
538 case QMetaType::Float:
539 case QMetaType::Double:
541 case QMetaType::QByteArray:
542 case QMetaType::QString:
544 case QMetaType::QDate:
545 case QMetaType::QTime:
546 case QMetaType::QDateTime:
548 case QMetaType::QUrl:
550 case QMetaType::QRect:
551 case QMetaType::QRectF:
553 case QMetaType::QSize:
554 case QMetaType::QSizeF:
556 case QMetaType::QPoint:
557 case QMetaType::QPointF:
559 case QMetaType::QVariant:
561 case QMetaType::QColor:
563 case QMetaType::QMatrix4x4:
565 case QMetaType::QVector2D:
567 case QMetaType::QVector3D:
569 case QMetaType::QVector4D:
571 case QMetaType::QQuaternion:
573 case QMetaType::QFont:
590Q_GLOBAL_STATIC(UniqueIdOthers, g_idOthers)
600 g_nodeNameMap->clear();
607 static constexpr const char *typeNames[] = {
622 constexpr uint nameCount =
sizeof(typeNames)/
sizeof(
const char*);
623 const bool nodeHasName = (node.name.size() > 0);
624 uint nameIdx = qMin(uint(node.nodeType), nameCount);
625 QString name = nodeHasName ? QString::fromUtf8(node.name + typeNames[nameIdx]) : QString::fromLatin1(getQmlElementName(node));
626 QString sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
629 if (
const auto it = g_nodeNameMap->constFind(&node); it != g_nodeNameMap->constEnd())
632 quint64 id = node.id;
634 QString candidate = sanitizedName;
636 if (
const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
637 g_idMap->insert(candidate, &node);
638 g_nodeNameMap->insert(&node, candidate);
642 candidate = QStringLiteral(
"%1%2").arg(sanitizedName).arg(id++);
643 }
while (--attempts);
650 QString name = !inName.isEmpty() ? QString::fromUtf8(inName +
"_timeline") :
"timeline0"_L1;
651 QString sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
655 QString candidate = sanitizedName;
657 if (
const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
658 if (
const auto oIt = g_idOthers->constFind(candidate); oIt == g_idOthers->constEnd()) {
659 g_idOthers->insert(candidate);
664 candidate = QStringLiteral(
"%1%2").arg(sanitizedName).arg(++id);
665 }
while (--attempts);
671 QString sourceCopy = filePath;
672 while (sourceCopy.startsWith(QChar::fromLatin1(
'.')) || sourceCopy.startsWith(QChar::fromLatin1(
'/')) || sourceCopy.startsWith(QChar::fromLatin1(
'\\')))
673 sourceCopy.remove(0, 1);
680static const char *
indent() {
return " "; }
693 for (quint8 i = 0; i < output.indent; i += QSSGQmlScopedIndent::QSSG_INDENT)
694 str += QString::fromLatin1(indent());
702 return output.stream;
713 output.scopeDepth = qMax(0, output.scopeDepth - 1);
719 output.stream <<
"import QtQuick\n"
720 <<
"import QtQuick3D\n\n";
722 output.stream <<
"import QtQuick.Timeline\n\n";
725static QString
toQuotedString(
const QString &text) {
return QStringLiteral(
"\"%1\"").arg(text); }
732 const auto meshFolder = getMeshFolder();
733 const auto extension = getMeshExtension();
735 return QString(meshFolder + name + extension);
744 const auto animationFolder = getAnimationFolder();
745 const auto extension = getAnimationExtension();
746 return QString(animationFolder + id + QStringLiteral(
"_")
747 + property + QStringLiteral(
"_")
748 + QString::number(index) + extension);
753 return var.toString();
758 switch (var.metaType().id()) {
759 case QMetaType::QVector2D: {
760 const auto vec2 = qvariant_cast<
QVector2D>(var);
761 return QLatin1String(
"Qt.vector2d(") + QString::number(vec2.x()) + QLatin1String(
", ") + QString::number(vec2.y()) + QLatin1Char(
')');
763 case QMetaType::QVector3D: {
764 const auto vec3 = qvariant_cast<
QVector3D>(var);
765 return QLatin1String(
"Qt.vector3d(") + QString::number(vec3.x()) + QLatin1String(
", ")
766 + QString::number(vec3.y()) + QLatin1String(
", ")
767 + QString::number(vec3.z()) + QLatin1Char(
')');
769 case QMetaType::QVector4D: {
770 const auto vec4 = qvariant_cast<
QVector4D>(var);
771 return QLatin1String(
"Qt.vector4d(") + QString::number(vec4.x()) + QLatin1String(
", ")
772 + QString::number(vec4.y()) + QLatin1String(
", ")
773 + QString::number(vec4.z()) + QLatin1String(
", ")
774 + QString::number(vec4.w()) + QLatin1Char(
')');
776 case QMetaType::QColor: {
777 const auto color = qvariant_cast<QColor>(var);
778 return colorToQml(color);
780 case QMetaType::QQuaternion: {
781 const auto &quat = qvariant_cast<QQuaternion>(var);
782 return QLatin1String(
"Qt.quaternion(") + QString::number(quat.scalar()) + QLatin1String(
", ")
783 + QString::number(quat.x()) + QLatin1String(
", ")
784 + QString::number(quat.y()) + QLatin1String(
", ")
785 + QString::number(quat.z()) + QLatin1Char(
')');
787 case QMetaType::QMatrix4x4: {
788 const auto mat44 = qvariant_cast<QMatrix4x4>(var);
789 return QLatin1String(
"Qt.matrix4x4(")
790 + QString::number(mat44(0, 0)) + u", " + QString::number(mat44(0, 1)) + u", " + QString::number(mat44(0, 2)) + u", " + QString::number(mat44(0, 3)) + u", "
791 + QString::number(mat44(1, 0)) + u", " + QString::number(mat44(1, 1)) + u", " + QString::number(mat44(1, 2)) + u", " + QString::number(mat44(1, 3)) + u", "
792 + QString::number(mat44(2, 0)) + u", " + QString::number(mat44(2, 1)) + u", " + QString::number(mat44(2, 2)) + u", " + QString::number(mat44(2, 3)) + u", "
793 + QString::number(mat44(3, 0)) + u", " + QString::number(mat44(3, 1)) + u", " + QString::number(mat44(3, 2)) + u", " + QString::number(mat44(3, 3)) + u')';
795 case QMetaType::Float:
796 case QMetaType::Double:
798 case QMetaType::Char:
799 case QMetaType::Long:
800 case QMetaType::LongLong:
801 case QMetaType::ULong:
802 case QMetaType::ULongLong:
803 case QMetaType::Bool:
804 return var.toString();
805 case QMetaType::QUrl:
816 return QStringLiteral(
"position");
818 return QStringLiteral(
"rotation");
820 return QStringLiteral(
"scale");
822 return QStringLiteral(
"weight");
824 return QStringLiteral(
"unknown");
831 const auto meshFolder = getMeshFolder();
832 const auto meshId = QSSGQmlUtilities::getIdForNode(meshNode);
833 const auto meshSourceName = QSSGQmlUtilities::getMeshSourceName(meshId);
834 Q_ASSERT(scene.meshStorage.size() > meshNode.idx);
835 const auto &mesh = scene.meshStorage.at(meshNode.idx);
838 if (!outdir.exists(meshFolder) && !outdir.mkdir(meshFolder)) {
839 qDebug() <<
"Failed to create meshes folder at" << outdir;
843 const QString path = outdir.path() + QDir::separator() + meshSourceName;
845 if (!file.open(QIODevice::WriteOnly)) {
846 return {QString(), QStringLiteral(
"Failed to find mesh at ") + path};
849 if (mesh.save(&file) == 0) {
853 return {meshSourceName, QString()};
861 QString assetPath = output.outdir.isAbsolutePath(texturePath.path()) ? texturePath.toString() : texturePath.path();
862 QFileInfo fi(assetPath);
863 if (fi.isRelative() && !output.sourceDir.isEmpty()) {
864 fi = QFileInfo(output.sourceDir + QChar(u'/') + assetPath);
867 indent(output) << comment() <<
"Source texture path expected: " << getTextureFolder() + texturePath.fileName() <<
"\n";
868 return {QString(), QStringLiteral(
"Failed to find texture at ") + assetPath};
871 const auto mapsFolder = getTextureFolder();
873 if (!output.outdir.exists(mapsFolder) && !output.outdir.mkdir(mapsFolder)) {
874 qDebug() <<
"Failed to create maps folder at" << output.outdir;
878 const QString relpath = mapsFolder + fi.fileName();
879 const auto newfilepath = QString(output.outdir.canonicalPath() + QDir::separator() + relpath);
880 if (!QFile::exists(newfilepath) && !QFile::copy(fi.canonicalFilePath(), newfilepath)) {
881 qDebug() <<
"Failed to copy file from" << fi.canonicalFilePath() <<
"to" << newfilepath;
885 return {relpath, QString()};
890 static const QRegularExpression re(QLatin1String(
"^Qt.[a-z0-9]*\\(([0-9.e\\+\\-, ]*)\\)"));
891 Q_ASSERT(re.isValid());
894 case QMetaType::QVector2D: {
895 QRegularExpressionMatch match = re.match(value);
896 if (match.hasMatch()) {
897 const auto comp = match.captured(1).split(QLatin1Char(
','));
898 if (comp.size() == 2) {
899 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
900 QLatin1String(
".y: ") + comp.at(1).trimmed() };
905 case QMetaType::QVector3D: {
906 QRegularExpressionMatch match = re.match(value);
907 if (match.hasMatch()) {
908 const auto comp = match.captured(1).split(QLatin1Char(
','));
909 if (comp.size() == 3) {
910 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
911 QLatin1String(
".y: ") + comp.at(1).trimmed(),
912 QLatin1String(
".z: ") + comp.at(2).trimmed() };
917 case QMetaType::QVector4D: {
918 QRegularExpressionMatch match = re.match(value);
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(
".w: ") + comp.at(3).trimmed() };
930 case QMetaType::QQuaternion: {
931 QRegularExpressionMatch match = re.match(value);
932 if (match.hasMatch()) {
933 const auto comp = match.captured(1).split(QLatin1Char(
','));
934 if (comp.size() == 4) {
935 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
936 QLatin1String(
".y: ") + comp.at(1).trimmed(),
937 QLatin1String(
".z: ") + comp.at(2).trimmed(),
938 QLatin1String(
".scalar: ") + comp.at(3).trimmed() };
953 if (mt.id() != QMetaType::QQuaternion)
954 return expandComponents(value, mt);
971 if (property.value.isNull()) {
973 result.notValidReason = QStringLiteral(
"Property value is null");
977 const QVariant &value = property.value;
978 result.name = QString::fromUtf8(property.name);
982 QString valueAsString = builtinQmlType(value);
983 if (valueAsString.size() > 0) {
984 result.value = valueAsString;
986 }
else if (value.metaType().flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) {
987 static const auto qmlEnumString = [](
const QLatin1String &element,
const QString &enumString) {
988 return QStringLiteral(
"%1.%2").arg(element).arg(enumString);
991 QString enumValue = asString(value);
992 if (enumValue.size() > 0) {
993 result.value = qmlEnumString(qmlElementName, enumValue);
997 QByteArray element(getQmlElementName(target));
998 if (element.size() > 0) {
1000 QByteArray keysString = flag.me.valueToKeys(
int(flag.value));
1001 if (keysString.size() > 0) {
1002 keysString.prepend(element +
'.');
1003 QByteArray replacement(
" | " + element +
'.');
1004 keysString.replace(
'|', replacement);
1005 result.value = QString::fromLatin1(keysString);
1011 if (list->count > 0) {
1012 const QString indentStr = indentString(output);
1014 const QString listIndentStr = indentString(output);
1019 for (
int i = 0, end = list->count; i != end; ++i) {
1022 str.append(listIndentStr);
1023 str.append(getIdForNode(*(list->head[i])));
1026 str.append(u'\n' + indentStr + u']');
1033 if (list.count > 0) {
1034 const QString indentStr = indentString(output);
1036 const QString listIndentStr = indentString(output);
1041 char *vptr =
reinterpret_cast<
char *>(list.data);
1042 auto size = list.mt.sizeOf();
1044 for (
int i = 0, end = list.count; i != end; ++i) {
1048 const QVariant var{list.mt,
reinterpret_cast<
void *>(vptr + (size * i))};
1049 QString valueString = builtinQmlType(var);
1050 if (valueString.isEmpty())
1051 valueString = asString(var);
1053 str.append(listIndentStr);
1054 str.append(valueString);
1057 str.append(u'\n' + indentStr + u']');
1062 }
else if (value.metaType().id() == qMetaTypeId<
QSSGSceneDesc::Node *>()) {
1063 if (
const auto node = qvariant_cast<QSSGSceneDesc::Node *>(value)) {
1067 Q_ASSERT(node->id != 0);
1071 if (node->runtimeType == QSSGSceneDesc::Node::RuntimeType::TextureData) {
1072 result.name = QStringLiteral(
"source");
1073 result.value = getIdForNode(*node->scene->root) + QLatin1Char(
'.') + getIdForNode(*node);
1075 result.value = getIdForNode(*node);
1079 }
else if (value.metaType() == QMetaType::fromType<QSSGSceneDesc::Mesh *>()) {
1080 if (
const auto meshNode = qvariant_cast<
const QSSGSceneDesc::Mesh *>(value)) {
1081 Q_ASSERT(meshNode->nodeType == QSSGSceneDesc::Node::Type::Mesh);
1082 Q_ASSERT(meshNode->scene);
1083 const auto &scene = *meshNode->scene;
1084 const auto& [meshSourceName, notValidReason] = meshAssetName(scene, *meshNode, output.outdir);
1085 result.notValidReason = notValidReason;
1086 if (!meshSourceName.isEmpty()) {
1087 result.value = toQuotedString(meshSourceName);
1091 }
else if (value.metaType() == QMetaType::fromType<QUrl>()) {
1092 if (
const auto url = qvariant_cast<QUrl>(value); !url.isEmpty()) {
1095 if (QSSGRenderGraphObject::isTexture(target.runtimeType)) {
1096 const auto& [relpath, notValidReason] = copyTextureAsset(url, output);
1097 result.notValidReason = notValidReason;
1098 if (!relpath.isEmpty()) {
1104 if (!path.isEmpty()) {
1105 result.value = toQuotedString(path);
1109 }
else if (target.runtimeType == QSSGSceneDesc::Material::RuntimeType::CustomMaterial) {
1111 if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Texture *>()) {
1112 if (
const auto texture = qvariant_cast<QSSGSceneDesc::Texture *>(value)) {
1113 Q_ASSERT(QSSGRenderGraphObject::isTexture(texture->runtimeType));
1114 result.value = QLatin1String(
"TextureInput { texture: ") +
1115 getIdForNode(*texture) + QLatin1String(
" }");
1119 }
else if (value.metaType() == QMetaType::fromType<QString>()) {
1121 result.value = toQuotedString(value.toString());
1124 result.notValidReason = QStringLiteral(
"Unsupported value type: ") + QString::fromUtf8(value.metaType().name());
1125 qWarning() << result.notValidReason;
1130 result.expandedProperties = ((output.options & OutputContext::Options::DesignStudioWorkarounds) == OutputContext::Options::DesignStudioWorkarounds)
1131 ? expandComponentsPartially(result.value, value.metaType())
1132 : expandComponents(result.value, value.metaType());
1139 const char *skipProperty =
nullptr)
1143 indent(output) << u"id: "_s << getIdForNode(node) << u'\n';
1146 if (node.name.size()) {
1147 const QString objectName = QString::fromLocal8Bit(node.name);
1148 if (!objectName.startsWith(u'*'))
1149 indent(output) << u"objectName: \""_s << node.name << u"\"\n"_s;
1152 const auto &properties = node.properties;
1153 auto it = properties.begin();
1154 const auto end = properties.end();
1155 for (; it != end; ++it) {
1156 const auto &property = *it;
1157 if (skipProperty && qstrcmp(property->name, skipProperty) == 0)
1163 indent(output) <<
"property " << typeName(property->value.metaType()).toByteArray() <<
' ' << result.name << u": "_s << result.value << u'\n';
1165 if (result.expandedProperties.size() > 1) {
1166 for (
const auto &va : result.expandedProperties)
1167 indent(output) << result.name << va << u'\n';
1169 indent(output) << result.name << u": "_s << result.value << u'\n';
1173 QString message = u"Skipped property: "_s + QString::fromUtf8(property->name);
1174 if (!result.notValidReason.isEmpty())
1175 message.append(u", reason: "_s + result.notValidReason);
1176 qDebug() << message;
1185 Q_ASSERT(transform.nodeType == QSSGSceneDesc::Node::Type::Transform && transform.runtimeType == QSSGSceneDesc::Node::RuntimeType::Node);
1186 indent(output) << qmlElementName<QSSGSceneDesc::Node::RuntimeType::Node>() << blockBegin(output);
1193 Q_ASSERT(material.nodeType == QSSGSceneDesc::Model::Type::Material);
1194 if (material.runtimeType == QSSGSceneDesc::Model::RuntimeType::SpecularGlossyMaterial) {
1195 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1196 }
else if (material.runtimeType == Model::RuntimeType::PrincipledMaterial) {
1197 indent(output) << qmlElementName<Material::RuntimeType::PrincipledMaterial>() << blockBegin(output);
1198 }
else if (material.runtimeType == Material::RuntimeType::CustomMaterial) {
1199 indent(output) << qmlElementName<Material::RuntimeType::CustomMaterial>() << blockBegin(output);
1200 }
else if (material.runtimeType == Material::RuntimeType::SpecularGlossyMaterial) {
1201 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1206 writeNodeProperties(material, output);
1211 QString escaped = value;
1212 escaped.replace(u'\\', u"\\\\"_s);
1213 escaped.replace(u'"', u"\\\""_s);
1214 escaped.replace(u'\n', u"\\n"_s);
1215 escaped.replace(u'\r', u"\\r"_s);
1216 return u'"' + escaped + u'"';
1222 Q_ASSERT(model.nodeType == Node::Type::Model);
1223 indent(output) << qmlElementName<QSSGSceneDesc::Node::RuntimeType::Model>() << blockBegin(output);
1224 const bool hasVariants = !model.variantMaterials.isEmpty() && model.scene
1225 && !model.scene->materialVariants.isEmpty();
1226 writeNodeProperties(model, output, hasVariants ?
"materials" :
nullptr);
1229 const auto materialListString = [](
const QList<Node *> &materials) {
1231 for (
const Node *material : materials)
1232 ids.append(getIdForNode(*material));
1233 return u'[' + ids.join(u", "_s) + u']';
1236 const QStringList &names = model.scene->materialVariants;
1237 const QString rootId = getIdForNode(*model.scene->root);
1238 for (qsizetype variant = 0; variant < model.variantMaterials.size() && variant < names.size(); ++variant) {
1239 expression += rootId + u".materialVariant === "_s + toQmlStringLiteral(names.at(variant)) + u" ? "_s
1240 + materialListString(model.variantMaterials.at(variant)) + u" : "_s;
1242 expression += materialListString(model.defaultMaterials);
1244 indent(output) << u"materials: "_s << expression << u'\n';
1251 Q_ASSERT(camera.nodeType == Node::Type::Camera);
1252 if (camera.runtimeType == Camera::RuntimeType::PerspectiveCamera)
1253 indent(output) << qmlElementName<Camera::RuntimeType::PerspectiveCamera>() << blockBegin(output);
1254 else if (camera.runtimeType == Camera::RuntimeType::OrthographicCamera)
1255 indent(output) << qmlElementName<Camera::RuntimeType::OrthographicCamera>() << blockBegin(output);
1258 writeNodeProperties(camera, output);
1264 Q_ASSERT(texture.nodeType == Node::Type::Texture && QSSGRenderGraphObject::isTexture(texture.runtimeType));
1265 if (texture.runtimeType == Texture::RuntimeType::Image2D)
1266 indent(output) << qmlElementName<Texture::RuntimeType::Image2D>() << blockBegin(output);
1267 else if (texture.runtimeType == Texture::RuntimeType::ImageCube)
1268 indent(output) << qmlElementName<Texture::RuntimeType::ImageCube>() << blockBegin(output);
1269 writeNodeProperties(texture, output);
1275 Q_ASSERT(skin.nodeType == Node::Type::Skin && skin.runtimeType == Node::RuntimeType::Skin);
1276 indent(output) << qmlElementName<Node::RuntimeType::Skin>() << blockBegin(output);
1277 writeNodeProperties(skin, output);
1283 Q_ASSERT(morphTarget.nodeType == Node::Type::MorphTarget);
1284 indent(output) << qmlElementName<QSSGSceneDesc::Node::RuntimeType::MorphTarget>() << blockBegin(output);
1285 writeNodeProperties(morphTarget, output);
1290 const auto textureFolder = getTextureFolder();
1292 const auto sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
1294 const auto ext = (fmt.isEmpty() || fmt.length() > 4) ? u".png"_s
1297 return QString(textureFolder + sanitizedName + ext);
1302 if (textureData.data.isEmpty())
1305 const auto mapsFolder = getTextureFolder();
1306 const auto id = getIdForNode(textureData);
1307 const bool isCompressed = ((textureData.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
1308 const QString textureSourceName = getTextureSourceName(id, isCompressed ? QString::fromUtf8(textureData.fmt)
1312 if (!outdir.exists(mapsFolder) && !outdir.mkdir(mapsFolder))
1315 const auto imagePath = QString(outdir.path() + QDir::separator() + textureSourceName);
1318 QFile file(imagePath);
1319 if (!file.open(QIODevice::WriteOnly)) {
1320 qWarning(
"Failed to open file %s: %s",
1321 qPrintable(file.fileName()), qPrintable(file.errorString()));
1324 file.write(textureData.data);
1328 const auto &texData = textureData.data;
1329 const auto &size = textureData.sz;
1331 image = QImage(
reinterpret_cast<
const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
1332 if (!image.save(imagePath))
1336 return textureSourceName;
1342 Q_ASSERT(textureData.nodeType == Node::Type::Texture && textureData.runtimeType == Node::RuntimeType::TextureData);
1344 QString textureSourcePath = outputTextureAsset(textureData, output.outdir);
1346 static const auto writeProperty = [](
const QString &type,
const QString &name,
const QString &value) {
1347 return QString::fromLatin1(
"property %1 %2: %3").arg(type, name, value);
1350 if (!textureSourcePath.isEmpty()) {
1351 const auto type = QLatin1String(
"url");
1352 const auto name = getIdForNode(textureData);
1354 indent(output
) << writeProperty(type, name, toQuotedString(textureSourcePath)) <<
'\n';
1362 const QString folder = getInstancesFolder();
1363 if (!outdir.exists(folder) && !outdir.mkdir(folder)) {
1364 qWarning() <<
"Failed to create instances folder at" << outdir;
1367 const QString sourceName = folder + getIdForNode(instancing) + QStringLiteral(
".bin");
1368 QFile file(outdir.path() + QDir::separator() + sourceName);
1369 if (!file.open(QIODevice::WriteOnly)) {
1370 qWarning(
"Failed to open file %s: %s", qPrintable(file.fileName()), qPrintable(file.errorString()));
1373 if (!QQuick3DFileInstancing::writeInstanceTable(&file, instancing.instanceData,
int(instancing.instanceCount)))
1381 Q_ASSERT(instancing.nodeType == Node::Type::Instancing
1382 && instancing.runtimeType == Node::RuntimeType::ModelInstance);
1384 const QString instanceSourcePath = outputInstanceTableAsset(instancing, output.outdir);
1386 indent(output) << qmlElementName<Node::RuntimeType::ModelInstance>() << blockBegin(output);
1387 writeNodeProperties(instancing, output);
1388 if (!instanceSourcePath.isEmpty()) {
1390 indent(output) << u"source: "_s << toQuotedString(instanceSourcePath) << u'\n';
1397 Q_ASSERT(light.nodeType == Node::Type::Light);
1398 if (light.runtimeType == Light::RuntimeType::DirectionalLight)
1399 indent(output) << qmlElementName<Light::RuntimeType::DirectionalLight>() << blockBegin(output);
1400 else if (light.runtimeType == Light::RuntimeType::SpotLight)
1401 indent(output) << qmlElementName<Light::RuntimeType::SpotLight>() << blockBegin(output);
1402 else if (light.runtimeType == Light::RuntimeType::PointLight)
1403 indent(output) << qmlElementName<Light::RuntimeType::PointLight>() << blockBegin(output);
1406 writeNodeProperties(light, output);
1412 Q_ASSERT(skeleton.nodeType == Node::Type::Skeleton && skeleton.runtimeType == Node::RuntimeType::Skeleton);
1413 indent(output) << qmlElementName<Node::RuntimeType::Skeleton>() << blockBegin(output);
1414 writeNodeProperties(skeleton, output);
1420 Q_ASSERT(joint.nodeType == Node::Type::Joint && joint.runtimeType == Node::RuntimeType::Joint);
1421 indent(output) << qmlElementName<Node::RuntimeType::Joint>() << blockBegin(output);
1422 writeNodeProperties(joint, output);
1429 Q_ASSERT(QSSGRenderGraphObject::isResource(node.runtimeType) || node.nodeType == Node::Type::Mesh || node.nodeType == Node::Type::Skeleton);
1434 switch (node.nodeType) {
1435 case Node::Type::Skin:
1438 case Node::Type::MorphTarget:
1441 case Node::Type::Instancing:
1444 case Node::Type::Skeleton:
1447 case Node::Type::Texture:
1448 if (node.runtimeType == Node::RuntimeType::Image2D)
1450 else if (node.runtimeType == Node::RuntimeType::ImageCube)
1452 else if (node.runtimeType == Node::RuntimeType::TextureData)
1457 case Node::Type::Material:
1460 case Node::Type::Mesh:
1464 qWarning(
"Unhandled resource type \'%d\'?",
int(node.runtimeType));
1471 const bool skipBlockEnd = (node.runtimeType == Node::RuntimeType::TextureData || node.nodeType == Node::Type::Mesh);
1472 if (!skipBlockEnd && processNode && output.scopeDepth != 0) {
1482 const bool processNode = !(node.properties.isEmpty() && node.children.isEmpty())
1486 switch (node.nodeType) {
1487 case Node::Type::Skeleton:
1490 case Node::Type::Joint:
1493 case Node::Type::Light:
1496 case Node::Type::Transform:
1499 case Node::Type::Camera:
1502 case Node::Type::Model:
1510 for (
const auto &cld : node.children) {
1511 if (!QSSGRenderGraphObject::isResource(cld->runtimeType) && output.type == OutputContext::NodeTree) {
1512 QSSGQmlScopedIndent scopedIndent(output);
1513 writeQmlForNode(*cld, output);
1519 const bool skipBlockEnd = (node.runtimeType == Node::RuntimeType::TextureData || node.nodeType == Node::Type::Mesh);
1520 if (!skipBlockEnd && processNode && output.scopeDepth != 0) {
1528 auto sortedResources = resources;
1529 std::sort(sortedResources.begin(), sortedResources.end(), [](
const QSSGSceneDesc::Node *a,
const QSSGSceneDesc::Node *b) {
1530 using RType = QSSGSceneDesc::Node::RuntimeType;
1531 if (a->runtimeType == RType::TextureData && b->runtimeType != RType::TextureData)
1533 if (a->runtimeType == RType::ImageCube && (b->runtimeType != RType::TextureData && b->runtimeType != RType::ImageCube))
1535 if (a->runtimeType == RType::Image2D && (b->runtimeType != RType::TextureData && b->runtimeType != RType::Image2D))
1540 for (
const auto &res : std::as_const(sortedResources))
1541 writeQmlForResourceNode(*res, output);
1546#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1547 QCborStreamWriter writer(&keyframeData);
1549 writer.startArray();
1551 writer.append(
"QTimelineKeyframes");
1553 const int keyframesDataVersion = 1;
1554 writer.append(keyframesDataVersion);
1555 writer.append(
int(channel.keys.at(0)->getValueQMetaType()));
1558 writer.startArray();
1559 quint8 compEnd = quint8(channel.keys.at(0)->getValueType());
1560 bool isQuaternion =
false;
1561 if (compEnd == quint8(QSSGSceneDesc::Animation::KeyPosition::ValueType::Quaternion)) {
1562 isQuaternion =
true;
1567 for (
const auto &key : channel.keys) {
1568 writer.append(key->time);
1570 writer.append(QEasingCurve::Linear);
1572 writer.append(key->value[3]);
1573 for (quint8 i = 0; i < compEnd; ++i)
1574 writer.append(key->value[i]);
1582 Q_UNUSED(keyframeData)
1592 const int duration = qCeil(anim
.length);
1594 const QString animationId = getIdForAnimation(anim.name);
1595 indent(output
) <<
"id: " << animationId <<
"\n";
1596 QString animationName = animationId;
1597 if (!anim.name.isEmpty())
1598 animationName = QString::fromLocal8Bit(anim.name);
1599 indent(output
) <<
"objectName: \"" << animationName <<
"\"\n";
1601 indent(output
) <<
"startFrame: 0\n";
1602 indent(output
) <<
"endFrame: " << duration <<
"\n";
1603 indent(output
) <<
"currentFrame: 0\n";
1607 if (generateTimelineAnimations) {
1608 indent(output
) <<
"enabled: true\n";
1609 indent(output
) <<
"animations: TimelineAnimation {\n";
1612 indent(output
) <<
"duration: " << duration <<
"\n";
1614 indent(output
) <<
"to: " << duration <<
"\n";
1615 indent(output
) <<
"running: true\n";
1616 indent(output
) <<
"loops: Animation.Infinite\n";
1621 for (
const auto &channel : anim.channels) {
1622 QString id = getIdForNode(*channel->target);
1623 QString propertyName = asString(channel->targetProperty);
1625 indent(output) <<
"KeyframeGroup {\n";
1627 QSSGQmlScopedIndent scopedIndent(output);
1628 indent(output) <<
"target: " << id <<
"\n";
1629 indent(output) <<
"property: " << toQuotedString(propertyName) <<
"\n";
1630 if (useBinaryKeyframes && channel->keys.size() != 1) {
1631 const auto animFolder = getAnimationFolder();
1632 const auto animSourceName = getAnimationSourceName(id, propertyName, index);
1633 if (!output.outdir.exists(animFolder) && !output.outdir.mkdir(animFolder)) {
1637 QFile file(output.outdir.path() + QDir::separator() + animSourceName);
1638 if (!file.open(QIODevice::WriteOnly))
1640 QByteArray keyframeData;
1644 generateKeyframeData(*channel, keyframeData);
1645 file.write(keyframeData);
1647 indent(output) <<
"keyframeSource: " << toQuotedString(animSourceName) <<
"\n";
1649 Q_ASSERT(!channel->keys.isEmpty());
1650 for (
const auto &key : std::as_const(channel->keys)) {
1651 indent(output) <<
"Keyframe {\n";
1653 QSSGQmlScopedIndent scopedIndent(output);
1654 indent(output) <<
"frame: " << key->time <<
"\n";
1655 indent(output) <<
"value: " << variantToQml(key->getValue()) <<
"\n";
1657 indent(output) << blockEnd(output);
1661 indent(output) << blockEnd(output);
1663 return {animationName, animationId};
1666void writeQml(
const QSSGSceneDesc::Scene &scene, QTextStream &stream,
const QDir &outdir,
const QJsonObject &optionsObject)
1668 static const auto checkBooleanOption = [](
const QLatin1String &optionName,
const QJsonObject &options,
bool defaultValue =
false) {
1669 const auto it = options.constFind(optionName);
1670 const auto end = options.constEnd();
1674 value = it->toObject().value(QLatin1String(
"value"));
1678 return value.toBool(defaultValue);
1681 auto root = scene.root;
1686 QJsonObject options = optionsObject;
1688 if (
auto it = options.constFind(QLatin1String(
"options")), end = options.constEnd(); it != end)
1689 options = it->toObject();
1692 if (checkBooleanOption(QLatin1String(
"expandValueComponents"), options))
1696 if (checkBooleanOption(QLatin1String(
"designStudioWorkarounds"), options))
1699 const bool useBinaryKeyframes = checkBooleanOption(
"useBinaryKeyframes"_L1, options);
1700 const bool generateTimelineAnimations = !checkBooleanOption(
"manualAnimations"_L1, options);
1704 writeImportHeader(output, scene.animations.count() > 0);
1707 writeQml(*root, output);
1709 if (!scene.materialVariants.isEmpty()) {
1712 QStringList variantNames;
1713 for (
const QString &name : scene.materialVariants)
1714 variantNames.append(toQmlStringLiteral(name));
1715 indent(output) << u"property string materialVariant: \"\"\n"_s;
1716 indent(output) << u"readonly property list<string> materialVariants: ["_s << variantNames.join(u", "_s)
1721 stream <<
indent() <<
"// Resources\n";
1723 writeQmlForResources(scene.resources, output);
1726 stream <<
indent() <<
"// Nodes:\n";
1727 for (
const auto &cld : std::as_const(root->children))
1728 writeQmlForNode(*cld, output);
1731 qsizetype animId = 0;
1733 stream <<
indent() <<
"// Animations:\n";
1734 QList<QPair<QString, QString>> animationMap;
1735 for (
const auto &cld : scene.animations) {
1736 QSSGQmlScopedIndent scopedIndent(output);
1737 auto mapValues = writeQmlForAnimation(*cld, animId++, output, useBinaryKeyframes, generateTimelineAnimations);
1738 animationMap.append(mapValues);
1739 indent(output) << blockEnd(output);
1742 if (!generateTimelineAnimations) {
1745 stream <<
indent() <<
"// An exported mapping of Timelines (--manualAnimations)\n";
1746 stream <<
indent() <<
"property var timelineMap: {\n";
1748 for (
const auto &mapValues : animationMap) {
1749 QSSGQmlScopedIndent scopedIndent(output);
1750 indent(output) <<
"\"" << mapValues.first <<
"\": " << mapValues.second <<
",\n";
1753 stream <<
indent() <<
"// A simple list of Timelines (--manualAnimations)\n";
1754 stream <<
indent() <<
"property var timelineList: [\n";
1755 for (
const auto &mapValues : animationMap) {
1756 QSSGQmlScopedIndent scopedIndent(output);
1757 indent(output) << mapValues.second <<
",\n";
1769#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1770 auto timeline =
new QQuickTimeline(parent);
1771 auto timelineKeyframeGroup = timeline->keyframeGroups();
1772 for (
const auto &channel : anim.channels) {
1773 auto keyframeGroup =
new QQuickKeyframeGroup(timeline);
1774 keyframeGroup->setTargetObject(channel->target->obj);
1775 keyframeGroup->setProperty(asString(channel->targetProperty));
1777 Q_ASSERT(!channel->keys.isEmpty());
1778 if (useBinaryKeyframes) {
1779 QByteArray keyframeData;
1780 generateKeyframeData(*channel, keyframeData);
1782 keyframeGroup->setKeyframeData(keyframeData);
1784 auto keyframes = keyframeGroup->keyframes();
1785 for (
const auto &key : std::as_const(channel->keys)) {
1786 auto keyframe =
new QQuickKeyframe(keyframeGroup);
1787 keyframe->setFrame(key->time);
1788 keyframe->setValue(key->getValue());
1789 keyframes.append(&keyframes, keyframe);
1792 (qobject_cast<QQmlParserStatus *>(keyframeGroup))->componentComplete();
1793 timelineKeyframeGroup.append(&timelineKeyframeGroup, keyframeGroup);
1795 timeline->setEndFrame(anim.length);
1796 timeline->setEnabled(isEnabled);
1798 auto timelineAnimation =
new QQuickTimelineAnimation(timeline);
1799 timelineAnimation->setObjectName(anim.name);
1800 timelineAnimation->setDuration(
int(anim.length));
1801 timelineAnimation->setFrom(0.0f);
1802 timelineAnimation->setTo(anim.length);
1803 timelineAnimation->setLoops(QQuickTimelineAnimation::Infinite);
1804 timelineAnimation->setTargetObject(timeline);
1806 (qobject_cast<QQmlParserStatus *>(timeline))->componentComplete();
1808 timelineAnimation->setRunning(
true);
1813 Q_UNUSED(useBinaryKeyframes)
1821 QSSG_ASSERT(node.scene !=
nullptr,
return);
1825 if (node.runtimeType == Material::RuntimeType::CustomMaterial) {
1826 QString sourceDir = node.scene->sourceDir;
1831 const auto &resources = node.scene->resources;
1832 writeQmlForResources(resources, output);
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
The QVector2D class represents a vector or vertex in 2D space.
The QVector3D class represents a vector or vertex in 3D space.
The QVector4D class represents a vector or vertex in 4D space.
static QString getInstancesFolder()
static const char * blockEnd()
static constexpr QByteArrayView qml_basic_types[]
QString builtinQmlType(const QVariant &var)
static void generateKeyframeData(const QSSGSceneDesc::Animation::Channel &channel, QByteArray &keyframeData)
void writeQmlComponent(const QSSGSceneDesc::Node &node, QTextStream &stream, const QDir &outDir)
static QString getIdForNode(const QSSGSceneDesc::Node &node)
QString asString(const QVariant &var)
static void writeNodeProperties(const QSSGSceneDesc::Node &node, OutputContext &output, const char *skipProperty=nullptr)
static QString getTextureFolder()
QString qmlComponentName(const QString &name)
static PropertyMap::PropertiesMap getObjectPropertiesMap(QObject *object)
static QString getAnimationFolder()
static QString getAnimationExtension()
static ValueToQmlResult valueToQml(const QSSGSceneDesc::Node &target, const QSSGSceneDesc::Property &property, OutputContext &output)
static QString outputInstanceTableAsset(const QSSGSceneDesc::Instancing &instancing, const QDir &outdir)
static void writeQml(const QSSGSceneDesc::Model &model, OutputContext &output)
QString colorToQml(const QColor &color)
void createTimelineAnimation(const QSSGSceneDesc::Animation &anim, QObject *parent, bool isEnabled, bool useBinaryKeyframes)
static QString getMeshExtension()
static const char * blockBegin()
static const char * blockBegin(OutputContext &output)
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()
QString sanitizeQmlId(const QString &id)
static QString toQmlStringLiteral(const QString &value)
static const char * blockEnd(OutputContext &output)
static QString outputTextureAsset(const QSSGSceneDesc::TextureData &textureData, const QDir &outdir)
static const char * comment()
static QTextStream & indent(OutputContext &output)
static QString toQuotedString(const QString &text)
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)
QPair< QString, QString > writeQmlForAnimation(const QSSGSceneDesc::Animation &anim, qsizetype index, OutputContext &output, bool useBinaryKeyframes=true, bool generateTimelineAnimations=true)
const char * getQmlElementName(const QSSGSceneDesc::Node &node)
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 * indent()
QString getAnimationSourceName(const QString &id, const QString &property, qsizetype index)
QString getMeshSourceName(const QString &name)
QString variantToQml(const QVariant &variant)
QString stripParentDirectory(const QString &filePath)
QString asString(QSSGSceneDesc::Animation::Channel::TargetProperty prop)
static void writeQmlForResourceNode(const QSSGSceneDesc::Node &node, OutputContext &output)
QString sanitizeQmlSourcePath(const QString &source, bool removeParentDirectory)
QString getTextureSourceName(const QString &name, const QString &fmt)
static void resetIdAllocator()
static QByteArrayView typeName(QMetaType mt)
void writeQml(const QSSGSceneDesc::Scene &scene, QTextStream &stream, const QDir &outdir, const QJsonObject &optionsObject)
static QString indentString(OutputContext &output)
void writeQml(const QSSGSceneDesc::Material &material, OutputContext &output)
static void writeQmlForNode(const QSSGSceneDesc::Node &node, OutputContext &output)
Combined button and popup list for selecting options.
@ DesignStudioWorkarounds
QSSGQmlScopedIndent(OutputContext &out)
QStringList expandedProperties