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";
604 static constexpr const char *typeNames[] = {
618 constexpr uint nameCount =
sizeof(typeNames)/
sizeof(
const char*);
619 const bool nodeHasName = (node.name.size() > 0);
620 uint nameIdx = qMin(uint(node.nodeType), nameCount);
621 QString name = nodeHasName ? QString::fromUtf8(node.name + typeNames[nameIdx]) : QString::fromLatin1(getQmlElementName(node));
622 QString sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
625 if (
const auto it = g_nodeNameMap->constFind(&node); it != g_nodeNameMap->constEnd())
628 quint64 id = node.id;
630 QString candidate = sanitizedName;
632 if (
const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
633 g_idMap->insert(candidate, &node);
634 g_nodeNameMap->insert(&node, candidate);
638 candidate = QStringLiteral(
"%1%2").arg(sanitizedName).arg(id++);
639 }
while (--attempts);
754 switch (var.metaType().id()) {
755 case QMetaType::QVector2D: {
756 const auto vec2 = qvariant_cast<
QVector2D>(var);
757 return QLatin1String(
"Qt.vector2d(") + QString::number(vec2.x()) + QLatin1String(
", ") + QString::number(vec2.y()) + QLatin1Char(
')');
759 case QMetaType::QVector3D: {
760 const auto vec3 = qvariant_cast<
QVector3D>(var);
761 return QLatin1String(
"Qt.vector3d(") + QString::number(vec3.x()) + QLatin1String(
", ")
762 + QString::number(vec3.y()) + QLatin1String(
", ")
763 + QString::number(vec3.z()) + QLatin1Char(
')');
765 case QMetaType::QVector4D: {
766 const auto vec4 = qvariant_cast<
QVector4D>(var);
767 return QLatin1String(
"Qt.vector4d(") + QString::number(vec4.x()) + QLatin1String(
", ")
768 + QString::number(vec4.y()) + QLatin1String(
", ")
769 + QString::number(vec4.z()) + QLatin1String(
", ")
770 + QString::number(vec4.w()) + QLatin1Char(
')');
772 case QMetaType::QColor: {
773 const auto color = qvariant_cast<QColor>(var);
774 return colorToQml(color);
776 case QMetaType::QQuaternion: {
777 const auto &quat = qvariant_cast<QQuaternion>(var);
778 return QLatin1String(
"Qt.quaternion(") + QString::number(quat.scalar()) + QLatin1String(
", ")
779 + QString::number(quat.x()) + QLatin1String(
", ")
780 + QString::number(quat.y()) + QLatin1String(
", ")
781 + QString::number(quat.z()) + QLatin1Char(
')');
783 case QMetaType::QMatrix4x4: {
784 const auto mat44 = qvariant_cast<QMatrix4x4>(var);
785 return QLatin1String(
"Qt.matrix4x4(")
786 + QString::number(mat44(0, 0)) + u", " + QString::number(mat44(0, 1)) + u", " + QString::number(mat44(0, 2)) + u", " + QString::number(mat44(0, 3)) + u", "
787 + QString::number(mat44(1, 0)) + u", " + QString::number(mat44(1, 1)) + u", " + QString::number(mat44(1, 2)) + u", " + QString::number(mat44(1, 3)) + u", "
788 + QString::number(mat44(2, 0)) + u", " + QString::number(mat44(2, 1)) + u", " + QString::number(mat44(2, 2)) + u", " + QString::number(mat44(2, 3)) + u", "
789 + QString::number(mat44(3, 0)) + u", " + QString::number(mat44(3, 1)) + u", " + QString::number(mat44(3, 2)) + u", " + QString::number(mat44(3, 3)) + u')';
791 case QMetaType::Float:
792 case QMetaType::Double:
794 case QMetaType::Char:
795 case QMetaType::Long:
796 case QMetaType::LongLong:
797 case QMetaType::ULong:
798 case QMetaType::ULongLong:
799 case QMetaType::Bool:
800 return var.toString();
801 case QMetaType::QUrl:
827 const auto meshFolder = getMeshFolder();
828 const auto meshId = QSSGQmlUtilities::getIdForNode(meshNode);
829 const auto meshSourceName = QSSGQmlUtilities::getMeshSourceName(meshId);
830 Q_ASSERT(scene.meshStorage.size() > meshNode.idx);
831 const auto &mesh = scene.meshStorage.at(meshNode.idx);
834 if (!outdir.exists(meshFolder) && !outdir.mkdir(meshFolder)) {
835 qDebug() <<
"Failed to create meshes folder at" << outdir;
839 const QString path = outdir.path() + QDir::separator() + meshSourceName;
841 if (!file.open(QIODevice::WriteOnly)) {
842 return {QString(), QStringLiteral(
"Failed to find mesh at ") + path};
845 if (mesh.save(&file) == 0) {
849 return {meshSourceName, QString()};
857 QString assetPath = output.outdir.isAbsolutePath(texturePath.path()) ? texturePath.toString() : texturePath.path();
858 QFileInfo fi(assetPath);
859 if (fi.isRelative() && !output.sourceDir.isEmpty()) {
860 fi = QFileInfo(output.sourceDir + QChar(u'/') + assetPath);
863 indent(output) << comment() <<
"Source texture path expected: " << getTextureFolder() + texturePath.fileName() <<
"\n";
864 return {QString(), QStringLiteral(
"Failed to find texture at ") + assetPath};
867 const auto mapsFolder = getTextureFolder();
869 if (!output.outdir.exists(mapsFolder) && !output.outdir.mkdir(mapsFolder)) {
870 qDebug() <<
"Failed to create maps folder at" << output.outdir;
874 const QString relpath = mapsFolder + fi.fileName();
875 const auto newfilepath = QString(output.outdir.canonicalPath() + QDir::separator() + relpath);
876 if (!QFile::exists(newfilepath) && !QFile::copy(fi.canonicalFilePath(), newfilepath)) {
877 qDebug() <<
"Failed to copy file from" << fi.canonicalFilePath() <<
"to" << newfilepath;
881 return {relpath, QString()};
886 static const QRegularExpression re(QLatin1String(
"^Qt.[a-z0-9]*\\(([0-9.e\\+\\-, ]*)\\)"));
887 Q_ASSERT(re.isValid());
890 case QMetaType::QVector2D: {
891 QRegularExpressionMatch match = re.match(value);
892 if (match.hasMatch()) {
893 const auto comp = match.captured(1).split(QLatin1Char(
','));
894 if (comp.size() == 2) {
895 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
896 QLatin1String(
".y: ") + comp.at(1).trimmed() };
901 case QMetaType::QVector3D: {
902 QRegularExpressionMatch match = re.match(value);
903 if (match.hasMatch()) {
904 const auto comp = match.captured(1).split(QLatin1Char(
','));
905 if (comp.size() == 3) {
906 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
907 QLatin1String(
".y: ") + comp.at(1).trimmed(),
908 QLatin1String(
".z: ") + comp.at(2).trimmed() };
913 case QMetaType::QVector4D: {
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(
".w: ") + comp.at(3).trimmed() };
926 case QMetaType::QQuaternion: {
927 QRegularExpressionMatch match = re.match(value);
928 if (match.hasMatch()) {
929 const auto comp = match.captured(1).split(QLatin1Char(
','));
930 if (comp.size() == 4) {
931 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
932 QLatin1String(
".y: ") + comp.at(1).trimmed(),
933 QLatin1String(
".z: ") + comp.at(2).trimmed(),
934 QLatin1String(
".scalar: ") + comp.at(3).trimmed() };
967 if (property.value.isNull()) {
969 result.notValidReason = QStringLiteral(
"Property value is null");
973 const QVariant &value = property.value;
974 result.name = QString::fromUtf8(property.name);
978 QString valueAsString = builtinQmlType(value);
979 if (valueAsString.size() > 0) {
980 result.value = valueAsString;
982 }
else if (value.metaType().flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) {
983 static const auto qmlEnumString = [](
const QLatin1String &element,
const QString &enumString) {
984 return QStringLiteral(
"%1.%2").arg(element).arg(enumString);
987 QString enumValue = asString(value);
988 if (enumValue.size() > 0) {
989 result.value = qmlEnumString(qmlElementName, enumValue);
993 QByteArray element(getQmlElementName(target));
994 if (element.size() > 0) {
996 QByteArray keysString = flag.me.valueToKeys(
int(flag.value));
997 if (keysString.size() > 0) {
998 keysString.prepend(element +
'.');
999 QByteArray replacement(
" | " + element +
'.');
1000 keysString.replace(
'|', replacement);
1001 result.value = QString::fromLatin1(keysString);
1007 if (list->count > 0) {
1008 const QString indentStr = indentString(output);
1010 const QString listIndentStr = indentString(output);
1015 for (
int i = 0, end = list->count; i != end; ++i) {
1018 str.append(listIndentStr);
1019 str.append(getIdForNode(*(list->head[i])));
1022 str.append(u'\n' + indentStr + u']');
1029 if (list.count > 0) {
1030 const QString indentStr = indentString(output);
1032 const QString listIndentStr = indentString(output);
1037 char *vptr =
reinterpret_cast<
char *>(list.data);
1038 auto size = list.mt.sizeOf();
1040 for (
int i = 0, end = list.count; i != end; ++i) {
1044 const QVariant var{list.mt,
reinterpret_cast<
void *>(vptr + (size * i))};
1045 QString valueString = builtinQmlType(var);
1046 if (valueString.isEmpty())
1047 valueString = asString(var);
1049 str.append(listIndentStr);
1050 str.append(valueString);
1053 str.append(u'\n' + indentStr + u']');
1058 }
else if (value.metaType().id() == qMetaTypeId<
QSSGSceneDesc::Node *>()) {
1059 if (
const auto node = qvariant_cast<QSSGSceneDesc::Node *>(value)) {
1063 Q_ASSERT(node->id != 0);
1067 if (node->runtimeType == QSSGSceneDesc::Node::RuntimeType::TextureData) {
1068 result.name = QStringLiteral(
"source");
1069 result.value = getIdForNode(*node->scene->root) + QLatin1Char(
'.') + getIdForNode(*node);
1071 result.value = getIdForNode(*node);
1075 }
else if (value.metaType() == QMetaType::fromType<QSSGSceneDesc::Mesh *>()) {
1076 if (
const auto meshNode = qvariant_cast<
const QSSGSceneDesc::Mesh *>(value)) {
1077 Q_ASSERT(meshNode->nodeType == QSSGSceneDesc::Node::Type::Mesh);
1078 Q_ASSERT(meshNode->scene);
1079 const auto &scene = *meshNode->scene;
1080 const auto& [meshSourceName, notValidReason] = meshAssetName(scene, *meshNode, output.outdir);
1081 result.notValidReason = notValidReason;
1082 if (!meshSourceName.isEmpty()) {
1083 result.value = toQuotedString(meshSourceName);
1087 }
else if (value.metaType() == QMetaType::fromType<QUrl>()) {
1088 if (
const auto url = qvariant_cast<QUrl>(value); !url.isEmpty()) {
1091 if (QSSGRenderGraphObject::isTexture(target.runtimeType)) {
1092 const auto& [relpath, notValidReason] = copyTextureAsset(url, output);
1093 result.notValidReason = notValidReason;
1094 if (!relpath.isEmpty()) {
1100 if (!path.isEmpty()) {
1101 result.value = toQuotedString(path);
1105 }
else if (target.runtimeType == QSSGSceneDesc::Material::RuntimeType::CustomMaterial) {
1107 if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Texture *>()) {
1108 if (
const auto texture = qvariant_cast<QSSGSceneDesc::Texture *>(value)) {
1109 Q_ASSERT(QSSGRenderGraphObject::isTexture(texture->runtimeType));
1110 result.value = QLatin1String(
"TextureInput { texture: ") +
1111 getIdForNode(*texture) + QLatin1String(
" }");
1115 }
else if (value.metaType() == QMetaType::fromType<QString>()) {
1117 result.value = toQuotedString(value.toString());
1120 result.notValidReason = QStringLiteral(
"Unsupported value type: ") + QString::fromUtf8(value.metaType().name());
1121 qWarning() << result.notValidReason;
1126 result.expandedProperties = ((output.options & OutputContext::Options::DesignStudioWorkarounds) == OutputContext::Options::DesignStudioWorkarounds)
1127 ? expandComponentsPartially(result.value, value.metaType())
1128 : expandComponents(result.value, value.metaType());
1138 indent(output) << u"id: "_s << getIdForNode(node) << u'\n';
1141 if (node.name.size()) {
1142 const QString objectName = QString::fromLocal8Bit(node.name);
1143 if (!objectName.startsWith(u'*'))
1144 indent(output) << u"objectName: \""_s << node.name << u"\"\n"_s;
1147 const auto &properties = node.properties;
1148 auto it = properties.begin();
1149 const auto end = properties.end();
1150 for (; it != end; ++it) {
1151 const auto &property = *it;
1156 indent(output) <<
"property " << typeName(property->value.metaType()).toByteArray() <<
' ' << result.name << u": "_s << result.value << u'\n';
1158 if (result.expandedProperties.size() > 1) {
1159 for (
const auto &va : result.expandedProperties)
1160 indent(output) << result.name << va << u'\n';
1162 indent(output) << result.name << u": "_s << result.value << u'\n';
1166 QString message = u"Skipped property: "_s + QString::fromUtf8(property->name);
1167 if (!result.notValidReason.isEmpty())
1168 message.append(u", reason: "_s + result.notValidReason);
1169 qDebug() << message;
1186 Q_ASSERT(material.nodeType == QSSGSceneDesc::Model::Type::Material);
1187 if (material.runtimeType == QSSGSceneDesc::Model::RuntimeType::SpecularGlossyMaterial) {
1188 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1189 }
else if (material.runtimeType == Model::RuntimeType::PrincipledMaterial) {
1190 indent(output) << qmlElementName<Material::RuntimeType::PrincipledMaterial>() << blockBegin(output);
1191 }
else if (material.runtimeType == Material::RuntimeType::CustomMaterial) {
1192 indent(output) << qmlElementName<Material::RuntimeType::CustomMaterial>() << blockBegin(output);
1193 }
else if (material.runtimeType == Material::RuntimeType::SpecularGlossyMaterial) {
1194 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1199 writeNodeProperties(material, output);
1263 if (textureData.data.isEmpty())
1266 const auto mapsFolder = getTextureFolder();
1267 const auto id = getIdForNode(textureData);
1268 const QString textureSourceName = getTextureSourceName(id, QString::fromUtf8(textureData.fmt));
1270 const bool isCompressed = ((textureData.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
1273 if (!outdir.exists(mapsFolder) && !outdir.mkdir(mapsFolder))
1276 const auto imagePath = QString(outdir.path() + QDir::separator() + textureSourceName);
1279 QFile file(imagePath);
1280 if (!file.open(QIODevice::WriteOnly)) {
1281 qWarning(
"Failed to open file %s: %s",
1282 qPrintable(file.fileName()), qPrintable(file.errorString()));
1285 file.write(textureData.data);
1289 const auto &texData = textureData.data;
1290 const auto &size = textureData.sz;
1292 image = QImage(
reinterpret_cast<
const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
1293 if (!image.save(imagePath))
1297 return textureSourceName;
1450 auto sortedResources = resources;
1451 std::sort(sortedResources.begin(), sortedResources.end(), [](
const QSSGSceneDesc::Node *a,
const QSSGSceneDesc::Node *b) {
1452 using RType = QSSGSceneDesc::Node::RuntimeType;
1453 if (a->runtimeType == RType::TextureData && b->runtimeType != RType::TextureData)
1455 if (a->runtimeType == RType::ImageCube && (b->runtimeType != RType::TextureData && b->runtimeType != RType::ImageCube))
1457 if (a->runtimeType == RType::Image2D && (b->runtimeType != RType::TextureData && b->runtimeType != RType::Image2D))
1462 for (
const auto &res : std::as_const(sortedResources))
1463 writeQmlForResourceNode(*res, output);
1514 const int duration = qCeil(anim
.length);
1516 const QString animationId = getIdForAnimation(anim.name);
1517 indent(output
) <<
"id: " << animationId <<
"\n";
1518 QString animationName = animationId;
1519 if (!anim.name.isEmpty())
1520 animationName = QString::fromLocal8Bit(anim.name);
1521 indent(output
) <<
"objectName: \"" << animationName <<
"\"\n";
1523 indent(output
) <<
"startFrame: 0\n";
1524 indent(output
) <<
"endFrame: " << duration <<
"\n";
1525 indent(output
) <<
"currentFrame: 0\n";
1529 if (generateTimelineAnimations) {
1530 indent(output
) <<
"enabled: true\n";
1531 indent(output
) <<
"animations: TimelineAnimation {\n";
1534 indent(output
) <<
"duration: " << duration <<
"\n";
1536 indent(output
) <<
"to: " << duration <<
"\n";
1537 indent(output
) <<
"running: true\n";
1538 indent(output
) <<
"loops: Animation.Infinite\n";
1543 for (
const auto &channel : anim.channels) {
1544 QString id = getIdForNode(*channel->target);
1545 QString propertyName = asString(channel->targetProperty);
1547 indent(output) <<
"KeyframeGroup {\n";
1549 QSSGQmlScopedIndent scopedIndent(output);
1550 indent(output) <<
"target: " << id <<
"\n";
1551 indent(output) <<
"property: " << toQuotedString(propertyName) <<
"\n";
1552 if (useBinaryKeyframes && channel->keys.size() != 1) {
1553 const auto animFolder = getAnimationFolder();
1554 const auto animSourceName = getAnimationSourceName(id, propertyName, index);
1555 if (!output.outdir.exists(animFolder) && !output.outdir.mkdir(animFolder)) {
1559 QFile file(output.outdir.path() + QDir::separator() + animSourceName);
1560 if (!file.open(QIODevice::WriteOnly))
1562 QByteArray keyframeData;
1566 generateKeyframeData(*channel, keyframeData);
1567 file.write(keyframeData);
1569 indent(output) <<
"keyframeSource: " << toQuotedString(animSourceName) <<
"\n";
1571 Q_ASSERT(!channel->keys.isEmpty());
1572 for (
const auto &key : std::as_const(channel->keys)) {
1573 indent(output) <<
"Keyframe {\n";
1575 QSSGQmlScopedIndent scopedIndent(output);
1576 indent(output) <<
"frame: " << key->time <<
"\n";
1577 indent(output) <<
"value: " << variantToQml(key->getValue()) <<
"\n";
1579 indent(output) << blockEnd(output);
1583 indent(output) << blockEnd(output);
1585 return {animationName, animationId};
1588void writeQml(
const QSSGSceneDesc::Scene &scene, QTextStream &stream,
const QDir &outdir,
const QJsonObject &optionsObject)
1590 static const auto checkBooleanOption = [](
const QLatin1String &optionName,
const QJsonObject &options,
bool defaultValue =
false) {
1591 const auto it = options.constFind(optionName);
1592 const auto end = options.constEnd();
1596 value = it->toObject().value(QLatin1String(
"value"));
1600 return value.toBool(defaultValue);
1603 auto root = scene.root;
1608 QJsonObject options = optionsObject;
1610 if (
auto it = options.constFind(QLatin1String(
"options")), end = options.constEnd(); it != end)
1611 options = it->toObject();
1614 if (checkBooleanOption(QLatin1String(
"expandValueComponents"), options))
1618 if (checkBooleanOption(QLatin1String(
"designStudioWorkarounds"), options))
1621 const bool useBinaryKeyframes = checkBooleanOption(
"useBinaryKeyframes"_L1, options);
1622 const bool generateTimelineAnimations = !checkBooleanOption(
"manualAnimations"_L1, options);
1626 writeImportHeader(output, scene.animations.count() > 0);
1629 writeQml(*root, output);
1631 stream <<
indent() <<
"// Resources\n";
1633 writeQmlForResources(scene.resources, output);
1636 stream <<
indent() <<
"// Nodes:\n";
1637 for (
const auto &cld : std::as_const(root->children))
1638 writeQmlForNode(*cld, output);
1641 qsizetype animId = 0;
1643 stream <<
indent() <<
"// Animations:\n";
1644 QList<QPair<QString, QString>> animationMap;
1645 for (
const auto &cld : scene.animations) {
1646 QSSGQmlScopedIndent scopedIndent(output);
1647 auto mapValues = writeQmlForAnimation(*cld, animId++, output, useBinaryKeyframes, generateTimelineAnimations);
1648 animationMap.append(mapValues);
1649 indent(output) << blockEnd(output);
1652 if (!generateTimelineAnimations) {
1655 stream <<
indent() <<
"// An exported mapping of Timelines (--manualAnimations)\n";
1656 stream <<
indent() <<
"property var timelineMap: {\n";
1658 for (
const auto &mapValues : animationMap) {
1659 QSSGQmlScopedIndent scopedIndent(output);
1660 indent(output) <<
"\"" << mapValues.first <<
"\": " << mapValues.second <<
",\n";
1663 stream <<
indent() <<
"// A simple list of Timelines (--manualAnimations)\n";
1664 stream <<
indent() <<
"property var timelineList: [\n";
1665 for (
const auto &mapValues : animationMap) {
1666 QSSGQmlScopedIndent scopedIndent(output);
1667 indent(output) << mapValues.second <<
",\n";
1679#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1680 auto timeline =
new QQuickTimeline(parent);
1681 auto timelineKeyframeGroup = timeline->keyframeGroups();
1682 for (
const auto &channel : anim.channels) {
1683 auto keyframeGroup =
new QQuickKeyframeGroup(timeline);
1684 keyframeGroup->setTargetObject(channel->target->obj);
1685 keyframeGroup->setProperty(asString(channel->targetProperty));
1687 Q_ASSERT(!channel->keys.isEmpty());
1688 if (useBinaryKeyframes) {
1689 QByteArray keyframeData;
1690 generateKeyframeData(*channel, keyframeData);
1692 keyframeGroup->setKeyframeData(keyframeData);
1694 auto keyframes = keyframeGroup->keyframes();
1695 for (
const auto &key : std::as_const(channel->keys)) {
1696 auto keyframe =
new QQuickKeyframe(keyframeGroup);
1697 keyframe->setFrame(key->time);
1698 keyframe->setValue(key->getValue());
1699 keyframes.append(&keyframes, keyframe);
1702 (qobject_cast<QQmlParserStatus *>(keyframeGroup))->componentComplete();
1703 timelineKeyframeGroup.append(&timelineKeyframeGroup, keyframeGroup);
1705 timeline->setEndFrame(anim.length);
1706 timeline->setEnabled(isEnabled);
1708 auto timelineAnimation =
new QQuickTimelineAnimation(timeline);
1709 timelineAnimation->setObjectName(anim.name);
1710 timelineAnimation->setDuration(
int(anim.length));
1711 timelineAnimation->setFrom(0.0f);
1712 timelineAnimation->setTo(anim.length);
1713 timelineAnimation->setLoops(QQuickTimelineAnimation::Infinite);
1714 timelineAnimation->setTargetObject(timeline);
1716 (qobject_cast<QQmlParserStatus *>(timeline))->componentComplete();
1718 timelineAnimation->setRunning(
true);
1723 Q_UNUSED(useBinaryKeyframes)