1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
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 | const auto &item = sl.at(&sl, i); |
404 | tl.append(&tl, item); |
405 | } |
406 | } |
407 | } |
408 | |
409 | static void cloneProperties(QObject &target, const QObject &source) |
410 | { |
411 | Q_ASSERT(target.metaObject() == source.metaObject()); |
412 | const auto smo = source.metaObject(); |
413 | auto tmo = target.metaObject(); |
414 | const int propCount = smo->propertyCount(); |
415 | for (int i = 0; i != propCount; ++i) { |
416 | auto sp = smo->property(index: i); |
417 | auto tp = tmo->property(index: i); |
418 | if (sp.typeId() == tp.typeId()) { |
419 | if (sp.typeId() == TypeInfo<QQuick3DMaterial>::qmlListTypeId()) |
420 | cloneQmlList<QQuick3DMaterial>(so: source, sp, to&: target, tp); |
421 | else if (sp.typeId() == TypeInfo<QQuick3DEffect>::qmlListTypeId()) |
422 | cloneQmlList<QQuick3DEffect>(so: source, sp, to&: target, tp); |
423 | else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsRenderPass>::qmlListTypeId()) |
424 | cloneQmlList<QQuick3DShaderUtilsRenderPass>(so: source, sp, to&: target, tp); |
425 | else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsShader>::qmlListTypeId()) |
426 | cloneQmlList<QQuick3DShaderUtilsShader>(so: source, sp, to&: target, tp); |
427 | else |
428 | tmo->property(index: i).write(obj: &target, value: smo->property(index: i).read(obj: &source)); |
429 | } |
430 | } |
431 | |
432 | // Clone the dynamic properties as well |
433 | for (const auto &prop : source.dynamicPropertyNames()) |
434 | target.setProperty(name: prop.constData(), value: source.property(name: prop.constData())); |
435 | } |
436 | |
437 | template <typename T> |
438 | inline QVariant fromStringEnumHelper(const QStringView &ref, const QMetaProperty &property) |
439 | { |
440 | bool ok = false; |
441 | const int v = property.enumerator().keyToValue(key: ref.toLatin1(), ok: &ok); |
442 | return ok ? QVariant::fromValue(T(v)) : QVariant(); |
443 | } |
444 | |
445 | static QVariant fromString(const QStringView &ref, const Context &ctx) |
446 | { |
447 | const auto &p = ctx.property; |
448 | if (!p.target) |
449 | return QVariant(); |
450 | |
451 | static const auto toBuiltinType = [](int type, const QStringView &ref, const QDir &workingDir) { |
452 | using namespace BuiltinHelpers; |
453 | bool ok = false; |
454 | switch (type) { |
455 | case QMetaType::Int: |
456 | { |
457 | const auto v = ref.toInt(ok: &ok); |
458 | return (ok ? QVariant::fromValue(value: v) : QVariant()); |
459 | } |
460 | break; |
461 | case QMetaType::Bool: |
462 | { |
463 | const auto v = ref.toInt(ok: &ok); |
464 | return (ok ? QVariant::fromValue(value: bool(v)) : QVariant()); |
465 | } |
466 | case QMetaType::Double: |
467 | { |
468 | const auto v = ref.toDouble(ok: &ok); |
469 | return (ok ? QVariant::fromValue(value: qreal(v)) : QVariant()); |
470 | } |
471 | break; |
472 | case QMetaType::QString: |
473 | return QVariant::fromValue(value: ref); |
474 | case QMetaType::QUrl: |
475 | { |
476 | if (ref.startsWith(c: u':') || ref.startsWith(c: QDir::separator())) |
477 | return QVariant::fromValue(value: QUrl::fromLocalFile(localfile: ref.toString())); |
478 | else if (ref.startsWith(c: u'#')) |
479 | return QVariant::fromValue(value: QUrl(ref.toString())); |
480 | else |
481 | return QVariant::fromValue(value: QUrl::fromUserInput(userInput: ref.toString(), workingDirectory: workingDir.canonicalPath())); |
482 | } |
483 | case QMetaType::QColor: |
484 | return QVariant::fromValue(value: QColor(ref)); |
485 | case QMetaType::QTime: |
486 | return QVariant::fromValue(value: QTime::fromString(string: ref.toString())); |
487 | case QMetaType::QDate: |
488 | return QVariant::fromValue(value: QDate::fromString(string: ref.toString())); |
489 | case QMetaType::QDateTime: |
490 | return QVariant::fromValue(value: QDateTime::fromString(string: ref.toString())); |
491 | case QMetaType::QRectF: |
492 | return QVariant::fromValue(value: toRect(ref)); |
493 | case QMetaType::QPointF: |
494 | return QVariant::fromValue(value: toPoint(ref)); |
495 | case QMetaType::QSizeF: |
496 | return QVariant::fromValue(value: toSize(ref)); |
497 | case QMetaType::QVector2D: |
498 | return QVariant::fromValue(value: toVec<QVector2D>(ref)); |
499 | case QMetaType::QVector3D: |
500 | return QVariant::fromValue(value: toVec<QVector3D>(ref)); |
501 | case QMetaType::QVector4D: |
502 | return QVariant::fromValue(value: toVec<QVector4D>(ref)); |
503 | case QMetaType::QQuaternion: |
504 | return QVariant::fromValue(value: toQuaternion(ref)); |
505 | } |
506 | |
507 | return QVariant(); |
508 | }; |
509 | |
510 | if (p.type != QMetaType::UnknownType) // Built in Qt types int, vector3d etc |
511 | return toBuiltinType(p.type, ref, ctx.workingDir); |
512 | |
513 | // hard mode, detect the property type |
514 | // We only care about the types that are relevant for us |
515 | if (p.targetType != QMetaType::UnknownType) { |
516 | Q_ASSERT(p.target); |
517 | Q_ASSERT(!p.name.isEmpty()); |
518 | const int idx = p.target->metaObject()->indexOfProperty(name: p.name.toLatin1().constData()); |
519 | const auto property = p.target->metaObject()->property(index: idx); |
520 | if (property.metaType().id() >= QMetaType::User) { |
521 | const QMetaType metaType = property.metaType(); |
522 | if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId() || p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
523 | // Common for both materials |
524 | if (metaType.id() == qMetaTypeId<QQuick3DMaterial::CullMode>()) |
525 | return fromStringEnumHelper<QQuick3DMaterial::CullMode>(ref, property); |
526 | if (metaType.id() == qMetaTypeId<QQuick3DMaterial::TextureChannelMapping>()) |
527 | return fromStringEnumHelper<QQuick3DMaterial::TextureChannelMapping>(ref, property); |
528 | |
529 | if (p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
530 | if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::Lighting>()) |
531 | return fromStringEnumHelper<QQuick3DPrincipledMaterial::Lighting>(ref, property); |
532 | if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::BlendMode>()) |
533 | return fromStringEnumHelper<QQuick3DPrincipledMaterial::BlendMode>(ref, property); |
534 | if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::AlphaMode>()) |
535 | return fromStringEnumHelper<QQuick3DPrincipledMaterial::AlphaMode>(ref, property); |
536 | } else if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId()) { |
537 | if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::Lighting>()) |
538 | return fromStringEnumHelper<QQuick3DDefaultMaterial::Lighting>(ref, property); |
539 | if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::BlendMode>()) |
540 | return fromStringEnumHelper<QQuick3DDefaultMaterial::BlendMode>(ref, property); |
541 | if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::SpecularModel>()) |
542 | return fromStringEnumHelper<QQuick3DDefaultMaterial::SpecularModel>(ref, property); |
543 | } |
544 | } else if (p.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId()) { |
545 | if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::ShadingMode>()) |
546 | return fromStringEnumHelper<QQuick3DCustomMaterial::ShadingMode>(ref, property); |
547 | if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::BlendMode>()) |
548 | return fromStringEnumHelper<QQuick3DCustomMaterial::BlendMode>(ref, property); |
549 | } else if (p.targetType == TypeInfo<QQuick3DSpotLight>::typeId() || p.targetType == TypeInfo<QQuick3DPointLight>::typeId()) { |
550 | if (metaType.id() == qMetaTypeId<QQuick3DAbstractLight::QSSGShadowMapQuality>()) |
551 | return fromStringEnumHelper<QQuick3DAbstractLight::QSSGShadowMapQuality>(ref, property); |
552 | } else if (p.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
553 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>()) |
554 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>(ref, property); |
555 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>()) |
556 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>(ref, property); |
557 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>()) |
558 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>(ref, property); |
559 | if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>()) |
560 | return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>(ref, property); |
561 | } else if (p.targetType == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) { |
562 | if (metaType.id() == qMetaTypeId<QQuick3DShaderUtilsShader::Stage>()) |
563 | return fromStringEnumHelper<QQuick3DShaderUtilsShader::Stage>(ref, property); |
564 | } |
565 | } else { // Qt type |
566 | return toBuiltinType(property.metaType().id(), ref, ctx.workingDir); |
567 | } |
568 | } |
569 | |
570 | if (ctx.dbgprint) |
571 | printf(format: "Unhandled type for property %s\n" , ref.toLatin1().constData()); |
572 | |
573 | return QVariant(); |
574 | } |
575 | |
576 | static QString getQmlFileExtension() { return QStringLiteral("qml" ); } |
577 | |
578 | struct Visitors |
579 | { |
580 | static void visit(const QQmlJS::AST::UiProgram &program, Context &ctx, int &ret) |
581 | { |
582 | using namespace QQmlJS::AST; |
583 | const bool = false; |
584 | if (readHeaders && program.headers) { // No real need for us to look at the includes |
585 | using HeaderItem = UiHeaderItemList; |
586 | using Headers = InvasiveListView<HeaderItem>; |
587 | |
588 | Headers (*program.headers); |
589 | for (const auto &header : headers) |
590 | printf("Type: %d\n" , header.kind); |
591 | } |
592 | if (program.members) |
593 | visit(*program.members, ctx, ret); |
594 | } |
595 | static void visit(const QQmlJS::AST::UiObjectInitializer &objInitializer, Context &ctx, int &ret) |
596 | { |
597 | using namespace QQmlJS::AST; |
598 | if (objInitializer.members) |
599 | visit(*objInitializer.members, ctx, ret); |
600 | } |
601 | |
602 | static void visit(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret) |
603 | { |
604 | using namespace QQmlJS::AST; |
605 | if (ctx.dbgprint) |
606 | printf("Object definition -> %s\n" , def.qualifiedTypeNameId->name.toLocal8Bit().constData()); |
607 | if (!(ctx.interceptODFunc && ctx.interceptODFunc(def, ctx, ret))) { |
608 | if (def.initializer) |
609 | visit(*static_cast<UiObjectInitializer *>(def.initializer), ctx, ret); |
610 | } |
611 | } |
612 | |
613 | static void visit(const QQmlJS::AST::UiArrayMemberList &memberList, Context &ctx, int &ret) |
614 | { |
615 | using namespace QQmlJS::AST; |
616 | using ArrayMemberItem = UiArrayMemberList; |
617 | using ArrayMembers = InvasiveListView<ArrayMemberItem>; |
618 | |
619 | ArrayMembers arrayMembers(memberList); |
620 | for (auto &object : arrayMembers) { |
621 | if (object.member->kind == Node::Kind_UiObjectDefinition) { |
622 | const auto &def = *static_cast<UiObjectDefinition *>(object.member); |
623 | visit(def, ctx, ret); |
624 | } |
625 | } |
626 | } |
627 | |
628 | static void visit(const QQmlJS::AST::UiScriptBinding &binding, Context &ctx, int &ret) |
629 | { |
630 | using namespace QQmlJS::AST; |
631 | if (ctx.dbgprint) |
632 | printf("Script binding -> %s " , binding.qualifiedId->name.toLocal8Bit().constData()); |
633 | |
634 | const auto oldName = ctx.property.name; // reentrancy |
635 | ctx.property.name = binding.qualifiedId->name; |
636 | |
637 | if (binding.statement) { |
638 | if (binding.statement->kind == Node::Kind_ExpressionStatement) { |
639 | const auto &expressionStatement = static_cast<const ExpressionStatement &>(*binding.statement); |
640 | visit(expressionStatement, ctx, ret); |
641 | } |
642 | } |
643 | ctx.property.name = oldName; |
644 | } |
645 | |
646 | static void visit(const QQmlJS::AST::UiArrayBinding &arrayBinding, Context &ctx, int &ret) |
647 | { |
648 | using namespace QQmlJS::AST; |
649 | if (ctx.dbgprint) |
650 | printf("Array binding(s) -> %s: [\n" , arrayBinding.qualifiedId->name.toLocal8Bit().constData()); |
651 | |
652 | const auto oldName = ctx.property.name; // reentrancy |
653 | ctx.property.name = arrayBinding.qualifiedId->name; |
654 | |
655 | if (arrayBinding.members) |
656 | visit(*arrayBinding.members, ctx, ret); |
657 | |
658 | if (ctx.dbgprint) |
659 | printf(format: "]\n" ); |
660 | |
661 | ctx.property.name = oldName; |
662 | } |
663 | |
664 | static void visit(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret) |
665 | { |
666 | using namespace QQmlJS::AST; |
667 | |
668 | if (ctx.dbgprint) |
669 | printf("Object binding -> %s: %s {\n" , objectBinding.qualifiedId->name.toLocal8Bit().constData(), objectBinding.qualifiedTypeNameId->name.toLocal8Bit().constData()); |
670 | |
671 | if (objectBinding.initializer) { |
672 | if (!(ctx.interceptOBFunc && ctx.interceptOBFunc(objectBinding, ctx, ret))) |
673 | visit(*objectBinding.initializer, ctx, ret); |
674 | } |
675 | |
676 | if (ctx.dbgprint) |
677 | printf(format: "}\n" ); |
678 | } |
679 | |
680 | static void visit(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret) |
681 | { |
682 | using namespace QQmlJS::AST; |
683 | |
684 | if (ctx.dbgprint) |
685 | printf("%s member -> %s " , (member.type == UiPublicMember::Signal ? "Signal" : "Property" ), member.name.toLocal8Bit().constData()); |
686 | |
687 | auto name = ctx.property.name; |
688 | ctx.property.name = member.name; |
689 | const auto typeCpy = ctx.property.type; |
690 | |
691 | if (!(ctx.interceptPMFunc && ctx.interceptPMFunc(member, ctx, ret))) { |
692 | if (member.statement) { |
693 | const auto &statement = member.statement; |
694 | if (statement->kind == Node::Kind_ExpressionStatement) |
695 | visit(static_cast<const ExpressionStatement &>(*statement), ctx, ret); |
696 | else if (ctx.dbgprint) |
697 | printf("Unhandled statement (%d)\n" , statement->kind); |
698 | } else if (member.binding) { |
699 | const auto &binding = member.binding; |
700 | if (binding->kind == Node::Kind_UiObjectBinding) |
701 | visit(static_cast<const UiObjectBinding &>(*binding), ctx, ret); |
702 | else if (ctx.dbgprint) |
703 | printf("Unhandled binding (%d)\n" , binding->kind); |
704 | } |
705 | } |
706 | |
707 | qSwap(value1&: ctx.property.name, value2&: name); |
708 | ctx.property.type = typeCpy; |
709 | } |
710 | |
711 | static void visit(const QQmlJS::AST::ExpressionStatement &exprStatement, Context &ctx, int &ret) |
712 | { |
713 | using namespace QQmlJS::AST; |
714 | if (exprStatement.expression) { |
715 | if (exprStatement.expression->kind == Node::Kind_IdentifierExpression) { |
716 | const auto &identExpression = static_cast<const IdentifierExpression &>(*exprStatement.expression); |
717 | visit(identExpression, ctx, ret); |
718 | } else if (exprStatement.expression->kind == Node::Kind_StringLiteral) { |
719 | const auto &stringLiteral = static_cast<const StringLiteral &>(*exprStatement.expression); |
720 | visit(stringLiteral, ctx, ret); |
721 | } else if (exprStatement.expression->kind == Node::Kind_NumericLiteral) { |
722 | const auto &numericLiteral = static_cast<const NumericLiteral &>(*exprStatement.expression); |
723 | visit(numericLiteral, ctx, ret); |
724 | } else if (exprStatement.expression->kind == Node::Kind_FieldMemberExpression) { |
725 | const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*exprStatement.expression); |
726 | visit(fieldMemberExpression, ctx, ret); |
727 | } else if (exprStatement.expression->kind == Node::Kind_TrueLiteral || exprStatement.expression->kind == Node::Kind_FalseLiteral) { |
728 | const bool v = (exprStatement.expression->kind == Node::Kind_TrueLiteral); |
729 | if (ctx.dbgprint) |
730 | printf(format: "-> TrueLiteral: %s\n" , v ? "true" : "false" ); |
731 | if (ctx.property.target) { |
732 | auto target = ctx.property.target; |
733 | const auto &name = ctx.property.name; |
734 | target->setProperty(name: name.toLatin1(), value: QVariant::fromValue(value: v)); |
735 | } |
736 | } else if (exprStatement.expression->kind == Node::Kind_ArrayPattern) { |
737 | const auto &arrayPattern = static_cast<const ArrayPattern &>(*exprStatement.expression); |
738 | visit(arrayPattern, ctx, ret); |
739 | } else if (exprStatement.expression->kind == Node::Kind_CallExpression) { |
740 | const auto &callExpression = static_cast<const CallExpression &>(*exprStatement.expression); |
741 | visit(callExpression, ctx, ret); |
742 | } else if (exprStatement.expression->kind == Node::Kind_UnaryMinusExpression) { |
743 | const auto &unaryMinusExpr = static_cast<const UnaryMinusExpression &>(*exprStatement.expression); |
744 | if (unaryMinusExpr.expression && unaryMinusExpr.expression->kind == Node::Kind_NumericLiteral) { |
745 | auto &numericLiteral = static_cast<NumericLiteral &>(*unaryMinusExpr.expression); |
746 | const auto value = numericLiteral.value; |
747 | numericLiteral.value *= -1; |
748 | visit(numericLiteral, ctx, ret); |
749 | numericLiteral.value = value; |
750 | } |
751 | } else if (exprStatement.expression->kind == Node::Kind_UnaryPlusExpression) { |
752 | const auto &unaryPlusExpr = static_cast<const UnaryPlusExpression &>(*exprStatement.expression); |
753 | if (unaryPlusExpr.expression) |
754 | visit(static_cast<const NumericLiteral &>(*unaryPlusExpr.expression), ctx, ret); |
755 | } else { |
756 | if (ctx.dbgprint) |
757 | printf("<expression: %d>\n" , exprStatement.expression->kind); |
758 | } |
759 | } |
760 | } |
761 | |
762 | static void visit(const QQmlJS::AST::IdentifierExpression &idExpr, Context &ctx, int &ret) |
763 | { |
764 | Q_UNUSED(ret); |
765 | if (ctx.dbgprint) |
766 | printf("-> Identifier: %s\n" , idExpr.name.toLocal8Bit().constData()); |
767 | |
768 | if (ctx.property.target && ctx.type != Context::Type::Component) { |
769 | const auto foundIt = ctx.identifierMap.constFind(key: idExpr.name); |
770 | const auto end = ctx.identifierMap.constEnd(); |
771 | if (foundIt != end) { // If an item was found it means this is a reference |
772 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
773 | if (QQuick3DMaterial *mat = qobject_cast<QQuick3DMaterial *>(*foundIt)) { |
774 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
775 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
776 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
777 | if (ctx.dbgprint) |
778 | printf(format: "Clearing inherited materials\n" ); |
779 | materials.clear(&materials); |
780 | ctx.property.memberState = Context::Property::Initialized; |
781 | } |
782 | materials.append(&materials, mat); |
783 | if (ctx.dbgprint) |
784 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
785 | } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) { |
786 | qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList); |
787 | if (ctx.dbgprint) |
788 | printf(format: "Setting instance list on model\n" ); |
789 | } |
790 | } else if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
791 | if (QQuick3DEffect *effect = qobject_cast<QQuick3DEffect *>(*foundIt)) { |
792 | auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects(); |
793 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
794 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
795 | if (ctx.dbgprint) |
796 | printf(format: "Clearing inherited effects\n" ); |
797 | effects.clear(&effects); |
798 | ctx.property.memberState = Context::Property::Initialized; |
799 | } |
800 | effects.append(&effects, effect); |
801 | if (ctx.dbgprint) |
802 | printf(format: "Appending effect to \'%s\'\n" , ctx.property.name.toLatin1().constData()); |
803 | } |
804 | } else if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
805 | if (QQuick3DShaderUtilsShader *shader = qobject_cast<QQuick3DShaderUtilsShader *>(*foundIt)) { |
806 | auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders(); |
807 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
808 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
809 | if (ctx.dbgprint) |
810 | printf(format: "Clearing inherited shaders\n" ); |
811 | shaders.clear(&shaders); |
812 | ctx.property.memberState = Context::Property::Initialized; |
813 | } |
814 | shaders.append(&shaders, shader); |
815 | if (ctx.dbgprint) |
816 | printf(format: "Appending shader to \'%s\'\n" , ctx.property.name.toLatin1().constData()); |
817 | } |
818 | } else if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) { |
819 | if (QQuick3DInstanceListEntry *listEntry = qobject_cast<QQuick3DInstanceListEntry *>(*foundIt)) { |
820 | auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances(); |
821 | // Since we are initializing this for the first time, make sure we clean out any inherited data! |
822 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
823 | if (ctx.dbgprint) |
824 | printf(format: "Clearing inherited instances\n" ); |
825 | instances.clear(&instances); |
826 | ctx.property.memberState = Context::Property::Initialized; |
827 | } |
828 | instances.append(&instances, listEntry); |
829 | if (ctx.dbgprint) |
830 | printf(format: "Appending instance entry to %s\n" , ctx.property.name.toLatin1().constData()); |
831 | } else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) { |
832 | qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instancingList); |
833 | if (ctx.dbgprint) |
834 | printf(format: "Setting instance list on model\n" ); |
835 | } |
836 | } else if (ctx.dbgprint) { |
837 | printf("Unhandled binding: %s\n" , idExpr.name.toLatin1().constData()); |
838 | } |
839 | } else { |
840 | // 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). |
841 | // 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). |
842 | ctx.identifierMap.insert(idExpr.name, ctx.property.target); |
843 | } |
844 | } |
845 | } |
846 | |
847 | static void visit(const QQmlJS::AST::StringLiteral &stringLiteral, Context &ctx, int &ret) |
848 | { |
849 | Q_UNUSED(ret); |
850 | if (ctx.dbgprint) |
851 | printf("-> StringLiteral: \"%s\"\n" , stringLiteral.value.toLocal8Bit().constData()); |
852 | |
853 | if (ctx.property.target) { |
854 | const auto &name = ctx.property.name; |
855 | const auto v = fromString(stringLiteral.value, ctx); |
856 | if (v.isValid()) { |
857 | const bool b = ctx.property.target->setProperty(name.toLatin1(), v); |
858 | if (b && ctx.dbgprint) |
859 | printf(format: "Property %s updated!\n" , name.toLatin1().constData()); |
860 | } |
861 | } |
862 | } |
863 | |
864 | static void visit(const QQmlJS::AST::NumericLiteral &numericLiteral, Context &ctx, int &ret) |
865 | { |
866 | Q_UNUSED(ret); |
867 | if (ctx.dbgprint) |
868 | printf("-> NumericLiteral: %f\n" , numericLiteral.value); |
869 | |
870 | if (ctx.property.target) { |
871 | auto target = ctx.property.target; |
872 | const auto &name = ctx.property.name; |
873 | target->setProperty(name.toLatin1(), QVariant::fromValue(numericLiteral.value)); |
874 | } |
875 | } |
876 | |
877 | static void visit(const QQmlJS::AST::FieldMemberExpression &fieldMemberExpression, Context &ctx, int &ret) |
878 | { |
879 | using namespace QQmlJS::AST; |
880 | |
881 | Q_UNUSED(ret); |
882 | if (ctx.dbgprint) |
883 | printf("-> FieldMemberExpression: %s\n" , fieldMemberExpression.name.toLocal8Bit().constData()); |
884 | |
885 | if (ctx.property.target) { |
886 | const auto &name = ctx.property.name; |
887 | const auto v = fromString(fieldMemberExpression.name, ctx); |
888 | if (v.isValid()) |
889 | ctx.property.target->setProperty(name.toLatin1(), v); |
890 | } |
891 | } |
892 | |
893 | static void visit(const QQmlJS::AST::ArrayPattern &arrayPattern, Context &ctx, int &ret) |
894 | { |
895 | Q_UNUSED(ret); |
896 | if (ctx.dbgprint) |
897 | printf(format: "-> [ " ); |
898 | |
899 | using namespace QQmlJS::AST; |
900 | using PatternElementItem = PatternElementList; |
901 | using PatternElementListView = InvasiveListView<PatternElementItem>; |
902 | |
903 | PatternElementListView elements(*arrayPattern.elements); |
904 | for (auto &element : elements) { |
905 | auto patternElement = element.element; |
906 | if (patternElement->type == PatternElement::Literal) { |
907 | if (patternElement->initializer && patternElement->initializer->kind == Node::Kind_IdentifierExpression) { |
908 | const auto &identExpression = static_cast<const IdentifierExpression &>(*patternElement->initializer); |
909 | visit(identExpression, ctx, ret); |
910 | } |
911 | } else if (ctx.dbgprint) { |
912 | printf("Unahandled(%d), " , patternElement->type); |
913 | } |
914 | } |
915 | |
916 | if (ctx.dbgprint) |
917 | printf(format: " ]\n" ); |
918 | |
919 | } |
920 | |
921 | static void visit(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret) |
922 | { |
923 | using namespace QQmlJS::AST; |
924 | |
925 | Q_UNUSED(ret); |
926 | Q_UNUSED(callExpression); |
927 | if (ctx.dbgprint) |
928 | printf("-> Call(%d)\n" , callExpression.base->kind); |
929 | |
930 | (ctx.interceptCallExpr && ctx.interceptCallExpr(callExpression, ctx, ret)); |
931 | } |
932 | |
933 | static void visit(const QQmlJS::AST::UiObjectMember &member, Context &ctx, int &ret) |
934 | { |
935 | using namespace QQmlJS::AST; |
936 | |
937 | if (member.kind == Node::Kind_UiObjectBinding) |
938 | visit(static_cast<const UiObjectBinding &>(member), ctx, ret); |
939 | else if (ctx.dbgprint) |
940 | printf("Unhandled member (%d)\n" , member.kind); |
941 | } |
942 | |
943 | static void visit(const QQmlJS::AST::UiObjectMemberList &memberList, Context &ctx, int &ret) |
944 | { |
945 | using namespace QQmlJS::AST; |
946 | using ObjectMemberItem = UiObjectMemberList; |
947 | using ObjectMembers = InvasiveListView<ObjectMemberItem>; |
948 | |
949 | const auto oldEvalType = ctx.property.memberState; |
950 | |
951 | ObjectMembers objectMembers(memberList); |
952 | for (const auto &member : objectMembers) { |
953 | if (member.member) { |
954 | if (member.member->kind == Node::Kind_UiScriptBinding) { |
955 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
956 | const auto &scriptBinding = static_cast<const UiScriptBinding &>(*member.member); |
957 | visit(scriptBinding, ctx, ret); |
958 | } else if (member.member->kind == Node::Kind_UiArrayBinding) { |
959 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
960 | const auto &arrayBinding = static_cast<const UiArrayBinding &>(*member.member); |
961 | visit(arrayBinding, ctx, ret); |
962 | } else if (member.member->kind == Node::Kind_UiObjectDefinition) { |
963 | const auto &objectDef = static_cast<const UiObjectDefinition &>(*member.member); |
964 | visit(objectDef, ctx, ret); |
965 | } else if (member.member->kind == Node::Kind_UiObjectBinding) { |
966 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
967 | const auto &objBinding = static_cast<const UiObjectBinding &>(*member.member); |
968 | visit(objBinding, ctx, ret); |
969 | } else if (member.member->kind == Node::Kind_UiPublicMember) { |
970 | ctx.property.memberState = Context::Property::MemberState::Uninitialized; |
971 | const auto &pubMember = static_cast<const UiPublicMember &>(*member.member); |
972 | visit(pubMember, ctx, ret); |
973 | } else { |
974 | if (ctx.dbgprint) |
975 | printf("<member %d>\n" , member.member->kind); |
976 | } |
977 | } |
978 | } |
979 | |
980 | ctx.property.memberState = oldEvalType; |
981 | } |
982 | |
983 | private: |
984 | Visitors() = delete; |
985 | Q_DISABLE_COPY(Visitors); |
986 | }; |
987 | |
988 | template <typename O, typename T> |
989 | T *buildType(const O &obj, Context &ctx, int &ret, const T *base = nullptr) |
990 | { |
991 | // swap -> reentrancy |
992 | Context::Property property; |
993 | qSwap(value1&: property, value2&: ctx.property); |
994 | Q_ASSERT(ctx.property.target == nullptr); |
995 | |
996 | T *instance = nullptr; |
997 | |
998 | if (ctx.dbgprint) |
999 | printf("Building %s!\n" , TypeInfo<T>::qmlTypeName()); |
1000 | |
1001 | if (obj.initializer) { |
1002 | instance = new T; |
1003 | if (base) |
1004 | cloneProperties(*instance, *base); |
1005 | |
1006 | if (obj.initializer) { |
1007 | ctx.property.target = instance; |
1008 | ctx.property.targetType = TypeInfo<T>::typeId(); |
1009 | Visitors::visit(*obj.initializer, ctx, ret); |
1010 | } |
1011 | } |
1012 | |
1013 | // swap back |
1014 | qSwap(value1&: property, value2&: ctx.property); |
1015 | |
1016 | return instance; |
1017 | } |
1018 | |
1019 | static QQuick3DAbstractLight *buildLight(const QQmlJS::AST::UiObjectDefinition &def, |
1020 | Context &ctx, |
1021 | int &ret, |
1022 | int lightType, |
1023 | const QQuick3DAbstractLight *base = nullptr) |
1024 | { |
1025 | if (lightType == TypeInfo<QQuick3DDirectionalLight>::typeId()) |
1026 | return buildType(def, ctx, ret, qobject_cast<const QQuick3DDirectionalLight *>(object: base)); |
1027 | if (lightType == TypeInfo<QQuick3DPointLight>::typeId()) |
1028 | return buildType(def, ctx, ret, qobject_cast<const QQuick3DPointLight *>(object: base)); |
1029 | if (lightType == TypeInfo<QQuick3DSpotLight>::typeId()) |
1030 | return buildType(def, ctx, ret, qobject_cast<const QQuick3DSpotLight *>(object: base)); |
1031 | return nullptr; |
1032 | } |
1033 | |
1034 | |
1035 | template <typename T> |
1036 | static void updateProperty(Context &ctx, T type, QStringView propName) |
1037 | { |
1038 | if (ctx.property.target) { |
1039 | if (ctx.dbgprint) |
1040 | printf(format: "Updating property %s\n" , propName.toLatin1().constData()); |
1041 | const auto &target = ctx.property.target; |
1042 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1043 | target->setProperty(propName.toLatin1().constData(), QVariant::fromValue(type)); |
1044 | ctx.property.memberState = Context::Property::Initialized; |
1045 | } else { |
1046 | const int idx = target->metaObject()->indexOfProperty(name: propName.toLatin1().constData()); |
1047 | if (idx != -1) { |
1048 | auto prop = target->metaObject()->property(index: idx); |
1049 | prop.write(target, QVariant::fromValue(type)); |
1050 | } |
1051 | } |
1052 | } |
1053 | } |
1054 | |
1055 | static bool interceptObjectBinding(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret) |
1056 | { |
1057 | if (ctx.dbgprint) |
1058 | printf(format: "Intercepted object binding!\n" ); |
1059 | |
1060 | bool handled = false; |
1061 | |
1062 | const auto &typeName = objectBinding.qualifiedTypeNameId->name.toString(); |
1063 | const auto &propName = objectBinding.qualifiedId->name; |
1064 | |
1065 | int type = -1; |
1066 | |
1067 | // Base type? |
1068 | const auto typeIt = s_typeMap->constFind(key: typeName); |
1069 | if (typeIt != s_typeMap->cend()) |
1070 | type = *typeIt; |
1071 | |
1072 | // Component? |
1073 | auto &components = ctx.components; |
1074 | const auto compIt = (type == -1) ? components.constFind(key: typeName) : components.cend(); |
1075 | QObject *base = nullptr; |
1076 | if (compIt != components.cend()) { |
1077 | type = compIt->type; |
1078 | base = compIt->ptr; |
1079 | } |
1080 | |
1081 | if (type != -1) { |
1082 | if (ctx.dbgprint) |
1083 | printf("Resolving: \'%s\'\n" , qPrintable(typeName)); |
1084 | |
1085 | if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
1086 | if (ctx.property.targetType == TypeInfo<QQuick3DViewport>::typeId()) { |
1087 | if (auto environment = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DSceneEnvironment *>(base))) { |
1088 | auto viewport = qobject_cast<QQuick3DViewport *>(object: ctx.property.target); |
1089 | Q_ASSERT(viewport); |
1090 | viewport->setEnvironment(environment); |
1091 | handled = true; |
1092 | } |
1093 | } |
1094 | } else if (type == TypeInfo<QQuick3DTexture>::typeId()) { |
1095 | if (auto tex = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DTexture *>(base))) { |
1096 | updateProperty(ctx, tex, propName); |
1097 | ctx.sceneData.textures.append(tex); |
1098 | } |
1099 | handled = true; |
1100 | } else if (type == TypeInfo<QQuick3DShaderUtilsTextureInput>::typeId()) { |
1101 | auto texInput = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsTextureInput *>(object: base)); |
1102 | if (texInput && texInput->texture()) { |
1103 | updateProperty(ctx, texInput, propName); |
1104 | ctx.sceneData.textures.append(texInput->texture()); |
1105 | } |
1106 | handled = true; |
1107 | } else if (type == TypeInfo<QQuick3DEffect>::typeId()) { |
1108 | if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
1109 | if (auto effect = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DEffect *>(base))) { |
1110 | auto sceneEnvironment = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target); |
1111 | Q_ASSERT(sceneEnvironment); |
1112 | auto effects = sceneEnvironment->effects(); |
1113 | effects.append(&effects, effect); |
1114 | handled = true; |
1115 | } |
1116 | } |
1117 | } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
1118 | if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) { |
1119 | if (auto pass = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsRenderPass *>(base))) { |
1120 | auto effect = qobject_cast<QQuick3DEffect *>(object: ctx.property.target); |
1121 | Q_ASSERT(effect); |
1122 | auto passes = effect->passes(); |
1123 | passes.append(&passes, pass); |
1124 | handled = true; |
1125 | } |
1126 | } |
1127 | } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) { |
1128 | if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
1129 | if (auto shader = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsShader *>(base))) { |
1130 | auto pass = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target); |
1131 | Q_ASSERT(pass); |
1132 | auto shaders = pass->shaders(); |
1133 | shaders.append(&shaders, shader); |
1134 | handled = true; |
1135 | } |
1136 | } |
1137 | } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) { |
1138 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1139 | if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DDefaultMaterial *>(base))) { |
1140 | auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target); |
1141 | Q_ASSERT(model); |
1142 | auto materials = model->materials(); |
1143 | materials.append(&materials, mat); |
1144 | handled = true; |
1145 | if (ctx.dbgprint) |
1146 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
1147 | } |
1148 | } |
1149 | } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
1150 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1151 | if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DPrincipledMaterial *>(base))) { |
1152 | auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target); |
1153 | Q_ASSERT(model); |
1154 | auto materials = model->materials(); |
1155 | materials.append(&materials, mat); |
1156 | handled = true; |
1157 | } |
1158 | } |
1159 | } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) { |
1160 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1161 | if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DCustomMaterial *>(base))) { |
1162 | auto model = qobject_cast<QQuick3DModel *>(object: ctx.property.target); |
1163 | Q_ASSERT(model); |
1164 | auto materials = model->materials(); |
1165 | materials.append(&materials, mat); |
1166 | handled = true; |
1167 | } |
1168 | } |
1169 | } else if (ctx.dbgprint) { |
1170 | printf(format: "Unhandled type\n" ); |
1171 | } |
1172 | } |
1173 | |
1174 | return handled; |
1175 | } |
1176 | |
1177 | static bool interceptObjectDef(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret) |
1178 | { |
1179 | const auto &typeName = def.qualifiedTypeNameId->name.toString(); |
1180 | |
1181 | if (ctx.dbgprint) |
1182 | printf("Intercepted object definition (\'%s\')!\n" , typeName.toLatin1().constData()); |
1183 | |
1184 | QString componentName; |
1185 | int type = -1; |
1186 | bool doRegisterComponent = false; |
1187 | |
1188 | // Base type? |
1189 | const auto typeIt = s_typeMap->constFind(key: typeName); |
1190 | if (typeIt != s_typeMap->cend()) |
1191 | type = *typeIt; |
1192 | |
1193 | // Component? |
1194 | auto &components = ctx.components; |
1195 | const auto compIt = (type == -1) ? components.constFind(key: typeName) : components.cend(); |
1196 | if (compIt != components.cend()) |
1197 | type = compIt->type; |
1198 | |
1199 | // If this is a new component register it |
1200 | if (ctx.type == Context::Type::Component && ctx.property.target == nullptr && type != -1) { |
1201 | const auto &fileName = ctx.currentFileInfo.fileName(); |
1202 | componentName = fileName.left(n: fileName.size() - 4); |
1203 | doRegisterComponent = !componentName.isEmpty(); |
1204 | } |
1205 | |
1206 | const auto registerComponent = [&ctx, &components, &componentName](Context::Component component) { |
1207 | if (ctx.dbgprint) |
1208 | printf(format: "Registering component \'%s\'\n" , qPrintable(componentName)); |
1209 | components.insert(key: componentName, value: component); |
1210 | }; |
1211 | |
1212 | if (type == TypeInfo<QQuick3DViewport>::typeId()) { |
1213 | const QQuick3DViewport *base = (compIt != components.cend()) ? qobject_cast<QQuick3DViewport *>(compIt->ptr) : nullptr; |
1214 | if (QQuick3DViewport *viewport = buildType(def, ctx, ret, base)) { |
1215 | // If this is a component we'll store it for lookups later. |
1216 | if (doRegisterComponent) |
1217 | registerComponent({ .ptr: viewport, .type: type }); |
1218 | // Only one viewport supported atm (see SceneEnvironment case as well). |
1219 | if (!ctx.sceneData.viewport) |
1220 | ctx.sceneData.viewport = viewport; |
1221 | } |
1222 | } else if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
1223 | const QQuick3DSceneEnvironment *base = (compIt != components.cend()) ? qobject_cast<QQuick3DSceneEnvironment *>(compIt->ptr) : nullptr; |
1224 | if (QQuick3DSceneEnvironment *sceneEnv = buildType(def, ctx, ret, base)) { |
1225 | // If this is a component we'll store it for lookups later. |
1226 | if (doRegisterComponent) |
1227 | registerComponent({ .ptr: sceneEnv, .type: type }); |
1228 | |
1229 | if (ctx.sceneData.viewport) |
1230 | ctx.sceneData.viewport->setEnvironment(sceneEnv); |
1231 | } |
1232 | } else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) { |
1233 | const QQuick3DPrincipledMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DPrincipledMaterial *>(compIt->ptr) : nullptr; |
1234 | if (QQuick3DPrincipledMaterial *mat = buildType(def, ctx, ret, base)) { |
1235 | // If this is a component we'll store it for lookups later. |
1236 | if (doRegisterComponent) |
1237 | registerComponent({ .ptr: mat, .type: type }); |
1238 | |
1239 | if (ctx.property.target) { |
1240 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1241 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
1242 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1243 | if (ctx.dbgprint) |
1244 | printf(format: "Clearing inherited materials\n" ); |
1245 | materials.clear(&materials); |
1246 | ctx.property.memberState = Context::Property::Initialized; |
1247 | } |
1248 | materials.append(&materials, mat); |
1249 | if (ctx.dbgprint) |
1250 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
1251 | } |
1252 | } |
1253 | |
1254 | // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list |
1255 | ctx.sceneData.materials.push_back(t: mat); |
1256 | } |
1257 | } else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) { |
1258 | const QQuick3DDefaultMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DDefaultMaterial *>(compIt->ptr) : nullptr; |
1259 | if (QQuick3DDefaultMaterial *mat = buildType(def, ctx, ret, base)) { |
1260 | // If this is a component we'll store it for lookups later. |
1261 | if (doRegisterComponent) |
1262 | registerComponent({ .ptr: mat, .type: type }); |
1263 | |
1264 | if (ctx.property.target) { |
1265 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1266 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
1267 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1268 | if (ctx.dbgprint) |
1269 | printf(format: "Clearing inherited materials\n" ); |
1270 | materials.clear(&materials); |
1271 | ctx.property.memberState = Context::Property::Initialized; |
1272 | } |
1273 | materials.append(&materials, mat); |
1274 | if (ctx.dbgprint) |
1275 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
1276 | } |
1277 | } |
1278 | |
1279 | // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list |
1280 | ctx.sceneData.materials.push_back(t: mat); |
1281 | } |
1282 | } else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) { |
1283 | const QQuick3DCustomMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DCustomMaterial *>(compIt->ptr) : nullptr; |
1284 | if (QQuick3DCustomMaterial *mat = buildType(def, ctx, ret, base)) { |
1285 | // If this is a component we'll store it for lookups later. |
1286 | if (doRegisterComponent) |
1287 | registerComponent({ .ptr: mat, .type: type }); |
1288 | |
1289 | if (ctx.property.target) { |
1290 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1291 | auto materials = qobject_cast<QQuick3DModel *>(object: ctx.property.target)->materials(); |
1292 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1293 | if (ctx.dbgprint) |
1294 | printf(format: "Clearing inherited materials\n" ); |
1295 | materials.clear(&materials); |
1296 | ctx.property.memberState = Context::Property::Initialized; |
1297 | } |
1298 | materials.append(&materials, mat); |
1299 | if (ctx.dbgprint) |
1300 | printf(format: "Appending material to %s\n" , ctx.property.name.toLatin1().constData()); |
1301 | } |
1302 | } |
1303 | |
1304 | // At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list |
1305 | ctx.sceneData.materials.push_back(t: mat); |
1306 | } |
1307 | } else if (type == TypeInfo<QQuick3DEffect>::typeId()) { |
1308 | const QQuick3DEffect *base = (compIt != components.cend()) ? qobject_cast<QQuick3DEffect *>(compIt->ptr) : nullptr; |
1309 | if (QQuick3DEffect *effect = buildType(def, ctx, ret, base)) { |
1310 | // If this is a component we'll store it for lookups later. |
1311 | if (doRegisterComponent) |
1312 | registerComponent({ .ptr: effect, .type: type }); |
1313 | |
1314 | if (ctx.property.target) { |
1315 | if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) { |
1316 | auto effects = qobject_cast<QQuick3DSceneEnvironment *>(object: ctx.property.target)->effects(); |
1317 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1318 | if (ctx.dbgprint) |
1319 | printf(format: "Clearing inherited effects\n" ); |
1320 | effects.clear(&effects); |
1321 | ctx.property.memberState = Context::Property::Initialized; |
1322 | } |
1323 | effects.append(&effects, effect); |
1324 | if (ctx.dbgprint) |
1325 | printf(format: "Appending effect to %s\n" , ctx.property.name.toLatin1().constData()); |
1326 | } |
1327 | } |
1328 | |
1329 | // At this point we don't know if this effect is going to be referenced somewhere else, so keep it in the list |
1330 | ctx.sceneData.effects.push_back(t: effect); |
1331 | } |
1332 | } else if (type == TypeInfo<QQuick3DDirectionalLight>::typeId() || type == TypeInfo<QQuick3DPointLight>::typeId() || type == TypeInfo<QQuick3DSpotLight>::typeId()) { |
1333 | const QQuick3DAbstractLight *base = (compIt != components.cend()) ? qobject_cast<QQuick3DAbstractLight *>(compIt->ptr) : nullptr; |
1334 | if (QQuick3DAbstractLight *light = buildLight(def, ctx, ret, type, base)) { |
1335 | // If this is a component we'll store it for lookups later. |
1336 | if (doRegisterComponent) |
1337 | registerComponent({ .ptr: light, .type: type }); |
1338 | |
1339 | ctx.sceneData.lights.push_back(t: light); |
1340 | } |
1341 | } else if (type == TypeInfo<QQuick3DTexture>::typeId()) { |
1342 | const QQuick3DTexture *base = (compIt != components.cend()) ? qobject_cast<QQuick3DTexture *>(compIt->ptr) : nullptr; |
1343 | if (QQuick3DTexture *tex = buildType(def, ctx, ret, base)) { |
1344 | // If this is a component we'll store it for lookups later. |
1345 | if (doRegisterComponent) |
1346 | registerComponent({ .ptr: tex, .type: type }); |
1347 | |
1348 | ctx.sceneData.textures.push_back(t: tex); |
1349 | } |
1350 | } else if (type == TypeInfo<QQuick3DModel>::typeId()) { |
1351 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DModel *>(compIt->ptr) : nullptr; |
1352 | if (auto *model = buildType(def, ctx, ret, base)) { |
1353 | // If this is a component we'll store it for lookups later. |
1354 | if (doRegisterComponent) |
1355 | registerComponent({ model, type }); |
1356 | |
1357 | ctx.sceneData.models.push_back(model); |
1358 | } |
1359 | } else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) { |
1360 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsShader *>(compIt->ptr) : nullptr; |
1361 | if (auto *shader = buildType(def, ctx, ret, base)) { |
1362 | // If this is a component we'll store it for lookups later. |
1363 | if (doRegisterComponent) |
1364 | registerComponent({ shader, type }); |
1365 | if (ctx.property.target) { |
1366 | if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
1367 | auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(object: ctx.property.target)->shaders(); |
1368 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1369 | if (ctx.dbgprint) |
1370 | printf(format: "Clearing inherited shaders\n" ); |
1371 | shaders.clear(&shaders); |
1372 | ctx.property.memberState = Context::Property::Initialized; |
1373 | } |
1374 | shaders.append(&shaders, shader); |
1375 | if (ctx.dbgprint) |
1376 | printf(format: "Appending shader to %s\n" , ctx.property.name.toLatin1().constData()); |
1377 | } |
1378 | } |
1379 | |
1380 | ctx.sceneData.shaders.push_back(shader); |
1381 | } |
1382 | } else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) { |
1383 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsRenderPass *>(compIt->ptr) : nullptr; |
1384 | if (auto *pass = buildType(def, ctx, ret, base)) { |
1385 | // If this is a component we'll store it for lookups later. |
1386 | if (doRegisterComponent) |
1387 | registerComponent({ pass, type }); |
1388 | if (ctx.property.target) { |
1389 | if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) { |
1390 | auto passes = qobject_cast<QQuick3DEffect *>(object: ctx.property.target)->passes(); |
1391 | if (ctx.property.memberState == Context::Property::Uninitialized) { |
1392 | if (ctx.dbgprint) |
1393 | printf(format: "Clearing inherited passes\n" ); |
1394 | passes.clear(&passes); |
1395 | ctx.property.memberState = Context::Property::Initialized; |
1396 | } |
1397 | passes.append(&passes, pass); |
1398 | if (ctx.dbgprint) |
1399 | printf(format: "Appending pass to %s\n" , ctx.property.name.toLatin1().constData()); |
1400 | } |
1401 | } |
1402 | } |
1403 | } else if (type == TypeInfo<QQuick3DInstanceList>::typeId()) { |
1404 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceList *>(compIt->ptr) : nullptr; |
1405 | if (auto *instanceList = buildType(def, ctx, ret, base)) { |
1406 | // If this is a component we'll store it for lookups later. |
1407 | if (doRegisterComponent) |
1408 | registerComponent({ instanceList, type }); |
1409 | if (ctx.property.target) { |
1410 | if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) { |
1411 | qobject_cast<QQuick3DModel *>(object: ctx.property.target)->setInstancing(instanceList); |
1412 | if (ctx.dbgprint) |
1413 | printf(format: "Setting instance list on %s\n" , ctx.property.name.toLatin1().constData()); |
1414 | } |
1415 | } |
1416 | } |
1417 | } else if (type == TypeInfo<QQuick3DInstanceListEntry>::typeId()) { |
1418 | const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceListEntry *>(compIt->ptr) : nullptr; |
1419 | if (auto *instanceListEntry = buildType(def, ctx, ret, base)) { |
1420 | // If this is a component we'll store it for lookups later. |
1421 | if (doRegisterComponent) |
1422 | registerComponent({ instanceListEntry, type }); |
1423 | if (ctx.property.target) { |
1424 | if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) { |
1425 | auto instances = qobject_cast<QQuick3DInstanceList *>(object: ctx.property.target)->instances(); |
1426 | instances.append(&instances, instanceListEntry); |
1427 | if (ctx.dbgprint) |
1428 | printf(format: "Appending instance list entry to %s\n" , ctx.property.name.toLatin1().constData()); |
1429 | } |
1430 | } |
1431 | } |
1432 | } else { |
1433 | if (ctx.dbgprint) |
1434 | printf(format: "Object def for \'%s\' was not handled\n" , ctx.property.name.toLatin1().constData()); |
1435 | return false; |
1436 | } |
1437 | |
1438 | return true; |
1439 | } |
1440 | |
1441 | static bool interceptPublicMember(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret) |
1442 | { |
1443 | Q_UNUSED(ret); |
1444 | using namespace QQmlJS::AST; |
1445 | |
1446 | if (ctx.dbgprint) |
1447 | printf(format: "Intercepted public member!\n" ); |
1448 | |
1449 | if (member.statement && member.statement->kind == Node::Kind_ExpressionStatement) { |
1450 | if ((ctx.property.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId() || ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) && member.memberType) { |
1451 | // For custom materials we have properties that are user provided, so we'll |
1452 | // need to add these to the objects properties (we add these here to be able |
1453 | // to piggyback on the existing type matching code) |
1454 | if (member.memberType->name == u"real" ) { |
1455 | ctx.property.type = QMetaType::Double; |
1456 | } else if (member.memberType->name == u"bool" ) { |
1457 | ctx.property.type = QMetaType::Bool; |
1458 | } else if (member.memberType->name == u"int" ) { |
1459 | ctx.property.type = QMetaType::Int; |
1460 | } else if (member.memberType->name == u"size" ) { |
1461 | ctx.property.type = QMetaType::QSizeF; |
1462 | } else if (member.memberType->name == u"rect" ) { |
1463 | ctx.property.type = QMetaType::QRectF; |
1464 | } else if (member.memberType->name == u"point" ) { |
1465 | ctx.property.type = QMetaType::QPointF; |
1466 | } else if (member.memberType->name == u"color" ) { |
1467 | ctx.property.type = QMetaType::QColor; |
1468 | } else if (member.memberType->name.startsWith(u"vector" )) { |
1469 | if (member.memberType->name.endsWith(u"2d" )) { |
1470 | ctx.property.type = QMetaType::QVector2D; |
1471 | } else if (member.memberType->name.endsWith(u"3d" )) { |
1472 | ctx.property.type = QMetaType::QVector3D; |
1473 | } else if (member.memberType->name.endsWith(u"4d" )) { |
1474 | ctx.property.type = QMetaType::QVector4D; |
1475 | } |
1476 | } else if (member.memberType->name == u"matrix4x4" ) { |
1477 | ctx.property.type = QMetaType::QMatrix4x4;; |
1478 | } else if (member.memberType->name == u"quaternion" ) { |
1479 | ctx.property.type = QMetaType::QQuaternion; |
1480 | } else if (member.memberType->name == u"var" ) { |
1481 | ctx.property.type = QMetaType::QVariant; |
1482 | } |
1483 | } |
1484 | } |
1485 | |
1486 | return false; |
1487 | } |
1488 | |
1489 | static bool interceptCallExpression(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret) |
1490 | { |
1491 | Q_UNUSED(ret); |
1492 | using namespace QQmlJS::AST; |
1493 | using namespace BuiltinHelpers; |
1494 | |
1495 | if (ctx.dbgprint) |
1496 | printf(format: "Intercepted call expression!\n" ); |
1497 | |
1498 | const bool ok = (ctx.property.target && !ctx.property.name.isEmpty()); |
1499 | if (callExpression.base && ok) { |
1500 | if (callExpression.base->kind == Node::Kind_FieldMemberExpression) { |
1501 | const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*callExpression.base); |
1502 | if (fieldMemberExpression.base) { |
1503 | if (fieldMemberExpression.base->kind == Node::Kind_IdentifierExpression) { |
1504 | const auto &identExpr = static_cast<const IdentifierExpression &>(*fieldMemberExpression.base); |
1505 | if (identExpr.name == u"Qt" ) { |
1506 | bool ok = false; |
1507 | QVariant v; |
1508 | if (fieldMemberExpression.name == u"point" ) { |
1509 | const auto point = toPoint(ArgumentListView(*callExpression.arguments), &ok); |
1510 | if (ctx.dbgprint) |
1511 | printf("Qt.point(%f, %f)\n" , point.x(), point.y()); |
1512 | setProperty(ctx.property, point); |
1513 | } else if (fieldMemberExpression.name == u"size" ) { |
1514 | const auto size = toSize(ArgumentListView(*callExpression.arguments), &ok); |
1515 | if (ctx.dbgprint) |
1516 | printf("Qt.size(%f, %f)\n" , size.width(), size.height()); |
1517 | setProperty(ctx.property, size); |
1518 | } else if (fieldMemberExpression.name == u"rect" ) { |
1519 | const auto rect = toRect(ArgumentListView(*callExpression.arguments), &ok); |
1520 | if (ctx.dbgprint) |
1521 | printf("Qt.rect(%f, %f, %f, %f)\n" , rect.x(), rect.y(), rect.width(), rect.height()); |
1522 | setProperty(ctx.property, rect); |
1523 | } else if (fieldMemberExpression.name.startsWith(u"vector" )) { |
1524 | if (fieldMemberExpression.name.endsWith(u"2d" )) { |
1525 | const auto vec2 = toVec<QVector2D>(ArgumentListView(*callExpression.arguments), &ok); |
1526 | if (ctx.dbgprint) |
1527 | printf("Qt.vector2d(%f, %f)\n" , vec2.x(), vec2.y()); |
1528 | setProperty(ctx.property, vec2); |
1529 | } else if (fieldMemberExpression.name.endsWith(u"3d" )) { |
1530 | const auto vec3 = toVec<QVector3D>(ArgumentListView(*callExpression.arguments), &ok); |
1531 | if (ctx.dbgprint) |
1532 | printf("Qt.vector3d(%f, %f, %f)\n" , vec3.x(), vec3.y(), vec3.z()); |
1533 | setProperty(ctx.property, vec3); |
1534 | } else if (fieldMemberExpression.name.endsWith(u"4d" )) { |
1535 | const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok); |
1536 | if (ctx.dbgprint) |
1537 | printf("Qt.vector4d(%f, %f, %f, %f)\n" , vec4.x(), vec4.y(), vec4.z(), vec4.w()); |
1538 | setProperty(ctx.property, vec4); |
1539 | } |
1540 | } else if (fieldMemberExpression.name == u"matrix4x4" ) { |
1541 | const auto mat44 = toMat44(ArgumentListView(*callExpression.arguments), &ok); |
1542 | if (ctx.dbgprint) |
1543 | printf("Qt.matrix4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n" , |
1544 | mat44(0, 0), mat44(0, 1), mat44(0, 2), mat44(0, 3), |
1545 | mat44(1, 0), mat44(1, 1), mat44(1, 2), mat44(1, 3), |
1546 | mat44(2, 0), mat44(2, 1), mat44(2, 2), mat44(2, 3), |
1547 | mat44(3, 0), mat44(3, 1), mat44(3, 2), mat44(3, 3)); |
1548 | setProperty(ctx.property, mat44); |
1549 | } else if (fieldMemberExpression.name == u"quaternion" ) { |
1550 | const auto quat = toQuaternion(ArgumentListView(*callExpression.arguments), &ok); |
1551 | if (ctx.dbgprint) |
1552 | printf("Qt.quaternion(%f, %f, %f, %f)\n" , quat.scalar(), quat.x(), quat.y(), quat.z()); |
1553 | setProperty(ctx.property, quat); |
1554 | } else if (fieldMemberExpression.name == u"rgba" ) { |
1555 | const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok); |
1556 | if (ok) { |
1557 | QColor color = QColor::fromRgbF(r: vec4.x(), g: vec4.y(), b: vec4.z(), a: vec4.w()); |
1558 | if (ctx.dbgprint) |
1559 | printf(format: "Qt.rgba(%f, %f, %f, %f)\n" , color.redF(), color.greenF(), color.blueF(), color.alphaF()); |
1560 | setProperty(property: ctx.property, v: color); |
1561 | } |
1562 | } |
1563 | if (ok && v.isValid() && ctx.property.target) |
1564 | ctx.property.target->setProperty(name: ctx.property.name.toLatin1().constData(), value: v); |
1565 | } |
1566 | } |
1567 | } |
1568 | } |
1569 | } |
1570 | |
1571 | return false; |
1572 | } |
1573 | |
1574 | static int parseQmlData(const QByteArray &code, Context &ctx) |
1575 | { |
1576 | Q_ASSERT(ctx.engine && ctx.engine->lexer()); |
1577 | ctx.identifierMap.clear(); // not visible outside the scope of this "code" |
1578 | if (ctx.dbgprint) |
1579 | printf(format: "Parsing %s\n" , qPrintable(ctx.currentFileInfo.filePath())); |
1580 | int ret = 0; |
1581 | ctx.engine->lexer()->setCode(code: QString::fromUtf8(ba: code), lineno: 1, qmlMode: true); |
1582 | QQmlJS::Parser parser(ctx.engine); |
1583 | const bool ok = parser.parse(); |
1584 | if (ok) { |
1585 | const auto program = parser.ast(); |
1586 | if (program) |
1587 | Visitors::visit(*program, ctx, ret); |
1588 | } else { |
1589 | ret = -1; |
1590 | qWarning("Parsing failed due to %s in %s:%d%d" , qPrintable(parser.errorMessage()), qPrintable(ctx.currentFileInfo.fileName()), parser.errorLineNumber(), parser.errorColumnNumber()); |
1591 | } |
1592 | |
1593 | return ret; |
1594 | } |
1595 | |
1596 | int MaterialParser::parseQmlData(const QByteArray &code, const QString &fileName, MaterialParser::SceneData &sceneData) |
1597 | { |
1598 | // set initial type map |
1599 | *s_typeMap = baseTypeMap(); |
1600 | |
1601 | QQmlJS::Engine engine; |
1602 | QQmlJS::Lexer lexer(&engine); |
1603 | |
1604 | Context ctx; |
1605 | ctx.engine = &engine; |
1606 | ctx.interceptODFunc = &interceptObjectDef; |
1607 | ctx.interceptOBFunc = &interceptObjectBinding; |
1608 | ctx.interceptPMFunc = &interceptPublicMember; |
1609 | ctx.interceptCallExpr = &interceptCallExpression; |
1610 | ctx.currentFileInfo = QFileInfo(fileName); |
1611 | ctx.type = Context::Type::Component; |
1612 | |
1613 | const int ret = ::parseQmlData(code, ctx); |
1614 | sceneData = std::move(ctx.sceneData); |
1615 | |
1616 | return ret; |
1617 | } |
1618 | |
1619 | int MaterialParser::parseQmlFiles(const QVector<QString> &filePaths, const QDir &sourceDir, SceneData &sceneData, bool verboseOutput) |
1620 | { |
1621 | // set initial type map |
1622 | *s_typeMap = baseTypeMap(); |
1623 | |
1624 | int ret = 0; |
1625 | |
1626 | if (filePaths.isEmpty()) { |
1627 | qWarning(msg: "No input files" ); |
1628 | return ret; |
1629 | } |
1630 | |
1631 | QQmlJS::Engine engine; |
1632 | QQmlJS::Lexer lexer(&engine); |
1633 | |
1634 | Context ctx; |
1635 | ctx.dbgprint = verboseOutput; |
1636 | ctx.engine = &engine; |
1637 | ctx.interceptODFunc = &interceptObjectDef; |
1638 | ctx.interceptOBFunc = &interceptObjectBinding; |
1639 | ctx.interceptPMFunc = &interceptPublicMember; |
1640 | ctx.interceptCallExpr = &interceptCallExpression; |
1641 | ctx.workingDir = sourceDir; |
1642 | |
1643 | QVector<QString> deferredOther; |
1644 | QVector<QString> deferredComponets; |
1645 | |
1646 | const QString sourcePath = sourceDir.canonicalPath() + QDir::separator(); |
1647 | |
1648 | const bool isMultifile = filePaths.size() != 1; |
1649 | |
1650 | // Go through and find the material components first |
1651 | for (const auto &v : filePaths) { |
1652 | QFileInfo ¤tFileInfo = ctx.currentFileInfo; |
1653 | if (!QFileInfo(v).isAbsolute()) |
1654 | currentFileInfo.setFile(sourcePath + v); |
1655 | else |
1656 | currentFileInfo.setFile(v); |
1657 | const bool maybeComponent = currentFileInfo.fileName().at(i: 0).isUpper(); |
1658 | if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) { |
1659 | const QString filePath = currentFileInfo.canonicalFilePath(); |
1660 | if (isMultifile && maybeComponent) { |
1661 | QFile f(filePath); |
1662 | if (!f.open(flags: QFile::ReadOnly)) { |
1663 | qWarning(msg: "Could not open file %s for reading!" , qPrintable(filePath)); |
1664 | return -1; |
1665 | } |
1666 | |
1667 | const QByteArray code = f.readAll(); |
1668 | int idx = code.indexOf(c: '{'); |
1669 | if (idx != -1) { |
1670 | const QByteArray section = code.mid(index: 0, len: idx); |
1671 | QVarLengthArray<const char *, 3> componentTypes { TypeInfo<QQuick3DPrincipledMaterial>::qmlTypeName(), |
1672 | TypeInfo<QQuick3DCustomMaterial>::qmlTypeName(), |
1673 | TypeInfo<QQuick3DDefaultMaterial>::qmlTypeName()}; |
1674 | for (const auto compType : std::as_const(t&: componentTypes)) { |
1675 | if ((idx = section.indexOf(bv: compType)) != -1) |
1676 | break; |
1677 | } |
1678 | if (idx != -1) { |
1679 | ctx.type = Context::Type::Component; |
1680 | ret = parseQmlData(code, ctx); |
1681 | if (ret != 0) |
1682 | break; |
1683 | } else { |
1684 | deferredComponets.push_back(t: filePath); |
1685 | } |
1686 | } else { |
1687 | qWarning(msg: "No items found in %s\n" , qPrintable(filePath)); |
1688 | } |
1689 | } else { |
1690 | deferredOther.push_back(t: filePath); |
1691 | } |
1692 | } else { |
1693 | qWarning(msg: "The file %s is either not a file or has the wrong extension!" , qPrintable(v)); |
1694 | } |
1695 | } |
1696 | |
1697 | const auto parsePaths = [&ctx, &ret](const QVector<QString> &paths, Context::Type type) { |
1698 | ctx.type = type; |
1699 | for (const auto &path : paths) { |
1700 | QFileInfo ¤tFileInfo = ctx.currentFileInfo; |
1701 | currentFileInfo.setFile(path); |
1702 | if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) { |
1703 | const QString filePath = currentFileInfo.canonicalFilePath(); |
1704 | QFile f(filePath); |
1705 | if (!f.open(flags: QFile::ReadOnly)) { |
1706 | qWarning(msg: "Could not open file %s for reading!" , qPrintable(filePath)); |
1707 | ret = -1; |
1708 | return; |
1709 | } |
1710 | |
1711 | const QByteArray code = f.readAll(); |
1712 | ret = parseQmlData(code, ctx); |
1713 | if (ret != 0) |
1714 | break; |
1715 | } |
1716 | } |
1717 | }; |
1718 | |
1719 | // Other components |
1720 | parsePaths(deferredComponets, Context::Type::Component); |
1721 | |
1722 | // Now parse the rest |
1723 | parsePaths(deferredOther, Context::Type::Application); |
1724 | |
1725 | sceneData = std::move(ctx.sceneData); |
1726 | |
1727 | return ret; |
1728 | } |
1729 | |
1730 | QT_END_NAMESPACE |
1731 | |