| 1 | // Copyright (C) 2020 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
| 3 | |
| 4 | #include "parser.h" |
| 5 | |
| 6 | #include <QtCore/qdir.h> |
| 7 | #include <QtCore/qfileinfo.h> |
| 8 | #include <QtCore/qstringview.h> |
| 9 | |
| 10 | #include <QtQml/qqmllist.h> |
| 11 | |
| 12 | // Parsing |
| 13 | #include <QtQml/private/qqmljsengine_p.h> |
| 14 | #include <QtQml/private/qqmljslexer_p.h> |
| 15 | #include <QtQml/private/qqmljsparser_p.h> |
| 16 | |
| 17 | #include <QtQuick3D/private/qquick3dobject_p.h> |
| 18 | #include <QtQuick3D/private/qquick3dviewport_p.h> |
| 19 | // Scene |
| 20 | #include <QtQuick3D/private/qquick3dsceneenvironment_p.h> |
| 21 | // Material(s) |
| 22 | #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h> |
| 23 | #include <QtQuick3D/private/qquick3ddefaultmaterial_p.h> |
| 24 | #include <QtQuick3D/private/qquick3dcustommaterial_p.h> |
| 25 | // Lights |
| 26 | #include <QtQuick3D/private/qquick3dspotlight_p.h> |
| 27 | #include <QtQuick3D/private/qquick3dpointlight_p.h> |
| 28 | #include <QtQuick3D/private/qquick3ddirectionallight_p.h> |
| 29 | |
| 30 | // Instancing |
| 31 | #include <QtQuick3D/private/qquick3dinstancing_p.h> |
| 32 | |
| 33 | #include <QtQuick3D/private/qquick3dshaderutils_p.h> |
| 34 | |
| 35 | #include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h> |
| 36 | |
| 37 | QT_BEGIN_NAMESPACE |
| 38 | |
| 39 | template<typename T, T *T::*N = &T::next> |
| 40 | struct InvasiveListView : protected QSSGInvasiveSingleLinkedList<T, N> |
| 41 | { |
| 42 | using QSSGInvasiveSingleLinkedList<T, N>::begin; |
| 43 | using QSSGInvasiveSingleLinkedList<T, N>::end; |
| 44 | using QSSGInvasiveSingleLinkedList<T, N>::m_head; |
| 45 | |
| 46 | explicit InvasiveListView(const T &obj) { m_head = &const_cast<T &>(obj); } |
| 47 | }; |
| 48 | |
| 49 | template <typename T> struct TypeInfo |
| 50 | { |
| 51 | static constexpr const char *qmlTypeName() { return "Unknown" ; } |
| 52 | static constexpr int typeId() { return QMetaType::UnknownType; } |
| 53 | }; |
| 54 | |
| 55 | #define DECLARE_QQ3D_TYPE(CLASS, TYPE_NAME) \ |
| 56 | template<> struct TypeInfo<CLASS> \ |
| 57 | { \ |
| 58 | static constexpr const char *qmlTypeName() { return #TYPE_NAME; } \ |
| 59 | inline static constexpr int typeId() { return qMetaTypeId<CLASS>(); } \ |
| 60 | inline static constexpr int qmlListTypeId() { return qMetaTypeId<QQmlListProperty<CLASS>>(); } \ |
| 61 | } |
| 62 | |
| 63 | DECLARE_QQ3D_TYPE(QQuick3DViewport, View3D); |
| 64 | DECLARE_QQ3D_TYPE(QQuick3DSceneEnvironment, SceneEnvironment); |
| 65 | DECLARE_QQ3D_TYPE(QQuick3DMaterial, Material); |
| 66 | DECLARE_QQ3D_TYPE(QQuick3DPrincipledMaterial, PrincipledMaterial); |
| 67 | DECLARE_QQ3D_TYPE(QQuick3DDefaultMaterial, DefaultMaterial); |
| 68 | DECLARE_QQ3D_TYPE(QQuick3DCustomMaterial, CustomMaterial); |
| 69 | DECLARE_QQ3D_TYPE(QQuick3DDirectionalLight, DirectionalLight); |
| 70 | DECLARE_QQ3D_TYPE(QQuick3DPointLight, PointLight); |
| 71 | DECLARE_QQ3D_TYPE(QQuick3DSpotLight, SpotLight); |
| 72 | DECLARE_QQ3D_TYPE(QQuick3DTexture, Texture); |
| 73 | DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsTextureInput, TextureInput); |
| 74 | DECLARE_QQ3D_TYPE(QQuick3DModel, Model); |
| 75 | DECLARE_QQ3D_TYPE(QQuick3DEffect, Effect); |
| 76 | DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsRenderPass, Pass); |
| 77 | DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsShader, Shader); |
| 78 | DECLARE_QQ3D_TYPE(QQuick3DInstanceList, InstanceList); |
| 79 | DECLARE_QQ3D_TYPE(QQuick3DInstanceListEntry, InstanceListEntry); |
| 80 | |
| 81 | using QmlTypeNames = QHash<QString, int>; |
| 82 | |
| 83 | #define QQ3D_TYPE_ENTRY(TYPE) { TypeInfo<TYPE>::qmlTypeName(), TypeInfo<TYPE>::typeId() } |
| 84 | |
| 85 | QmlTypeNames baseTypeMap() |
| 86 | { |
| 87 | return { QQ3D_TYPE_ENTRY(QQuick3DViewport), |
| 88 | QQ3D_TYPE_ENTRY(QQuick3DSceneEnvironment), |
| 89 | QQ3D_TYPE_ENTRY(QQuick3DPrincipledMaterial), |
| 90 | QQ3D_TYPE_ENTRY(QQuick3DDefaultMaterial), |
| 91 | QQ3D_TYPE_ENTRY(QQuick3DCustomMaterial), |
| 92 | QQ3D_TYPE_ENTRY(QQuick3DDirectionalLight), |
| 93 | QQ3D_TYPE_ENTRY(QQuick3DPointLight), |
| 94 | QQ3D_TYPE_ENTRY(QQuick3DSpotLight), |
| 95 | QQ3D_TYPE_ENTRY(QQuick3DTexture), |
| 96 | QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsTextureInput), |
| 97 | QQ3D_TYPE_ENTRY(QQuick3DModel), |
| 98 | QQ3D_TYPE_ENTRY(QQuick3DEffect), |
| 99 | QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsRenderPass), |
| 100 | QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsShader), |
| 101 | QQ3D_TYPE_ENTRY(QQuick3DInstanceList), |
| 102 | QQ3D_TYPE_ENTRY(QQuick3DInstanceListEntry) |
| 103 | }; |
| 104 | } |
| 105 | |
| 106 | Q_GLOBAL_STATIC(QmlTypeNames, s_typeMap) |
| 107 | |
| 108 | struct Context |
| 109 | { |
| 110 | enum class Type |
| 111 | { |
| 112 | Application, |
| 113 | Component |
| 114 | }; |
| 115 | |
| 116 | struct Property { |
| 117 | enum MemberState : quint8 |
| 118 | { |
| 119 | Uninitialized, |
| 120 | Initialized |
| 121 | }; |
| 122 | |
| 123 | QObject *target = nullptr; |
| 124 | QStringView name; |
| 125 | int targetType = QMetaType::UnknownType; |
| 126 | QMetaType::Type type = QMetaType::UnknownType; |
| 127 | MemberState memberState = Uninitialized; |
| 128 | }; |
| 129 | |
| 130 | template<typename T> using Vector = QVector<T>; |
| 131 | using InterceptObjDefFunc = bool (*)(const QQmlJS::AST::UiObjectDefinition &, Context &, int &); |
| 132 | using InterceptObjBinding = bool (*)(const QQmlJS::AST::UiObjectBinding &, Context &, int &); |
| 133 | using InterceptPublicMember = bool (*)(const QQmlJS::AST::UiPublicMember &, Context &, int &); |
| 134 | using InterceptCallExpression = bool (*)(const QQmlJS::AST::CallExpression &, Context &, int &); |
| 135 | |
| 136 | QQmlJS::Engine *engine = nullptr; |
| 137 | QDir workingDir; // aka source directory |
| 138 | QFileInfo currentFileInfo; |
| 139 | |
| 140 | MaterialParser::SceneData sceneData; |
| 141 | Property property; |
| 142 | |
| 143 | struct Component |
| 144 | { |
| 145 | QObject *ptr = nullptr; |
| 146 | int type = QMetaType::UnknownType; |
| 147 | }; |
| 148 | |
| 149 | QHash<QStringView, QObject *> identifierMap; |
| 150 | QHash<QString, Component> components; |
| 151 | InterceptObjDefFunc interceptODFunc = nullptr; |
| 152 | InterceptObjBinding interceptOBFunc = nullptr; |
| 153 | InterceptPublicMember interceptPMFunc = nullptr; |
| 154 | InterceptCallExpression interceptCallExpr = nullptr; |
| 155 | Type type = Type::Application; |
| 156 | bool dbgprint = false; |
| 157 | }; |
| 158 | |
| 159 | Q_DECLARE_TYPEINFO(Context::Component, Q_PRIMITIVE_TYPE); |
| 160 | |
| 161 | namespace BuiltinHelpers { |
| 162 | |
| 163 | using ArgumentListView = InvasiveListView<QQmlJS::AST::ArgumentList>; |
| 164 | |
| 165 | template <typename T> Q_REQUIRED_RESULT constexpr quint8 componentCount() { Q_STATIC_ASSERT(true); return 0; } |
| 166 | template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector2D>() { return 2; } |
| 167 | template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector3D>() { return 3; } |
| 168 | template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector4D>() { return 4; } |
| 169 | |
| 170 | static double expressionValue(const QQmlJS::AST::ExpressionNode &expr) { |
| 171 | using namespace QQmlJS::AST; |
| 172 | |
| 173 | if (expr.kind == Node::Kind_NumericLiteral) { |
| 174 | return static_cast<const NumericLiteral &>(expr).value; |
| 175 | } else if (expr.kind == Node::Kind_UnaryMinusExpression) { |
| 176 | const auto &minusExpr = static_cast<const UnaryMinusExpression &>(expr); |
| 177 | if (minusExpr.expression && minusExpr.expression->kind == Node::Kind_NumericLiteral) |
| 178 | return static_cast<const NumericLiteral &>(*minusExpr.expression).value * -1.0; |
| 179 | } else if (expr.kind == Node::Kind_UnaryPlusExpression) { |
| 180 | const auto &plusExpr = static_cast<const UnaryPlusExpression &>(expr); |
| 181 | if (plusExpr.expression && plusExpr.expression->kind == Node::Kind_NumericLiteral) |
| 182 | return static_cast<const NumericLiteral &>(*plusExpr.expression).value; |
| 183 | } else { |
| 184 | printf("Expression type \'%d\' unhandled!\n" , expr.kind); |
| 185 | } |
| 186 | |
| 187 | return 0.0; |
| 188 | } |
| 189 | |
| 190 | template <typename T> |
| 191 | static inline bool setProperty(const Context::Property &property, const T &v) |
| 192 | { |
| 193 | return property.target->setProperty(property.name.toLatin1(), QVariant::fromValue(v)); |
| 194 | } |
| 195 | |
| 196 | template<typename Vec> |
| 197 | static Vec toVec(const ArgumentListView &list, bool *ok = nullptr) |
| 198 | { |
| 199 | using namespace QQmlJS::AST; |
| 200 | int i = 0; |
| 201 | const int e = componentCount<Vec>(); |
| 202 | Vec vec; |
| 203 | for (const auto &listItem : list) { |
| 204 | if (listItem.expression && i != e) |
| 205 | vec[i] = expressionValue(*listItem.expression); |
| 206 | ++i; |
| 207 | } |
| 208 | |
| 209 | if (ok) |
| 210 | *ok = (i == e); |
| 211 | |
| 212 | return vec; |
| 213 | } |
| 214 | |
| 215 | static QPointF toPoint(const ArgumentListView &list, bool *ok = nullptr) |
| 216 | { |
| 217 | using namespace QQmlJS::AST; |
| 218 | int i = 0; |
| 219 | const int e = 2; |
| 220 | qreal args[e]; |
| 221 | for (const auto &listItem : list) { |
| 222 | if (listItem.expression && i != e) |
| 223 | args[i] = expressionValue(*listItem.expression); |
| 224 | ++i; |
| 225 | } |
| 226 | |
| 227 | if (ok) |
| 228 | *ok = (i == e); |
| 229 | |
| 230 | return QPointF(args[0], args[1]); |
| 231 | } |
| 232 | |
| 233 | static QSizeF toSize(const ArgumentListView &list, bool *ok = nullptr) |
| 234 | { |
| 235 | using namespace QQmlJS::AST; |
| 236 | int i = 0; |
| 237 | const int e = 2; |
| 238 | qreal args[e]; |
| 239 | for (const auto &listItem : list) { |
| 240 | if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e) |
| 241 | args[i] = expressionValue(*listItem.expression); |
| 242 | ++i; |
| 243 | } |
| 244 | |
| 245 | if (ok) |
| 246 | *ok = (i == e); |
| 247 | |
| 248 | return QSizeF(args[0], args[1]); |
| 249 | } |
| 250 | |
| 251 | static QRectF toRect(const ArgumentListView &list, bool *ok = nullptr) |
| 252 | { |
| 253 | using namespace QQmlJS::AST; |
| 254 | int i = 0; |
| 255 | const int e = 4; |
| 256 | qreal args[e]; |
| 257 | for (const auto &listItem : list) { |
| 258 | if (listItem.expression && i != e) |
| 259 | args[i] = expressionValue(*listItem.expression); |
| 260 | ++i; |
| 261 | } |
| 262 | |
| 263 | if (ok) |
| 264 | *ok = (i == e); |
| 265 | |
| 266 | return QRectF(args[0], args[1], args[2], args[3]); |
| 267 | } |
| 268 | |
| 269 | static QMatrix4x4 toMat44(const ArgumentListView &list, bool *ok = nullptr) |
| 270 | { |
| 271 | using namespace QQmlJS::AST; |
| 272 | int i = 0; |
| 273 | const int e = 16; |
| 274 | float args[e]; |
| 275 | for (const auto &listItem : list) { |
| 276 | if (listItem.expression && i != e) |
| 277 | args[i] = float(expressionValue(*listItem.expression)); |
| 278 | ++i; |
| 279 | } |
| 280 | |
| 281 | if (ok) |
| 282 | *ok = (i == e); |
| 283 | |
| 284 | return QMatrix4x4(args); |
| 285 | } |
| 286 | |
| 287 | static QQuaternion toQuaternion(const ArgumentListView &list, bool *ok = nullptr) |
| 288 | { |
| 289 | using namespace QQmlJS::AST; |
| 290 | int i = 0; |
| 291 | const int e = 4; |
| 292 | float args[e]; |
| 293 | for (const auto &listItem : list) { |
| 294 | if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e) |
| 295 | args[i] = float(expressionValue(*listItem.expression)); |
| 296 | ++i; |
| 297 | } |
| 298 | |
| 299 | if (ok) |
| 300 | *ok = (i == e); |
| 301 | |
| 302 | return QQuaternion(args[0], args[1], args[2], args[3]); |
| 303 | } |
| 304 | |
| 305 | // String variants |
| 306 | // Note: Unlike the call variants we assume arguments are correct (can be converted), |
| 307 | // as they should have failed earlier, during parsing, if they were not. |
| 308 | template <typename Vec> |
| 309 | static Vec toVec(const QStringView &ref) |
| 310 | { |
| 311 | const auto args = ref.split(sep: u','); |
| 312 | Vec vec; |
| 313 | bool ok = false; |
| 314 | if (args.size() == componentCount<Vec>()) { |
| 315 | for (int i = 0; i != componentCount<Vec>(); ++i) { |
| 316 | vec[i] = args.at(i).toDouble(ok: &ok); |
| 317 | Q_ASSERT(ok); |
| 318 | } |
| 319 | } |
| 320 | |
| 321 | return vec; |
| 322 | } |
| 323 | |
| 324 | static QPointF toPoint(const QStringView &ref) |
| 325 | { |
| 326 | const auto args = ref.split(sep: u"," ); |
| 327 | if (args.size() == 2) { |
| 328 | bool ok = false; |
| 329 | const auto arg0 = args.at(i: 0).toDouble(ok: &ok); |
| 330 | Q_ASSERT(ok); |
| 331 | const auto arg1 = args.at(i: 1).toDouble(ok: &ok); |
| 332 | Q_ASSERT(ok); |
| 333 | return QPointF(arg0, arg1); |
| 334 | } |
| 335 | return QPointF(); |
| 336 | } |
| 337 | |
| 338 | static QSizeF toSize(const QStringView &ref) |
| 339 | { |
| 340 | const auto args = ref.split(sep: u'x'); |
| 341 | if (args.size() == 2) { |
| 342 | bool ok = false; |
| 343 | const auto arg0 = args.at(i: 0).toDouble(ok: &ok); |
| 344 | Q_ASSERT(ok); |
| 345 | const auto arg1 = args.at(i: 1).toDouble(ok: &ok); |
| 346 | Q_ASSERT(ok); |
| 347 | return QSizeF(arg0, arg1); |
| 348 | } |
| 349 | return QSizeF(); |
| 350 | } |
| 351 | |
| 352 | static QRectF toRect(const QStringView &ref) |
| 353 | { |
| 354 | auto args = ref.split(sep: u"," ); |
| 355 | if (args.size() == 3) { |
| 356 | bool ok = false; |
| 357 | const auto arg0 = args.at(i: 0).toDouble(ok: &ok); |
| 358 | Q_ASSERT(ok); |
| 359 | const auto arg1 = args.at(i: 1).toDouble(ok: &ok); |
| 360 | Q_ASSERT(ok); |
| 361 | args = args.at(i: 2).split(sep: u'x'); |
| 362 | if (args.size() == 2) { |
| 363 | const auto arg2 = args.at(i: 0).toDouble(ok: &ok); |
| 364 | Q_ASSERT(ok); |
| 365 | const auto arg3 = args.at(i: 1).toDouble(ok: &ok); |
| 366 | Q_ASSERT(ok); |
| 367 | return QRectF(arg0, arg1, arg2, arg3); |
| 368 | } |
| 369 | } |
| 370 | return QRectF(); |
| 371 | } |
| 372 | |
| 373 | static QQuaternion toQuaternion(const QStringView &ref) |
| 374 | { |
| 375 | const auto args = ref.split(sep: u','); |
| 376 | if (args.size() == 4) { |
| 377 | bool ok = false; |
| 378 | const auto arg0 = args.at(i: 0).toDouble(ok: &ok); |
| 379 | Q_ASSERT(ok); |
| 380 | const auto arg1 = args.at(i: 1).toDouble(ok: &ok); |
| 381 | Q_ASSERT(ok); |
| 382 | const auto arg2 = args.at(i: 2).toDouble(ok: &ok); |
| 383 | Q_ASSERT(ok); |
| 384 | const auto arg3 = args.at(i: 3).toDouble(ok: &ok); |
| 385 | Q_ASSERT(ok); |
| 386 | return QQuaternion(arg0, arg1, arg2, arg3); |
| 387 | } |
| 388 | return QQuaternion(); |
| 389 | } |
| 390 | |
| 391 | } // BuiltinHelpers |
| 392 | |
| 393 | template <typename T> |
| 394 | static void cloneQmlList(const QObject &so, QMetaProperty &sp, QObject &to, QMetaProperty &tp) { |
| 395 | Q_ASSERT(sp.typeId() == tp.typeId()); |
| 396 | const auto tv = tp.read(obj: &to); |
| 397 | const auto sv = sp.read(obj: &so); |
| 398 | if (sv.isValid() && tv.isValid()) { |
| 399 | auto tl = tv.value<QQmlListProperty<T>>(); |
| 400 | auto sl = sv.value<QQmlListProperty<T>>(); |
| 401 | const auto count = sl.count(&sl); |
| 402 | for (int i = 0; count != i; ++i) |
| 403 | tl.append(&tl, sl.at(&sl, i)); |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | static void cloneProperties(QObject &target, const QObject &source) |
| 408 | { |
| 409 | Q_ASSERT(target.metaObject() == source.metaObject()); |
| 410 | const auto smo = source.metaObject(); |
| 411 | auto tmo = target.metaObject(); |
| 412 | const int propCount = smo->propertyCount(); |
| 413 | for (int i = 0; i != propCount; ++i) { |
| 414 | auto sp = smo->property(index: i); |
| 415 | auto tp = tmo->property(index: i); |
| 416 | if (sp.typeId() == tp.typeId()) { |
| 417 | if (sp.typeId() == TypeInfo<QQuick3DMaterial>::qmlListTypeId()) |
| 418 | cloneQmlList<QQuick3DMaterial>(so: source, sp, to&: target, tp); |
| 419 | else if (sp.typeId() == TypeInfo<QQuick3DEffect>::qmlListTypeId()) |
| 420 | cloneQmlList<QQuick3DEffect>(so: source, sp, to&: target, tp); |
| 421 | else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsRenderPass>::qmlListTypeId()) |
| 422 | cloneQmlList<QQuick3DShaderUtilsRenderPass>(so: source, sp, to&: target, tp); |
| 423 | else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsShader>::qmlListTypeId()) |
| 424 | cloneQmlList<QQuick3DShaderUtilsShader>(so: source, sp, to&: target, tp); |
| 425 | else |
| 426 | tmo->property(index: i).write(obj: &target, value: smo->property(index: i).read(obj: &source)); |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | // Clone the dynamic properties as well |
| 431 | for (const auto &prop : source.dynamicPropertyNames()) |
| 432 | target.setProperty(name: prop.constData(), value: source.property(name: prop.constData())); |
| 433 | } |
| 434 | |
| 435 | template <typename T> |
| 436 | inline QVariant fromStringEnumHelper(const QStringView &ref, const QMetaProperty &property) |
| 437 | { |
| 438 | bool ok = false; |
| 439 | const int v = property.enumerator().keyToValue(key: ref.toLatin1(), ok: &ok); |
| 440 | return ok ? QVariant::fromValue(T(v)) : QVariant(); |
| 441 | } |
| 442 | |
| 443 | static QVariant fromString(const QStringView &ref, const Context &ctx) |
| 444 | { |
| 445 | const auto &p = ctx.property; |
| 446 | if (!p.target) |
| 447 | return QVariant(); |
| 448 | |
| 449 | static const auto toBuiltinType = [](int type, const QStringView &ref, const QDir &workingDir) { |
| 450 | using namespace BuiltinHelpers; |
| 451 | bool ok = false; |
| 452 | switch (type) { |
| 453 | case QMetaType::Int: |
| 454 | { |
| 455 | const auto v = ref.toInt(ok: &ok); |
| 456 | return (ok ? QVariant::fromValue(value: v) : QVariant()); |
| 457 | } |
| 458 | break; |
| 459 | case QMetaType::Bool: |
| 460 | { |
| 461 | const auto v = ref.toInt(ok: &ok); |
| 462 | return (ok ? QVariant::fromValue(value: bool(v)) : QVariant()); |
| 463 | } |
| 464 | case QMetaType::Double: |
| 465 | { |
| 466 | const auto v = ref.toDouble(ok: &ok); |
| 467 | return (ok ? QVariant::fromValue(value: qreal(v)) : QVariant()); |
| 468 | } |
| 469 | break; |
| 470 | case QMetaType::QString: |
| 471 | return QVariant::fromValue(value: ref); |
| 472 | case QMetaType::QUrl: |
| 473 | { |
| 474 | if (ref.startsWith(c: u':') || ref.startsWith(c: QDir::separator())) |
| 475 | return QVariant::fromValue(value: QUrl::fromLocalFile(localfile: ref.toString())); |
| 476 | else if (ref.startsWith(c: u'#')) |
| 477 | return QVariant::fromValue(value: QUrl(ref.toString())); |
| 478 | else |
| 479 | return QVariant::fromValue(value: QUrl::fromUserInput(userInput: ref.toString(), workingDirectory: workingDir.canonicalPath())); |
| 480 | } |
| 481 | case QMetaType::QColor: |
| 482 | return QVariant::fromValue(value: QColor(ref)); |
| 483 | case QMetaType::QTime: |
| 484 | return QVariant::fromValue(value: QTime::fromString(string: ref.toString())); |
| 485 | case QMetaType::QDate: |
| 486 | return QVariant::fromValue(value: QDate::fromString(string: ref.toString())); |
| 487 | case QMetaType::QDateTime: |
| 488 | return QVariant::fromValue(value: QDateTime::fromString(string: ref.toString())); |
| 489 | case QMetaType::QRectF: |
| 490 | return QVariant::fromValue(value: toRect(ref)); |
| 491 | case QMetaType::QPointF: |
| 492 | return QVariant::fromValue(value: toPoint(ref)); |
| 493 | case QMetaType::QSizeF: |
| 494 | return QVariant::fromValue(value: toSize(ref)); |
| 495 | case QMetaType::QVector2D: |
| 496 | return QVariant::fromValue(value: toVec<QVector2D>(ref)); |
| 497 | case QMetaType::QVector3D: |
| 498 | return QVariant::fromValue(value: toVec<QVector3D>(ref)); |
| 499 | case QMetaType::QVector4D: |
| 500 | return QVariant::fromValue(value: toVec<QVector4D>(ref)); |
| 501 | case QMetaType::QQuaternion: |
| 502 | return QVariant::fromValue(value: toQuaternion(ref)); |
| 503 | } |
| 504 | |
| 505 | return QVariant(); |
| 506 | }; |
| 507 | |
| 508 | if (p.type != QMetaType::UnknownType) // Built in Qt types int, vector3d etc |
| 509 | return toBuiltinType(p.type, ref, ctx.workingDir); |
| 510 | |
| 511 | // hard mode, detect the property type |
| 512 | // We only care about the types that are relevant for us |
| 513 | if (p.targetType != QMetaType::UnknownType) { |
| 514 | Q_ASSERT(p.target); |
| 515 | Q_ASSERT(!p.name.isEmpty()); |
| 516 | const int idx = p.target->metaObject()->indexOfProperty(name: p.name.toLatin1().constData()); |
| 517 | const auto property = p.target->metaObject()->property(index: idx); |
| 518 | if (property.metaType().id() >= QMetaType::User) { |
| 519 | const QMetaType metaType = property.metaType(); |
| 520 | if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId() || p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
| 521 | // Common for both materials |
| 522 | if (metaType.id() == qMetaTypeId<QQuick3DMaterial::CullMode>()) |
| 523 | return fromStringEnumHelper<QQuick3DMaterial::CullMode>(ref, property); |
| 524 | if (metaType.id() == qMetaTypeId<QQuick3DMaterial::TextureChannelMapping>()) |
| 525 | return fromStringEnumHelper<QQuick3DMaterial::TextureChannelMapping>(ref, property); |
| 526 | |
| 527 | if (p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
| 528 | if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::Lighting>()) |
| 529 | return fromStringEnumHelper<QQuick3DPrincipledMaterial::Lighting>(ref, property); |
| 530 | if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::BlendMode>()) |
| 531 | return fromStringEnumHelper<QQuick3DPrincipledMaterial::BlendMode>(ref, property); |
| 532 | if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::AlphaMode>()) |
| 533 | return fromStringEnumHelper<QQuick3DPrincipledMaterial::AlphaMode>(ref, property); |
| 534 | } else if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId()) { |
| 535 | if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::Lighting>()) |
| 536 | return fromStringEnumHelper<QQuick3DDefaultMaterial::Lighting>(ref, property); |
| 537 | if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::BlendMode>()) |
| 538 | return fromStringEnumHelper<QQuick3DDefaultMaterial::BlendMode>(ref, property); |
| 539 | if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::SpecularModel>()) |
| 540 | return fromStringEnumHelper<QQuick3DDefaultMaterial::SpecularModel>(ref, property); |
| 541 | } |
| 542 | } else if (p.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId()) { |
| 543 | if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::ShadingMode>()) |
| 544 | return fromStringEnumHelper<QQuick3DCustomMaterial::ShadingMode>(ref, property); |
| 545 | if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::BlendMode>()) |
| 546 | return fromStringEnumHelper<QQuick3DCustomMaterial::BlendMode>(ref, property); |
| 547 | } else if (p.targetType == TypeInfo<QQuick3DSpotLight>::typeId() || p.targetType == TypeInfo<QQuick3DPointLight>::typeId()) { |
| 548 | if (metaType.id() == qMetaTypeId<QQuick3DAbstractLight::QSSGShadowMapQuality>()) |
| 549 | return fromStringEnumHelper<QQuick3DAbstractLight::QSSGShadowMapQuality>(ref, property); |
| 550 | } else if (p.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
| 551 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>()) |
| 552 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>(ref, property); |
| 553 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>()) |
| 554 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>(ref, property); |
| 555 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>()) |
| 556 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>(ref, property); |
| 557 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>()) |
| 558 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>(ref, property); |
| 559 | } else if (p.targetType == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) { |
| 560 | if (metaType.id() == qMetaTypeId<QQuick3DShaderUtilsShader::Stage>()) |
| 561 | return fromStringEnumHelper<QQuick3DShaderUtilsShader::Stage>(ref, property); |
| 562 | } |
| 563 | } else { // Qt type |
| 564 | return toBuiltinType(property.metaType().id(), ref, ctx.workingDir); |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | if (ctx.dbgprint) |
| 569 | printf(format: "Unhandled type for property %s\n" , ref.toLatin1().constData()); |
| 570 | |
| 571 | return QVariant(); |
| 572 | } |
| 573 | |
| 574 | static QString getQmlFileExtension() { return QStringLiteral("qml" ); } |
| 575 | |
| 576 | struct Visitors |
| 577 | { |
| 578 | static void visit(const QQmlJS::AST::UiProgram &program, Context &ctx, int &ret) |
| 579 | { |
| 580 | using namespace QQmlJS::AST; |
| 581 | const bool = false; |
| 582 | if (readHeaders && program.headers) { // No real need for us to look at the includes |
| 583 | using HeaderItem = UiHeaderItemList; |
| 584 | using Headers = InvasiveListView<HeaderItem>; |
| 585 | |
| 586 | Headers (*program.headers); |
| 587 | for (const auto &header : headers) |
| 588 | printf("Type: %d\n" , header.kind); |
| 589 | } |
| 590 | if (program.members) |
| 591 | visit(*program.members, ctx, ret); |
| 592 | } |
| 593 | static void visit(const QQmlJS::AST::UiObjectInitializer &objInitializer, Context &ctx, int &ret) |
| 594 | { |
| 595 | using namespace QQmlJS::AST; |
| 596 | if (objInitializer.members) |
| 597 | visit(*objInitializer.members, ctx, ret); |
| 598 | } |
| 599 | |
| 600 | static void visit(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret) |
| 601 | { |
| 602 | using namespace QQmlJS::AST; |
| 603 | if (ctx.dbgprint) |
| 604 | printf("Object definition -> %s\n" , def.qualifiedTypeNameId->name.toLocal8Bit().constData()); |
| 605 | if (!(ctx.interceptODFunc && ctx.interceptODFunc(def, ctx, ret))) { |
| 606 | if (def.initializer) |
| 607 | visit(*static_cast<UiObjectInitializer *>(def.initializer), ctx, ret); |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | static void visit(const QQmlJS::AST::UiArrayMemberList &memberList, Context &ctx, int &ret) |
| 612 | { |
| 613 | using namespace QQmlJS::AST; |
| 614 | using ArrayMemberItem = UiArrayMemberList; |
| 615 | using ArrayMembers = InvasiveListView<ArrayMemberItem>; |
| 616 | |
| 617 | ArrayMembers arrayMembers(memberList); |
| 618 | for (auto &object : arrayMembers) { |
| 619 | if (object.member->kind == Node::Kind_UiObjectDefinition) { |
| 620 | const auto &def = *static_cast<UiObjectDefinition *>(object.member); |
| 621 | visit(def, ctx, ret); |
| 622 | } |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | static void visit(const QQmlJS::AST::UiScriptBinding &binding, Context &ctx, int &ret) |
| 627 | { |
| 628 | using namespace QQmlJS::AST; |
| 629 | if (ctx.dbgprint) |
| 630 | printf("Script binding -> %s " , binding.qualifiedId->name.toLocal8Bit().constData()); |
| 631 | |
| 632 | const auto oldName = ctx.property.name; // reentrancy |
| 633 | ctx.property.name = binding.qualifiedId->name; |
| 634 | |
| 635 | if (binding.statement) { |
| 636 | if (binding.statement->kind == Node::Kind_ExpressionStatement) { |
| 637 | const auto &expressionStatement = static_cast<const ExpressionStatement &>(*binding.statement); |
| 638 | visit(expressionStatement, ctx, ret); |
| 639 | } |
| 640 | } |
| 641 | ctx.property.name = oldName; |
| 642 | } |
| 643 | |
| 644 | static void visit(const QQmlJS::AST::UiArrayBinding &arrayBinding, Context &ctx, int &ret) |
| 645 | { |
| 646 | using namespace QQmlJS::AST; |
| 647 | if (ctx.dbgprint) |
| 648 | printf("Array binding(s) -> %s: [\n" , arrayBinding.qualifiedId->name.toLocal8Bit().constData()); |
| 649 | |
| 650 | const auto oldName = ctx.property.name; // reentrancy |
| 651 | ctx.property.name = arrayBinding.qualifiedId->name; |
| 652 | |
| 653 | if (arrayBinding.members) |
| 654 | visit(*arrayBinding.members, ctx, ret); |
| 655 | |
| 656 | if (ctx.dbgprint) |
| 657 | printf(format: "]\n" ); |
| 658 | |
| 659 | ctx.property.name = oldName; |
| 660 | } |
| 661 | |
| 662 | static void visit(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret) |
| 663 | { |
| 664 | using namespace QQmlJS::AST; |
| 665 | |
| 666 | if (ctx.dbgprint) |
| 667 | printf("Object binding -> %s: %s {\n" , objectBinding.qualifiedId->name.toLocal8Bit().constData(), objectBinding.qualifiedTypeNameId->name.toLocal8Bit().constData()); |
| 668 | |
| 669 | if (objectBinding.initializer) { |
| 670 | if (!(ctx.interceptOBFunc && ctx.interceptOBFunc(objectBinding, ctx, ret))) |
| 671 | visit(*objectBinding.initializer, ctx, ret); |
| 672 | } |
| 673 | |
| 674 | if (ctx.dbgprint) |
| 675 | printf(format: "}\n" ); |
| 676 | } |
| 677 | |
| 678 | static void visit(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret) |
| 679 | { |
| 680 | using namespace QQmlJS::AST; |
| 681 | |
| 682 | if (ctx.dbgprint) |
| 683 | printf("%s member -> %s " , (member.type == UiPublicMember::Signal ? "Signal" : "Property" ), member.name.toLocal8Bit().constData()); |
| 684 | |
| 685 | auto name = ctx.property.name; |
| 686 | ctx.property.name = member.name; |
| 687 | const auto typeCpy = ctx.property.type; |
| 688 | |
| 689 | if (!(ctx.interceptPMFunc && ctx.interceptPMFunc(member, ctx, ret))) { |
| 690 | if (member.statement) { |
| 691 | const auto &statement = member.statement; |
| 692 | if (statement->kind == Node::Kind_ExpressionStatement) |
| 693 | visit(static_cast<const ExpressionStatement &>(*statement), ctx, ret); |
| 694 | else if (ctx.dbgprint) |
| 695 | printf("Unhandled statement (%d)\n" , statement->kind); |
| 696 | } else if (member.binding) { |
| 697 | const auto &binding = member.binding; |
| 698 | if (binding->kind == Node::Kind_UiObjectBinding) |
| 699 | visit(static_cast<const UiObjectBinding &>(*binding), ctx, ret); |
| 700 | else if (ctx.dbgprint) |
| 701 | printf("Unhandled binding (%d)\n" , binding->kind); |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | qSwap(value1&: ctx.property.name, value2&: name); |
| 706 | ctx.property.type = typeCpy; |
| 707 | } |
| 708 | |
| 709 | static void visit(const QQmlJS::AST::ExpressionStatement &exprStatement, Context &ctx, int &ret) |
| 710 | { |
| 711 | using namespace QQmlJS::AST; |
| 712 | if (exprStatement.expression) { |
| 713 | if (exprStatement.expression->kind == Node::Kind_IdentifierExpression) { |
| 714 | const auto &identExpression = static_cast<const IdentifierExpression &>(*exprStatement.expression); |
| 715 | visit(identExpression, ctx, ret); |
| 716 | } else if (exprStatement.expression->kind == Node::Kind_StringLiteral) { |
| 717 | const auto &stringLiteral = static_cast<const StringLiteral &>(*exprStatement.expression); |
| 718 | visit(stringLiteral, ctx, ret); |
| 719 | } else if (exprStatement.expression->kind == Node::Kind_NumericLiteral) { |
| 720 | const auto &numericLiteral = static_cast<const NumericLiteral &>(*exprStatement.expression); |
| 721 | visit(numericLiteral, ctx, ret); |
| 722 | } else if (exprStatement.expression->kind == Node::Kind_FieldMemberExpression) { |
| 723 | const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*exprStatement.expression); |
| 724 | visit(fieldMemberExpression, ctx, ret); |
| 725 | } else if (exprStatement.expression->kind == Node::Kind_TrueLiteral || exprStatement.expression->kind == Node::Kind_FalseLiteral) { |
| 726 | const bool v = (exprStatement.expression->kind == Node::Kind_TrueLiteral); |
| 727 | if (ctx.dbgprint) |
| 728 | printf(format: "-> TrueLiteral: %s\n" , v ? "true" : "false" ); |
| 729 | if (ctx.property.target) { |
| 730 | auto target = ctx.property.target; |
| 731 | const auto &name = ctx.property.name; |
| 732 | target->setProperty(name: name.toLatin1(), value: QVariant::fromValue(value: v)); |
| 733 | } |
| 734 | } else if (exprStatement.expression->kind == Node::Kind_ArrayPattern) { |
| 735 | const auto &arrayPattern = static_cast<const ArrayPattern &>(*exprStatement.expression); |
| 736 | visit(arrayPattern, ctx, ret); |
| 737 | } else if (exprStatement.expression->kind == Node::Kind_CallExpression) { |
| 738 | const auto &callExpression = static_cast<const CallExpression &>(*exprStatement.expression); |
| 739 | visit(callExpression, ctx, ret); |
| 740 | } else if (exprStatement.expression->kind == Node::Kind_UnaryMinusExpression) { |
| 741 | const auto &unaryMinusExpr = static_cast<const UnaryMinusExpression &>(*exprStatement.expression); |
| 742 | if (unaryMinusExpr.expression && unaryMinusExpr.expression->kind == Node::Kind_NumericLiteral) { |
| 743 | auto &numericLiteral = static_cast<NumericLiteral &>(*unaryMinusExpr.expression); |
| 744 | const auto value = numericLiteral.value; |
| 745 | numericLiteral.value *= -1; |
| 746 | visit(numericLiteral, ctx, ret); |
| 747 | numericLiteral.value = value; |
| 748 | } |
| 749 | } else if (exprStatement.expression->kind == Node::Kind_UnaryPlusExpression) { |
| 750 | const auto &unaryPlusExpr = static_cast<const UnaryPlusExpression &>(*exprStatement.expression); |
| 751 | if (unaryPlusExpr.expression) |
| 752 | visit(static_cast<const NumericLiteral &>(*unaryPlusExpr.expression), ctx, ret); |
| 753 | } else { |
| 754 | if (ctx.dbgprint) |
| 755 | printf("<expression: %d>\n" , exprStatement.expression->kind); |
| 756 | } |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | static void visit(const QQmlJS::AST::IdentifierExpression &idExpr, Context &ctx, int &ret) |
| 761 | { |
| 762 | Q_UNUSED(ret); |
| 763 | if (ctx.dbgprint) |
| 764 | printf("-> Identifier: %s\n" , idExpr.name.toLocal8Bit().constData()); |
| 765 | |
| 766 | if (ctx.property.target && ctx.type != Context::Type::Component) { |
| 767 | const auto foundIt = ctx.identifierMap.constFind(idExpr.name); |
| 768 | const auto end = ctx.identifierMap.constEnd(); |
| 769 | if (foundIt != end) { // If an item was found it means this is a reference |
| 770 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 771 | if (QQuick3DMaterial *mat = qobject_cast<QQuick3DMaterial *>(*foundIt)) { |
| 772 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
| 773 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
| 774 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 775 | if (ctx.dbgprint) |
| 776 | printf(format: "Clearing inherited materials\n" ); |
| 777 | materials.clear(&materials); |
| 778 | ctx.property.memberState = Context::Property::Initialized; |
| 779 | } |
| 780 | materials.append(&materials, mat); |
| 781 | if (ctx.dbgprint) |
| 782 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
| 783 | } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) { |
| 784 | qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList); |
| 785 | if (ctx.dbgprint) |
| 786 | printf(format: "Setting instance list on model\n" ); |
| 787 | } |
| 788 | } else if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
| 789 | if (QQuick3DEffect *effect = qobject_cast<QQuick3DEffect *>(*foundIt)) { |
| 790 | auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects(); |
| 791 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
| 792 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 793 | if (ctx.dbgprint) |
| 794 | printf(format: "Clearing inherited effects\n" ); |
| 795 | effects.clear(&effects); |
| 796 | ctx.property.memberState = Context::Property::Initialized; |
| 797 | } |
| 798 | effects.append(&effects, effect); |
| 799 | if (ctx.dbgprint) |
| 800 | printf(format: "Appending effect to \'%s\'\n" , ctx.property.name.toLatin1().constData()); |
| 801 | } |
| 802 | } else if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
| 803 | if (QQuick3DShaderUtilsShader *shader = qobject_cast<QQuick3DShaderUtilsShader *>(*foundIt)) { |
| 804 | auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders(); |
| 805 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
| 806 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 807 | if (ctx.dbgprint) |
| 808 | printf(format: "Clearing inherited shaders\n" ); |
| 809 | shaders.clear(&shaders); |
| 810 | ctx.property.memberState = Context::Property::Initialized; |
| 811 | } |
| 812 | shaders.append(&shaders, shader); |
| 813 | if (ctx.dbgprint) |
| 814 | printf(format: "Appending shader to \'%s\'\n" , ctx.property.name.toLatin1().constData()); |
| 815 | } |
| 816 | } else if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) { |
| 817 | if (QQuick3DInstanceListEntry *listEntry = qobject_cast<QQuick3DInstanceListEntry *>(*foundIt)) { |
| 818 | auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances(); |
| 819 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
| 820 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 821 | if (ctx.dbgprint) |
| 822 | printf(format: "Clearing inherited instances\n" ); |
| 823 | instances.clear(&instances); |
| 824 | ctx.property.memberState = Context::Property::Initialized; |
| 825 | } |
| 826 | instances.append(&instances, listEntry); |
| 827 | if (ctx.dbgprint) |
| 828 | printf(format: "Appending instance entry to %s\n" , ctx.property.name.toLatin1().constData()); |
| 829 | } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) { |
| 830 | qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList); |
| 831 | if (ctx.dbgprint) |
| 832 | printf(format: "Setting instance list on model\n" ); |
| 833 | } |
| 834 | } else if (ctx.dbgprint) { |
| 835 | printf("Unhandled binding: %s\n" , idExpr.name.toLatin1().constData()); |
| 836 | } |
| 837 | } else { |
| 838 | // If no item with 'this' id was found, then that id is for 'this' object (if this not the case, then something is broken, e.g., a ref to an unknown item). |
| 839 | // NOTE: This can be a problem in the future and we might need to add some more guards, but for now it just won't generate the correct shader(s). |
| 840 | ctx.identifierMap.insert(idExpr.name, ctx.property.target); |
| 841 | } |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | static void visit(const QQmlJS::AST::StringLiteral &stringLiteral, Context &ctx, int &ret) |
| 846 | { |
| 847 | Q_UNUSED(ret); |
| 848 | if (ctx.dbgprint) |
| 849 | printf("-> StringLiteral: \"%s\"\n" , stringLiteral.value.toLocal8Bit().constData()); |
| 850 | |
| 851 | if (ctx.property.target) { |
| 852 | const auto &name = ctx.property.name; |
| 853 | const auto v = fromString(stringLiteral.value, ctx); |
| 854 | if (v.isValid()) { |
| 855 | const bool b = ctx.property.target->setProperty(name.toLatin1(), v); |
| 856 | if (b && ctx.dbgprint) |
| 857 | printf(format: "Property %s updated!\n" , name.toLatin1().constData()); |
| 858 | } |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | static void visit(const QQmlJS::AST::NumericLiteral &numericLiteral, Context &ctx, int &ret) |
| 863 | { |
| 864 | Q_UNUSED(ret); |
| 865 | if (ctx.dbgprint) |
| 866 | printf("-> NumericLiteral: %f\n" , numericLiteral.value); |
| 867 | |
| 868 | if (ctx.property.target) { |
| 869 | auto target = ctx.property.target; |
| 870 | const auto &name = ctx.property.name; |
| 871 | target->setProperty(name.toLatin1(), QVariant::fromValue(numericLiteral.value)); |
| 872 | } |
| 873 | } |
| 874 | |
| 875 | static void visit(const QQmlJS::AST::FieldMemberExpression &fieldMemberExpression, Context &ctx, int &ret) |
| 876 | { |
| 877 | using namespace QQmlJS::AST; |
| 878 | |
| 879 | Q_UNUSED(ret); |
| 880 | if (ctx.dbgprint) |
| 881 | printf("-> FieldMemberExpression: %s\n" , fieldMemberExpression.name.toLocal8Bit().constData()); |
| 882 | |
| 883 | if (ctx.property.target) { |
| 884 | const auto &name = ctx.property.name; |
| 885 | const auto v = fromString(fieldMemberExpression.name, ctx); |
| 886 | if (v.isValid()) |
| 887 | ctx.property.target->setProperty(name.toLatin1(), v); |
| 888 | } |
| 889 | } |
| 890 | |
| 891 | static void visit(const QQmlJS::AST::ArrayPattern &arrayPattern, Context &ctx, int &ret) |
| 892 | { |
| 893 | Q_UNUSED(ret); |
| 894 | if (ctx.dbgprint) |
| 895 | printf(format: "-> [ " ); |
| 896 | |
| 897 | using namespace QQmlJS::AST; |
| 898 | using PatternElementItem = PatternElementList; |
| 899 | using PatternElementListView = InvasiveListView<PatternElementItem>; |
| 900 | |
| 901 | PatternElementListView elements(*arrayPattern.elements); |
| 902 | for (auto &element : elements) { |
| 903 | auto patternElement = element.element; |
| 904 | if (patternElement->type == PatternElement::Literal) { |
| 905 | if (patternElement->initializer && patternElement->initializer->kind == Node::Kind_IdentifierExpression) { |
| 906 | const auto &identExpression = static_cast<const IdentifierExpression &>(*patternElement->initializer); |
| 907 | visit(identExpression, ctx, ret); |
| 908 | } |
| 909 | } else if (ctx.dbgprint) { |
| 910 | printf("Unahandled(%d), " , patternElement->type); |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | if (ctx.dbgprint) |
| 915 | printf(format: " ]\n" ); |
| 916 | |
| 917 | } |
| 918 | |
| 919 | static void visit(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret) |
| 920 | { |
| 921 | using namespace QQmlJS::AST; |
| 922 | |
| 923 | Q_UNUSED(ret); |
| 924 | Q_UNUSED(callExpression); |
| 925 | if (ctx.dbgprint) |
| 926 | printf("-> Call(%d)\n" , callExpression.base->kind); |
| 927 | |
| 928 | (ctx.interceptCallExpr && ctx.interceptCallExpr(callExpression, ctx, ret)); |
| 929 | } |
| 930 | |
| 931 | static void visit(const QQmlJS::AST::UiObjectMember &member, Context &ctx, int &ret) |
| 932 | { |
| 933 | using namespace QQmlJS::AST; |
| 934 | |
| 935 | if (member.kind == Node::Kind_UiObjectBinding) |
| 936 | visit(static_cast<const UiObjectBinding &>(member), ctx, ret); |
| 937 | else if (ctx.dbgprint) |
| 938 | printf("Unhandled member (%d)\n" , member.kind); |
| 939 | } |
| 940 | |
| 941 | static void visit(const QQmlJS::AST::UiObjectMemberList &memberList, Context &ctx, int &ret) |
| 942 | { |
| 943 | using namespace QQmlJS::AST; |
| 944 | using ObjectMemberItem = UiObjectMemberList; |
| 945 | using ObjectMembers = InvasiveListView<ObjectMemberItem>; |
| 946 | |
| 947 | const auto oldEvalType = ctx.property.memberState; |
| 948 | |
| 949 | ObjectMembers objectMembers(memberList); |
| 950 | for (const auto &member : objectMembers) { |
| 951 | if (member.member) { |
| 952 | if (member.member->kind == Node::Kind_UiScriptBinding) { |
| 953 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
| 954 | const auto &scriptBinding = static_cast<const UiScriptBinding &>(*member.member); |
| 955 | visit(scriptBinding, ctx, ret); |
| 956 | } else if (member.member->kind == Node::Kind_UiArrayBinding) { |
| 957 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
| 958 | const auto &arrayBinding = static_cast<const UiArrayBinding &>(*member.member); |
| 959 | visit(arrayBinding, ctx, ret); |
| 960 | } else if (member.member->kind == Node::Kind_UiObjectDefinition) { |
| 961 | const auto &objectDef = static_cast<const UiObjectDefinition &>(*member.member); |
| 962 | visit(objectDef, ctx, ret); |
| 963 | } else if (member.member->kind == Node::Kind_UiObjectBinding) { |
| 964 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
| 965 | const auto &objBinding = static_cast<const UiObjectBinding &>(*member.member); |
| 966 | visit(objBinding, ctx, ret); |
| 967 | } else if (member.member->kind == Node::Kind_UiPublicMember) { |
| 968 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
| 969 | const auto &pubMember = static_cast<const UiPublicMember &>(*member.member); |
| 970 | visit(pubMember, ctx, ret); |
| 971 | } else { |
| 972 | if (ctx.dbgprint) |
| 973 | printf("<member %d>\n" , member.member->kind); |
| 974 | } |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | ctx.property.memberState = oldEvalType; |
| 979 | } |
| 980 | |
| 981 | private: |
| 982 | Visitors() = delete; |
| 983 | Q_DISABLE_COPY(Visitors); |
| 984 | }; |
| 985 | |
| 986 | template <typename O, typename T> |
| 987 | T *buildType(const O &obj, Context &ctx, int &ret, const T *base = nullptr) |
| 988 | { |
| 989 | // swap -> reentrancy |
| 990 | Context::Property property; |
| 991 | qSwap(value1&: property, value2&: ctx.property); |
| 992 | Q_ASSERT(ctx.property.target == nullptr); |
| 993 | |
| 994 | T *instance = nullptr; |
| 995 | |
| 996 | if (ctx.dbgprint) |
| 997 | printf("Building %s!\n" , TypeInfo<T>::qmlTypeName()); |
| 998 | |
| 999 | if (obj.initializer) { |
| 1000 | instance = new T; |
| 1001 | if (base) |
| 1002 | cloneProperties(*instance, *base); |
| 1003 | |
| 1004 | if (obj.initializer) { |
| 1005 | ctx.property.target = instance; |
| 1006 | ctx.property.targetType = TypeInfo<T>::typeId(); |
| 1007 | Visitors::visit(*obj.initializer, ctx, ret); |
| 1008 | } |
| 1009 | } |
| 1010 | |
| 1011 | // swap back |
| 1012 | qSwap(value1&: property, value2&: ctx.property); |
| 1013 | |
| 1014 | return instance; |
| 1015 | } |
| 1016 | |
| 1017 | static QQuick3DAbstractLight *buildLight(const QQmlJS::AST::UiObjectDefinition &def, |
| 1018 | Context &ctx, |
| 1019 | int &ret, |
| 1020 | int lightType, |
| 1021 | const QQuick3DAbstractLight *base = nullptr) |
| 1022 | { |
| 1023 | if (lightType == TypeInfo<QQuick3DDirectionalLight>::typeId()) |
| 1024 | return buildType(def, ctx, ret, qobject_cast<const QQuick3DDirectionalLight *>(object: base)); |
| 1025 | if (lightType == TypeInfo<QQuick3DPointLight>::typeId()) |
| 1026 | return buildType(def, ctx, ret, qobject_cast<const QQuick3DPointLight *>(object: base)); |
| 1027 | if (lightType == TypeInfo<QQuick3DSpotLight>::typeId()) |
| 1028 | return buildType(def, ctx, ret, qobject_cast<const QQuick3DSpotLight *>(object: base)); |
| 1029 | return nullptr; |
| 1030 | } |
| 1031 | |
| 1032 | |
| 1033 | template <typename T> |
| 1034 | static void updateProperty(Context &ctx, T type, QStringView propName) |
| 1035 | { |
| 1036 | if (ctx.property.target) { |
| 1037 | if (ctx.dbgprint) |
| 1038 | printf(format: "Updating property %s\n" , propName.toLatin1().constData()); |
| 1039 | const auto &target = ctx.property.target; |
| 1040 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1041 | target->setProperty(propName.toLatin1().constData(), QVariant::fromValue(type)); |
| 1042 | ctx.property.memberState = Context::Property::Initialized; |
| 1043 | } else { |
| 1044 | const int idx = target->metaObject()->indexOfProperty(name: propName.toLatin1().constData()); |
| 1045 | if (idx != -1) { |
| 1046 | auto prop = target->metaObject()->property(index: idx); |
| 1047 | prop.write(target, QVariant::fromValue(type)); |
| 1048 | } |
| 1049 | } |
| 1050 | } |
| 1051 | } |
| 1052 | |
| 1053 | static bool interceptObjectBinding(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret) |
| 1054 | { |
| 1055 | if (ctx.dbgprint) |
| 1056 | printf(format: "Intercepted object binding!\n" ); |
| 1057 | |
| 1058 | bool handled = false; |
| 1059 | |
| 1060 | const auto &typeName = objectBinding.qualifiedTypeNameId->name.toString(); |
| 1061 | const auto &propName = objectBinding.qualifiedId->name; |
| 1062 | |
| 1063 | int type = -1; |
| 1064 | |
| 1065 | // Base type? |
| 1066 | const auto typeIt = s_typeMap->constFind(typeName); |
| 1067 | if (typeIt != s_typeMap->cend()) |
| 1068 | type = *typeIt; |
| 1069 | |
| 1070 | // Component? |
| 1071 | auto &components = ctx.components; |
| 1072 | const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend(); |
| 1073 | QObject *base = nullptr; |
| 1074 | if (compIt != components.cend()) { |
| 1075 | type = compIt->type; |
| 1076 | base = compIt->ptr; |
| 1077 | } |
| 1078 | |
| 1079 | if (type != -1) { |
| 1080 | if (ctx.dbgprint) |
| 1081 | printf("Resolving: \'%s\'\n" , qPrintable(typeName)); |
| 1082 | |
| 1083 | if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
| 1084 | if (ctx.property.targetType == TypeInfo<QQuick3DViewport>::typeId()) { |
| 1085 | if (auto environment = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DSceneEnvironment *>(base))) { |
| 1086 | auto viewport = qobject_cast<QQuick3DViewport *>(object: ctx.property.target); |
| 1087 | Q_ASSERT(viewport); |
| 1088 | viewport->setEnvironment(environment); |
| 1089 | handled = true; |
| 1090 | } |
| 1091 | } |
| 1092 | } else if (type == TypeInfo<QQuick3DTexture>::typeId()) { |
| 1093 | if (auto tex = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DTexture *>(base))) { |
| 1094 | updateProperty(ctx, tex, propName); |
| 1095 | ctx.sceneData.textures.append(tex); |
| 1096 | } |
| 1097 | handled = true; |
| 1098 | } else if (type == TypeInfo<QQuick3DShaderUtilsTextureInput>::typeId()) { |
| 1099 | auto texInput = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsTextureInput *>(object: base)); |
| 1100 | if (texInput && texInput->texture()) { |
| 1101 | updateProperty(ctx, texInput, propName); |
| 1102 | ctx.sceneData.textures.append(texInput->texture()); |
| 1103 | } |
| 1104 | handled = true; |
| 1105 | } else if (type == TypeInfo<QQuick3DEffect>::typeId()) { |
| 1106 | if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
| 1107 | if (auto effect = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DEffect *>(base))) { |
| 1108 | auto sceneEnvironment = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target); |
| 1109 | Q_ASSERT(sceneEnvironment); |
| 1110 | auto effects = sceneEnvironment->effects(); |
| 1111 | effects.append(&effects, effect); |
| 1112 | handled = true; |
| 1113 | } |
| 1114 | } |
| 1115 | } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
| 1116 | if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) { |
| 1117 | if (auto pass = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsRenderPass *>(base))) { |
| 1118 | auto effect = qobject_cast<QQuick3DEffect *>(object: ctx.property.target); |
| 1119 | Q_ASSERT(effect); |
| 1120 | auto passes = effect->passes(); |
| 1121 | passes.append(&passes, pass); |
| 1122 | handled = true; |
| 1123 | } |
| 1124 | } |
| 1125 | } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) { |
| 1126 | if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
| 1127 | if (auto shader = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsShader *>(base))) { |
| 1128 | auto pass = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target); |
| 1129 | Q_ASSERT(pass); |
| 1130 | auto shaders = pass->shaders(); |
| 1131 | shaders.append(&shaders, shader); |
| 1132 | handled = true; |
| 1133 | } |
| 1134 | } |
| 1135 | } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) { |
| 1136 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1137 | if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DDefaultMaterial *>(base))) { |
| 1138 | auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target); |
| 1139 | Q_ASSERT(model); |
| 1140 | auto materials = model->materials(); |
| 1141 | materials.append(&materials, mat); |
| 1142 | handled = true; |
| 1143 | if (ctx.dbgprint) |
| 1144 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1145 | } |
| 1146 | } |
| 1147 | } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
| 1148 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1149 | if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DPrincipledMaterial *>(base))) { |
| 1150 | auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target); |
| 1151 | Q_ASSERT(model); |
| 1152 | auto materials = model->materials(); |
| 1153 | materials.append(&materials, mat); |
| 1154 | handled = true; |
| 1155 | } |
| 1156 | } |
| 1157 | } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) { |
| 1158 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1159 | if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DCustomMaterial *>(base))) { |
| 1160 | auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target); |
| 1161 | Q_ASSERT(model); |
| 1162 | auto materials = model->materials(); |
| 1163 | materials.append(&materials, mat); |
| 1164 | handled = true; |
| 1165 | } |
| 1166 | } |
| 1167 | } else if (ctx.dbgprint) { |
| 1168 | printf(format: "Unhandled type\n" ); |
| 1169 | } |
| 1170 | } |
| 1171 | |
| 1172 | return handled; |
| 1173 | } |
| 1174 | |
| 1175 | static bool interceptObjectDef(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret) |
| 1176 | { |
| 1177 | const auto &typeName = def.qualifiedTypeNameId->name.toString(); |
| 1178 | |
| 1179 | if (ctx.dbgprint) |
| 1180 | printf("Intercepted object definition (\'%s\')!\n" , typeName.toLatin1().constData()); |
| 1181 | |
| 1182 | QString componentName; |
| 1183 | int type = -1; |
| 1184 | bool doRegisterComponent = false; |
| 1185 | |
| 1186 | // Base type? |
| 1187 | const auto typeIt = s_typeMap->constFind(typeName); |
| 1188 | if (typeIt != s_typeMap->cend()) |
| 1189 | type = *typeIt; |
| 1190 | |
| 1191 | // Component? |
| 1192 | auto &components = ctx.components; |
| 1193 | const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend(); |
| 1194 | if (compIt != components.cend()) |
| 1195 | type = compIt->type; |
| 1196 | |
| 1197 | // If this is a new component register it |
| 1198 | if (ctx.type == Context::Type::Component && ctx.property.target == nullptr && type != -1) { |
| 1199 | const auto &fileName = ctx.currentFileInfo.fileName(); |
| 1200 | componentName = fileName.left(n: fileName.size() - 4); |
| 1201 | doRegisterComponent = !componentName.isEmpty(); |
| 1202 | } |
| 1203 | |
| 1204 | const auto registerComponent = [&ctx, &components, &componentName](Context::Component component) { |
| 1205 | if (ctx.dbgprint) |
| 1206 | printf(format: "Registering component \'%s\'\n" , qPrintable(componentName)); |
| 1207 | components.insert(key: componentName, value: component); |
| 1208 | }; |
| 1209 | |
| 1210 | if (type == TypeInfo<QQuick3DViewport>::typeId()) { |
| 1211 | const QQuick3DViewport *base = (compIt != components.cend()) ? qobject_cast<QQuick3DViewport *>(compIt->ptr) : nullptr; |
| 1212 | if (QQuick3DViewport *viewport = buildType(def, ctx, ret, base)) { |
| 1213 | // If this is a component we'll store it for lookups later. |
| 1214 | if (doRegisterComponent) |
| 1215 | registerComponent({ .ptr: viewport, .type: type }); |
| 1216 | // Only one viewport supported atm (see SceneEnvironment case as well). |
| 1217 | if (!ctx.sceneData.viewport) |
| 1218 | ctx.sceneData.viewport = viewport; |
| 1219 | } |
| 1220 | } else if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
| 1221 | const QQuick3DSceneEnvironment *base = (compIt != components.cend()) ? qobject_cast<QQuick3DSceneEnvironment *>(compIt->ptr) : nullptr; |
| 1222 | if (QQuick3DSceneEnvironment *sceneEnv = buildType(def, ctx, ret, base)) { |
| 1223 | // If this is a component we'll store it for lookups later. |
| 1224 | if (doRegisterComponent) |
| 1225 | registerComponent({ .ptr: sceneEnv, .type: type }); |
| 1226 | |
| 1227 | if (ctx.sceneData.viewport) |
| 1228 | ctx.sceneData.viewport->setEnvironment(sceneEnv); |
| 1229 | } |
| 1230 | } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
| 1231 | const QQuick3DPrincipledMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DPrincipledMaterial *>(compIt->ptr) : nullptr; |
| 1232 | if (QQuick3DPrincipledMaterial *mat = buildType(def, ctx, ret, base)) { |
| 1233 | // If this is a component we'll store it for lookups later. |
| 1234 | if (doRegisterComponent) |
| 1235 | registerComponent({ .ptr: mat, .type: type }); |
| 1236 | |
| 1237 | if (ctx.property.target) { |
| 1238 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1239 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
| 1240 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1241 | if (ctx.dbgprint) |
| 1242 | printf(format: "Clearing inherited materials\n" ); |
| 1243 | materials.clear(&materials); |
| 1244 | ctx.property.memberState = Context::Property::Initialized; |
| 1245 | } |
| 1246 | materials.append(&materials, mat); |
| 1247 | if (ctx.dbgprint) |
| 1248 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1249 | } |
| 1250 | } |
| 1251 | |
| 1252 | // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list |
| 1253 | ctx.sceneData.materials.push_back(t: mat); |
| 1254 | } |
| 1255 | } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) { |
| 1256 | const QQuick3DDefaultMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DDefaultMaterial *>(compIt->ptr) : nullptr; |
| 1257 | if (QQuick3DDefaultMaterial *mat = buildType(def, ctx, ret, base)) { |
| 1258 | // If this is a component we'll store it for lookups later. |
| 1259 | if (doRegisterComponent) |
| 1260 | registerComponent({ .ptr: mat, .type: type }); |
| 1261 | |
| 1262 | if (ctx.property.target) { |
| 1263 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1264 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
| 1265 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1266 | if (ctx.dbgprint) |
| 1267 | printf(format: "Clearing inherited materials\n" ); |
| 1268 | materials.clear(&materials); |
| 1269 | ctx.property.memberState = Context::Property::Initialized; |
| 1270 | } |
| 1271 | materials.append(&materials, mat); |
| 1272 | if (ctx.dbgprint) |
| 1273 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1274 | } |
| 1275 | } |
| 1276 | |
| 1277 | // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list |
| 1278 | ctx.sceneData.materials.push_back(t: mat); |
| 1279 | } |
| 1280 | } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) { |
| 1281 | const QQuick3DCustomMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DCustomMaterial *>(compIt->ptr) : nullptr; |
| 1282 | if (QQuick3DCustomMaterial *mat = buildType(def, ctx, ret, base)) { |
| 1283 | // If this is a component we'll store it for lookups later. |
| 1284 | if (doRegisterComponent) |
| 1285 | registerComponent({ .ptr: mat, .type: type }); |
| 1286 | |
| 1287 | if (ctx.property.target) { |
| 1288 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1289 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
| 1290 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1291 | if (ctx.dbgprint) |
| 1292 | printf(format: "Clearing inherited materials\n" ); |
| 1293 | materials.clear(&materials); |
| 1294 | ctx.property.memberState = Context::Property::Initialized; |
| 1295 | } |
| 1296 | materials.append(&materials, mat); |
| 1297 | if (ctx.dbgprint) |
| 1298 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1299 | } |
| 1300 | } |
| 1301 | |
| 1302 | // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list |
| 1303 | ctx.sceneData.materials.push_back(t: mat); |
| 1304 | } |
| 1305 | } else if (type == TypeInfo<QQuick3DEffect>::typeId()) { |
| 1306 | const QQuick3DEffect *base = (compIt != components.cend()) ? qobject_cast<QQuick3DEffect *>(compIt->ptr) : nullptr; |
| 1307 | if (QQuick3DEffect *effect = buildType(def, ctx, ret, base)) { |
| 1308 | // If this is a component we'll store it for lookups later. |
| 1309 | if (doRegisterComponent) |
| 1310 | registerComponent({ .ptr: effect, .type: type }); |
| 1311 | |
| 1312 | if (ctx.property.target) { |
| 1313 | if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
| 1314 | auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects(); |
| 1315 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1316 | if (ctx.dbgprint) |
| 1317 | printf(format: "Clearing inherited effects\n" ); |
| 1318 | effects.clear(&effects); |
| 1319 | ctx.property.memberState = Context::Property::Initialized; |
| 1320 | } |
| 1321 | effects.append(&effects, effect); |
| 1322 | if (ctx.dbgprint) |
| 1323 | printf(format: "Appending effect to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1324 | } |
| 1325 | } |
| 1326 | |
| 1327 | // At this point we don't know if this effect is going to be referenced somewhere else, so keep it in the list |
| 1328 | ctx.sceneData.effects.push_back(t: effect); |
| 1329 | } |
| 1330 | } else if (type == TypeInfo<QQuick3DDirectionalLight>::typeId() || type == TypeInfo<QQuick3DPointLight>::typeId() || type == TypeInfo<QQuick3DSpotLight>::typeId()) { |
| 1331 | const QQuick3DAbstractLight *base = (compIt != components.cend()) ? qobject_cast<QQuick3DAbstractLight *>(compIt->ptr) : nullptr; |
| 1332 | if (QQuick3DAbstractLight *light = buildLight(def, ctx, ret, type, base)) { |
| 1333 | // If this is a component we'll store it for lookups later. |
| 1334 | if (doRegisterComponent) |
| 1335 | registerComponent({ .ptr: light, .type: type }); |
| 1336 | |
| 1337 | ctx.sceneData.lights.push_back(t: light); |
| 1338 | } |
| 1339 | } else if (type == TypeInfo<QQuick3DTexture>::typeId()) { |
| 1340 | const QQuick3DTexture *base = (compIt != components.cend()) ? qobject_cast<QQuick3DTexture *>(compIt->ptr) : nullptr; |
| 1341 | if (QQuick3DTexture *tex = buildType(def, ctx, ret, base)) { |
| 1342 | // If this is a component we'll store it for lookups later. |
| 1343 | if (doRegisterComponent) |
| 1344 | registerComponent({ .ptr: tex, .type: type }); |
| 1345 | |
| 1346 | ctx.sceneData.textures.push_back(t: tex); |
| 1347 | } |
| 1348 | } else if (type == TypeInfo<QQuick3DModel>::typeId()) { |
| 1349 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DModel *>(compIt->ptr) : nullptr; |
| 1350 | if (auto *model = buildType(def, ctx, ret, base)) { |
| 1351 | // If this is a component we'll store it for lookups later. |
| 1352 | if (doRegisterComponent) |
| 1353 | registerComponent({ model, type }); |
| 1354 | |
| 1355 | ctx.sceneData.models.push_back(model); |
| 1356 | } |
| 1357 | } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) { |
| 1358 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsShader *>(compIt->ptr) : nullptr; |
| 1359 | if (auto *shader = buildType(def, ctx, ret, base)) { |
| 1360 | // If this is a component we'll store it for lookups later. |
| 1361 | if (doRegisterComponent) |
| 1362 | registerComponent({ shader, type }); |
| 1363 | if (ctx.property.target) { |
| 1364 | if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
| 1365 | auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders(); |
| 1366 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1367 | if (ctx.dbgprint) |
| 1368 | printf(format: "Clearing inherited shaders\n" ); |
| 1369 | shaders.clear(&shaders); |
| 1370 | ctx.property.memberState = Context::Property::Initialized; |
| 1371 | } |
| 1372 | shaders.append(&shaders, shader); |
| 1373 | if (ctx.dbgprint) |
| 1374 | printf(format: "Appending shader to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1375 | } |
| 1376 | } |
| 1377 | |
| 1378 | ctx.sceneData.shaders.push_back(shader); |
| 1379 | } |
| 1380 | } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
| 1381 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsRenderPass *>(compIt->ptr) : nullptr; |
| 1382 | if (auto *pass = buildType(def, ctx, ret, base)) { |
| 1383 | // If this is a component we'll store it for lookups later. |
| 1384 | if (doRegisterComponent) |
| 1385 | registerComponent({ pass, type }); |
| 1386 | if (ctx.property.target) { |
| 1387 | if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) { |
| 1388 | auto passes = qobject_cast<QQuick3DEffect *>(object: ctx.property.target)->passes(); |
| 1389 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
| 1390 | if (ctx.dbgprint) |
| 1391 | printf(format: "Clearing inherited passes\n" ); |
| 1392 | passes.clear(&passes); |
| 1393 | ctx.property.memberState = Context::Property::Initialized; |
| 1394 | } |
| 1395 | passes.append(&passes, pass); |
| 1396 | if (ctx.dbgprint) |
| 1397 | printf(format: "Appending pass to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1398 | } |
| 1399 | } |
| 1400 | } |
| 1401 | } else if (type == TypeInfo<QQuick3DInstanceList>::typeId()) { |
| 1402 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceList *>(compIt->ptr) : nullptr; |
| 1403 | if (auto *instanceList = buildType(def, ctx, ret, base)) { |
| 1404 | // If this is a component we'll store it for lookups later. |
| 1405 | if (doRegisterComponent) |
| 1406 | registerComponent({ instanceList, type }); |
| 1407 | if (ctx.property.target) { |
| 1408 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
| 1409 | qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instanceList); |
| 1410 | if (ctx.dbgprint) |
| 1411 | printf(format: "Setting instance list on %s\n" , ctx.property.name.toLatin1().constData()); |
| 1412 | } |
| 1413 | } |
| 1414 | } |
| 1415 | } else if (type == TypeInfo<QQuick3DInstanceListEntry>::typeId()) { |
| 1416 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceListEntry *>(compIt->ptr) : nullptr; |
| 1417 | if (auto *instanceListEntry = buildType(def, ctx, ret, base)) { |
| 1418 | // If this is a component we'll store it for lookups later. |
| 1419 | if (doRegisterComponent) |
| 1420 | registerComponent({ instanceListEntry, type }); |
| 1421 | if (ctx.property.target) { |
| 1422 | if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) { |
| 1423 | auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances(); |
| 1424 | instances.append(&instances, instanceListEntry); |
| 1425 | if (ctx.dbgprint) |
| 1426 | printf(format: "Appending instance list entry to %s\n" , ctx.property.name.toLatin1().constData()); |
| 1427 | } |
| 1428 | } |
| 1429 | } |
| 1430 | } else { |
| 1431 | if (ctx.dbgprint) |
| 1432 | printf(format: "Object def for \'%s\' was not handled\n" , ctx.property.name.toLatin1().constData()); |
| 1433 | return false; |
| 1434 | } |
| 1435 | |
| 1436 | return true; |
| 1437 | } |
| 1438 | |
| 1439 | static bool interceptPublicMember(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret) |
| 1440 | { |
| 1441 | Q_UNUSED(ret); |
| 1442 | using namespace QQmlJS::AST; |
| 1443 | |
| 1444 | if (ctx.dbgprint) |
| 1445 | printf(format: "Intercepted public member!\n" ); |
| 1446 | |
| 1447 | if (member.statement && member.statement->kind == Node::Kind_ExpressionStatement) { |
| 1448 | if ((ctx.property.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId() || ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) && member.memberType) { |
| 1449 | // For custom materials we have properties that are user provided, so we'll |
| 1450 | // need to add these to the objects properties (we add these here to be able |
| 1451 | // to piggyback on the existing type matching code) |
| 1452 | if (member.memberType->name == u"real" ) { |
| 1453 | ctx.property.type = QMetaType::Double; |
| 1454 | } else if (member.memberType->name == u"bool" ) { |
| 1455 | ctx.property.type = QMetaType::Bool; |
| 1456 | } else if (member.memberType->name == u"int" ) { |
| 1457 | ctx.property.type = QMetaType::Int; |
| 1458 | } else if (member.memberType->name == u"size" ) { |
| 1459 | ctx.property.type = QMetaType::QSizeF; |
| 1460 | } else if (member.memberType->name == u"rect" ) { |
| 1461 | ctx.property.type = QMetaType::QRectF; |
| 1462 | } else if (member.memberType->name == u"point" ) { |
| 1463 | ctx.property.type = QMetaType::QPointF; |
| 1464 | } else if (member.memberType->name == u"color" ) { |
| 1465 | ctx.property.type = QMetaType::QColor; |
| 1466 | } else if (member.memberType->name.startsWith(u"vector" )) { |
| 1467 | if (member.memberType->name.endsWith(u"2d" )) { |
| 1468 | ctx.property.type = QMetaType::QVector2D; |
| 1469 | } else if (member.memberType->name.endsWith(u"3d" )) { |
| 1470 | ctx.property.type = QMetaType::QVector3D; |
| 1471 | } else if (member.memberType->name.endsWith(u"4d" )) { |
| 1472 | ctx.property.type = QMetaType::QVector4D; |
| 1473 | } |
| 1474 | } else if (member.memberType->name == u"matrix4x4" ) { |
| 1475 | ctx.property.type = QMetaType::QMatrix4x4;; |
| 1476 | } else if (member.memberType->name == u"quaternion" ) { |
| 1477 | ctx.property.type = QMetaType::QQuaternion; |
| 1478 | } else if (member.memberType->name == u"var" ) { |
| 1479 | ctx.property.type = QMetaType::QVariant; |
| 1480 | } |
| 1481 | } |
| 1482 | } |
| 1483 | |
| 1484 | return false; |
| 1485 | } |
| 1486 | |
| 1487 | static bool interceptCallExpression(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret) |
| 1488 | { |
| 1489 | Q_UNUSED(ret); |
| 1490 | using namespace QQmlJS::AST; |
| 1491 | using namespace BuiltinHelpers; |
| 1492 | |
| 1493 | if (ctx.dbgprint) |
| 1494 | printf(format: "Intercepted call expression!\n" ); |
| 1495 | |
| 1496 | const bool ok = (ctx.property.target && !ctx.property.name.isEmpty()); |
| 1497 | if (callExpression.base && ok) { |
| 1498 | if (callExpression.base->kind == Node::Kind_FieldMemberExpression) { |
| 1499 | const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*callExpression.base); |
| 1500 | if (fieldMemberExpression.base) { |
| 1501 | if (fieldMemberExpression.base->kind == Node::Kind_IdentifierExpression) { |
| 1502 | const auto &identExpr = static_cast<const IdentifierExpression &>(*fieldMemberExpression.base); |
| 1503 | if (identExpr.name == u"Qt" ) { |
| 1504 | bool ok = false; |
| 1505 | QVariant v; |
| 1506 | if (fieldMemberExpression.name == u"point" ) { |
| 1507 | const auto point = toPoint(ArgumentListView(*callExpression.arguments), &ok); |
| 1508 | if (ctx.dbgprint) |
| 1509 | printf("Qt.point(%f, %f)\n" , point.x(), point.y()); |
| 1510 | setProperty(ctx.property, point); |
| 1511 | } else if (fieldMemberExpression.name == u"size" ) { |
| 1512 | const auto size = toSize(ArgumentListView(*callExpression.arguments), &ok); |
| 1513 | if (ctx.dbgprint) |
| 1514 | printf("Qt.size(%f, %f)\n" , size.width(), size.height()); |
| 1515 | setProperty(ctx.property, size); |
| 1516 | } else if (fieldMemberExpression.name == u"rect" ) { |
| 1517 | const auto rect = toRect(ArgumentListView(*callExpression.arguments), &ok); |
| 1518 | if (ctx.dbgprint) |
| 1519 | printf("Qt.rect(%f, %f, %f, %f)\n" , rect.x(), rect.y(), rect.width(), rect.height()); |
| 1520 | setProperty(ctx.property, rect); |
| 1521 | } else if (fieldMemberExpression.name.startsWith(u"vector" )) { |
| 1522 | if (fieldMemberExpression.name.endsWith(u"2d" )) { |
| 1523 | const auto vec2 = toVec<QVector2D>(ArgumentListView(*callExpression.arguments), &ok); |
| 1524 | if (ctx.dbgprint) |
| 1525 | printf("Qt.vector2d(%f, %f)\n" , vec2.x(), vec2.y()); |
| 1526 | setProperty(ctx.property, vec2); |
| 1527 | } else if (fieldMemberExpression.name.endsWith(u"3d" )) { |
| 1528 | const auto vec3 = toVec<QVector3D>(ArgumentListView(*callExpression.arguments), &ok); |
| 1529 | if (ctx.dbgprint) |
| 1530 | printf("Qt.vector3d(%f, %f, %f)\n" , vec3.x(), vec3.y(), vec3.z()); |
| 1531 | setProperty(ctx.property, vec3); |
| 1532 | } else if (fieldMemberExpression.name.endsWith(u"4d" )) { |
| 1533 | const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok); |
| 1534 | if (ctx.dbgprint) |
| 1535 | printf("Qt.vector4d(%f, %f, %f, %f)\n" , vec4.x(), vec4.y(), vec4.z(), vec4.w()); |
| 1536 | setProperty(ctx.property, vec4); |
| 1537 | } |
| 1538 | } else if (fieldMemberExpression.name == u"matrix4x4" ) { |
| 1539 | const auto mat44 = toMat44(ArgumentListView(*callExpression.arguments), &ok); |
| 1540 | if (ctx.dbgprint) |
| 1541 | printf("Qt.matrix4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n" , |
| 1542 | mat44(0, 0), mat44(0, 1), mat44(0, 2), mat44(0, 3), |
| 1543 | mat44(1, 0), mat44(1, 1), mat44(1, 2), mat44(1, 3), |
| 1544 | mat44(2, 0), mat44(2, 1), mat44(2, 2), mat44(2, 3), |
| 1545 | mat44(3, 0), mat44(3, 1), mat44(3, 2), mat44(3, 3)); |
| 1546 | setProperty(ctx.property, mat44); |
| 1547 | } else if (fieldMemberExpression.name == u"quaternion" ) { |
| 1548 | const auto quat = toQuaternion(ArgumentListView(*callExpression.arguments), &ok); |
| 1549 | if (ctx.dbgprint) |
| 1550 | printf("Qt.quaternion(%f, %f, %f, %f)\n" , quat.scalar(), quat.x(), quat.y(), quat.z()); |
| 1551 | setProperty(ctx.property, quat); |
| 1552 | } else if (fieldMemberExpression.name == u"rgba" ) { |
| 1553 | const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok); |
| 1554 | if (ok) { |
| 1555 | QColor color = QColor::fromRgbF(r: vec4.x(), g: vec4.y(), b: vec4.z(), a: vec4.w()); |
| 1556 | if (ctx.dbgprint) |
| 1557 | printf(format: "Qt.rgba(%f, %f, %f, %f)\n" , color.redF(), color.greenF(), color.blueF(), color.alphaF()); |
| 1558 | setProperty(property: ctx.property, v: color); |
| 1559 | } |
| 1560 | } |
| 1561 | if (ok && v.isValid() && ctx.property.target) |
| 1562 | ctx.property.target->setProperty(name: ctx.property.name.toLatin1().constData(), value: v); |
| 1563 | } |
| 1564 | } |
| 1565 | } |
| 1566 | } |
| 1567 | } |
| 1568 | |
| 1569 | return false; |
| 1570 | } |
| 1571 | |
| 1572 | static int parseQmlData(const QByteArray &code, Context &ctx) |
| 1573 | { |
| 1574 | Q_ASSERT(ctx.engine && ctx.engine->lexer()); |
| 1575 | ctx.identifierMap.clear(); // not visible outside the scope of this "code" |
| 1576 | if (ctx.dbgprint) |
| 1577 | printf(format: "Parsing %s\n" , qPrintable(ctx.currentFileInfo.filePath())); |
| 1578 | int ret = 0; |
| 1579 | ctx.engine->lexer()->setCode(code: QString::fromUtf8(ba: code), lineno: 1, qmlMode: true); |
| 1580 | QQmlJS::Parser parser(ctx.engine); |
| 1581 | const bool ok = parser.parse(); |
| 1582 | if (ok) { |
| 1583 | const auto program = parser.ast(); |
| 1584 | if (program) |
| 1585 | Visitors::visit(*program, ctx, ret); |
| 1586 | } else { |
| 1587 | ret = -1; |
| 1588 | qWarning("Parsing failed due to %s in %s:%d%d" , qPrintable(parser.errorMessage()), qPrintable(ctx.currentFileInfo.fileName()), parser.errorLineNumber(), parser.errorColumnNumber()); |
| 1589 | } |
| 1590 | |
| 1591 | return ret; |
| 1592 | } |
| 1593 | |
| 1594 | int MaterialParser::parseQmlData(const QByteArray &code, const QString &fileName, MaterialParser::SceneData &sceneData) |
| 1595 | { |
| 1596 | // set initial type map |
| 1597 | *s_typeMap = baseTypeMap(); |
| 1598 | |
| 1599 | QQmlJS::Engine engine; |
| 1600 | QQmlJS::Lexer lexer(&engine); |
| 1601 | |
| 1602 | Context ctx; |
| 1603 | ctx.engine = &engine; |
| 1604 | ctx.interceptODFunc = &interceptObjectDef; |
| 1605 | ctx.interceptOBFunc = &interceptObjectBinding; |
| 1606 | ctx.interceptPMFunc = &interceptPublicMember; |
| 1607 | ctx.interceptCallExpr = &interceptCallExpression; |
| 1608 | ctx.currentFileInfo = QFileInfo(fileName); |
| 1609 | ctx.type = Context::Type::Component; |
| 1610 | |
| 1611 | const int ret = ::parseQmlData(code, ctx); |
| 1612 | sceneData = std::move(ctx.sceneData); |
| 1613 | |
| 1614 | return ret; |
| 1615 | } |
| 1616 | |
| 1617 | int MaterialParser::parseQmlFiles(const QVector<QString> &filePaths, const QDir &sourceDir, SceneData &sceneData, bool verboseOutput) |
| 1618 | { |
| 1619 | // set initial type map |
| 1620 | *s_typeMap = baseTypeMap(); |
| 1621 | |
| 1622 | int ret = 0; |
| 1623 | |
| 1624 | if (filePaths.isEmpty()) { |
| 1625 | qWarning(msg: "No input files" ); |
| 1626 | return ret; |
| 1627 | } |
| 1628 | |
| 1629 | QQmlJS::Engine engine; |
| 1630 | QQmlJS::Lexer lexer(&engine); |
| 1631 | |
| 1632 | Context ctx; |
| 1633 | ctx.dbgprint = verboseOutput; |
| 1634 | ctx.engine = &engine; |
| 1635 | ctx.interceptODFunc = &interceptObjectDef; |
| 1636 | ctx.interceptOBFunc = &interceptObjectBinding; |
| 1637 | ctx.interceptPMFunc = &interceptPublicMember; |
| 1638 | ctx.interceptCallExpr = &interceptCallExpression; |
| 1639 | ctx.workingDir = sourceDir; |
| 1640 | |
| 1641 | QVector<QString> deferredOther; |
| 1642 | QVector<QString> deferredComponets; |
| 1643 | |
| 1644 | const QString sourcePath = sourceDir.canonicalPath() + QDir::separator(); |
| 1645 | |
| 1646 | const bool isMultifile = filePaths.size() != 1; |
| 1647 | |
| 1648 | // Go through and find the material components first |
| 1649 | for (const auto &v : filePaths) { |
| 1650 | QFileInfo ¤tFileInfo = ctx.currentFileInfo; |
| 1651 | if (!QFileInfo(v).isAbsolute()) |
| 1652 | currentFileInfo.setFile(sourcePath + v); |
| 1653 | else |
| 1654 | currentFileInfo.setFile(v); |
| 1655 | const bool maybeComponent = currentFileInfo.fileName().at(i: 0).isUpper(); |
| 1656 | if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) { |
| 1657 | const QString filePath = currentFileInfo.canonicalFilePath(); |
| 1658 | if (isMultifile && maybeComponent) { |
| 1659 | QFile f(filePath); |
| 1660 | if (!f.open(flags: QFile::ReadOnly)) { |
| 1661 | qWarning(msg: "Could not open file %s for reading!" , qPrintable(filePath)); |
| 1662 | return -1; |
| 1663 | } |
| 1664 | |
| 1665 | const QByteArray code = f.readAll(); |
| 1666 | int idx = code.indexOf(ch: '{'); |
| 1667 | if (idx != -1) { |
| 1668 | const QByteArray section = code.mid(index: 0, len: idx); |
| 1669 | QVarLengthArray<const char *, 3> componentTypes { TypeInfo<QQuick3DPrincipledMaterial>::qmlTypeName(), |
| 1670 | TypeInfo<QQuick3DCustomMaterial>::qmlTypeName(), |
| 1671 | TypeInfo<QQuick3DDefaultMaterial>::qmlTypeName()}; |
| 1672 | for (const auto compType : std::as_const(t&: componentTypes)) { |
| 1673 | if ((idx = section.indexOf(bv: compType)) != -1) |
| 1674 | break; |
| 1675 | } |
| 1676 | if (idx != -1) { |
| 1677 | ctx.type = Context::Type::Component; |
| 1678 | ret = parseQmlData(code, ctx); |
| 1679 | if (ret != 0) |
| 1680 | break; |
| 1681 | } else { |
| 1682 | deferredComponets.push_back(t: filePath); |
| 1683 | } |
| 1684 | } else { |
| 1685 | qWarning(msg: "No items found in %s\n" , qPrintable(filePath)); |
| 1686 | } |
| 1687 | } else { |
| 1688 | deferredOther.push_back(t: filePath); |
| 1689 | } |
| 1690 | } else { |
| 1691 | qWarning(msg: "The file %s is either not a file or has the wrong extension!" , qPrintable(v)); |
| 1692 | } |
| 1693 | } |
| 1694 | |
| 1695 | const auto parsePaths = [&ctx, &ret](const QVector<QString> &paths, Context::Type type) { |
| 1696 | ctx.type = type; |
| 1697 | for (const auto &path : paths) { |
| 1698 | QFileInfo ¤tFileInfo = ctx.currentFileInfo; |
| 1699 | currentFileInfo.setFile(path); |
| 1700 | if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) { |
| 1701 | const QString filePath = currentFileInfo.canonicalFilePath(); |
| 1702 | QFile f(filePath); |
| 1703 | if (!f.open(flags: QFile::ReadOnly)) { |
| 1704 | qWarning(msg: "Could not open file %s for reading!" , qPrintable(filePath)); |
| 1705 | ret = -1; |
| 1706 | return; |
| 1707 | } |
| 1708 | |
| 1709 | const QByteArray code = f.readAll(); |
| 1710 | ret = parseQmlData(code, ctx); |
| 1711 | if (ret != 0) |
| 1712 | break; |
| 1713 | } |
| 1714 | } |
| 1715 | }; |
| 1716 | |
| 1717 | // Other components |
| 1718 | parsePaths(deferredComponets, Context::Type::Component); |
| 1719 | |
| 1720 | // Now parse the rest |
| 1721 | parsePaths(deferredOther, Context::Type::Application); |
| 1722 | |
| 1723 | sceneData = std::move(ctx.sceneData); |
| 1724 | |
| 1725 | return ret; |
| 1726 | } |
| 1727 | |
| 1728 | QT_END_NAMESPACE |
| 1729 | |