121 if (!idCopy.isEmpty() && idCopy.at(0).isNumber())
122 idCopy.prepend(QStringLiteral(
"node"));
125 if (idCopy.startsWith(QChar::fromLatin1(
'#')))
129 static QRegularExpression regExp(QStringLiteral(
"\\W"));
130 idCopy.replace(regExp, QStringLiteral(
"_"));
134 if (!idCopy.isEmpty() && idCopy[0].isUpper()) {
137 int len = idCopy.length();
138 while (i < len && idCopy[i].isUpper()) {
139 idCopy[i] = idCopy[i].toLower();
145 static QSet<QByteArray> keywords {
241 if (keywords.contains(idCopy.toUtf8())) {
242 idCopy += QStringLiteral(
"_");
246 if (idCopy.isEmpty())
247 idCopy = QStringLiteral(
"node");
424 using RuntimeType = QSSGSceneDesc::Node::RuntimeType;
425 switch (node.runtimeType) {
426 case RuntimeType::Node:
427 return qmlElementName<RuntimeType::Node>();
428 case RuntimeType::PrincipledMaterial:
429 return qmlElementName<RuntimeType::PrincipledMaterial>();
430 case RuntimeType::SpecularGlossyMaterial:
431 return qmlElementName<RuntimeType::SpecularGlossyMaterial>();
432 case RuntimeType::CustomMaterial:
433 return qmlElementName<RuntimeType::CustomMaterial>();
434 case RuntimeType::Image2D:
435 return qmlElementName<RuntimeType::Image2D>();
436 case RuntimeType::ImageCube:
437 return qmlElementName<RuntimeType::ImageCube>();
438 case RuntimeType::TextureData:
439 return qmlElementName<RuntimeType::TextureData>();
440 case RuntimeType::Model:
441 return qmlElementName<RuntimeType::Model>();
442 case RuntimeType::OrthographicCamera:
443 return qmlElementName<RuntimeType::OrthographicCamera>();
444 case RuntimeType::PerspectiveCamera:
445 return qmlElementName<RuntimeType::PerspectiveCamera>();
446 case RuntimeType::DirectionalLight:
447 return qmlElementName<RuntimeType::DirectionalLight>();
448 case RuntimeType::PointLight:
449 return qmlElementName<RuntimeType::PointLight>();
450 case RuntimeType::SpotLight:
451 return qmlElementName<RuntimeType::SpotLight>();
452 case RuntimeType::Skeleton:
453 return qmlElementName<RuntimeType::Skeleton>();
454 case RuntimeType::Joint:
455 return qmlElementName<RuntimeType::Joint>();
456 case RuntimeType::Skin:
457 return qmlElementName<RuntimeType::Skin>();
458 case RuntimeType::MorphTarget:
459 return qmlElementName<RuntimeType::MorphTarget>();
461 return "UNKNOWN_TYPE";
589 static constexpr const char *typeNames[] = {
603 constexpr uint nameCount =
sizeof(typeNames)/
sizeof(
const char*);
604 const bool nodeHasName = (node.name.size() > 0);
605 uint nameIdx = qMin(uint(node.nodeType), nameCount);
606 QString name = nodeHasName ? QString::fromUtf8(node.name + typeNames[nameIdx]) : QString::fromLatin1(getQmlElementName(node));
607 QString sanitizedName = QSSGQmlUtilities::sanitizeQmlId(name);
610 if (
const auto it = g_nodeNameMap->constFind(&node); it != g_nodeNameMap->constEnd())
613 quint64 id = node.id;
615 QString candidate = sanitizedName;
617 if (
const auto it = g_idMap->constFind(candidate); it == g_idMap->constEnd()) {
618 g_idMap->insert(candidate, &node);
619 g_nodeNameMap->insert(&node, candidate);
623 candidate = QStringLiteral(
"%1%2").arg(sanitizedName).arg(id++);
624 }
while (--attempts);
739 switch (var.metaType().id()) {
740 case QMetaType::QVector2D: {
741 const auto vec2 = qvariant_cast<
QVector2D>(var);
742 return QLatin1String(
"Qt.vector2d(") + QString::number(vec2.x()) + QLatin1String(
", ") + QString::number(vec2.y()) + QLatin1Char(
')');
744 case QMetaType::QVector3D: {
745 const auto vec3 = qvariant_cast<
QVector3D>(var);
746 return QLatin1String(
"Qt.vector3d(") + QString::number(vec3.x()) + QLatin1String(
", ")
747 + QString::number(vec3.y()) + QLatin1String(
", ")
748 + QString::number(vec3.z()) + QLatin1Char(
')');
750 case QMetaType::QVector4D: {
751 const auto vec4 = qvariant_cast<
QVector4D>(var);
752 return QLatin1String(
"Qt.vector4d(") + QString::number(vec4.x()) + QLatin1String(
", ")
753 + QString::number(vec4.y()) + QLatin1String(
", ")
754 + QString::number(vec4.z()) + QLatin1String(
", ")
755 + QString::number(vec4.w()) + QLatin1Char(
')');
757 case QMetaType::QColor: {
758 const auto color = qvariant_cast<QColor>(var);
759 return colorToQml(color);
761 case QMetaType::QQuaternion: {
762 const auto &quat = qvariant_cast<QQuaternion>(var);
763 return QLatin1String(
"Qt.quaternion(") + QString::number(quat.scalar()) + QLatin1String(
", ")
764 + QString::number(quat.x()) + QLatin1String(
", ")
765 + QString::number(quat.y()) + QLatin1String(
", ")
766 + QString::number(quat.z()) + QLatin1Char(
')');
768 case QMetaType::QMatrix4x4: {
769 const auto mat44 = qvariant_cast<QMatrix4x4>(var);
770 return QLatin1String(
"Qt.matrix4x4(")
771 + QString::number(mat44(0, 0)) + u", " + QString::number(mat44(0, 1)) + u", " + QString::number(mat44(0, 2)) + u", " + QString::number(mat44(0, 3)) + u", "
772 + QString::number(mat44(1, 0)) + u", " + QString::number(mat44(1, 1)) + u", " + QString::number(mat44(1, 2)) + u", " + QString::number(mat44(1, 3)) + u", "
773 + QString::number(mat44(2, 0)) + u", " + QString::number(mat44(2, 1)) + u", " + QString::number(mat44(2, 2)) + u", " + QString::number(mat44(2, 3)) + u", "
774 + QString::number(mat44(3, 0)) + u", " + QString::number(mat44(3, 1)) + u", " + QString::number(mat44(3, 2)) + u", " + QString::number(mat44(3, 3)) + u')';
776 case QMetaType::Float:
777 case QMetaType::Double:
779 case QMetaType::Char:
780 case QMetaType::Long:
781 case QMetaType::LongLong:
782 case QMetaType::ULong:
783 case QMetaType::ULongLong:
784 case QMetaType::Bool:
785 return var.toString();
786 case QMetaType::QUrl:
812 const auto meshFolder = getMeshFolder();
813 const auto meshId = QSSGQmlUtilities::getIdForNode(meshNode);
814 const auto meshSourceName = QSSGQmlUtilities::getMeshSourceName(meshId);
815 Q_ASSERT(scene.meshStorage.size() > meshNode.idx);
816 const auto &mesh = scene.meshStorage.at(meshNode.idx);
819 if (!outdir.exists(meshFolder) && !outdir.mkdir(meshFolder)) {
820 qDebug() <<
"Failed to create meshes folder at" << outdir;
824 const QString path = outdir.path() + QDir::separator() + meshSourceName;
826 if (!file.open(QIODevice::WriteOnly)) {
827 return {QString(), QStringLiteral(
"Failed to find mesh at ") + path};
830 if (mesh.save(&file) == 0) {
834 return {meshSourceName, QString()};
842 QString assetPath = output.outdir.isAbsolutePath(texturePath.path()) ? texturePath.toString() : texturePath.path();
843 QFileInfo fi(assetPath);
844 if (fi.isRelative() && !output.sourceDir.isEmpty()) {
845 fi = QFileInfo(output.sourceDir + QChar(u'/') + assetPath);
848 indent(output) << comment() <<
"Source texture path expected: " << getTextureFolder() + texturePath.fileName() <<
"\n";
849 return {QString(), QStringLiteral(
"Failed to find texture at ") + assetPath};
852 const auto mapsFolder = getTextureFolder();
854 if (!output.outdir.exists(mapsFolder) && !output.outdir.mkdir(mapsFolder)) {
855 qDebug() <<
"Failed to create maps folder at" << output.outdir;
859 const QString relpath = mapsFolder + fi.fileName();
860 const auto newfilepath = QString(output.outdir.canonicalPath() + QDir::separator() + relpath);
861 if (!QFile::exists(newfilepath) && !QFile::copy(fi.canonicalFilePath(), newfilepath)) {
862 qDebug() <<
"Failed to copy file from" << fi.canonicalFilePath() <<
"to" << newfilepath;
866 return {relpath, QString()};
871 static const QRegularExpression re(QLatin1String(
"^Qt.[a-z0-9]*\\(([0-9.e\\+\\-, ]*)\\)"));
872 Q_ASSERT(re.isValid());
875 case QMetaType::QVector2D: {
876 QRegularExpressionMatch match = re.match(value);
877 if (match.hasMatch()) {
878 const auto comp = match.captured(1).split(QLatin1Char(
','));
879 if (comp.size() == 2) {
880 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
881 QLatin1String(
".y: ") + comp.at(1).trimmed() };
886 case QMetaType::QVector3D: {
887 QRegularExpressionMatch match = re.match(value);
888 if (match.hasMatch()) {
889 const auto comp = match.captured(1).split(QLatin1Char(
','));
890 if (comp.size() == 3) {
891 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
892 QLatin1String(
".y: ") + comp.at(1).trimmed(),
893 QLatin1String(
".z: ") + comp.at(2).trimmed() };
898 case QMetaType::QVector4D: {
899 QRegularExpressionMatch match = re.match(value);
900 if (match.hasMatch()) {
901 const auto comp = match.captured(1).split(QLatin1Char(
','));
902 if (comp.size() == 4) {
903 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
904 QLatin1String(
".y: ") + comp.at(1).trimmed(),
905 QLatin1String(
".z: ") + comp.at(2).trimmed(),
906 QLatin1String(
".w: ") + comp.at(3).trimmed() };
911 case QMetaType::QQuaternion: {
912 QRegularExpressionMatch match = re.match(value);
913 if (match.hasMatch()) {
914 const auto comp = match.captured(1).split(QLatin1Char(
','));
915 if (comp.size() == 4) {
916 return { QLatin1String(
".x: ") + comp.at(0).trimmed(),
917 QLatin1String(
".y: ") + comp.at(1).trimmed(),
918 QLatin1String(
".z: ") + comp.at(2).trimmed(),
919 QLatin1String(
".scalar: ") + comp.at(3).trimmed() };
952 if (property.value.isNull()) {
954 result.notValidReason = QStringLiteral(
"Property value is null");
958 const QVariant &value = property.value;
959 result.name = QString::fromUtf8(property.name);
963 QString valueAsString = builtinQmlType(value);
964 if (valueAsString.size() > 0) {
965 result.value = valueAsString;
967 }
else if (value.metaType().flags() & (QMetaType::IsEnumeration | QMetaType::IsUnsignedEnumeration)) {
968 static const auto qmlEnumString = [](
const QLatin1String &element,
const QString &enumString) {
969 return QStringLiteral(
"%1.%2").arg(element).arg(enumString);
972 QString enumValue = asString(value);
973 if (enumValue.size() > 0) {
974 result.value = qmlEnumString(qmlElementName, enumValue);
978 QByteArray element(getQmlElementName(target));
979 if (element.size() > 0) {
981 QByteArray keysString = flag.me.valueToKeys(
int(flag.value));
982 if (keysString.size() > 0) {
983 keysString.prepend(element +
'.');
984 QByteArray replacement(
" | " + element +
'.');
985 keysString.replace(
'|', replacement);
986 result.value = QString::fromLatin1(keysString);
992 if (list->count > 0) {
993 const QString indentStr = indentString(output);
995 const QString listIndentStr = indentString(output);
1000 for (
int i = 0, end = list->count; i != end; ++i) {
1003 str.append(listIndentStr);
1004 str.append(getIdForNode(*(list->head[i])));
1007 str.append(u'\n' + indentStr + u']');
1014 if (list.count > 0) {
1015 const QString indentStr = indentString(output);
1017 const QString listIndentStr = indentString(output);
1022 char *vptr =
reinterpret_cast<
char *>(list.data);
1023 auto size = list.mt.sizeOf();
1025 for (
int i = 0, end = list.count; i != end; ++i) {
1029 const QVariant var{list.mt,
reinterpret_cast<
void *>(vptr + (size * i))};
1030 QString valueString = builtinQmlType(var);
1031 if (valueString.isEmpty())
1032 valueString = asString(var);
1034 str.append(listIndentStr);
1035 str.append(valueString);
1038 str.append(u'\n' + indentStr + u']');
1043 }
else if (value.metaType().id() == qMetaTypeId<
QSSGSceneDesc::Node *>()) {
1044 if (
const auto node = qvariant_cast<QSSGSceneDesc::Node *>(value)) {
1048 Q_ASSERT(node->id != 0);
1052 if (node->runtimeType == QSSGSceneDesc::Node::RuntimeType::TextureData) {
1053 result.name = QStringLiteral(
"source");
1054 result.value = getIdForNode(*node->scene->root) + QLatin1Char(
'.') + getIdForNode(*node);
1056 result.value = getIdForNode(*node);
1060 }
else if (value.metaType() == QMetaType::fromType<QSSGSceneDesc::Mesh *>()) {
1061 if (
const auto meshNode = qvariant_cast<
const QSSGSceneDesc::Mesh *>(value)) {
1062 Q_ASSERT(meshNode->nodeType == QSSGSceneDesc::Node::Type::Mesh);
1063 Q_ASSERT(meshNode->scene);
1064 const auto &scene = *meshNode->scene;
1065 const auto& [meshSourceName, notValidReason] = meshAssetName(scene, *meshNode, output.outdir);
1066 result.notValidReason = notValidReason;
1067 if (!meshSourceName.isEmpty()) {
1068 result.value = toQuotedString(meshSourceName);
1072 }
else if (value.metaType() == QMetaType::fromType<QUrl>()) {
1073 if (
const auto url = qvariant_cast<QUrl>(value); !url.isEmpty()) {
1076 if (QSSGRenderGraphObject::isTexture(target.runtimeType)) {
1077 const auto& [relpath, notValidReason] = copyTextureAsset(url, output);
1078 result.notValidReason = notValidReason;
1079 if (!relpath.isEmpty()) {
1085 if (!path.isEmpty()) {
1086 result.value = toQuotedString(path);
1090 }
else if (target.runtimeType == QSSGSceneDesc::Material::RuntimeType::CustomMaterial) {
1092 if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::Texture *>()) {
1093 if (
const auto texture = qvariant_cast<QSSGSceneDesc::Texture *>(value)) {
1094 Q_ASSERT(QSSGRenderGraphObject::isTexture(texture->runtimeType));
1095 result.value = QLatin1String(
"TextureInput { texture: ") +
1096 getIdForNode(*texture) + QLatin1String(
" }");
1100 }
else if (value.metaType() == QMetaType::fromType<QString>()) {
1102 result.value = toQuotedString(value.toString());
1105 result.notValidReason = QStringLiteral(
"Unsupported value type: ") + QString::fromUtf8(value.metaType().name());
1106 qWarning() << result.notValidReason;
1111 result.expandedProperties = ((output.options & OutputContext::Options::DesignStudioWorkarounds) == OutputContext::Options::DesignStudioWorkarounds)
1112 ? expandComponentsPartially(result.value, value.metaType())
1113 : expandComponents(result.value, value.metaType());
1123 indent(output) << u"id: "_s << getIdForNode(node) << u'\n';
1126 if (node.name.size()) {
1127 const QString objectName = QString::fromLocal8Bit(node.name);
1128 if (!objectName.startsWith(u'*'))
1129 indent(output) << u"objectName: \""_s << node.name << u"\"\n"_s;
1132 const auto &properties = node.properties;
1133 auto it = properties.begin();
1134 const auto end = properties.end();
1135 for (; it != end; ++it) {
1136 const auto &property = *it;
1141 indent(output) <<
"property " << typeName(property->value.metaType()).toByteArray() <<
' ' << result.name << u": "_s << result.value << u'\n';
1143 if (result.expandedProperties.size() > 1) {
1144 for (
const auto &va : result.expandedProperties)
1145 indent(output) << result.name << va << u'\n';
1147 indent(output) << result.name << u": "_s << result.value << u'\n';
1151 QString message = u"Skipped property: "_s + QString::fromUtf8(property->name);
1152 if (!result.notValidReason.isEmpty())
1153 message.append(u", reason: "_s + result.notValidReason);
1154 qDebug() << message;
1171 Q_ASSERT(material.nodeType == QSSGSceneDesc::Model::Type::Material);
1172 if (material.runtimeType == QSSGSceneDesc::Model::RuntimeType::SpecularGlossyMaterial) {
1173 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1174 }
else if (material.runtimeType == Model::RuntimeType::PrincipledMaterial) {
1175 indent(output) << qmlElementName<Material::RuntimeType::PrincipledMaterial>() << blockBegin(output);
1176 }
else if (material.runtimeType == Material::RuntimeType::CustomMaterial) {
1177 indent(output) << qmlElementName<Material::RuntimeType::CustomMaterial>() << blockBegin(output);
1178 }
else if (material.runtimeType == Material::RuntimeType::SpecularGlossyMaterial) {
1179 indent(output) << qmlElementName<Material::RuntimeType::SpecularGlossyMaterial>() << blockBegin(output);
1184 writeNodeProperties(material, output);
1248 if (textureData.data.isEmpty())
1251 const auto mapsFolder = getTextureFolder();
1252 const auto id = getIdForNode(textureData);
1253 const QString textureSourceName = getTextureSourceName(id, QString::fromUtf8(textureData.fmt));
1255 const bool isCompressed = ((textureData.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
1258 if (!outdir.exists(mapsFolder) && !outdir.mkdir(mapsFolder))
1261 const auto imagePath = QString(outdir.path() + QDir::separator() + textureSourceName);
1264 QFile file(imagePath);
1265 if (!file.open(QIODevice::WriteOnly)) {
1266 qWarning(
"Failed to open file %s: %s",
1267 qPrintable(file.fileName()), qPrintable(file.errorString()));
1270 file.write(textureData.data);
1274 const auto &texData = textureData.data;
1275 const auto &size = textureData.sz;
1277 image = QImage(
reinterpret_cast<
const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
1278 if (!image.save(imagePath))
1282 return textureSourceName;
1435 auto sortedResources = resources;
1436 std::sort(sortedResources.begin(), sortedResources.end(), [](
const QSSGSceneDesc::Node *a,
const QSSGSceneDesc::Node *b) {
1437 using RType = QSSGSceneDesc::Node::RuntimeType;
1438 if (a->runtimeType == RType::TextureData && b->runtimeType != RType::TextureData)
1440 if (a->runtimeType == RType::ImageCube && (b->runtimeType != RType::TextureData && b->runtimeType != RType::ImageCube))
1442 if (a->runtimeType == RType::Image2D && (b->runtimeType != RType::TextureData && b->runtimeType != RType::Image2D))
1447 for (
const auto &res : std::as_const(sortedResources))
1448 writeQmlForResourceNode(*res, output);
1499 const int duration = qCeil(anim
.length);
1501 const QString animationId = getIdForAnimation(anim.name);
1502 indent(output
) <<
"id: " << animationId <<
"\n";
1503 QString animationName = animationId;
1504 if (!anim.name.isEmpty())
1505 animationName = QString::fromLocal8Bit(anim.name);
1506 indent(output
) <<
"objectName: \"" << animationName <<
"\"\n";
1508 indent(output
) <<
"startFrame: 0\n";
1509 indent(output
) <<
"endFrame: " << duration <<
"\n";
1510 indent(output
) <<
"currentFrame: 0\n";
1514 if (generateTimelineAnimations) {
1515 indent(output
) <<
"enabled: true\n";
1516 indent(output
) <<
"animations: TimelineAnimation {\n";
1519 indent(output
) <<
"duration: " << duration <<
"\n";
1521 indent(output
) <<
"to: " << duration <<
"\n";
1522 indent(output
) <<
"running: true\n";
1523 indent(output
) <<
"loops: Animation.Infinite\n";
1528 for (
const auto &channel : anim.channels) {
1529 QString id = getIdForNode(*channel->target);
1530 QString propertyName = asString(channel->targetProperty);
1532 indent(output) <<
"KeyframeGroup {\n";
1534 QSSGQmlScopedIndent scopedIndent(output);
1535 indent(output) <<
"target: " << id <<
"\n";
1536 indent(output) <<
"property: " << toQuotedString(propertyName) <<
"\n";
1537 if (useBinaryKeyframes && channel->keys.size() != 1) {
1538 const auto animFolder = getAnimationFolder();
1539 const auto animSourceName = getAnimationSourceName(id, propertyName, index);
1540 if (!output.outdir.exists(animFolder) && !output.outdir.mkdir(animFolder)) {
1544 QFile file(output.outdir.path() + QDir::separator() + animSourceName);
1545 if (!file.open(QIODevice::WriteOnly))
1547 QByteArray keyframeData;
1551 generateKeyframeData(*channel, keyframeData);
1552 file.write(keyframeData);
1554 indent(output) <<
"keyframeSource: " << toQuotedString(animSourceName) <<
"\n";
1556 Q_ASSERT(!channel->keys.isEmpty());
1557 for (
const auto &key : channel->keys) {
1558 indent(output) <<
"Keyframe {\n";
1560 QSSGQmlScopedIndent scopedIndent(output);
1561 indent(output) <<
"frame: " << key->time <<
"\n";
1562 indent(output) <<
"value: " << variantToQml(key->getValue()) <<
"\n";
1564 indent(output) << blockEnd(output);
1568 indent(output) << blockEnd(output);
1570 return {animationName, animationId};
1573void writeQml(
const QSSGSceneDesc::Scene &scene, QTextStream &stream,
const QDir &outdir,
const QJsonObject &optionsObject)
1575 static const auto checkBooleanOption = [](
const QLatin1String &optionName,
const QJsonObject &options,
bool defaultValue =
false) {
1576 const auto it = options.constFind(optionName);
1577 const auto end = options.constEnd();
1581 value = it->toObject().value(QLatin1String(
"value"));
1585 return value.toBool(defaultValue);
1588 auto root = scene.root;
1591 QJsonObject options = optionsObject;
1593 if (
auto it = options.constFind(QLatin1String(
"options")), end = options.constEnd(); it != end)
1594 options = it->toObject();
1597 if (checkBooleanOption(QLatin1String(
"expandValueComponents"), options))
1601 if (checkBooleanOption(QLatin1String(
"designStudioWorkarounds"), options))
1604 const bool useBinaryKeyframes = checkBooleanOption(
"useBinaryKeyframes"_L1, options);
1605 const bool generateTimelineAnimations = !checkBooleanOption(
"manualAnimations"_L1, options);
1609 writeImportHeader(output, scene.animations.count() > 0);
1612 writeQml(*root, output);
1614 stream <<
indent() <<
"// Resources\n";
1616 writeQmlForResources(scene.resources, output);
1619 stream <<
indent() <<
"// Nodes:\n";
1620 for (
const auto &cld : root->children)
1621 writeQmlForNode(*cld, output);
1624 qsizetype animId = 0;
1626 stream <<
indent() <<
"// Animations:\n";
1627 QList<QPair<QString, QString>> animationMap;
1628 for (
const auto &cld : scene.animations) {
1629 QSSGQmlScopedIndent scopedIndent(output);
1630 auto mapValues = writeQmlForAnimation(*cld, animId++, output, useBinaryKeyframes, generateTimelineAnimations);
1631 animationMap.append(mapValues);
1632 indent(output) << blockEnd(output);
1635 if (!generateTimelineAnimations) {
1638 stream <<
indent() <<
"// An exported mapping of Timelines (--manualAnimations)\n";
1639 stream <<
indent() <<
"property var timelineMap: {\n";
1641 for (
const auto &mapValues : animationMap) {
1642 QSSGQmlScopedIndent scopedIndent(output);
1643 indent(output) <<
"\"" << mapValues.first <<
"\": " << mapValues.second <<
",\n";
1646 stream <<
indent() <<
"// A simple list of Timelines (--manualAnimations)\n";
1647 stream <<
indent() <<
"property var timelineList: [\n";
1648 for (
const auto &mapValues : animationMap) {
1649 QSSGQmlScopedIndent scopedIndent(output);
1650 indent(output) << mapValues.second <<
",\n";
1662#ifdef QT_QUICK3D_ENABLE_RT_ANIMATIONS
1663 auto timeline =
new QQuickTimeline(parent);
1664 auto timelineKeyframeGroup = timeline->keyframeGroups();
1665 for (
const auto &channel : anim.channels) {
1666 auto keyframeGroup =
new QQuickKeyframeGroup(timeline);
1667 keyframeGroup->setTargetObject(channel->target->obj);
1668 keyframeGroup->setProperty(asString(channel->targetProperty));
1670 Q_ASSERT(!channel->keys.isEmpty());
1671 if (useBinaryKeyframes) {
1672 QByteArray keyframeData;
1673 generateKeyframeData(*channel, keyframeData);
1675 keyframeGroup->setKeyframeData(keyframeData);
1677 auto keyframes = keyframeGroup->keyframes();
1678 for (
const auto &key : channel->keys) {
1679 auto keyframe =
new QQuickKeyframe(keyframeGroup);
1680 keyframe->setFrame(key->time);
1681 keyframe->setValue(key->getValue());
1682 keyframes.append(&keyframes, keyframe);
1685 (qobject_cast<QQmlParserStatus *>(keyframeGroup))->componentComplete();
1686 timelineKeyframeGroup.append(&timelineKeyframeGroup, keyframeGroup);
1688 timeline->setEndFrame(anim.length);
1689 timeline->setEnabled(isEnabled);
1691 auto timelineAnimation =
new QQuickTimelineAnimation(timeline);
1692 timelineAnimation->setObjectName(anim.name);
1693 timelineAnimation->setDuration(
int(anim.length));
1694 timelineAnimation->setFrom(0.0f);
1695 timelineAnimation->setTo(anim.length);
1696 timelineAnimation->setLoops(QQuickTimelineAnimation::Infinite);
1697 timelineAnimation->setTargetObject(timeline);
1699 (qobject_cast<QQmlParserStatus *>(timeline))->componentComplete();
1701 timelineAnimation->setRunning(
true);
1706 Q_UNUSED(useBinaryKeyframes)