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");
426 using RuntimeType = QSSGSceneDesc::Node::RuntimeType;
427 switch (node.runtimeType) {
428 case RuntimeType::Node:
429 return qmlElementName<RuntimeType::Node>();
430 case RuntimeType::PrincipledMaterial:
431 return qmlElementName<RuntimeType::PrincipledMaterial>();
432 case RuntimeType::SpecularGlossyMaterial:
433 return qmlElementName<RuntimeType::SpecularGlossyMaterial>();
434 case RuntimeType::CustomMaterial:
435 return qmlElementName<RuntimeType::CustomMaterial>();
436 case RuntimeType::Image2D:
437 return qmlElementName<RuntimeType::Image2D>();
438 case RuntimeType::ImageCube:
439 return qmlElementName<RuntimeType::ImageCube>();
440 case RuntimeType::TextureData:
441 return qmlElementName<RuntimeType::TextureData>();
442 case RuntimeType::Model:
443 return qmlElementName<RuntimeType::Model>();
444 case RuntimeType::OrthographicCamera:
445 return qmlElementName<RuntimeType::OrthographicCamera>();
446 case RuntimeType::PerspectiveCamera:
447 return qmlElementName<RuntimeType::PerspectiveCamera>();
448 case RuntimeType::DirectionalLight:
449 return qmlElementName<RuntimeType::DirectionalLight>();
450 case RuntimeType::PointLight:
451 return qmlElementName<RuntimeType::PointLight>();
452 case RuntimeType::SpotLight:
453 return qmlElementName<RuntimeType::SpotLight>();
454 case RuntimeType::Skeleton:
455 return qmlElementName<RuntimeType::Skeleton>();
456 case RuntimeType::Joint:
457 return qmlElementName<RuntimeType::Joint>();
458 case RuntimeType::Skin:
459 return qmlElementName<RuntimeType::Skin>();
460 case RuntimeType::MorphTarget:
461 return qmlElementName<RuntimeType::MorphTarget>();
463 return "UNKNOWN_TYPE";
591 static constexpr const char *typeNames[] = {
605 constexpr uint nameCount =
sizeof(typeNames)/
sizeof(
const char*);
606 const bool nodeHasName = (node.name.size() > 0);
607 uint nameIdx = qMin(uint(node.nodeType), nameCount);
608 QString name = nodeHasName ? QString::fromUtf8(node.name + typeNames[nameIdx]) : QString::fromLatin1(getQmlElementName(node));
609 QString sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
612 if (
const auto it = g_nodeNameMap->constFind(&node); it != g_nodeNameMap->constEnd())
615 quint64 id = node.id;
617 QString candidate = sanitizedName;
619 if (
const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
620 g_idMap->insert(candidate, &node);
621 g_nodeNameMap->insert(&node, candidate);
625 candidate = QStringLiteral(
"%1%2").arg(sanitizedName).arg(id++);
626 }
while (--attempts);
741 switch (var.metaType().id()) {
742 case QMetaType::QVector2D: {
743 const auto vec2 = qvariant_cast<
QVector2D>(var);
744 return QLatin1String(
"Qt.vector2d(") + QString::number(vec2.x()) + QLatin1String(
", ") + QString::number(vec2.y()) + QLatin1Char(
')');
746 case QMetaType::QVector3D: {
747 const auto vec3 = qvariant_cast<
QVector3D>(var);
748 return QLatin1String(
"Qt.vector3d(") + QString::number(vec3.x()) + QLatin1String(
", ")
749 + QString::number(vec3.y()) + QLatin1String(
", ")
750 + QString::number(vec3.z()) + QLatin1Char(
')');
752 case QMetaType::QVector4D: {
753 const auto vec4 = qvariant_cast<
QVector4D>(var);
754 return QLatin1String(
"Qt.vector4d(") + QString::number(vec4.x()) + QLatin1String(
", ")
755 + QString::number(vec4.y()) + QLatin1String(
", ")
756 + QString::number(vec4.z()) + QLatin1String(
", ")
757 + QString::number(vec4.w()) + QLatin1Char(
')');
759 case QMetaType::QColor: {
760 const auto color = qvariant_cast<QColor>(var);
761 return colorToQml(color);
763 case QMetaType::QQuaternion: {
764 const auto &quat = qvariant_cast<QQuaternion>(var);
765 return QLatin1String(
"Qt.quaternion(") + QString::number(quat.scalar()) + QLatin1String(
", ")
766 + QString::number(quat.x()) + QLatin1String(
", ")
767 + QString::number(quat.y()) + QLatin1String(
", ")
768 + QString::number(quat.z()) + QLatin1Char(
')');
770 case QMetaType::QMatrix4x4: {
771 const auto mat44 = qvariant_cast<QMatrix4x4>(var);
772 return QLatin1String(
"Qt.matrix4x4(")
773 + QString::number(mat44(0, 0)) + u", " + QString::number(mat44(0, 1)) + u", " + QString::number(mat44(0, 2)) + u", " + QString::number(mat44(0, 3)) + u", "
774 + QString::number(mat44(1, 0)) + u", " + QString::number(mat44(1, 1)) + u", " + QString::number(mat44(1, 2)) + u", " + QString::number(mat44(1, 3)) + u", "
775 + QString::number(mat44(2, 0)) + u", " + QString::number(mat44(2, 1)) + u", " + QString::number(mat44(2, 2)) + u", " + QString::number(mat44(2, 3)) + u", "
776 + QString::number(mat44(3, 0)) + u", " + QString::number(mat44(3, 1)) + u", " + QString::number(mat44(3, 2)) + u", " + QString::number(mat44(3, 3)) + u')';
778 case QMetaType::Float:
779 case QMetaType::Double:
781 case QMetaType::Char:
782 case QMetaType::Long:
783 case QMetaType::LongLong:
784 case QMetaType::ULong:
785 case QMetaType::ULongLong:
786 case QMetaType::Bool:
787 return var.toString();
788 case QMetaType::QUrl:
814 const auto meshFolder = getMeshFolder();
815 const auto meshId = QSSGQmlUtilities::getIdForNode(meshNode);
816 const auto meshSourceName = QSSGQmlUtilities::getMeshSourceName(meshId);
817 Q_ASSERT(scene.meshStorage.size() > meshNode.idx);
818 const auto &mesh = scene.meshStorage.at(meshNode.idx);
821 if (!outdir.exists(meshFolder) && !outdir.mkdir(meshFolder)) {
822 qDebug() <<
"Failed to create meshes folder at" << outdir;
826 const QString path = outdir.path() + QDir::separator() + meshSourceName;
828 if (!file.open(QIODevice::WriteOnly)) {
829 return {QString(), QStringLiteral(
"Failed to find mesh at ") + path};
832 if (mesh.save(&file) == 0) {
836 return {meshSourceName, QString()};
844 QString assetPath = output.outdir.isAbsolutePath(texturePath.path()) ? texturePath.toString() : texturePath.path();
845 QFileInfo fi(assetPath);
846 if (fi.isRelative() && !output.sourceDir.isEmpty()) {
847 fi = QFileInfo(output.sourceDir + QChar(u'/') + assetPath);
850 indent(output) << comment() <<
"Source texture path expected: " << getTextureFolder() + texturePath.fileName() <<
"\n";
851 return {QString(), QStringLiteral(
"Failed to find texture at ") + assetPath};
854 const auto mapsFolder = getTextureFolder();
856 if (!output.outdir.exists(mapsFolder) && !output.outdir.mkdir(mapsFolder)) {
857 qDebug() <<
"Failed to create maps folder at" << output.outdir;
861 const QString relpath = mapsFolder + fi.fileName();
862 const auto newfilepath = QString(output.outdir.canonicalPath() + QDir::separator() + relpath);
863 if (!QFile::exists(newfilepath) && !QFile::copy(fi.canonicalFilePath(), newfilepath)) {
864 qDebug() <<
"Failed to copy file from" << fi.canonicalFilePath() <<
"to" << newfilepath;
868 return {relpath, QString()};
873 static const QRegularExpression re(QLatin1String(
"^Qt.[a-z0-9]*\\(([0-9.e\\+\\-, ]*)\\)"));
874 Q_ASSERT(re.isValid());
877 case QMetaType::QVector2D: {
878 QRegularExpressionMatch match = re.match(value);
879 if (match.hasMatch()) {
880 const auto comp = match.captured(1).split(QLatin1Char(
','));
881 if (comp.size() == 2) {
882 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
883 QLatin1String(
".y: ") + comp.at(1).trimmed() };
888 case QMetaType::QVector3D: {
889 QRegularExpressionMatch match = re.match(value);
890 if (match.hasMatch()) {
891 const auto comp = match.captured(1).split(QLatin1Char(
','));
892 if (comp.size() == 3) {
893 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
894 QLatin1String(
".y: ") + comp.at(1).trimmed(),
895 QLatin1String(
".z: ") + comp.at(2).trimmed() };
900 case QMetaType::QVector4D: {
901 QRegularExpressionMatch match = re.match(value);
902 if (match.hasMatch()) {
903 const auto comp = match.captured(1).split(QLatin1Char(
','));
904 if (comp.size() == 4) {
905 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
906 QLatin1String(
".y: ") + comp.at(1).trimmed(),
907 QLatin1String(
".z: ") + comp.at(2).trimmed(),
908 QLatin1String(
".w: ") + comp.at(3).trimmed() };
913 case QMetaType::QQuaternion: {
914 QRegularExpressionMatch match = re.match(value);
915 if (match.hasMatch()) {
916 const auto comp = match.captured(1).split(QLatin1Char(
','));
917 if (comp.size() == 4) {
918 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
919 QLatin1String(
".y: ") + comp.at(1).trimmed(),
920 QLatin1String(
".z: ") + comp.at(2).trimmed(),
921 QLatin1String(
".scalar: ") + comp.at(3).trimmed() };
954 if (property.value.isNull()) {
956 result.notValidReason = QStringLiteral(
"Property value is null");
960 const QVariant &value = property.value;
961 result.name = QString::fromUtf8(property.name);
965 QString valueAsString = builtinQmlType(value);
966 if (valueAsString.size() > 0) {
967 result.value = valueAsString;
969 }
else if (value.metaType().flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) {
970 static const auto qmlEnumString = [](
const QLatin1String &element,
const QString &enumString) {
971 return QStringLiteral(
"%1.%2").arg(element).arg(enumString);
974 QString enumValue = asString(value);
975 if (enumValue.size() > 0) {
976 result.value = qmlEnumString(qmlElementName, enumValue);
980 QByteArray element(getQmlElementName(target));
981 if (element.size() > 0) {
983 QByteArray keysString = flag.me.valueToKeys(
int(flag.value));
984 if (keysString.size() > 0) {
985 keysString.prepend(element +
'.');
986 QByteArray replacement(
" | " + element +
'.');
987 keysString.replace(
'|', replacement);
988 result.value = QString::fromLatin1(keysString);
994 if (list->count > 0) {
995 const QString indentStr = indentString(output);
997 const QString listIndentStr = indentString(output);
1002 for (
int i = 0, end = list->count; i != end; ++i) {
1005 str.append(listIndentStr);
1006 str.append(getIdForNode(*(list->head[i])));
1009 str.append(u'\n' + indentStr + u']');
1016 if (list.count > 0) {
1017 const QString indentStr = indentString(output);
1019 const QString listIndentStr = indentString(output);
1024 char *vptr =
reinterpret_cast<
char *>(list.data);
1025 auto size = list.mt.sizeOf();
1027 for (
int i = 0, end = list.count; i != end; ++i) {
1031 const QVariant var{list.mt,
reinterpret_cast<
void *>(vptr + (size * i))};
1032 QString valueString = builtinQmlType(var);
1033 if (valueString.isEmpty())
1034 valueString = asString(var);
1036 str.append(listIndentStr);
1037 str.append(valueString);
1040 str.append(u'\n' + indentStr + u']');
1045 }
else if (value.metaType().id() == qMetaTypeId<
QSSGSceneDesc::Node *>()) {
1046 if (
const auto node = qvariant_cast<QSSGSceneDesc::Node *>(value)) {
1050 Q_ASSERT(node->id != 0);
1054 if (node->runtimeType == QSSGSceneDesc::Node::RuntimeType::TextureData) {
1055 result.name = QStringLiteral(
"source");
1056 result.value = getIdForNode(*node->scene->root) + QLatin1Char(
'.') + getIdForNode(*node);
1058 result.value = getIdForNode(*node);
1062 }
else if (value.metaType() == QMetaType::fromType<QSSGSceneDesc::Mesh *>()) {
1063 if (
const auto meshNode = qvariant_cast<
const QSSGSceneDesc::Mesh *>(value)) {
1064 Q_ASSERT(meshNode->nodeType == QSSGSceneDesc::Node::Type::Mesh);
1065 Q_ASSERT(meshNode->scene);
1066 const auto &scene = *meshNode->scene;
1067 const auto& [meshSourceName, notValidReason] = meshAssetName(scene, *meshNode, output.outdir);
1068 result.notValidReason = notValidReason;
1069 if (!meshSourceName.isEmpty()) {
1070 result.value = toQuotedString(meshSourceName);
1074 }
else if (value.metaType() == QMetaType::fromType<QUrl>()) {
1075 if (
const auto url = qvariant_cast<QUrl>(value); !url.isEmpty()) {
1078 if (QSSGRenderGraphObject::isTexture(target.runtimeType)) {
1079 const auto& [relpath, notValidReason] = copyTextureAsset(url, output);
1080 result.notValidReason = notValidReason;
1081 if (!relpath.isEmpty()) {
1087 if (!path.isEmpty()) {
1088 result.value = toQuotedString(path);
1092 }
else if (target.runtimeType == QSSGSceneDesc::Material::RuntimeType::CustomMaterial) {
1094 if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Texture *>()) {
1095 if (
const auto texture = qvariant_cast<QSSGSceneDesc::Texture *>(value)) {
1096 Q_ASSERT(QSSGRenderGraphObject::isTexture(texture->runtimeType));
1097 result.value = QLatin1String(
"TextureInput { texture: ") +
1098 getIdForNode(*texture) + QLatin1String(
" }");
1102 }
else if (value.metaType() == QMetaType::fromType<QString>()) {
1104 result.value = toQuotedString(value.toString());
1107 result.notValidReason = QStringLiteral(
"Unsupported value type: ") + QString::fromUtf8(value.metaType().name());
1108 qWarning() << result.notValidReason;
1113 result.expandedProperties = ((output.options & OutputContext::Options::DesignStudioWorkarounds) == OutputContext::Options::DesignStudioWorkarounds)
1114 ? expandComponentsPartially(result.value, value.metaType())
1115 : expandComponents(result.value, value.metaType());
1125 indent(output) << u"id: "_s << getIdForNode(node) << u'\n';
1128 if (node.name.size()) {
1129 const QString objectName = QString::fromLocal8Bit(node.name);
1130 if (!objectName.startsWith(u'*'))
1131 indent(output) << u"objectName: \""_s << node.name << u"\"\n"_s;
1134 const auto &properties = node.properties;
1135 auto it = properties.begin();
1136 const auto end = properties.end();
1137 for (; it != end; ++it) {
1138 const auto &property = *it;
1143 indent(output) <<
"property " << typeName(property->value.metaType()).toByteArray() <<
' ' << result.name << u": "_s << result.value << u'\n';
1145 if (result.expandedProperties.size() > 1) {
1146 for (
const auto &va : result.expandedProperties)
1147 indent(output) << result.name << va << u'\n';
1149 indent(output) << result.name << u": "_s << result.value << u'\n';
1153 QString message = u"Skipped property: "_s + QString::fromUtf8(property->name);
1154 if (!result.notValidReason.isEmpty())
1155 message.append(u", reason: "_s + result.notValidReason);
1156 qDebug() << message;
1173 Q_ASSERT(material.nodeType == QSSGSceneDesc::Model::Type::Material);
1174 if (material.runtimeType == QSSGSceneDesc::Model::RuntimeType::SpecularGlossyMaterial) {
1175 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1176 }
else if (material.runtimeType == Model::RuntimeType::PrincipledMaterial) {
1177 indent(output) << qmlElementName<Material::RuntimeType::PrincipledMaterial>() << blockBegin(output);
1178 }
else if (material.runtimeType == Material::RuntimeType::CustomMaterial) {
1179 indent(output) << qmlElementName<Material::RuntimeType::CustomMaterial>() << blockBegin(output);
1180 }
else if (material.runtimeType == Material::RuntimeType::SpecularGlossyMaterial) {
1181 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1186 writeNodeProperties(material, output);
1250 if (textureData.data.isEmpty())
1253 const auto mapsFolder = getTextureFolder();
1254 const auto id = getIdForNode(textureData);
1255 const QString textureSourceName = getTextureSourceName(id, QString::fromUtf8(textureData.fmt));
1257 const bool isCompressed = ((textureData.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
1260 if (!outdir.exists(mapsFolder) && !outdir.mkdir(mapsFolder))
1263 const auto imagePath = QString(outdir.path() + QDir::separator() + textureSourceName);
1266 QFile file(imagePath);
1267 if (!file.open(QIODevice::WriteOnly)) {
1268 qWarning(
"Failed to open file %s: %s",
1269 qPrintable(file.fileName()), qPrintable(file.errorString()));
1272 file.write(textureData.data);
1276 const auto &texData = textureData.data;
1277 const auto &size = textureData.sz;
1279 image = QImage(
reinterpret_cast<
const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
1280 if (!image.save(imagePath))
1284 return textureSourceName;
1437 auto sortedResources = resources;
1438 std::sort(sortedResources.begin(), sortedResources.end(), [](
const QSSGSceneDesc::Node *a,
const QSSGSceneDesc::Node *b) {
1439 using RType = QSSGSceneDesc::Node::RuntimeType;
1440 if (a->runtimeType == RType::TextureData && b->runtimeType != RType::TextureData)
1442 if (a->runtimeType == RType::ImageCube && (b->runtimeType != RType::TextureData && b->runtimeType != RType::ImageCube))
1444 if (a->runtimeType == RType::Image2D && (b->runtimeType != RType::TextureData && b->runtimeType != RType::Image2D))
1449 for (
const auto &res : std::as_const(sortedResources))
1450 writeQmlForResourceNode(*res, output);
1501 const int duration = qCeil(anim
.length);
1503 const QString animationId = getIdForAnimation(anim.name);
1504 indent(output
) <<
"id: " << animationId <<
"\n";
1505 QString animationName = animationId;
1506 if (!anim.name.isEmpty())
1507 animationName = QString::fromLocal8Bit(anim.name);
1508 indent(output
) <<
"objectName: \"" << animationName <<
"\"\n";
1510 indent(output
) <<
"startFrame: 0\n";
1511 indent(output
) <<
"endFrame: " << duration <<
"\n";
1512 indent(output
) <<
"currentFrame: 0\n";
1516 if (generateTimelineAnimations) {
1517 indent(output
) <<
"enabled: true\n";
1518 indent(output
) <<
"animations: TimelineAnimation {\n";
1521 indent(output
) <<
"duration: " << duration <<
"\n";
1523 indent(output
) <<
"to: " << duration <<
"\n";
1524 indent(output
) <<
"running: true\n";
1525 indent(output
) <<
"loops: Animation.Infinite\n";
1530 for (
const auto &channel : anim.channels) {
1531 QString id = getIdForNode(*channel->target);
1532 QString propertyName = asString(channel->targetProperty);
1534 indent(output) <<
"KeyframeGroup {\n";
1536 QSSGQmlScopedIndent scopedIndent(output);
1537 indent(output) <<
"target: " << id <<
"\n";
1538 indent(output) <<
"property: " << toQuotedString(propertyName) <<
"\n";
1539 if (useBinaryKeyframes && channel->keys.size() != 1) {
1540 const auto animFolder = getAnimationFolder();
1541 const auto animSourceName = getAnimationSourceName(id, propertyName, index);
1542 if (!output.outdir.exists(animFolder) && !output.outdir.mkdir(animFolder)) {
1546 QFile file(output.outdir.path() + QDir::separator() + animSourceName);
1547 if (!file.open(QIODevice::WriteOnly))
1549 QByteArray keyframeData;
1553 generateKeyframeData(*channel, keyframeData);
1554 file.write(keyframeData);
1556 indent(output) <<
"keyframeSource: " << toQuotedString(animSourceName) <<
"\n";
1558 Q_ASSERT(!channel->keys.isEmpty());
1559 for (
const auto &key : channel->keys) {
1560 indent(output) <<
"Keyframe {\n";
1562 QSSGQmlScopedIndent scopedIndent(output);
1563 indent(output) <<
"frame: " << key->time <<
"\n";
1564 indent(output) <<
"value: " << variantToQml(key->getValue()) <<
"\n";
1566 indent(output) << blockEnd(output);
1570 indent(output) << blockEnd(output);
1572 return {animationName, animationId};
1575void writeQml(
const QSSGSceneDesc::Scene &scene, QTextStream &stream,
const QDir &outdir,
const QJsonObject &optionsObject)
1577 static const auto checkBooleanOption = [](
const QLatin1String &optionName,
const QJsonObject &options,
bool defaultValue =
false) {
1578 const auto it = options.constFind(optionName);
1579 const auto end = options.constEnd();
1583 value = it->toObject().value(QLatin1String(
"value"));
1587 return value.toBool(defaultValue);
1590 auto root = scene.root;
1593 QJsonObject options = optionsObject;
1595 if (
auto it = options.constFind(QLatin1String(
"options")), end = options.constEnd(); it != end)
1596 options = it->toObject();
1599 if (checkBooleanOption(QLatin1String(
"expandValueComponents"), options))
1603 if (checkBooleanOption(QLatin1String(
"designStudioWorkarounds"), options))
1606 const bool useBinaryKeyframes = checkBooleanOption(
"useBinaryKeyframes"_L1, options);
1607 const bool generateTimelineAnimations = !checkBooleanOption(
"manualAnimations"_L1, options);
1611 writeImportHeader(output, scene.animations.count() > 0);
1614 writeQml(*root, output);
1616 stream <<
indent() <<
"// Resources\n";
1618 writeQmlForResources(scene.resources, output);
1621 stream <<
indent() <<
"// Nodes:\n";
1622 for (
const auto &cld : root->children)
1623 writeQmlForNode(*cld, output);
1626 qsizetype animId = 0;
1628 stream <<
indent() <<
"// Animations:\n";
1629 QList<QPair<QString, QString>> animationMap;
1630 for (
const auto &cld : scene.animations) {
1631 QSSGQmlScopedIndent scopedIndent(output);
1632 auto mapValues = writeQmlForAnimation(*cld, animId++, output, useBinaryKeyframes, generateTimelineAnimations);
1633 animationMap.append(mapValues);
1634 indent(output) << blockEnd(output);
1637 if (!generateTimelineAnimations) {
1640 stream <<
indent() <<
"// An exported mapping of Timelines (--manualAnimations)\n";
1641 stream <<
indent() <<
"property var timelineMap: {\n";
1643 for (
const auto &mapValues : animationMap) {
1644 QSSGQmlScopedIndent scopedIndent(output);
1645 indent(output) <<
"\"" << mapValues.first <<
"\": " << mapValues.second <<
",\n";
1648 stream <<
indent() <<
"// A simple list of Timelines (--manualAnimations)\n";
1649 stream <<
indent() <<
"property var timelineList: [\n";
1650 for (
const auto &mapValues : animationMap) {
1651 QSSGQmlScopedIndent scopedIndent(output);
1652 indent(output) << mapValues.second <<
",\n";
1664#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1665 auto timeline =
new QQuickTimeline(parent);
1666 auto timelineKeyframeGroup = timeline->keyframeGroups();
1667 for (
const auto &channel : anim.channels) {
1668 auto keyframeGroup =
new QQuickKeyframeGroup(timeline);
1669 keyframeGroup->setTargetObject(channel->target->obj);
1670 keyframeGroup->setProperty(asString(channel->targetProperty));
1672 Q_ASSERT(!channel->keys.isEmpty());
1673 if (useBinaryKeyframes) {
1674 QByteArray keyframeData;
1675 generateKeyframeData(*channel, keyframeData);
1677 keyframeGroup->setKeyframeData(keyframeData);
1679 auto keyframes = keyframeGroup->keyframes();
1680 for (
const auto &key : channel->keys) {
1681 auto keyframe =
new QQuickKeyframe(keyframeGroup);
1682 keyframe->setFrame(key->time);
1683 keyframe->setValue(key->getValue());
1684 keyframes.append(&keyframes, keyframe);
1687 (qobject_cast<QQmlParserStatus *>(keyframeGroup))->componentComplete();
1688 timelineKeyframeGroup.append(&timelineKeyframeGroup, keyframeGroup);
1690 timeline->setEndFrame(anim.length);
1691 timeline->setEnabled(isEnabled);
1693 auto timelineAnimation =
new QQuickTimelineAnimation(timeline);
1694 timelineAnimation->setObjectName(anim.name);
1695 timelineAnimation->setDuration(
int(anim.length));
1696 timelineAnimation->setFrom(0.0f);
1697 timelineAnimation->setTo(anim.length);
1698 timelineAnimation->setLoops(QQuickTimelineAnimation::Infinite);
1699 timelineAnimation->setTargetObject(timeline);
1701 (qobject_cast<QQmlParserStatus *>(timeline))->componentComplete();
1703 timelineAnimation->setRunning(
true);
1708 Q_UNUSED(useBinaryKeyframes)