1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qshadergraphloader_p.h"
5
6#include "qshadernodesloader_p.h"
7
8#include <QtCore/qdebug.h>
9#include <QtCore/qiodevice.h>
10#include <QtCore/qjsonarray.h>
11#include <QtCore/qjsondocument.h>
12#include <QtCore/qjsonobject.h>
13#include <QtCore/qmetaobject.h>
14
15QT_BEGIN_NAMESPACE
16
17namespace Qt3DRender
18{
19void qt_register_ShaderLanguage_enums();
20
21QShaderGraphLoader::QShaderGraphLoader() noexcept
22 : m_status(Null),
23 m_device(nullptr)
24{
25 qt_register_ShaderLanguage_enums();
26}
27
28QShaderGraphLoader::Status QShaderGraphLoader::status() const noexcept
29{
30 return m_status;
31}
32
33QShaderGraph QShaderGraphLoader::graph() const noexcept
34{
35 return m_graph;
36}
37
38QIODevice *QShaderGraphLoader::device() const noexcept
39{
40 return m_device;
41}
42
43void QShaderGraphLoader::setDevice(QIODevice *device) noexcept
44{
45 m_device = device;
46 m_graph = QShaderGraph();
47 m_status = !m_device ? Null
48 : (m_device->openMode() & QIODevice::ReadOnly) ? Waiting
49 : Error;
50}
51
52QHash<QString, QShaderNode> QShaderGraphLoader::prototypes() const noexcept
53{
54 return m_prototypes;
55}
56
57void QShaderGraphLoader::setPrototypes(const QHash<QString, QShaderNode> &prototypes) noexcept
58{
59 m_prototypes = prototypes;
60}
61
62void QShaderGraphLoader::load()
63{
64 if (m_status == Error)
65 return;
66
67 auto error = QJsonParseError();
68 const QJsonDocument document = QJsonDocument::fromJson(json: m_device->readAll(), error: &error);
69
70 if (error.error != QJsonParseError::NoError) {
71 qWarning() << "Invalid JSON document:" << error.errorString();
72 m_status = Error;
73 return;
74 }
75
76 if (document.isEmpty() || !document.isObject()) {
77 qWarning() << "Invalid JSON document, root should be an object";
78 m_status = Error;
79 return;
80 }
81
82 const QJsonObject root = document.object();
83
84 const QJsonValue nodesValue = root.value(QStringLiteral("nodes"));
85 if (!nodesValue.isArray()) {
86 qWarning() << "Invalid nodes property, should be an array";
87 m_status = Error;
88 return;
89 }
90
91 const QJsonValue edgesValue = root.value(QStringLiteral("edges"));
92 if (!edgesValue.isArray()) {
93 qWarning() << "Invalid edges property, should be an array";
94 m_status = Error;
95 return;
96 }
97
98 bool hasError = false;
99
100 const QJsonValue prototypesValue = root.value(QStringLiteral("prototypes"));
101 if (!prototypesValue.isUndefined()) {
102 if (prototypesValue.isObject()) {
103 QShaderNodesLoader loader;
104 loader.load(prototypesObject: prototypesValue.toObject());
105 m_prototypes.insert(hash: loader.nodes());
106 } else {
107 qWarning() << "Invalid prototypes property, should be an object";
108 m_status = Error;
109 return;
110 }
111 }
112
113 const QJsonArray nodes = nodesValue.toArray();
114 for (const QJsonValue nodeValue : nodes) {
115 if (!nodeValue.isObject()) {
116 qWarning() << "Invalid node found";
117 hasError = true;
118 continue;
119 }
120
121 const QJsonObject nodeObject = nodeValue.toObject();
122
123 const QString uuidString = nodeObject.value(QStringLiteral("uuid")).toString();
124 const QUuid uuid = QUuid(uuidString);
125 if (uuid.isNull()) {
126 qWarning() << "Invalid UUID found in node:" << uuidString;
127 hasError = true;
128 continue;
129 }
130
131 const QString type = nodeObject.value(QStringLiteral("type")).toString();
132 if (!m_prototypes.contains(key: type)) {
133 qWarning() << "Unsupported node type found:" << type;
134 hasError = true;
135 continue;
136 }
137
138 const QJsonArray layersArray = nodeObject.value(QStringLiteral("layers")).toArray();
139 auto layers = QStringList();
140 for (const QJsonValue layerValue : layersArray)
141 layers.append(t: layerValue.toString());
142
143 QShaderNode node = m_prototypes.value(key: type);
144 node.setUuid(uuid);
145 node.setLayers(layers);
146
147 const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
148 if (parametersValue.isObject()) {
149 const QJsonObject parametersObject = parametersValue.toObject();
150 for (const QString &parameterName : parametersObject.keys()) {
151 const QJsonValue parameterValue = parametersObject.value(key: parameterName);
152 if (parameterValue.isObject()) {
153 const QJsonObject parameterObject = parameterValue.toObject();
154 const QString type = parameterObject.value(QStringLiteral("type")).toString();
155 const QMetaType typeId = QMetaType::fromName(name: type.toUtf8());
156
157 const QString value = parameterObject.value(QStringLiteral("value")).toString();
158 auto variant = QVariant(value);
159
160 if (typeId.flags() & QMetaType::IsEnumeration) {
161 const QMetaObject *metaObject = typeId.metaObject();
162 const char *className = metaObject->className();
163 const QByteArray enumName = type.mid(position: static_cast<int>(qstrlen(str: className)) + 2).toUtf8();
164 const QMetaEnum metaEnum = metaObject->enumerator(index: metaObject->indexOfEnumerator(name: enumName));
165 const int enumValue = metaEnum.keyToValue(key: value.toUtf8());
166 variant = QVariant(enumValue);
167 variant.convert(type: typeId);
168 } else {
169 variant.convert(type: typeId);
170 }
171 node.setParameter(name: parameterName, value: variant);
172 } else {
173 node.setParameter(name: parameterName, value: parameterValue.toVariant());
174 }
175 }
176 }
177
178 m_graph.addNode(node);
179 }
180
181 const QJsonArray edges = edgesValue.toArray();
182 for (const QJsonValue edgeValue : edges) {
183 if (!edgeValue.isObject()) {
184 qWarning() << "Invalid edge found";
185 hasError = true;
186 continue;
187 }
188
189 const QJsonObject edgeObject = edgeValue.toObject();
190
191 const QString sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString();
192 const QUuid sourceUuid = QUuid(sourceUuidString);
193 if (sourceUuid.isNull()) {
194 qWarning() << "Invalid source UUID found in edge:" << sourceUuidString;
195 hasError = true;
196 continue;
197 }
198
199 const QString sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString();
200
201 const QString targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString();
202 const QUuid targetUuid = QUuid(targetUuidString);
203 if (targetUuid.isNull()) {
204 qWarning() << "Invalid target UUID found in edge:" << targetUuidString;
205 hasError = true;
206 continue;
207 }
208
209 const QString targetPort = edgeObject.value(QStringLiteral("targetPort")).toString();
210
211 const QJsonArray layersArray = edgeObject.value(QStringLiteral("layers")).toArray();
212 auto layers = QStringList();
213 for (const QJsonValue layerValue : layersArray)
214 layers.append(t: layerValue.toString());
215
216 auto edge = QShaderGraph::Edge();
217 edge.sourceNodeUuid = sourceUuid;
218 edge.sourcePortName = sourcePort;
219 edge.targetNodeUuid = targetUuid;
220 edge.targetPortName = targetPort;
221 edge.layers = layers;
222 m_graph.addEdge(edge);
223 }
224
225 if (hasError) {
226 m_status = Error;
227 m_graph = QShaderGraph();
228 } else {
229 m_status = Ready;
230 }
231}
232}
233QT_END_NAMESPACE
234

source code of qt3d/src/render/shadergraph/qshadergraphloader.cpp