Revision control
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:critical reason:data-parser
++
++ #include "uniformmodel.h"
++ #include "propertyhandler.h"
++ #include "effectmanager.h"
++
++ UniformModel::UniformModel(QObject *parent)
++ : QAbstractListModel(parent)
++ {
++
++ }
++
++ void UniformModel::setModelData(UniformTable *data)
++ {
++ beginResetModel();
++ m_uniformTable = data;
++ endResetModel();
++ updateCanMoveStatus();
++ Q_EMIT uniformsChanged();
++ }
++
++ int UniformModel::rowCount(const QModelIndex &) const
++ {
++ return int((m_uniformTable != nullptr) ? m_uniformTable->size() : 0);
++ }
++
++ QVariant UniformModel::data(const QModelIndex &index, int role) const
++ {
++ if (!m_uniformTable || !index.isValid())
++ return QVariant();
++
++ if (index.row() >= m_uniformTable->size())
++ return false;
++
++ const auto &uniform = (*m_uniformTable)[index.row()];
++
++ if (role == Type) {
++ return QVariant::fromValue(uniform.type);
++ } else if (role == NodeId) {
++ return QVariant::fromValue(uniform.nodeId);
++ } else if (role == Name) {
++ return QVariant::fromValue(QString::fromLatin1(uniform.name));
++ } else if (role == Value) {
++ switch (uniform.type) {
++ case Uniform::Type::Sampler:
++ return uniform.value.value<QString>();
++ case Uniform::Type::Bool:
++ return uniform.value.value<bool>();
++ case Uniform::Type::Int:
++ return uniform.value.value<int>();
++ case Uniform::Type::Float:
++ return uniform.value.value<double>();
++ case Uniform::Type::Vec2:
++ return uniform.value.value<QVector2D>();
++ case Uniform::Type::Vec3:
++ return uniform.value.value<QVector3D>();
++ case Uniform::Type::Vec4:
++ return uniform.value.value<QVector4D>();
++ case Uniform::Type::Color:
++ return uniform.value.value<QColor>();
++ case Uniform::Type::Define:
++ return uniform.value.value<QString>();
++ }
++ } else if (role == DefaultValue) {
++ switch (uniform.type) {
++ case Uniform::Type::Sampler:
++ return uniform.defaultValue.value<QString>();
++ case Uniform::Type::Bool:
++ return uniform.defaultValue.value<bool>();
++ case Uniform::Type::Int:
++ return uniform.defaultValue.value<int>();
++ case Uniform::Type::Float:
++ return uniform.defaultValue.value<double>();
++ case Uniform::Type::Vec2:
++ return uniform.defaultValue.value<QVector2D>();
++ case Uniform::Type::Vec3:
++ return uniform.defaultValue.value<QVector3D>();
++ case Uniform::Type::Vec4:
++ return uniform.defaultValue.value<QVector4D>();
++ case Uniform::Type::Color:
++ return uniform.defaultValue.value<QColor>();
++ case Uniform::Type::Define:
++ return uniform.defaultValue.value<QString>();
++ }
++ } else if (role == MinValue) {
++ switch (uniform.type) {
++ case Uniform::Type::Sampler:
++ return uniform.minValue.value<QString>();
++ case Uniform::Type::Bool:
++ return uniform.minValue.value<bool>();
++ case Uniform::Type::Int:
++ return uniform.minValue.value<int>();
++ case Uniform::Type::Float:
++ return uniform.minValue.value<double>();
++ case Uniform::Type::Vec2:
++ return uniform.minValue.value<QVector2D>();
++ case Uniform::Type::Vec3:
++ return uniform.minValue.value<QVector3D>();
++ case Uniform::Type::Vec4:
++ return uniform.minValue.value<QVector4D>();
++ case Uniform::Type::Color:
++ return uniform.minValue.value<QColor>();
++ case Uniform::Type::Define:
++ return uniform.minValue.value<QString>();
++ }
++ } else if (role == MaxValue) {
++ switch (uniform.type) {
++ case Uniform::Type::Sampler:
++ return uniform.maxValue.value<QString>();
++ case Uniform::Type::Bool:
++ return uniform.maxValue.value<bool>();
++ case Uniform::Type::Int:
++ return uniform.maxValue.value<int>();
++ case Uniform::Type::Float:
++ return uniform.maxValue.value<double>();
++ case Uniform::Type::Vec2:
++ return uniform.maxValue.value<QVector2D>();
++ case Uniform::Type::Vec3:
++ return uniform.maxValue.value<QVector3D>();
++ case Uniform::Type::Vec4:
++ return uniform.maxValue.value<QVector4D>();
++ case Uniform::Type::Color:
++ return uniform.maxValue.value<QColor>();
++ case Uniform::Type::Define:
++ return uniform.maxValue.value<QString>();
++ }
++ } else if (role == Description) {
++ return QVariant::fromValue(uniform.description);
++ } else if (role == CustomValue) {
++ return QVariant::fromValue(uniform.customValue);
++ } else if (role == UseCustomValue) {
++ return QVariant::fromValue(uniform.useCustomValue);
++ } else if (role == Visible) {
++ return QVariant::fromValue(uniform.visible);
++ } else if (role == ExportProperty) {
++ return QVariant::fromValue(uniform.exportProperty);
++ } else if (role == CanMoveUp) {
++ return QVariant::fromValue(uniform.canMoveUp);
++ } else if (role == CanMoveDown) {
++ return QVariant::fromValue(uniform.canMoveDown);
++ } else if (role == EnableMipmap) {
++ return QVariant::fromValue(uniform.enableMipmap);
++ } else if (role == ExportImage) {
++ return QVariant::fromValue(uniform.exportImage);
++ }
++
++ return QVariant();
++ }
++
++ QHash<int, QByteArray> UniformModel::roleNames() const
++ {
++ QHash<int, QByteArray> roles;
++ roles[Type] = "type";
++ roles[Name] = "name";
++ roles[Value] = "value";
++ roles[DefaultValue] = "defaultValue";
++ roles[MinValue] = "minValue";
++ roles[MaxValue] = "maxValue";
++ roles[NodeId] = "nodeId";
++ roles[Description] = "description";
++ roles[CustomValue] = "customValue";
++ roles[UseCustomValue] = "useCustomValue";
++ roles[Visible] = "visible";
++ roles[ExportProperty] = "exportProperty";
++ roles[CanMoveUp] = "canMoveUp";
++ roles[CanMoveDown] = "canMoveDown";
++ roles[EnableMipmap] = "enableMipmap";
++ roles[ExportImage] = "exportImage";
++ return roles;
++ }
++
++ bool UniformModel::setData(const QModelIndex &index, const QVariant &value, int role)
++ {
++ if (!index.isValid() || !m_uniformTable)
++ return false;
++
++ if (index.row() >= m_uniformTable->size())
++ return false;
++
++ auto &uniform = (*m_uniformTable)[index.row()];
++
++ bool ok = true;
++
++ if (role == Type) {
++ const auto v = value.toInt(&ok);
++ if (ok)
++ uniform.type = static_cast<Uniform::Type>(v);
++ } else if (role == Name) {
++ uniform.name = value.toString().toUtf8();
++ } else if (role == Value) {
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ case Uniform::Type::Int:
++ case Uniform::Type::Float:
++ case Uniform::Type::Vec2:
++ case Uniform::Type::Vec3:
++ case Uniform::Type::Vec4:
++ case Uniform::Type::Color:
++ case Uniform::Type::Sampler:
++ case Uniform::Type::Define:
++ uniform.value = value;
++ break;
++ }
++ } else if (role == DefaultValue) {
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ case Uniform::Type::Int:
++ case Uniform::Type::Float:
++ case Uniform::Type::Vec2:
++ case Uniform::Type::Vec3:
++ case Uniform::Type::Vec4:
++ case Uniform::Type::Color:
++ case Uniform::Type::Sampler:
++ case Uniform::Type::Define:
++ uniform.defaultValue = value;
++ break;
++ }
++ } else if (role == MinValue) {
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ case Uniform::Type::Int:
++ case Uniform::Type::Float:
++ case Uniform::Type::Vec2:
++ case Uniform::Type::Vec3:
++ case Uniform::Type::Vec4:
++ case Uniform::Type::Color:
++ case Uniform::Type::Sampler:
++ case Uniform::Type::Define:
++ uniform.minValue = value;
++ break;
++ }
++ } else if (role == MaxValue) {
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ case Uniform::Type::Int:
++ case Uniform::Type::Float:
++ case Uniform::Type::Vec2:
++ case Uniform::Type::Vec3:
++ case Uniform::Type::Vec4:
++ case Uniform::Type::Color:
++ case Uniform::Type::Sampler:
++ case Uniform::Type::Define:
++ uniform.maxValue = value;
++ break;
++ }
++ } else if (role == Description) {
++ uniform.description = value.toString();
++ } else if (role == CustomValue) {
++ uniform.customValue = value.toString();
++ } else if (role == UseCustomValue) {
++ uniform.useCustomValue = value.toBool();
++ } else if (role == ExportProperty) {
++ uniform.exportProperty = value.toBool();
++ } else if (role == CanMoveUp) {
++ uniform.canMoveUp = value.toBool();
++ } else if (role == CanMoveDown) {
++ uniform.canMoveDown = value.toBool();
++ } else if (role == EnableMipmap) {
++ uniform.enableMipmap = value.toBool();
++ } else if (role == ExportImage) {
++ uniform.exportImage = value.toBool();
++ }
++
++ if (role == Value) {
++ // Don't update uniforms when value is changed, as those
++ // go through g_propertyData
++ g_propertyData.insert(uniform.name, value);
++ if (m_effectManager)
++ m_effectManager->setUnsavedChanges(true);
++ }
++ if (role != Value || !uniform.exportProperty) {
++ // But non-exported (const value) properties need update
++ Q_EMIT uniformsChanged();
++ }
++
++ if (ok)
++ emit dataChanged(index, index, {role});
++
++ return ok;
++ }
++
++ // Initialize the value variant with correct type
++ QVariant UniformModel::getInitializedVariant(Uniform::Type type, bool maxValue)
++ {
++ switch (type) {
++ case Uniform::Type::Sampler:
++ return QString();
++ case Uniform::Type::Bool:
++ return maxValue ? true : false;
++ case Uniform::Type::Int:
++ return maxValue ? 100 : 0;
++ case Uniform::Type::Float:
++ return maxValue ? 1.0 : 0.0;
++ case Uniform::Type::Vec2:
++ return maxValue ? QVector2D(1.0, 1.0) : QVector2D(0.0, 0.0);
++ case Uniform::Type::Vec3:
++ return maxValue ? QVector3D(1.0, 1.0, 1.0) : QVector3D(0.0, 0.0, 0.0);
++ case Uniform::Type::Vec4:
++ return maxValue ? QVector4D(1.0, 1.0, 1.0, 1.0) : QVector4D(0.0, 0.0, 0.0, 0.0);
++ case Uniform::Type::Color:
++ return maxValue ? QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f) : QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f);
++ case Uniform::Type::Define:
++ return QString();
++ }
++ return QVariant();
++ }
++
++ // Return name for the image property Image element
++ QString UniformModel::getImageElementName(const Uniform &uniform)
++ {
++ if (uniform.value.toString().isEmpty())
++ return QStringLiteral("null");
++ QString simplifiedName = uniform.name.simplified();
++ simplifiedName = simplifiedName.remove(' ');
++ return QStringLiteral("imageItem") + simplifiedName;
++ }
++
++ // This will update the row content at rowIndex
++ // When rowIndex = -1, new row will be appended
++ bool UniformModel::updateRow(int nodeId, int rowIndex, int type, const QString &id,
++ const QVariant &defaultValue, const QString &description, const QString &customValue,
++ bool useCustomValue, const QVariant &minValue, const QVariant &maxValue,
++ bool enableMipmap, bool exportImage)
++ {
++ if (m_uniformTable == nullptr)
++ return false;
++
++ bool addNewRow = false;
++ if (rowIndex == -1) {
++ addNewRow = true;
++ // Add new row after the last of its node so uniforms are
++ // in continuous groups per node.
++ rowIndex = m_uniformTable->size();
++ for (int i = m_uniformTable->size() - 1; i >= 0; i--) {
++ auto &uniform = (*m_uniformTable)[i];
++ if (uniform.nodeId == nodeId) {
++ rowIndex = i + 1;
++ break;
++ }
++ }
++ }
++
++ if (addNewRow && !validateUniformName(id))
++ return false;
++
++ if (addNewRow)
++ beginInsertRows(QModelIndex(), rowIndex, rowIndex);
++
++ Uniform newUniform = { };
++ auto &uniform = addNewRow ? newUniform : (*m_uniformTable)[rowIndex];
++
++ uniform.type = Uniform::Type(type);
++ uniform.name = id.toLocal8Bit();
++ uniform.description = description;
++ uniform.customValue = customValue;
++ uniform.useCustomValue = useCustomValue;
++ uniform.enableMipmap = enableMipmap;
++ uniform.exportImage = exportImage;
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ uniform.defaultValue = defaultValue;
++ break;
++ case Uniform::Type::Int:
++ case Uniform::Type::Float:
++ case Uniform::Type::Vec2:
++ case Uniform::Type::Vec3:
++ case Uniform::Type::Vec4:
++ case Uniform::Type::Color:
++ case Uniform::Type::Define:
++ uniform.defaultValue = defaultValue;
++ uniform.minValue = minValue;
++ uniform.maxValue = maxValue;
++ break;
++ case Uniform::Type::Sampler:
++ if (defaultValue.metaType().id() == QMetaType::QUrl)
++ uniform.defaultValue = defaultValue.toString();
++ else
++ uniform.defaultValue = "";
++ // Update the mipmap property
++ QString mipmapProperty = m_effectManager->mipmapPropertyName(uniform.name);
++ g_propertyData[mipmapProperty] = enableMipmap;
++ break;
++ }
++
++ if (addNewRow) {
++ // When adding a property, value is default value
++ uniform.value = uniform.defaultValue;
++ g_propertyData.insert(uniform.name, uniform.value);
++ // Note: NodeId is only updated when adding new property
++ // Properties can't be moved to other nodes
++ uniform.nodeId = nodeId;
++ m_uniformTable->insert(rowIndex, uniform);
++ endInsertRows();
++ updateCanMoveStatus();
++ if (uniform.type == Uniform::Type::Sampler) {
++ QString fs = "\n";
++ fs += "// *** Autogenerated - Remove if not needed ***\n";
++ fs += "// vec4 " + uniform.name + "Tex = texture(" + uniform.name + ", texCoord);\n";
++ Q_EMIT addFSCode(fs);
++ }
++ } else {
++ if (!uniform.useCustomValue)
++ g_propertyData.insert(uniform.name, uniform.value);
++ Q_EMIT dataChanged(QAbstractItemModel::createIndex(rowIndex, 0),
++ QAbstractItemModel::createIndex(rowIndex, 0));
++ }
++ // Update QML component as useCustomValue, customValue etc. might have changed
++ Q_EMIT qmlComponentChanged();
++ Q_EMIT uniformsChanged();
++ return true;
++ }
++
++ bool UniformModel::resetValue(int rowIndex)
++ {
++ auto &uniform = (*m_uniformTable)[rowIndex];
++ auto index = QAbstractItemModel::createIndex(rowIndex, 0);
++ setData(index, uniform.defaultValue, Value);
++
++ g_propertyData.insert(uniform.name, uniform.defaultValue);
++
++ emit dataChanged(index, index, {Value});
++
++ // Image element may change, so update component
++ if (uniform.type == Uniform::Type::Sampler)
++ Q_EMIT qmlComponentChanged();
++ Q_EMIT uniformsChanged();
++
++ return true;
++ }
++
++ // Updates the image data at rowIndex
++ bool UniformModel::setImage(int rowIndex, const QVariant &value)
++ {
++ auto &uniform = (*m_uniformTable)[rowIndex];
++ uniform.value = value.toString();
++
++ g_propertyData.insert(uniform.name, value);
++
++ emit dataChanged(QAbstractItemModel::createIndex(0, 0),
++ QAbstractItemModel::createIndex(rowIndex, 0));
++
++ // Image element may change, so update component
++ if (uniform.type == Uniform::Type::Sampler)
++ Q_EMIT qmlComponentChanged();
++ Q_EMIT uniformsChanged();
++ return true;
++ }
++
++ // Move uniform up/down in the list
++ void UniformModel::moveIndex(int rowIndex, int direction)
++ {
++ if (m_uniformTable == nullptr)
++ return;
++
++ // Note: beginMoveRows index behaves differently than QList move index.
++ int destinationRowIndex = rowIndex + 2;
++ int destinationListIndex = rowIndex + 1;
++ if (direction == -1) {
++ destinationRowIndex = rowIndex - 1;
++ destinationListIndex = rowIndex - 1;
++ }
++
++ if (destinationListIndex >= 0 && m_uniformTable->size() > destinationListIndex) {
++ beginMoveRows(QModelIndex(), rowIndex, rowIndex, QModelIndex(), destinationRowIndex);
++ m_uniformTable->move(rowIndex, destinationListIndex);
++ endMoveRows();
++ updateCanMoveStatus();
++ }
++ }
++
++ void UniformModel::removeRow(int rowIndex, int rows)
++ {
++ if (m_uniformTable == nullptr)
++ return;
++
++ if (m_uniformTable->size() > rowIndex) {
++ rows = qBound(1, rows, m_uniformTable->size());
++ beginRemoveRows(QModelIndex(), rowIndex, rowIndex + rows - 1);
++ m_uniformTable->remove(rowIndex, rows);
++ endRemoveRows();
++ updateCanMoveStatus();
++
++ emit dataChanged(QAbstractItemModel::createIndex(0, 0),
++ QAbstractItemModel::createIndex(rowIndex, 0));
++ Q_EMIT uniformsChanged();
++ }
++ }
++
++ bool UniformModel::validateUniformName(const QString &uniformName)
++ {
++ if (!m_uniformTable)
++ return false;
++
++ // must be unique
++ for (const auto &uniform : *m_uniformTable) {
++ if (uniform.name == uniformName)
++ return false;
++ }
++
++ return true;
++ }
++
++ QString UniformModel::typeToProperty(Uniform::Type type)
++ {
++ QString property;
++ switch (type) {
++ case Uniform::Type::Bool:
++ property = "bool";
++ break;
++ case Uniform::Type::Int:
++ property = "int";
++ break;
++ case Uniform::Type::Float:
++ property = "real";
++ break;
++ case Uniform::Type::Vec2:
++ property = "point";
++ break;
++ case Uniform::Type::Vec3:
++ property = "vector3d";
++ break;
++ case Uniform::Type::Vec4:
++ property = "vector4d";
++ break;
++ case Uniform::Type::Color:
++ property = "color";
++ break;
++ case Uniform::Type::Sampler:
++ property = "var";
++ break;
++ case Uniform::Type::Define:
++ property = "var";
++ break;
++ }
++ return property;
++ }
++
++ QString UniformModel::typeToUniform(Uniform::Type type)
++ {
++ QString property;
++ switch (type) {
++ case Uniform::Type::Bool:
++ property = "bool";
++ break;
++ case Uniform::Type::Int:
++ property = "int";
++ break;
++ case Uniform::Type::Float:
++ property = "float";
++ break;
++ case Uniform::Type::Vec2:
++ property = "vec2";
++ break;
++ case Uniform::Type::Vec3:
++ property = "vec3";
++ break;
++ case Uniform::Type::Vec4:
++ case Uniform::Type::Color:
++ property = "vec4";
++ break;
++ case Uniform::Type::Sampler:
++ property = "sampler2D";
++ break;
++ case Uniform::Type::Define:
++ property = "define";
++ break;
++ }
++ return property;
++ }
++
++ // Get value in QML format that used for exports
++ QString UniformModel::valueAsString(const Uniform &uniform)
++ {
++ QString s;
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ s = uniform.value.toBool() ? "true" : "false";
++ break;
++ case Uniform::Type::Int:
++ s = QString::number(uniform.value.toInt());
++ break;
++ case Uniform::Type::Float:
++ s = QString::number(uniform.value.toDouble());
++ break;
++ case Uniform::Type::Vec2:
++ {
++ QVector2D v2 = uniform.value.value<QVector2D>();
++ s = QString("Qt.point(%1, %2)").arg(v2.x()).arg(v2.y());
++ break;
++ }
++ case Uniform::Type::Vec3:
++ {
++ QVector3D v3 = uniform.value.value<QVector3D>();
++ s = QString("Qt.vector3d(%1, %2, %3)").arg(v3.x()).arg(v3.y()).arg(v3.z());
++ break;
++ }
++ case Uniform::Type::Vec4:
++ {
++ QVector4D v4 = uniform.value.value<QVector4D>();
++ s = QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x()).arg(v4.y()).arg(v4.z()).arg(v4.w());
++ break;
++ }
++ case Uniform::Type::Color:
++ {
++ QColor c = uniform.value.value<QColor>();
++ s = QString("Qt.rgba(%1, %2, %3, %4)").arg(c.redF()).arg(c.greenF()).arg(c.blueF()).arg(c.alphaF());
++ break;
++ }
++ case Uniform::Type::Sampler:
++ {
++ s = getImageElementName(uniform);
++ break;
++ }
++ case Uniform::Type::Define:
++ {
++ s = uniform.value.toString();
++ break;
++ }
++ }
++ return s;
++ }
++
++ // Get value in GLSL format that is used for non-exported const properties
++ QString UniformModel::valueAsVariable(const Uniform &uniform)
++ {
++ QString s;
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ s = uniform.value.toBool() ? "true" : "false";
++ break;
++ case Uniform::Type::Int:
++ s = QString::number(uniform.value.toInt());
++ break;
++ case Uniform::Type::Float:
++ s = QString::number(uniform.value.toDouble());
++ break;
++ case Uniform::Type::Vec2:
++ {
++ QVector2D v2 = uniform.value.value<QVector2D>();
++ s = QString("vec2(%1, %2)").arg(v2.x()).arg(v2.y());
++ break;
++ }
++ case Uniform::Type::Vec3:
++ {
++ QVector3D v3 = uniform.value.value<QVector3D>();
++ s = QString("vec3(%1, %2, %3)").arg(v3.x()).arg(v3.y()).arg(v3.z());
++ break;
++ }
++ case Uniform::Type::Vec4:
++ {
++ QVector4D v4 = uniform.value.value<QVector4D>();
++ s = QString("vec4(%1, %2, %3, %4)").arg(v4.x()).arg(v4.y()).arg(v4.z()).arg(v4.w());
++ break;
++ }
++ case Uniform::Type::Color:
++ {
++ QColor c = uniform.value.value<QColor>();
++ s = QString("vec4(%1, %2, %3, %4)").arg(c.redF()).arg(c.greenF()).arg(c.blueF()).arg(c.alphaF());
++ break;
++ }
++ default:
++ qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type)).toLatin1();
++ break;
++ }
++ return s;
++ }
++
++ // Get value in QML binding that used for previews
++ QString UniformModel::valueAsBinding(const Uniform &uniform)
++ {
++ QString s;
++ switch (uniform.type) {
++ case Uniform::Type::Bool:
++ case Uniform::Type::Int:
++ case Uniform::Type::Float:
++ case Uniform::Type::Define:
++ s = "g_propertyData." + uniform.name;
++ break;
++ case Uniform::Type::Vec2:
++ {
++ QString sx = QString("g_propertyData.%1.x").arg(uniform.name);
++ QString sy = QString("g_propertyData.%1.y").arg(uniform.name);
++ s = QString("Qt.point(%1, %2)").arg(sx, sy);
++ break;
++ }
++ case Uniform::Type::Vec3:
++ {
++ QString sx = QString("g_propertyData.%1.x").arg(uniform.name);
++ QString sy = QString("g_propertyData.%1.y").arg(uniform.name);
++ QString sz = QString("g_propertyData.%1.z").arg(uniform.name);
++ s = QString("Qt.vector3d(%1, %2, %3)").arg(sx, sy, sz);
++ break;
++ }
++ case Uniform::Type::Vec4:
++ {
++ QString sx = QString("g_propertyData.%1.x").arg(uniform.name);
++ QString sy = QString("g_propertyData.%1.y").arg(uniform.name);
++ QString sz = QString("g_propertyData.%1.z").arg(uniform.name);
++ QString sw = QString("g_propertyData.%1.w").arg(uniform.name);
++ s = QString("Qt.vector4d(%1, %2, %3, %4)").arg(sx, sy, sz, sw);
++ break;
++ }
++ case Uniform::Type::Color:
++ {
++ QString sr = QString("g_propertyData.%1.r").arg(uniform.name);
++ QString sg = QString("g_propertyData.%1.g").arg(uniform.name);
++ QString sb = QString("g_propertyData.%1.b").arg(uniform.name);
++ QString sa = QString("g_propertyData.%1.a").arg(uniform.name);
++ s = QString("Qt.rgba(%1, %2, %3, %4)").arg(sr, sg, sb, sa);
++ break;
++ }
++ case Uniform::Type::Sampler:
++ {
++ s = getImageElementName(uniform);
++ break;
++ }
++ }
++ return s;
++ }
++
++ QString UniformModel::variantAsDataString(const Uniform::Type type, const QVariant &variant)
++ {
++ QString s;
++ switch (type) {
++ case Uniform::Type::Bool:
++ s = variant.toBool() ? "true" : "false";
++ break;
++ case Uniform::Type::Int:
++ s = QString::number(variant.toInt());
++ break;
++ case Uniform::Type::Float:
++ s = QString::number(variant.toDouble());
++ break;
++ case Uniform::Type::Vec2:
++ {
++ QStringList list;
++ QVector2D v2 = variant.value<QVector2D>();
++ list << QString::number(v2.x());
++ list << QString::number(v2.y());
++ s = list.join(", ");
++ break;
++ }
++ case Uniform::Type::Vec3:
++ {
++ QStringList list;
++ QVector3D v3 = variant.value<QVector3D>();
++ list << QString::number(v3.x());
++ list << QString::number(v3.y());
++ list << QString::number(v3.z());
++ s = list.join(", ");
++ break;
++ }
++ case Uniform::Type::Vec4:
++ {
++ QStringList list;
++ QVector4D v4 = variant.value<QVector4D>();
++ list << QString::number(v4.x());
++ list << QString::number(v4.y());
++ list << QString::number(v4.z());
++ list << QString::number(v4.w());
++ s = list.join(", ");
++ break;
++ }
++ case Uniform::Type::Color:
++ {
++ QStringList list;
++ QColor c = variant.value<QColor>();
++ list << QString::number(c.redF(), 'g', 3);
++ list << QString::number(c.greenF(), 'g', 3);
++ list << QString::number(c.blueF(), 'g', 3);
++ list << QString::number(c.alphaF(), 'g', 3);
++ s = list.join(", ");
++ break;
++ }
++ case Uniform::Type::Sampler:
++ case Uniform::Type::Define:
++ {
++ s = variant.toString();
++ break;
++ }
++ }
++ return s;
++ }
++
++ void UniformModel::appendUniform(Uniform uniform)
++ {
++ if (!validateUniformName(uniform.name)) {
++ qWarning() << "Invalid uniform name, can't add it";
++ return;
++ }
++
++ int rowIndex = m_uniformTable->size();
++ beginInsertRows(QModelIndex(), rowIndex, rowIndex);
++ m_uniformTable->insert(rowIndex, uniform);
++ endInsertRows();
++ updateCanMoveStatus();
++
++ emit dataChanged(QAbstractItemModel::createIndex(0, 0),
++ QAbstractItemModel::createIndex(rowIndex, 0));
++
++ Q_EMIT uniformsChanged();
++
++ }
++
++ void UniformModel::setUniformValueData(Uniform *uniform, const QString &value, const QString &defaultValue, const QString &minValue, const QString &maxValue)
++ {
++ if (!uniform)
++ return;
++
++ uniform->value = value.isEmpty() ? getInitializedVariant(uniform->type, false) : valueStringToVariant(uniform->type, value);
++ uniform->defaultValue = defaultValue.isEmpty() ? getInitializedVariant(uniform->type, false) : valueStringToVariant(uniform->type, defaultValue);
++ uniform->minValue = minValue.isEmpty() ? getInitializedVariant(uniform->type, false) : valueStringToVariant(uniform->type, minValue);
++ uniform->maxValue = maxValue.isEmpty() ? getInitializedVariant(uniform->type, true) : valueStringToVariant(uniform->type, maxValue);
++
++ // Update the value data in bindings
++ g_propertyData.insert(uniform->name, uniform->value);
++ }
++
++ UniformModel::Uniform::Type UniformModel::typeFromString(const QString &typeString) const
++ {
++ if (typeString == "bool")
++ return UniformModel::Uniform::Type::Bool;
++ else if (typeString == "int")
++ return UniformModel::Uniform::Type::Int;
++ else if (typeString == "float")
++ return UniformModel::Uniform::Type::Float;
++ else if (typeString == "vec2")
++ return UniformModel::Uniform::Type::Vec2;
++ else if (typeString == "vec3")
++ return UniformModel::Uniform::Type::Vec3;
++ else if (typeString == "vec4")
++ return UniformModel::Uniform::Type::Vec4;
++ else if (typeString == "color")
++ return UniformModel::Uniform::Type::Color;
++ else if (typeString == "image")
++ return UniformModel::Uniform::Type::Sampler;
++ else if (typeString == "define")
++ return UniformModel::Uniform::Type::Define;
++
++ qWarning() << QString("Unknown type: %1").arg(typeString).toLatin1();
++ return UniformModel::Uniform::Type::Float;
++ }
++
++ QString UniformModel::stringFromType(Uniform::Type type)
++ {
++ QString property;
++ switch (type) {
++ case Uniform::Type::Bool:
++ property = "bool";
++ break;
++ case Uniform::Type::Int:
++ property = "int";
++ break;
++ case Uniform::Type::Float:
++ property = "float";
++ break;
++ case Uniform::Type::Vec2:
++ property = "vec2";
++ break;
++ case Uniform::Type::Vec3:
++ property = "vec3";
++ break;
++ case Uniform::Type::Vec4:
++ property = "vec4";
++ break;
++ case Uniform::Type::Color:
++ property = "color";
++ break;
++ case Uniform::Type::Sampler:
++ property = "image";
++ break;
++ case Uniform::Type::Define:
++ property = "define";
++ break;
++ }
++ return property;
++ }
++
++ QVariant UniformModel::valueStringToVariant(const Uniform::Type type, const QString &value)
++ {
++ QVariant variant;
++ switch (type) {
++ case UniformModel::Uniform::Type::Bool:
++ variant = (value == "true") ? true : false;
++ break;
++ case UniformModel::Uniform::Type::Int:
++ case UniformModel::Uniform::Type::Float:
++ variant = value;
++ break;
++ case UniformModel::Uniform::Type::Vec2:
++ {
++ QStringList list = value.split(QLatin1Char(','));
++ if (list.size() >= 2)
++ variant = QVector2D(list.at(0).toDouble(), list.at(1).toDouble());
++ }
++ break;
++ case UniformModel::Uniform::Type::Vec3:
++ {
++ QStringList list = value.split(QLatin1Char(','));
++ if (list.size() >= 3)
++ variant = QVector3D(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble());
++ }
++ break;
++ case UniformModel::Uniform::Type::Vec4:
++ {
++ QStringList list = value.split(QLatin1Char(','));
++ if (list.size() >= 4)
++ variant = QVector4D(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble(), list.at(3).toDouble());
++ }
++ break;
++ case UniformModel::Uniform::Type::Color:
++ {
++ QStringList list = value.split(QLatin1Char(','));
++ if (list.size() >= 4)
++ variant = QColor::fromRgbF(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble(), list.at(3).toDouble());
++ }
++ break;
++ case UniformModel::Uniform::Type::Sampler:
++ variant = value;
++ break;
++ case UniformModel::Uniform::Type::Define:
++ variant = value;
++ break;
++ }
++
++ return variant;
++ }
++
++ // Update canMoveUp and canMoveDown.
++ // When uniform is first/last of its node it can't move up/down.
++ void UniformModel::updateCanMoveStatus() {
++ if (m_uniformTable->isEmpty())
++ return;
++ QList<int> usedIds;
++ for (int i = 0; i < m_uniformTable->size() ; i++) {
++ auto &uniform = (*m_uniformTable)[i];
++ if (usedIds.contains(uniform.nodeId)) {
++ uniform.canMoveUp = true;
++ } else {
++ uniform.canMoveUp = false;
++ usedIds << uniform.nodeId;
++ }
++ }
++ usedIds.clear();
++ for (int i = m_uniformTable->size() - 1; i >= 0; i--) {
++ auto &uniform = (*m_uniformTable)[i];
++ if (usedIds.contains(uniform.nodeId)) {
++ uniform.canMoveDown = true;
++ } else {
++ uniform.canMoveDown = false;
++ usedIds << uniform.nodeId;
++ }
++ }
++ int lastRowIndex = m_uniformTable->size();
++ emit dataChanged(QAbstractItemModel::createIndex(0, 0),
++ QAbstractItemModel::createIndex(lastRowIndex, 0));
++ }
++