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 | |