ff4ff35918
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
1731 lines
77 KiB
C++
1731 lines
77 KiB
C++
// Copyright (C) 2020 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
|
|
|
#include "parser.h"
|
|
|
|
#include <QtCore/qdir.h>
|
|
#include <QtCore/qfileinfo.h>
|
|
#include <QtCore/qstringview.h>
|
|
|
|
#include <QtQml/qqmllist.h>
|
|
|
|
// Parsing
|
|
#include <QtQml/private/qqmljsengine_p.h>
|
|
#include <QtQml/private/qqmljslexer_p.h>
|
|
#include <QtQml/private/qqmljsparser_p.h>
|
|
|
|
#include <QtQuick3D/private/qquick3dobject_p.h>
|
|
#include <QtQuick3D/private/qquick3dviewport_p.h>
|
|
// Scene
|
|
#include <QtQuick3D/private/qquick3dsceneenvironment_p.h>
|
|
// Material(s)
|
|
#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
|
|
#include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
|
|
#include <QtQuick3D/private/qquick3dcustommaterial_p.h>
|
|
// Lights
|
|
#include <QtQuick3D/private/qquick3dspotlight_p.h>
|
|
#include <QtQuick3D/private/qquick3dpointlight_p.h>
|
|
#include <QtQuick3D/private/qquick3ddirectionallight_p.h>
|
|
|
|
// Instancing
|
|
#include <QtQuick3D/private/qquick3dinstancing_p.h>
|
|
|
|
#include <QtQuick3D/private/qquick3dshaderutils_p.h>
|
|
|
|
#include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h>
|
|
|
|
#include <QtGui/qquaternion.h>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
template<typename T, T *T::*N = &T::next>
|
|
struct InvasiveListView : protected QSSGInvasiveSingleLinkedList<T, N>
|
|
{
|
|
using QSSGInvasiveSingleLinkedList<T, N>::begin;
|
|
using QSSGInvasiveSingleLinkedList<T, N>::end;
|
|
using QSSGInvasiveSingleLinkedList<T, N>::m_head;
|
|
|
|
explicit InvasiveListView(const T &obj) { m_head = &const_cast<T &>(obj); }
|
|
};
|
|
|
|
template <typename T> struct TypeInfo
|
|
{
|
|
static constexpr const char *qmlTypeName() { return "Unknown"; }
|
|
static constexpr int typeId() { return QMetaType::UnknownType; }
|
|
};
|
|
|
|
#define DECLARE_QQ3D_TYPE(CLASS, TYPE_NAME) \
|
|
template<> struct TypeInfo<CLASS> \
|
|
{ \
|
|
static constexpr const char *qmlTypeName() { return #TYPE_NAME; } \
|
|
inline static constexpr int typeId() { return qMetaTypeId<CLASS>(); } \
|
|
inline static constexpr int qmlListTypeId() { return qMetaTypeId<QQmlListProperty<CLASS>>(); } \
|
|
}
|
|
|
|
DECLARE_QQ3D_TYPE(QQuick3DViewport, View3D);
|
|
DECLARE_QQ3D_TYPE(QQuick3DSceneEnvironment, SceneEnvironment);
|
|
DECLARE_QQ3D_TYPE(QQuick3DMaterial, Material);
|
|
DECLARE_QQ3D_TYPE(QQuick3DPrincipledMaterial, PrincipledMaterial);
|
|
DECLARE_QQ3D_TYPE(QQuick3DDefaultMaterial, DefaultMaterial);
|
|
DECLARE_QQ3D_TYPE(QQuick3DCustomMaterial, CustomMaterial);
|
|
DECLARE_QQ3D_TYPE(QQuick3DDirectionalLight, DirectionalLight);
|
|
DECLARE_QQ3D_TYPE(QQuick3DPointLight, PointLight);
|
|
DECLARE_QQ3D_TYPE(QQuick3DSpotLight, SpotLight);
|
|
DECLARE_QQ3D_TYPE(QQuick3DTexture, Texture);
|
|
DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsTextureInput, TextureInput);
|
|
DECLARE_QQ3D_TYPE(QQuick3DModel, Model);
|
|
DECLARE_QQ3D_TYPE(QQuick3DEffect, Effect);
|
|
DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsRenderPass, Pass);
|
|
DECLARE_QQ3D_TYPE(QQuick3DShaderUtilsShader, Shader);
|
|
DECLARE_QQ3D_TYPE(QQuick3DInstanceList, InstanceList);
|
|
DECLARE_QQ3D_TYPE(QQuick3DInstanceListEntry, InstanceListEntry);
|
|
|
|
using QmlTypeNames = QHash<QString, int>;
|
|
|
|
#define QQ3D_TYPE_ENTRY(TYPE) { TypeInfo<TYPE>::qmlTypeName(), TypeInfo<TYPE>::typeId() }
|
|
|
|
QmlTypeNames baseTypeMap()
|
|
{
|
|
return { QQ3D_TYPE_ENTRY(QQuick3DViewport),
|
|
QQ3D_TYPE_ENTRY(QQuick3DSceneEnvironment),
|
|
QQ3D_TYPE_ENTRY(QQuick3DPrincipledMaterial),
|
|
QQ3D_TYPE_ENTRY(QQuick3DDefaultMaterial),
|
|
QQ3D_TYPE_ENTRY(QQuick3DCustomMaterial),
|
|
QQ3D_TYPE_ENTRY(QQuick3DDirectionalLight),
|
|
QQ3D_TYPE_ENTRY(QQuick3DPointLight),
|
|
QQ3D_TYPE_ENTRY(QQuick3DSpotLight),
|
|
QQ3D_TYPE_ENTRY(QQuick3DTexture),
|
|
QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsTextureInput),
|
|
QQ3D_TYPE_ENTRY(QQuick3DModel),
|
|
QQ3D_TYPE_ENTRY(QQuick3DEffect),
|
|
QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsRenderPass),
|
|
QQ3D_TYPE_ENTRY(QQuick3DShaderUtilsShader),
|
|
QQ3D_TYPE_ENTRY(QQuick3DInstanceList),
|
|
QQ3D_TYPE_ENTRY(QQuick3DInstanceListEntry)
|
|
};
|
|
}
|
|
|
|
Q_GLOBAL_STATIC(QmlTypeNames, s_typeMap)
|
|
|
|
struct Context
|
|
{
|
|
enum class Type
|
|
{
|
|
Application,
|
|
Component
|
|
};
|
|
|
|
struct Property {
|
|
enum MemberState : quint8
|
|
{
|
|
Uninitialized,
|
|
Initialized
|
|
};
|
|
|
|
QObject *target = nullptr;
|
|
QStringView name;
|
|
int targetType = QMetaType::UnknownType;
|
|
QMetaType::Type type = QMetaType::UnknownType;
|
|
MemberState memberState = Uninitialized;
|
|
};
|
|
|
|
template<typename T> using Vector = QVector<T>;
|
|
using InterceptObjDefFunc = bool (*)(const QQmlJS::AST::UiObjectDefinition &, Context &, int &);
|
|
using InterceptObjBinding = bool (*)(const QQmlJS::AST::UiObjectBinding &, Context &, int &);
|
|
using InterceptPublicMember = bool (*)(const QQmlJS::AST::UiPublicMember &, Context &, int &);
|
|
using InterceptCallExpression = bool (*)(const QQmlJS::AST::CallExpression &, Context &, int &);
|
|
|
|
QQmlJS::Engine *engine = nullptr;
|
|
QDir workingDir; // aka source directory
|
|
QFileInfo currentFileInfo;
|
|
|
|
MaterialParser::SceneData sceneData;
|
|
Property property;
|
|
|
|
struct Component
|
|
{
|
|
QObject *ptr = nullptr;
|
|
int type = QMetaType::UnknownType;
|
|
};
|
|
|
|
QHash<QStringView, QObject *> identifierMap;
|
|
QHash<QString, Component> components;
|
|
InterceptObjDefFunc interceptODFunc = nullptr;
|
|
InterceptObjBinding interceptOBFunc = nullptr;
|
|
InterceptPublicMember interceptPMFunc = nullptr;
|
|
InterceptCallExpression interceptCallExpr = nullptr;
|
|
Type type = Type::Application;
|
|
bool dbgprint = false;
|
|
};
|
|
|
|
Q_DECLARE_TYPEINFO(Context::Component, Q_PRIMITIVE_TYPE);
|
|
|
|
namespace BuiltinHelpers {
|
|
|
|
using ArgumentListView = InvasiveListView<QQmlJS::AST::ArgumentList>;
|
|
|
|
template <typename T> Q_REQUIRED_RESULT constexpr quint8 componentCount() { Q_STATIC_ASSERT(true); return 0; }
|
|
template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector2D>() { return 2; }
|
|
template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector3D>() { return 3; }
|
|
template <> Q_REQUIRED_RESULT constexpr quint8 componentCount<QVector4D>() { return 4; }
|
|
|
|
static double expressionValue(const QQmlJS::AST::ExpressionNode &expr) {
|
|
using namespace QQmlJS::AST;
|
|
|
|
if (expr.kind == Node::Kind_NumericLiteral) {
|
|
return static_cast<const NumericLiteral &>(expr).value;
|
|
} else if (expr.kind == Node::Kind_UnaryMinusExpression) {
|
|
const auto &minusExpr = static_cast<const UnaryMinusExpression &>(expr);
|
|
if (minusExpr.expression && minusExpr.expression->kind == Node::Kind_NumericLiteral)
|
|
return static_cast<const NumericLiteral &>(*minusExpr.expression).value * -1.0;
|
|
} else if (expr.kind == Node::Kind_UnaryPlusExpression) {
|
|
const auto &plusExpr = static_cast<const UnaryPlusExpression &>(expr);
|
|
if (plusExpr.expression && plusExpr.expression->kind == Node::Kind_NumericLiteral)
|
|
return static_cast<const NumericLiteral &>(*plusExpr.expression).value;
|
|
} else {
|
|
printf("Expression type \'%d\' unhandled!\n", expr.kind);
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
template <typename T>
|
|
static inline bool setProperty(const Context::Property &property, const T &v)
|
|
{
|
|
return property.target->setProperty(property.name.toLatin1(), QVariant::fromValue(v));
|
|
}
|
|
|
|
template<typename Vec>
|
|
static Vec toVec(const ArgumentListView &list, bool *ok = nullptr)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
int i = 0;
|
|
const int e = componentCount<Vec>();
|
|
Vec vec;
|
|
for (const auto &listItem : list) {
|
|
if (listItem.expression && i != e)
|
|
vec[i] = expressionValue(*listItem.expression);
|
|
++i;
|
|
}
|
|
|
|
if (ok)
|
|
*ok = (i == e);
|
|
|
|
return vec;
|
|
}
|
|
|
|
static QPointF toPoint(const ArgumentListView &list, bool *ok = nullptr)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
int i = 0;
|
|
const int e = 2;
|
|
qreal args[e];
|
|
for (const auto &listItem : list) {
|
|
if (listItem.expression && i != e)
|
|
args[i] = expressionValue(*listItem.expression);
|
|
++i;
|
|
}
|
|
|
|
if (ok)
|
|
*ok = (i == e);
|
|
|
|
return QPointF(args[0], args[1]);
|
|
}
|
|
|
|
static QSizeF toSize(const ArgumentListView &list, bool *ok = nullptr)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
int i = 0;
|
|
const int e = 2;
|
|
qreal args[e];
|
|
for (const auto &listItem : list) {
|
|
if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e)
|
|
args[i] = expressionValue(*listItem.expression);
|
|
++i;
|
|
}
|
|
|
|
if (ok)
|
|
*ok = (i == e);
|
|
|
|
return QSizeF(args[0], args[1]);
|
|
}
|
|
|
|
static QRectF toRect(const ArgumentListView &list, bool *ok = nullptr)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
int i = 0;
|
|
const int e = 4;
|
|
qreal args[e];
|
|
for (const auto &listItem : list) {
|
|
if (listItem.expression && i != e)
|
|
args[i] = expressionValue(*listItem.expression);
|
|
++i;
|
|
}
|
|
|
|
if (ok)
|
|
*ok = (i == e);
|
|
|
|
return QRectF(args[0], args[1], args[2], args[3]);
|
|
}
|
|
|
|
static QMatrix4x4 toMat44(const ArgumentListView &list, bool *ok = nullptr)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
int i = 0;
|
|
const int e = 16;
|
|
float args[e];
|
|
for (const auto &listItem : list) {
|
|
if (listItem.expression && i != e)
|
|
args[i] = float(expressionValue(*listItem.expression));
|
|
++i;
|
|
}
|
|
|
|
if (ok)
|
|
*ok = (i == e);
|
|
|
|
return QMatrix4x4(args);
|
|
}
|
|
|
|
static QQuaternion toQuaternion(const ArgumentListView &list, bool *ok = nullptr)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
int i = 0;
|
|
const int e = 4;
|
|
float args[e];
|
|
for (const auto &listItem : list) {
|
|
if (listItem.expression && listItem.expression->kind == Node::Kind_NumericLiteral && i != e)
|
|
args[i] = float(expressionValue(*listItem.expression));
|
|
++i;
|
|
}
|
|
|
|
if (ok)
|
|
*ok = (i == e);
|
|
|
|
return QQuaternion(args[0], args[1], args[2], args[3]);
|
|
}
|
|
|
|
// String variants
|
|
// Note: Unlike the call variants we assume arguments are correct (can be converted),
|
|
// as they should have failed earlier, during parsing, if they were not.
|
|
template <typename Vec>
|
|
static Vec toVec(const QStringView &ref)
|
|
{
|
|
const auto args = ref.split(u',');
|
|
Vec vec;
|
|
bool ok = false;
|
|
if (args.size() == componentCount<Vec>()) {
|
|
for (int i = 0; i != componentCount<Vec>(); ++i) {
|
|
vec[i] = args.at(i).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
}
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
static QPointF toPoint(const QStringView &ref)
|
|
{
|
|
const auto args = ref.split(u",");
|
|
if (args.size() == 2) {
|
|
bool ok = false;
|
|
const auto arg0 = args.at(0).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg1 = args.at(1).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
return QPointF(arg0, arg1);
|
|
}
|
|
return QPointF();
|
|
}
|
|
|
|
static QSizeF toSize(const QStringView &ref)
|
|
{
|
|
const auto args = ref.split(u'x');
|
|
if (args.size() == 2) {
|
|
bool ok = false;
|
|
const auto arg0 = args.at(0).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg1 = args.at(1).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
return QSizeF(arg0, arg1);
|
|
}
|
|
return QSizeF();
|
|
}
|
|
|
|
static QRectF toRect(const QStringView &ref)
|
|
{
|
|
auto args = ref.split(u",");
|
|
if (args.size() == 3) {
|
|
bool ok = false;
|
|
const auto arg0 = args.at(0).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg1 = args.at(1).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
args = args.at(2).split(u'x');
|
|
if (args.size() == 2) {
|
|
const auto arg2 = args.at(0).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg3 = args.at(1).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
return QRectF(arg0, arg1, arg2, arg3);
|
|
}
|
|
}
|
|
return QRectF();
|
|
}
|
|
|
|
static QQuaternion toQuaternion(const QStringView &ref)
|
|
{
|
|
const auto args = ref.split(u',');
|
|
if (args.size() == 4) {
|
|
bool ok = false;
|
|
const auto arg0 = args.at(0).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg1 = args.at(1).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg2 = args.at(2).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
const auto arg3 = args.at(3).toDouble(&ok);
|
|
Q_ASSERT(ok);
|
|
return QQuaternion(arg0, arg1, arg2, arg3);
|
|
}
|
|
return QQuaternion();
|
|
}
|
|
|
|
} // BuiltinHelpers
|
|
|
|
template <typename T>
|
|
static void cloneQmlList(const QObject &so, QMetaProperty &sp, QObject &to, QMetaProperty &tp) {
|
|
Q_ASSERT(sp.typeId() == tp.typeId());
|
|
const auto tv = tp.read(&to);
|
|
const auto sv = sp.read(&so);
|
|
if (sv.isValid() && tv.isValid()) {
|
|
auto tl = tv.value<QQmlListProperty<T>>();
|
|
auto sl = sv.value<QQmlListProperty<T>>();
|
|
const auto count = sl.count(&sl);
|
|
for (int i = 0; count != i; ++i)
|
|
tl.append(&tl, sl.at(&sl, i));
|
|
}
|
|
}
|
|
|
|
static void cloneProperties(QObject &target, const QObject &source)
|
|
{
|
|
Q_ASSERT(target.metaObject() == source.metaObject());
|
|
const auto smo = source.metaObject();
|
|
auto tmo = target.metaObject();
|
|
const int propCount = smo->propertyCount();
|
|
for (int i = 0; i != propCount; ++i) {
|
|
auto sp = smo->property(i);
|
|
auto tp = tmo->property(i);
|
|
if (sp.typeId() == tp.typeId()) {
|
|
if (sp.typeId() == TypeInfo<QQuick3DMaterial>::qmlListTypeId())
|
|
cloneQmlList<QQuick3DMaterial>(source, sp, target, tp);
|
|
else if (sp.typeId() == TypeInfo<QQuick3DEffect>::qmlListTypeId())
|
|
cloneQmlList<QQuick3DEffect>(source, sp, target, tp);
|
|
else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsRenderPass>::qmlListTypeId())
|
|
cloneQmlList<QQuick3DShaderUtilsRenderPass>(source, sp, target, tp);
|
|
else if (sp.typeId() == TypeInfo<QQuick3DShaderUtilsShader>::qmlListTypeId())
|
|
cloneQmlList<QQuick3DShaderUtilsShader>(source, sp, target, tp);
|
|
else
|
|
tmo->property(i).write(&target, smo->property(i).read(&source));
|
|
}
|
|
}
|
|
|
|
// Clone the dynamic properties as well
|
|
for (const auto &prop : source.dynamicPropertyNames())
|
|
target.setProperty(prop.constData(), source.property(prop.constData()));
|
|
}
|
|
|
|
template <typename T>
|
|
inline QVariant fromStringEnumHelper(const QStringView &ref, const QMetaProperty &property)
|
|
{
|
|
bool ok = false;
|
|
const int v = property.enumerator().keyToValue(ref.toLatin1(), &ok);
|
|
return ok ? QVariant::fromValue(T(v)) : QVariant();
|
|
}
|
|
|
|
static QVariant fromString(const QStringView &ref, const Context &ctx)
|
|
{
|
|
const auto &p = ctx.property;
|
|
if (!p.target)
|
|
return QVariant();
|
|
|
|
static const auto toBuiltinType = [](int type, const QStringView &ref, const QDir &workingDir) {
|
|
using namespace BuiltinHelpers;
|
|
bool ok = false;
|
|
switch (type) {
|
|
case QMetaType::Int:
|
|
{
|
|
const auto v = ref.toInt(&ok);
|
|
return (ok ? QVariant::fromValue(v) : QVariant());
|
|
}
|
|
break;
|
|
case QMetaType::Bool:
|
|
{
|
|
const auto v = ref.toInt(&ok);
|
|
return (ok ? QVariant::fromValue(bool(v)) : QVariant());
|
|
}
|
|
case QMetaType::Double:
|
|
{
|
|
const auto v = ref.toDouble(&ok);
|
|
return (ok ? QVariant::fromValue(qreal(v)) : QVariant());
|
|
}
|
|
break;
|
|
case QMetaType::QString:
|
|
return QVariant::fromValue(ref);
|
|
case QMetaType::QUrl:
|
|
{
|
|
if (ref.startsWith(u':') || ref.startsWith(QDir::separator()))
|
|
return QVariant::fromValue(QUrl::fromLocalFile(ref.toString()));
|
|
else if (ref.startsWith(u'#'))
|
|
return QVariant::fromValue(QUrl(ref.toString()));
|
|
else
|
|
return QVariant::fromValue(QUrl::fromUserInput(ref.toString(), workingDir.canonicalPath()));
|
|
}
|
|
case QMetaType::QColor:
|
|
return QVariant::fromValue(QColor(ref));
|
|
case QMetaType::QTime:
|
|
return QVariant::fromValue(QTime::fromString(ref.toString()));
|
|
case QMetaType::QDate:
|
|
return QVariant::fromValue(QDate::fromString(ref.toString()));
|
|
case QMetaType::QDateTime:
|
|
return QVariant::fromValue(QDateTime::fromString(ref.toString()));
|
|
case QMetaType::QRectF:
|
|
return QVariant::fromValue(toRect(ref));
|
|
case QMetaType::QPointF:
|
|
return QVariant::fromValue(toPoint(ref));
|
|
case QMetaType::QSizeF:
|
|
return QVariant::fromValue(toSize(ref));
|
|
case QMetaType::QVector2D:
|
|
return QVariant::fromValue(toVec<QVector2D>(ref));
|
|
case QMetaType::QVector3D:
|
|
return QVariant::fromValue(toVec<QVector3D>(ref));
|
|
case QMetaType::QVector4D:
|
|
return QVariant::fromValue(toVec<QVector4D>(ref));
|
|
case QMetaType::QQuaternion:
|
|
return QVariant::fromValue(toQuaternion(ref));
|
|
}
|
|
|
|
return QVariant();
|
|
};
|
|
|
|
if (p.type != QMetaType::UnknownType) // Built in Qt types int, vector3d etc
|
|
return toBuiltinType(p.type, ref, ctx.workingDir);
|
|
|
|
// hard mode, detect the property type
|
|
// We only care about the types that are relevant for us
|
|
if (p.targetType != QMetaType::UnknownType) {
|
|
Q_ASSERT(p.target);
|
|
Q_ASSERT(!p.name.isEmpty());
|
|
const int idx = p.target->metaObject()->indexOfProperty(p.name.toLatin1().constData());
|
|
const auto property = p.target->metaObject()->property(idx);
|
|
if (property.metaType().id() >= QMetaType::User) {
|
|
const QMetaType metaType = property.metaType();
|
|
if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId() || p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
|
|
// Common for both materials
|
|
if (metaType.id() == qMetaTypeId<QQuick3DMaterial::CullMode>())
|
|
return fromStringEnumHelper<QQuick3DMaterial::CullMode>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DMaterial::TextureChannelMapping>())
|
|
return fromStringEnumHelper<QQuick3DMaterial::TextureChannelMapping>(ref, property);
|
|
|
|
if (p.targetType == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
|
|
if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::Lighting>())
|
|
return fromStringEnumHelper<QQuick3DPrincipledMaterial::Lighting>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::BlendMode>())
|
|
return fromStringEnumHelper<QQuick3DPrincipledMaterial::BlendMode>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DPrincipledMaterial::AlphaMode>())
|
|
return fromStringEnumHelper<QQuick3DPrincipledMaterial::AlphaMode>(ref, property);
|
|
} else if (p.targetType == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
|
|
if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::Lighting>())
|
|
return fromStringEnumHelper<QQuick3DDefaultMaterial::Lighting>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::BlendMode>())
|
|
return fromStringEnumHelper<QQuick3DDefaultMaterial::BlendMode>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DDefaultMaterial::SpecularModel>())
|
|
return fromStringEnumHelper<QQuick3DDefaultMaterial::SpecularModel>(ref, property);
|
|
}
|
|
} else if (p.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
|
|
if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::ShadingMode>())
|
|
return fromStringEnumHelper<QQuick3DCustomMaterial::ShadingMode>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DCustomMaterial::BlendMode>())
|
|
return fromStringEnumHelper<QQuick3DCustomMaterial::BlendMode>(ref, property);
|
|
} else if (p.targetType == TypeInfo<QQuick3DSpotLight>::typeId() || p.targetType == TypeInfo<QQuick3DPointLight>::typeId()) {
|
|
if (metaType.id() == qMetaTypeId<QQuick3DAbstractLight::QSSGShadowMapQuality>())
|
|
return fromStringEnumHelper<QQuick3DAbstractLight::QSSGShadowMapQuality>(ref, property);
|
|
} else if (p.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
|
|
if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>())
|
|
return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>())
|
|
return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAModeValues>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>())
|
|
return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentAAQualityValues>(ref, property);
|
|
if (metaType.id() == qMetaTypeId<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>())
|
|
return fromStringEnumHelper<QQuick3DSceneEnvironment::QQuick3DEnvironmentTonemapModes>(ref, property);
|
|
} else if (p.targetType == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
|
|
if (metaType.id() == qMetaTypeId<QQuick3DShaderUtilsShader::Stage>())
|
|
return fromStringEnumHelper<QQuick3DShaderUtilsShader::Stage>(ref, property);
|
|
}
|
|
} else { // Qt type
|
|
return toBuiltinType(property.metaType().id(), ref, ctx.workingDir);
|
|
}
|
|
}
|
|
|
|
if (ctx.dbgprint)
|
|
printf("Unhandled type for property %s\n", ref.toLatin1().constData());
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
static QString getQmlFileExtension() { return QStringLiteral("qml"); }
|
|
|
|
struct Visitors
|
|
{
|
|
static void visit(const QQmlJS::AST::UiProgram &program, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
const bool readHeaders = false;
|
|
if (readHeaders && program.headers) { // No real need for us to look at the includes
|
|
using HeaderItem = UiHeaderItemList;
|
|
using Headers = InvasiveListView<HeaderItem>;
|
|
|
|
Headers headers(*program.headers);
|
|
for (const auto &header : headers)
|
|
printf("Type: %d\n", header.kind);
|
|
}
|
|
if (program.members)
|
|
visit(*program.members, ctx, ret);
|
|
}
|
|
static void visit(const QQmlJS::AST::UiObjectInitializer &objInitializer, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
if (objInitializer.members)
|
|
visit(*objInitializer.members, ctx, ret);
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
if (ctx.dbgprint)
|
|
printf("Object definition -> %s\n", def.qualifiedTypeNameId->name.toLocal8Bit().constData());
|
|
if (!(ctx.interceptODFunc && ctx.interceptODFunc(def, ctx, ret))) {
|
|
if (def.initializer)
|
|
visit(*static_cast<UiObjectInitializer *>(def.initializer), ctx, ret);
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiArrayMemberList &memberList, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
using ArrayMemberItem = UiArrayMemberList;
|
|
using ArrayMembers = InvasiveListView<ArrayMemberItem>;
|
|
|
|
ArrayMembers arrayMembers(memberList);
|
|
for (auto &object : arrayMembers) {
|
|
if (object.member->kind == Node::Kind_UiObjectDefinition) {
|
|
const auto &def = *static_cast<UiObjectDefinition *>(object.member);
|
|
visit(def, ctx, ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiScriptBinding &binding, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
if (ctx.dbgprint)
|
|
printf("Script binding -> %s ", binding.qualifiedId->name.toLocal8Bit().constData());
|
|
|
|
const auto oldName = ctx.property.name; // reentrancy
|
|
ctx.property.name = binding.qualifiedId->name;
|
|
|
|
if (binding.statement) {
|
|
if (binding.statement->kind == Node::Kind_ExpressionStatement) {
|
|
const auto &expressionStatement = static_cast<const ExpressionStatement &>(*binding.statement);
|
|
visit(expressionStatement, ctx, ret);
|
|
}
|
|
}
|
|
ctx.property.name = oldName;
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiArrayBinding &arrayBinding, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
if (ctx.dbgprint)
|
|
printf("Array binding(s) -> %s: [\n", arrayBinding.qualifiedId->name.toLocal8Bit().constData());
|
|
|
|
const auto oldName = ctx.property.name; // reentrancy
|
|
ctx.property.name = arrayBinding.qualifiedId->name;
|
|
|
|
if (arrayBinding.members)
|
|
visit(*arrayBinding.members, ctx, ret);
|
|
|
|
if (ctx.dbgprint)
|
|
printf("]\n");
|
|
|
|
ctx.property.name = oldName;
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
|
|
if (ctx.dbgprint)
|
|
printf("Object binding -> %s: %s {\n", objectBinding.qualifiedId->name.toLocal8Bit().constData(), objectBinding.qualifiedTypeNameId->name.toLocal8Bit().constData());
|
|
|
|
if (objectBinding.initializer) {
|
|
if (!(ctx.interceptOBFunc && ctx.interceptOBFunc(objectBinding, ctx, ret)))
|
|
visit(*objectBinding.initializer, ctx, ret);
|
|
}
|
|
|
|
if (ctx.dbgprint)
|
|
printf("}\n");
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
|
|
if (ctx.dbgprint)
|
|
printf("%s member -> %s ", (member.type == UiPublicMember::Signal ? "Signal" : "Property"), member.name.toLocal8Bit().constData());
|
|
|
|
auto name = ctx.property.name;
|
|
ctx.property.name = member.name;
|
|
const auto typeCpy = ctx.property.type;
|
|
|
|
if (!(ctx.interceptPMFunc && ctx.interceptPMFunc(member, ctx, ret))) {
|
|
if (member.statement) {
|
|
const auto &statement = member.statement;
|
|
if (statement->kind == Node::Kind_ExpressionStatement)
|
|
visit(static_cast<const ExpressionStatement &>(*statement), ctx, ret);
|
|
else if (ctx.dbgprint)
|
|
printf("Unhandled statement (%d)\n", statement->kind);
|
|
} else if (member.binding) {
|
|
const auto &binding = member.binding;
|
|
if (binding->kind == Node::Kind_UiObjectBinding)
|
|
visit(static_cast<const UiObjectBinding &>(*binding), ctx, ret);
|
|
else if (ctx.dbgprint)
|
|
printf("Unhandled binding (%d)\n", binding->kind);
|
|
}
|
|
}
|
|
|
|
qSwap(ctx.property.name, name);
|
|
ctx.property.type = typeCpy;
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::ExpressionStatement &exprStatement, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
if (exprStatement.expression) {
|
|
if (exprStatement.expression->kind == Node::Kind_IdentifierExpression) {
|
|
const auto &identExpression = static_cast<const IdentifierExpression &>(*exprStatement.expression);
|
|
visit(identExpression, ctx, ret);
|
|
} else if (exprStatement.expression->kind == Node::Kind_StringLiteral) {
|
|
const auto &stringLiteral = static_cast<const StringLiteral &>(*exprStatement.expression);
|
|
visit(stringLiteral, ctx, ret);
|
|
} else if (exprStatement.expression->kind == Node::Kind_NumericLiteral) {
|
|
const auto &numericLiteral = static_cast<const NumericLiteral &>(*exprStatement.expression);
|
|
visit(numericLiteral, ctx, ret);
|
|
} else if (exprStatement.expression->kind == Node::Kind_FieldMemberExpression) {
|
|
const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*exprStatement.expression);
|
|
visit(fieldMemberExpression, ctx, ret);
|
|
} else if (exprStatement.expression->kind == Node::Kind_TrueLiteral || exprStatement.expression->kind == Node::Kind_FalseLiteral) {
|
|
const bool v = (exprStatement.expression->kind == Node::Kind_TrueLiteral);
|
|
if (ctx.dbgprint)
|
|
printf("-> TrueLiteral: %s\n", v ? "true" : "false");
|
|
if (ctx.property.target) {
|
|
auto target = ctx.property.target;
|
|
const auto &name = ctx.property.name;
|
|
target->setProperty(name.toLatin1(), QVariant::fromValue(v));
|
|
}
|
|
} else if (exprStatement.expression->kind == Node::Kind_ArrayPattern) {
|
|
const auto &arrayPattern = static_cast<const ArrayPattern &>(*exprStatement.expression);
|
|
visit(arrayPattern, ctx, ret);
|
|
} else if (exprStatement.expression->kind == Node::Kind_CallExpression) {
|
|
const auto &callExpression = static_cast<const CallExpression &>(*exprStatement.expression);
|
|
visit(callExpression, ctx, ret);
|
|
} else if (exprStatement.expression->kind == Node::Kind_UnaryMinusExpression) {
|
|
const auto &unaryMinusExpr = static_cast<const UnaryMinusExpression &>(*exprStatement.expression);
|
|
if (unaryMinusExpr.expression && unaryMinusExpr.expression->kind == Node::Kind_NumericLiteral) {
|
|
auto &numericLiteral = static_cast<NumericLiteral &>(*unaryMinusExpr.expression);
|
|
const auto value = numericLiteral.value;
|
|
numericLiteral.value *= -1;
|
|
visit(numericLiteral, ctx, ret);
|
|
numericLiteral.value = value;
|
|
}
|
|
} else if (exprStatement.expression->kind == Node::Kind_UnaryPlusExpression) {
|
|
const auto &unaryPlusExpr = static_cast<const UnaryPlusExpression &>(*exprStatement.expression);
|
|
if (unaryPlusExpr.expression)
|
|
visit(static_cast<const NumericLiteral &>(*unaryPlusExpr.expression), ctx, ret);
|
|
} else {
|
|
if (ctx.dbgprint)
|
|
printf("<expression: %d>\n", exprStatement.expression->kind);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::IdentifierExpression &idExpr, Context &ctx, int &ret)
|
|
{
|
|
Q_UNUSED(ret);
|
|
if (ctx.dbgprint)
|
|
printf("-> Identifier: %s\n", idExpr.name.toLocal8Bit().constData());
|
|
|
|
if (ctx.property.target && ctx.type != Context::Type::Component) {
|
|
const auto foundIt = ctx.identifierMap.constFind(idExpr.name);
|
|
const auto end = ctx.identifierMap.constEnd();
|
|
if (foundIt != end) { // If an item was found it means this is a reference
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
if (QQuick3DMaterial *mat = qobject_cast<QQuick3DMaterial *>(*foundIt)) {
|
|
auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
|
|
// Since we are initializing this for the first time, make sure we clean out any inherited data!
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited materials\n");
|
|
materials.clear(&materials);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
materials.append(&materials, mat);
|
|
if (ctx.dbgprint)
|
|
printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
|
|
} else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
|
|
qobject_cast<QQuick3DModel *>(ctx.property.target)->setInstancing(instancingList);
|
|
if (ctx.dbgprint)
|
|
printf("Setting instance list on model\n");
|
|
}
|
|
} else if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
|
|
if (QQuick3DEffect *effect = qobject_cast<QQuick3DEffect *>(*foundIt)) {
|
|
auto effects = qobject_cast<QQuick3DSceneEnvironment *>(ctx.property.target)->effects();
|
|
// Since we are initializing this for the first time, make sure we clean out any inherited data!
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited effects\n");
|
|
effects.clear(&effects);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
effects.append(&effects, effect);
|
|
if (ctx.dbgprint)
|
|
printf("Appending effect to \'%s\'\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
} else if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
|
|
if (QQuick3DShaderUtilsShader *shader = qobject_cast<QQuick3DShaderUtilsShader *>(*foundIt)) {
|
|
auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(ctx.property.target)->shaders();
|
|
// Since we are initializing this for the first time, make sure we clean out any inherited data!
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited shaders\n");
|
|
shaders.clear(&shaders);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
shaders.append(&shaders, shader);
|
|
if (ctx.dbgprint)
|
|
printf("Appending shader to \'%s\'\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
} else if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
|
|
if (QQuick3DInstanceListEntry *listEntry = qobject_cast<QQuick3DInstanceListEntry *>(*foundIt)) {
|
|
auto instances = qobject_cast<QQuick3DInstanceList *>(ctx.property.target)->instances();
|
|
// Since we are initializing this for the first time, make sure we clean out any inherited data!
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited instances\n");
|
|
instances.clear(&instances);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
instances.append(&instances, listEntry);
|
|
if (ctx.dbgprint)
|
|
printf("Appending instance entry to %s\n", ctx.property.name.toLatin1().constData());
|
|
} else if (QQuick3DInstanceList *instancingList = qobject_cast<QQuick3DInstanceList *>(*foundIt)) {
|
|
qobject_cast<QQuick3DModel *>(ctx.property.target)->setInstancing(instancingList);
|
|
if (ctx.dbgprint)
|
|
printf("Setting instance list on model\n");
|
|
}
|
|
} else if (ctx.dbgprint) {
|
|
printf("Unhandled binding: %s\n", idExpr.name.toLatin1().constData());
|
|
}
|
|
} else {
|
|
// 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).
|
|
// 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).
|
|
ctx.identifierMap.insert(idExpr.name, ctx.property.target);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::StringLiteral &stringLiteral, Context &ctx, int &ret)
|
|
{
|
|
Q_UNUSED(ret);
|
|
if (ctx.dbgprint)
|
|
printf("-> StringLiteral: \"%s\"\n", stringLiteral.value.toLocal8Bit().constData());
|
|
|
|
if (ctx.property.target) {
|
|
const auto &name = ctx.property.name;
|
|
const auto v = fromString(stringLiteral.value, ctx);
|
|
if (v.isValid()) {
|
|
const bool b = ctx.property.target->setProperty(name.toLatin1(), v);
|
|
if (b && ctx.dbgprint)
|
|
printf("Property %s updated!\n", name.toLatin1().constData());
|
|
}
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::NumericLiteral &numericLiteral, Context &ctx, int &ret)
|
|
{
|
|
Q_UNUSED(ret);
|
|
if (ctx.dbgprint)
|
|
printf("-> NumericLiteral: %f\n", numericLiteral.value);
|
|
|
|
if (ctx.property.target) {
|
|
auto target = ctx.property.target;
|
|
const auto &name = ctx.property.name;
|
|
target->setProperty(name.toLatin1(), QVariant::fromValue(numericLiteral.value));
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::FieldMemberExpression &fieldMemberExpression, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
|
|
Q_UNUSED(ret);
|
|
if (ctx.dbgprint)
|
|
printf("-> FieldMemberExpression: %s\n", fieldMemberExpression.name.toLocal8Bit().constData());
|
|
|
|
if (ctx.property.target) {
|
|
const auto &name = ctx.property.name;
|
|
const auto v = fromString(fieldMemberExpression.name, ctx);
|
|
if (v.isValid())
|
|
ctx.property.target->setProperty(name.toLatin1(), v);
|
|
}
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::ArrayPattern &arrayPattern, Context &ctx, int &ret)
|
|
{
|
|
Q_UNUSED(ret);
|
|
if (ctx.dbgprint)
|
|
printf("-> [ ");
|
|
|
|
using namespace QQmlJS::AST;
|
|
using PatternElementItem = PatternElementList;
|
|
using PatternElementListView = InvasiveListView<PatternElementItem>;
|
|
|
|
PatternElementListView elements(*arrayPattern.elements);
|
|
for (auto &element : elements) {
|
|
auto patternElement = element.element;
|
|
if (patternElement->type == PatternElement::Literal) {
|
|
if (patternElement->initializer && patternElement->initializer->kind == Node::Kind_IdentifierExpression) {
|
|
const auto &identExpression = static_cast<const IdentifierExpression &>(*patternElement->initializer);
|
|
visit(identExpression, ctx, ret);
|
|
}
|
|
} else if (ctx.dbgprint) {
|
|
printf("Unahandled(%d), ", patternElement->type);
|
|
}
|
|
}
|
|
|
|
if (ctx.dbgprint)
|
|
printf(" ]\n");
|
|
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
|
|
Q_UNUSED(ret);
|
|
Q_UNUSED(callExpression);
|
|
if (ctx.dbgprint)
|
|
printf("-> Call(%d)\n", callExpression.base->kind);
|
|
|
|
(ctx.interceptCallExpr && ctx.interceptCallExpr(callExpression, ctx, ret));
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiObjectMember &member, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
|
|
if (member.kind == Node::Kind_UiObjectBinding)
|
|
visit(static_cast<const UiObjectBinding &>(member), ctx, ret);
|
|
else if (ctx.dbgprint)
|
|
printf("Unhandled member (%d)\n", member.kind);
|
|
}
|
|
|
|
static void visit(const QQmlJS::AST::UiObjectMemberList &memberList, Context &ctx, int &ret)
|
|
{
|
|
using namespace QQmlJS::AST;
|
|
using ObjectMemberItem = UiObjectMemberList;
|
|
using ObjectMembers = InvasiveListView<ObjectMemberItem>;
|
|
|
|
const auto oldEvalType = ctx.property.memberState;
|
|
|
|
ObjectMembers objectMembers(memberList);
|
|
for (const auto &member : objectMembers) {
|
|
if (member.member) {
|
|
if (member.member->kind == Node::Kind_UiScriptBinding) {
|
|
ctx.property.memberState = Context::Property::MemberState::Uninitialized;
|
|
const auto &scriptBinding = static_cast<const UiScriptBinding &>(*member.member);
|
|
visit(scriptBinding, ctx, ret);
|
|
} else if (member.member->kind == Node::Kind_UiArrayBinding) {
|
|
ctx.property.memberState = Context::Property::MemberState::Uninitialized;
|
|
const auto &arrayBinding = static_cast<const UiArrayBinding &>(*member.member);
|
|
visit(arrayBinding, ctx, ret);
|
|
} else if (member.member->kind == Node::Kind_UiObjectDefinition) {
|
|
const auto &objectDef = static_cast<const UiObjectDefinition &>(*member.member);
|
|
visit(objectDef, ctx, ret);
|
|
} else if (member.member->kind == Node::Kind_UiObjectBinding) {
|
|
ctx.property.memberState = Context::Property::MemberState::Uninitialized;
|
|
const auto &objBinding = static_cast<const UiObjectBinding &>(*member.member);
|
|
visit(objBinding, ctx, ret);
|
|
} else if (member.member->kind == Node::Kind_UiPublicMember) {
|
|
ctx.property.memberState = Context::Property::MemberState::Uninitialized;
|
|
const auto &pubMember = static_cast<const UiPublicMember &>(*member.member);
|
|
visit(pubMember, ctx, ret);
|
|
} else {
|
|
if (ctx.dbgprint)
|
|
printf("<member %d>\n", member.member->kind);
|
|
}
|
|
}
|
|
}
|
|
|
|
ctx.property.memberState = oldEvalType;
|
|
}
|
|
|
|
private:
|
|
Visitors() = delete;
|
|
Q_DISABLE_COPY(Visitors);
|
|
};
|
|
|
|
template <typename O, typename T>
|
|
T *buildType(const O &obj, Context &ctx, int &ret, const T *base = nullptr)
|
|
{
|
|
// swap -> reentrancy
|
|
Context::Property property;
|
|
qSwap(property, ctx.property);
|
|
Q_ASSERT(ctx.property.target == nullptr);
|
|
|
|
T *instance = nullptr;
|
|
|
|
if (ctx.dbgprint)
|
|
printf("Building %s!\n", TypeInfo<T>::qmlTypeName());
|
|
|
|
if (obj.initializer) {
|
|
instance = new T;
|
|
if (base)
|
|
cloneProperties(*instance, *base);
|
|
|
|
if (obj.initializer) {
|
|
ctx.property.target = instance;
|
|
ctx.property.targetType = TypeInfo<T>::typeId();
|
|
Visitors::visit(*obj.initializer, ctx, ret);
|
|
}
|
|
}
|
|
|
|
// swap back
|
|
qSwap(property, ctx.property);
|
|
|
|
return instance;
|
|
}
|
|
|
|
static QQuick3DAbstractLight *buildLight(const QQmlJS::AST::UiObjectDefinition &def,
|
|
Context &ctx,
|
|
int &ret,
|
|
int lightType,
|
|
const QQuick3DAbstractLight *base = nullptr)
|
|
{
|
|
if (lightType == TypeInfo<QQuick3DDirectionalLight>::typeId())
|
|
return buildType(def, ctx, ret, qobject_cast<const QQuick3DDirectionalLight *>(base));
|
|
if (lightType == TypeInfo<QQuick3DPointLight>::typeId())
|
|
return buildType(def, ctx, ret, qobject_cast<const QQuick3DPointLight *>(base));
|
|
if (lightType == TypeInfo<QQuick3DSpotLight>::typeId())
|
|
return buildType(def, ctx, ret, qobject_cast<const QQuick3DSpotLight *>(base));
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
static void updateProperty(Context &ctx, T type, QStringView propName)
|
|
{
|
|
if (ctx.property.target) {
|
|
if (ctx.dbgprint)
|
|
printf("Updating property %s\n", propName.toLatin1().constData());
|
|
const auto &target = ctx.property.target;
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
target->setProperty(propName.toLatin1().constData(), QVariant::fromValue(type));
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
} else {
|
|
const int idx = target->metaObject()->indexOfProperty(propName.toLatin1().constData());
|
|
if (idx != -1) {
|
|
auto prop = target->metaObject()->property(idx);
|
|
prop.write(target, QVariant::fromValue(type));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool interceptObjectBinding(const QQmlJS::AST::UiObjectBinding &objectBinding, Context &ctx, int &ret)
|
|
{
|
|
if (ctx.dbgprint)
|
|
printf("Intercepted object binding!\n");
|
|
|
|
bool handled = false;
|
|
|
|
const auto &typeName = objectBinding.qualifiedTypeNameId->name.toString();
|
|
const auto &propName = objectBinding.qualifiedId->name;
|
|
|
|
int type = -1;
|
|
|
|
// Base type?
|
|
const auto typeIt = s_typeMap->constFind(typeName);
|
|
if (typeIt != s_typeMap->cend())
|
|
type = *typeIt;
|
|
|
|
// Component?
|
|
auto &components = ctx.components;
|
|
const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend();
|
|
QObject *base = nullptr;
|
|
if (compIt != components.cend()) {
|
|
type = compIt->type;
|
|
base = compIt->ptr;
|
|
}
|
|
|
|
if (type != -1) {
|
|
if (ctx.dbgprint)
|
|
printf("Resolving: \'%s\'\n", qPrintable(typeName));
|
|
|
|
if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DViewport>::typeId()) {
|
|
if (auto environment = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DSceneEnvironment *>(base))) {
|
|
auto viewport = qobject_cast<QQuick3DViewport *>(ctx.property.target);
|
|
Q_ASSERT(viewport);
|
|
viewport->setEnvironment(environment);
|
|
handled = true;
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
|
|
if (auto tex = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DTexture *>(base))) {
|
|
updateProperty(ctx, tex, propName);
|
|
ctx.sceneData.textures.append(tex);
|
|
}
|
|
handled = true;
|
|
} else if (type == TypeInfo<QQuick3DShaderUtilsTextureInput>::typeId()) {
|
|
auto texInput = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsTextureInput *>(base));
|
|
if (texInput && texInput->texture()) {
|
|
updateProperty(ctx, texInput, propName);
|
|
ctx.sceneData.textures.append(texInput->texture());
|
|
}
|
|
handled = true;
|
|
} else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
|
|
if (auto effect = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DEffect *>(base))) {
|
|
auto sceneEnvironment = qobject_cast<QQuick3DSceneEnvironment *>(ctx.property.target);
|
|
Q_ASSERT(sceneEnvironment);
|
|
auto effects = sceneEnvironment->effects();
|
|
effects.append(&effects, effect);
|
|
handled = true;
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
|
|
if (auto pass = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsRenderPass *>(base))) {
|
|
auto effect = qobject_cast<QQuick3DEffect *>(ctx.property.target);
|
|
Q_ASSERT(effect);
|
|
auto passes = effect->passes();
|
|
passes.append(&passes, pass);
|
|
handled = true;
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
|
|
if (auto shader = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DShaderUtilsShader *>(base))) {
|
|
auto pass = qobject_cast<QQuick3DShaderUtilsRenderPass *>(ctx.property.target);
|
|
Q_ASSERT(pass);
|
|
auto shaders = pass->shaders();
|
|
shaders.append(&shaders, shader);
|
|
handled = true;
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DDefaultMaterial *>(base))) {
|
|
auto model = qobject_cast<QQuick3DModel *>(ctx.property.target);
|
|
Q_ASSERT(model);
|
|
auto materials = model->materials();
|
|
materials.append(&materials, mat);
|
|
handled = true;
|
|
if (ctx.dbgprint)
|
|
printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DPrincipledMaterial *>(base))) {
|
|
auto model = qobject_cast<QQuick3DModel *>(ctx.property.target);
|
|
Q_ASSERT(model);
|
|
auto materials = model->materials();
|
|
materials.append(&materials, mat);
|
|
handled = true;
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
if (auto mat = buildType(objectBinding, ctx, ret, qobject_cast<QQuick3DCustomMaterial *>(base))) {
|
|
auto model = qobject_cast<QQuick3DModel *>(ctx.property.target);
|
|
Q_ASSERT(model);
|
|
auto materials = model->materials();
|
|
materials.append(&materials, mat);
|
|
handled = true;
|
|
}
|
|
}
|
|
} else if (ctx.dbgprint) {
|
|
printf("Unhandled type\n");
|
|
}
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
static bool interceptObjectDef(const QQmlJS::AST::UiObjectDefinition &def, Context &ctx, int &ret)
|
|
{
|
|
const auto &typeName = def.qualifiedTypeNameId->name.toString();
|
|
|
|
if (ctx.dbgprint)
|
|
printf("Intercepted object definition (\'%s\')!\n", typeName.toLatin1().constData());
|
|
|
|
QString componentName;
|
|
int type = -1;
|
|
bool doRegisterComponent = false;
|
|
|
|
// Base type?
|
|
const auto typeIt = s_typeMap->constFind(typeName);
|
|
if (typeIt != s_typeMap->cend())
|
|
type = *typeIt;
|
|
|
|
// Component?
|
|
auto &components = ctx.components;
|
|
const auto compIt = (type == -1) ? components.constFind(typeName) : components.cend();
|
|
if (compIt != components.cend())
|
|
type = compIt->type;
|
|
|
|
// If this is a new component register it
|
|
if (ctx.type == Context::Type::Component && ctx.property.target == nullptr && type != -1) {
|
|
const auto &fileName = ctx.currentFileInfo.fileName();
|
|
componentName = fileName.left(fileName.size() - 4);
|
|
doRegisterComponent = !componentName.isEmpty();
|
|
}
|
|
|
|
const auto registerComponent = [&ctx, &components, &componentName](Context::Component component) {
|
|
if (ctx.dbgprint)
|
|
printf("Registering component \'%s\'\n", qPrintable(componentName));
|
|
components.insert(componentName, component);
|
|
};
|
|
|
|
if (type == TypeInfo<QQuick3DViewport>::typeId()) {
|
|
const QQuick3DViewport *base = (compIt != components.cend()) ? qobject_cast<QQuick3DViewport *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DViewport *viewport = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ viewport, type });
|
|
// Only one viewport supported atm (see SceneEnvironment case as well).
|
|
if (!ctx.sceneData.viewport)
|
|
ctx.sceneData.viewport = viewport;
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
|
|
const QQuick3DSceneEnvironment *base = (compIt != components.cend()) ? qobject_cast<QQuick3DSceneEnvironment *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DSceneEnvironment *sceneEnv = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ sceneEnv, type });
|
|
|
|
if (ctx.sceneData.viewport)
|
|
ctx.sceneData.viewport->setEnvironment(sceneEnv);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DPrincipledMaterial>::typeId()) {
|
|
const QQuick3DPrincipledMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DPrincipledMaterial *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DPrincipledMaterial *mat = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ mat, type });
|
|
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited materials\n");
|
|
materials.clear(&materials);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
materials.append(&materials, mat);
|
|
if (ctx.dbgprint)
|
|
printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
|
|
// At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
|
|
ctx.sceneData.materials.push_back(mat);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DDefaultMaterial>::typeId()) {
|
|
const QQuick3DDefaultMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DDefaultMaterial *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DDefaultMaterial *mat = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ mat, type });
|
|
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited materials\n");
|
|
materials.clear(&materials);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
materials.append(&materials, mat);
|
|
if (ctx.dbgprint)
|
|
printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
|
|
// At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
|
|
ctx.sceneData.materials.push_back(mat);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DCustomMaterial>::typeId()) {
|
|
const QQuick3DCustomMaterial *base = (compIt != components.cend()) ? qobject_cast<QQuick3DCustomMaterial *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DCustomMaterial *mat = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ mat, type });
|
|
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
auto materials = qobject_cast<QQuick3DModel *>(ctx.property.target)->materials();
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited materials\n");
|
|
materials.clear(&materials);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
materials.append(&materials, mat);
|
|
if (ctx.dbgprint)
|
|
printf("Appending material to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
|
|
// At this point we don't know if this material is going to be referenced somewhere else, so keep it in the list
|
|
ctx.sceneData.materials.push_back(mat);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DEffect>::typeId()) {
|
|
const QQuick3DEffect *base = (compIt != components.cend()) ? qobject_cast<QQuick3DEffect *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DEffect *effect = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ effect, type });
|
|
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DSceneEnvironment>::typeId()) {
|
|
auto effects = qobject_cast<QQuick3DSceneEnvironment *>(ctx.property.target)->effects();
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited effects\n");
|
|
effects.clear(&effects);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
effects.append(&effects, effect);
|
|
if (ctx.dbgprint)
|
|
printf("Appending effect to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
|
|
// At this point we don't know if this effect is going to be referenced somewhere else, so keep it in the list
|
|
ctx.sceneData.effects.push_back(effect);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DDirectionalLight>::typeId() || type == TypeInfo<QQuick3DPointLight>::typeId() || type == TypeInfo<QQuick3DSpotLight>::typeId()) {
|
|
const QQuick3DAbstractLight *base = (compIt != components.cend()) ? qobject_cast<QQuick3DAbstractLight *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DAbstractLight *light = buildLight(def, ctx, ret, type, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ light, type });
|
|
|
|
ctx.sceneData.lights.push_back(light);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DTexture>::typeId()) {
|
|
const QQuick3DTexture *base = (compIt != components.cend()) ? qobject_cast<QQuick3DTexture *>(compIt->ptr) : nullptr;
|
|
if (QQuick3DTexture *tex = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ tex, type });
|
|
|
|
ctx.sceneData.textures.push_back(tex);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DModel>::typeId()) {
|
|
const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DModel *>(compIt->ptr) : nullptr;
|
|
if (auto *model = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ model, type });
|
|
|
|
ctx.sceneData.models.push_back(model);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DShaderUtilsShader>::typeId()) {
|
|
const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsShader *>(compIt->ptr) : nullptr;
|
|
if (auto *shader = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ shader, type });
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
|
|
auto shaders = qobject_cast<QQuick3DShaderUtilsRenderPass *>(ctx.property.target)->shaders();
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited shaders\n");
|
|
shaders.clear(&shaders);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
shaders.append(&shaders, shader);
|
|
if (ctx.dbgprint)
|
|
printf("Appending shader to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
|
|
ctx.sceneData.shaders.push_back(shader);
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DShaderUtilsRenderPass>::typeId()) {
|
|
const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DShaderUtilsRenderPass *>(compIt->ptr) : nullptr;
|
|
if (auto *pass = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ pass, type });
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) {
|
|
auto passes = qobject_cast<QQuick3DEffect *>(ctx.property.target)->passes();
|
|
if (ctx.property.memberState == Context::Property::Uninitialized) {
|
|
if (ctx.dbgprint)
|
|
printf("Clearing inherited passes\n");
|
|
passes.clear(&passes);
|
|
ctx.property.memberState = Context::Property::Initialized;
|
|
}
|
|
passes.append(&passes, pass);
|
|
if (ctx.dbgprint)
|
|
printf("Appending pass to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DInstanceList>::typeId()) {
|
|
const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceList *>(compIt->ptr) : nullptr;
|
|
if (auto *instanceList = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ instanceList, type });
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DModel>::typeId()) {
|
|
qobject_cast<QQuick3DModel *>(ctx.property.target)->setInstancing(instanceList);
|
|
if (ctx.dbgprint)
|
|
printf("Setting instance list on %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
}
|
|
} else if (type == TypeInfo<QQuick3DInstanceListEntry>::typeId()) {
|
|
const auto *base = (compIt != components.cend()) ? qobject_cast<QQuick3DInstanceListEntry *>(compIt->ptr) : nullptr;
|
|
if (auto *instanceListEntry = buildType(def, ctx, ret, base)) {
|
|
// If this is a component we'll store it for lookups later.
|
|
if (doRegisterComponent)
|
|
registerComponent({ instanceListEntry, type });
|
|
if (ctx.property.target) {
|
|
if (ctx.property.targetType == TypeInfo<QQuick3DInstanceList>::typeId()) {
|
|
auto instances = qobject_cast<QQuick3DInstanceList *>(ctx.property.target)->instances();
|
|
instances.append(&instances, instanceListEntry);
|
|
if (ctx.dbgprint)
|
|
printf("Appending instance list entry to %s\n", ctx.property.name.toLatin1().constData());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (ctx.dbgprint)
|
|
printf("Object def for \'%s\' was not handled\n", ctx.property.name.toLatin1().constData());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool interceptPublicMember(const QQmlJS::AST::UiPublicMember &member, Context &ctx, int &ret)
|
|
{
|
|
Q_UNUSED(ret);
|
|
using namespace QQmlJS::AST;
|
|
|
|
if (ctx.dbgprint)
|
|
printf("Intercepted public member!\n");
|
|
|
|
if (member.statement && member.statement->kind == Node::Kind_ExpressionStatement) {
|
|
if ((ctx.property.targetType == TypeInfo<QQuick3DCustomMaterial>::typeId() || ctx.property.targetType == TypeInfo<QQuick3DEffect>::typeId()) && member.memberType) {
|
|
// For custom materials we have properties that are user provided, so we'll
|
|
// need to add these to the objects properties (we add these here to be able
|
|
// to piggyback on the existing type matching code)
|
|
if (member.memberType->name == u"real") {
|
|
ctx.property.type = QMetaType::Double;
|
|
} else if (member.memberType->name == u"bool") {
|
|
ctx.property.type = QMetaType::Bool;
|
|
} else if (member.memberType->name == u"int") {
|
|
ctx.property.type = QMetaType::Int;
|
|
} else if (member.memberType->name == u"size") {
|
|
ctx.property.type = QMetaType::QSizeF;
|
|
} else if (member.memberType->name == u"rect") {
|
|
ctx.property.type = QMetaType::QRectF;
|
|
} else if (member.memberType->name == u"point") {
|
|
ctx.property.type = QMetaType::QPointF;
|
|
} else if (member.memberType->name == u"color") {
|
|
ctx.property.type = QMetaType::QColor;
|
|
} else if (member.memberType->name.startsWith(u"vector")) {
|
|
if (member.memberType->name.endsWith(u"2d")) {
|
|
ctx.property.type = QMetaType::QVector2D;
|
|
} else if (member.memberType->name.endsWith(u"3d")) {
|
|
ctx.property.type = QMetaType::QVector3D;
|
|
} else if (member.memberType->name.endsWith(u"4d")) {
|
|
ctx.property.type = QMetaType::QVector4D;
|
|
}
|
|
} else if (member.memberType->name == u"matrix4x4") {
|
|
ctx.property.type = QMetaType::QMatrix4x4;;
|
|
} else if (member.memberType->name == u"quaternion") {
|
|
ctx.property.type = QMetaType::QQuaternion;
|
|
} else if (member.memberType->name == u"var") {
|
|
ctx.property.type = QMetaType::QVariant;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool interceptCallExpression(const QQmlJS::AST::CallExpression &callExpression, Context &ctx, int &ret)
|
|
{
|
|
Q_UNUSED(ret);
|
|
using namespace QQmlJS::AST;
|
|
using namespace BuiltinHelpers;
|
|
|
|
if (ctx.dbgprint)
|
|
printf("Intercepted call expression!\n");
|
|
|
|
const bool ok = (ctx.property.target && !ctx.property.name.isEmpty());
|
|
if (callExpression.base && ok) {
|
|
if (callExpression.base->kind == Node::Kind_FieldMemberExpression) {
|
|
const auto &fieldMemberExpression = static_cast<const FieldMemberExpression &>(*callExpression.base);
|
|
if (fieldMemberExpression.base) {
|
|
if (fieldMemberExpression.base->kind == Node::Kind_IdentifierExpression) {
|
|
const auto &identExpr = static_cast<const IdentifierExpression &>(*fieldMemberExpression.base);
|
|
if (identExpr.name == u"Qt") {
|
|
bool ok = false;
|
|
QVariant v;
|
|
if (fieldMemberExpression.name == u"point") {
|
|
const auto point = toPoint(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.point(%f, %f)\n", point.x(), point.y());
|
|
setProperty(ctx.property, point);
|
|
} else if (fieldMemberExpression.name == u"size") {
|
|
const auto size = toSize(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.size(%f, %f)\n", size.width(), size.height());
|
|
setProperty(ctx.property, size);
|
|
} else if (fieldMemberExpression.name == u"rect") {
|
|
const auto rect = toRect(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.rect(%f, %f, %f, %f)\n", rect.x(), rect.y(), rect.width(), rect.height());
|
|
setProperty(ctx.property, rect);
|
|
} else if (fieldMemberExpression.name.startsWith(u"vector")) {
|
|
if (fieldMemberExpression.name.endsWith(u"2d")) {
|
|
const auto vec2 = toVec<QVector2D>(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.vector2d(%f, %f)\n", vec2.x(), vec2.y());
|
|
setProperty(ctx.property, vec2);
|
|
} else if (fieldMemberExpression.name.endsWith(u"3d")) {
|
|
const auto vec3 = toVec<QVector3D>(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.vector3d(%f, %f, %f)\n", vec3.x(), vec3.y(), vec3.z());
|
|
setProperty(ctx.property, vec3);
|
|
} else if (fieldMemberExpression.name.endsWith(u"4d")) {
|
|
const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.vector4d(%f, %f, %f, %f)\n", vec4.x(), vec4.y(), vec4.z(), vec4.w());
|
|
setProperty(ctx.property, vec4);
|
|
}
|
|
} else if (fieldMemberExpression.name == u"matrix4x4") {
|
|
const auto mat44 = toMat44(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.matrix4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n",
|
|
mat44(0, 0), mat44(0, 1), mat44(0, 2), mat44(0, 3),
|
|
mat44(1, 0), mat44(1, 1), mat44(1, 2), mat44(1, 3),
|
|
mat44(2, 0), mat44(2, 1), mat44(2, 2), mat44(2, 3),
|
|
mat44(3, 0), mat44(3, 1), mat44(3, 2), mat44(3, 3));
|
|
setProperty(ctx.property, mat44);
|
|
} else if (fieldMemberExpression.name == u"quaternion") {
|
|
const auto quat = toQuaternion(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ctx.dbgprint)
|
|
printf("Qt.quaternion(%f, %f, %f, %f)\n", quat.scalar(), quat.x(), quat.y(), quat.z());
|
|
setProperty(ctx.property, quat);
|
|
} else if (fieldMemberExpression.name == u"rgba") {
|
|
const auto vec4 = toVec<QVector4D>(ArgumentListView(*callExpression.arguments), &ok);
|
|
if (ok) {
|
|
QColor color = QColor::fromRgbF(vec4.x(), vec4.y(), vec4.z(), vec4.w());
|
|
if (ctx.dbgprint)
|
|
printf("Qt.rgba(%f, %f, %f, %f)\n", color.redF(), color.greenF(), color.blueF(), color.alphaF());
|
|
setProperty(ctx.property, color);
|
|
}
|
|
}
|
|
if (ok && v.isValid() && ctx.property.target)
|
|
ctx.property.target->setProperty(ctx.property.name.toLatin1().constData(), v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int parseQmlData(const QByteArray &code, Context &ctx)
|
|
{
|
|
Q_ASSERT(ctx.engine && ctx.engine->lexer());
|
|
ctx.identifierMap.clear(); // not visible outside the scope of this "code"
|
|
if (ctx.dbgprint)
|
|
printf("Parsing %s\n", qPrintable(ctx.currentFileInfo.filePath()));
|
|
int ret = 0;
|
|
ctx.engine->lexer()->setCode(QString::fromUtf8(code), 1, true);
|
|
QQmlJS::Parser parser(ctx.engine);
|
|
const bool ok = parser.parse();
|
|
if (ok) {
|
|
const auto program = parser.ast();
|
|
if (program)
|
|
Visitors::visit(*program, ctx, ret);
|
|
} else {
|
|
ret = -1;
|
|
qWarning("Parsing failed due to %s in %s:%d%d", qPrintable(parser.errorMessage()), qPrintable(ctx.currentFileInfo.fileName()), parser.errorLineNumber(), parser.errorColumnNumber());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int MaterialParser::parseQmlData(const QByteArray &code, const QString &fileName, MaterialParser::SceneData &sceneData)
|
|
{
|
|
// set initial type map
|
|
*s_typeMap = baseTypeMap();
|
|
|
|
QQmlJS::Engine engine;
|
|
QQmlJS::Lexer lexer(&engine);
|
|
|
|
Context ctx;
|
|
ctx.engine = &engine;
|
|
ctx.interceptODFunc = &interceptObjectDef;
|
|
ctx.interceptOBFunc = &interceptObjectBinding;
|
|
ctx.interceptPMFunc = &interceptPublicMember;
|
|
ctx.interceptCallExpr = &interceptCallExpression;
|
|
ctx.currentFileInfo = QFileInfo(fileName);
|
|
ctx.type = Context::Type::Component;
|
|
|
|
const int ret = ::parseQmlData(code, ctx);
|
|
sceneData = std::move(ctx.sceneData);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int MaterialParser::parseQmlFiles(const QVector<QString> &filePaths, const QDir &sourceDir, SceneData &sceneData, bool verboseOutput)
|
|
{
|
|
// set initial type map
|
|
*s_typeMap = baseTypeMap();
|
|
|
|
int ret = 0;
|
|
|
|
if (filePaths.isEmpty()) {
|
|
qWarning("No input files");
|
|
return ret;
|
|
}
|
|
|
|
QQmlJS::Engine engine;
|
|
QQmlJS::Lexer lexer(&engine);
|
|
|
|
Context ctx;
|
|
ctx.dbgprint = verboseOutput;
|
|
ctx.engine = &engine;
|
|
ctx.interceptODFunc = &interceptObjectDef;
|
|
ctx.interceptOBFunc = &interceptObjectBinding;
|
|
ctx.interceptPMFunc = &interceptPublicMember;
|
|
ctx.interceptCallExpr = &interceptCallExpression;
|
|
ctx.workingDir = sourceDir;
|
|
|
|
QVector<QString> deferredOther;
|
|
QVector<QString> deferredComponets;
|
|
|
|
const QString sourcePath = sourceDir.canonicalPath() + QDir::separator();
|
|
|
|
const bool isMultifile = filePaths.size() != 1;
|
|
|
|
// Go through and find the material components first
|
|
for (const auto &v : filePaths) {
|
|
QFileInfo ¤tFileInfo = ctx.currentFileInfo;
|
|
if (!QFileInfo(v).isAbsolute())
|
|
currentFileInfo.setFile(sourcePath + v);
|
|
else
|
|
currentFileInfo.setFile(v);
|
|
const bool maybeComponent = currentFileInfo.fileName().at(0).isUpper();
|
|
if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
|
|
const QString filePath = currentFileInfo.canonicalFilePath();
|
|
if (isMultifile && maybeComponent) {
|
|
QFile f(filePath);
|
|
if (!f.open(QFile::ReadOnly)) {
|
|
qWarning("Could not open file %s for reading!", qPrintable(filePath));
|
|
return -1;
|
|
}
|
|
|
|
const QByteArray code = f.readAll();
|
|
int idx = code.indexOf('{');
|
|
if (idx != -1) {
|
|
const QByteArray section = code.mid(0, idx);
|
|
QVarLengthArray<const char *, 3> componentTypes { TypeInfo<QQuick3DPrincipledMaterial>::qmlTypeName(),
|
|
TypeInfo<QQuick3DCustomMaterial>::qmlTypeName(),
|
|
TypeInfo<QQuick3DDefaultMaterial>::qmlTypeName()};
|
|
for (const auto compType : std::as_const(componentTypes)) {
|
|
if ((idx = section.indexOf(compType)) != -1)
|
|
break;
|
|
}
|
|
if (idx != -1) {
|
|
ctx.type = Context::Type::Component;
|
|
ret = parseQmlData(code, ctx);
|
|
if (ret != 0)
|
|
break;
|
|
} else {
|
|
deferredComponets.push_back(filePath);
|
|
}
|
|
} else {
|
|
qWarning("No items found in %s\n", qPrintable(filePath));
|
|
}
|
|
} else {
|
|
deferredOther.push_back(filePath);
|
|
}
|
|
} else {
|
|
qWarning("The file %s is either not a file or has the wrong extension!", qPrintable(v));
|
|
}
|
|
}
|
|
|
|
const auto parsePaths = [&ctx, &ret](const QVector<QString> &paths, Context::Type type) {
|
|
ctx.type = type;
|
|
for (const auto &path : paths) {
|
|
QFileInfo ¤tFileInfo = ctx.currentFileInfo;
|
|
currentFileInfo.setFile(path);
|
|
if (currentFileInfo.isFile() && currentFileInfo.suffix() == getQmlFileExtension()) {
|
|
const QString filePath = currentFileInfo.canonicalFilePath();
|
|
QFile f(filePath);
|
|
if (!f.open(QFile::ReadOnly)) {
|
|
qWarning("Could not open file %s for reading!", qPrintable(filePath));
|
|
ret = -1;
|
|
return;
|
|
}
|
|
|
|
const QByteArray code = f.readAll();
|
|
ret = parseQmlData(code, ctx);
|
|
if (ret != 0)
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Other components
|
|
parsePaths(deferredComponets, Context::Type::Component);
|
|
|
|
// Now parse the rest
|
|
parsePaths(deferredOther, Context::Type::Application);
|
|
|
|
sceneData = std::move(ctx.sceneData);
|
|
|
|
return ret;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|