Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qssgsceneedit.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qssgsceneedit_p.h"
5
6#include <QtGui/QGuiApplication>
7
8#include <QtCore/QVariant>
9#include <QtCore/QHash>
10#include <QtCore/QMetaProperty>
11#include <QtCore/QUrl>
12
13#include <QtCore/QJsonObject>
14#include <QtCore/QJsonDocument>
15#include <QtCore/QJsonArray>
16
17#include <QtQuick3DAssetImport/private/qssgassetimportmanager_p.h>
18#include <QtQuick3DAssetUtils/private/qssgscenedesc_p.h>
19#include <QtQuick3DAssetUtils/private/qssgqmlutilities_p.h>
20#include <QtQuick3DAssetUtils/private/qssgrtutilities_p.h>
21
23using namespace Qt::Literals::StringLiterals;
24namespace QSSGQmlUtilities {
25
26static const char* typeNames[] =
27{
28 "Transform",
29 "Camera",
30 "Model",
31 "Texture",
32 "Material",
33 "Light",
34 "Mesh",
35 "Skin",
36 "Skeleton",
37 "Joint",
38 "MorphTarget",
39 "ERROR"
40};
41
42static constexpr qsizetype nNodeTypes = std::size(typeNames) - 1;
43
45{
46 int i = 0;
47 while (i < nNodeTypes) {
48 if (typeName == typeNames[i])
49 break;
50 ++i;
51 }
53}
54
56{
57 for (auto *prop : node->properties) {
58 auto &val = prop->value;
59 if (qvariant_cast<QSSGSceneDesc::Node *>(val) == resource) {
60 if (replacement)
61 val = QVariant::fromValue(replacement);
62 }
63 if (val.metaType().id() == qMetaTypeId<QSSGSceneDesc::NodeList *>()) {
64 const auto &list = *qvariant_cast<QSSGSceneDesc::NodeList *>(val);
65 for (int i = 0, end = list.count; i != end; ++i) {
66 if (list.head[i] == resource) {
67 list.head[i] = replacement;
68 }
69 }
70 }
71 }
72 for (auto *child : node->children)
73 replaceReferencesToResource(child, resource, replacement);
74}
75
76// TODO: optimize this by using a hashmap or similar
79{
80 if (!root || name.isEmpty())
81 return nullptr;
82
83 if (root->name == name && root->nodeType == type)
84 return root;
85
86 for (auto *child : root->children) {
87 if (auto *ret = findNode(child, name, type, parent)) {
88 if (parent && !*parent)
89 *parent = root;
90 return ret;
91 }
92 }
93 return nullptr;
94}
95
97{
98 if (name.isEmpty())
99 return nullptr; // Empty strings by definition means nothing
100 for (auto *resource : scene->resources) {
101 if (resource->name == name && resource->nodeType == nodeType)
102 return resource;
103 }
104
105 return nullptr;
106}
107
108using NodeSet = QSet<QSSGSceneDesc::Node *>;
110
111static NodeSet flattenTree(QSSGSceneDesc::Node *node, NodeFilter *excludeFunction = nullptr)
112{
113 NodeSet ret = { node };
114 for (auto *child : node->children)
115 if (!excludeFunction || !excludeFunction(child))
116 ret.unite(flattenTree(child));
117 return ret;
118}
119
121{
122 parent->children.removeOne(child);
123}
124
126{
127 auto isTargeted = [nodes](QSSGSceneDesc::Animation::Channel *channel) { return nodes.contains(channel->target); };
128 const auto end_it = animation->channels.end();
129 auto remove_it = std::remove_if(animation->channels.begin(), end_it, isTargeted);
130 for (auto it = remove_it; it != end_it; ++it)
131 delete *it;
132 animation->channels.erase(remove_it, end_it);
133}
134
136{
137 const auto children = flattenTree(node);
138 for (auto *animation : node->scene->animations)
140 for (auto *child : children)
141 delete child;
142}
143
145{
146 auto *propList = &node->properties;
147
148 auto findName = [name](QSSGSceneDesc::Property *p) { return p->name == name; };
149 auto it = std::find_if(propList->begin(), propList->end(), findName);
150 if (it != propList->end()) {
152 propList->erase(it);
153 delete p;
154 }
155}
156
158{
159 auto it = nodeRef.constBegin();
160 if (it == nodeRef.constEnd())
161 return nullptr;
162 auto nodeType = nodeTypeFromName(it.key().toUtf8());
163 auto nodeName = it.value().toString().toUtf8();
164 auto *node = findResource(scene, nodeName, nodeType);
165 if (!node)
166 node = findNode(scene->root, nodeName, nodeType);
167 return node;
168}
169
171{
172 QVarLengthArray<QSSGSceneDesc::Node *> nodes;
173
174 for (auto json : array) {
175 auto *node = nodeFromJson(scene, json.toObject());
176 if (!node) {
177 qWarning() << "Could not find node for" << json;
178 continue;
179 }
180 nodes.append(node);
181 }
182 auto *nodeList = new QSSGSceneDesc::NodeList(reinterpret_cast<void **>(nodes.data()), nodes.count());
183 return nodeList;
184}
185
186/*
187 JSON format
188
189 Node reference: {"<nodeTypeName>": "<name>"}
190 URL: {"url": "<filepath>"}
191 List: [ {"<nodeTypeName>": "<name>"}, ... ]
192 */
193
194void setProperty(QSSGSceneDesc::Node *node, const QStringView propertyName, const QJsonValue &value)
195{
197
198 if (value.isArray()) {
200 } else if (value.isObject()) {
201 auto obj = value.toObject();
202 if (obj.contains(u"url")) {
203 auto path = obj.value(u"url").toString();
205 } else {
208 }
209 } else {
210 var = value.toVariant(); // The rest of the special handling happens in QSSGRuntimeUtils::applyPropertyValue
211 }
212
213 const auto name = propertyName.toUtf8();
214 removeProperty(node, name); // TODO: change property if it exists, instead of deleting and adding
215 auto *property = QSSGSceneDesc::setProperty(*node, name, std::move(var));
216
217 if (node->obj)
219}
220
221
223{
224 auto name = addition.value(u"name").toString().toUtf8();
225 auto typeName = addition.value(u"type").toString().toUtf8();
226 if (name.isEmpty() || typeName.isEmpty()) {
227 qWarning("Can't create node without name or type");
228 return nullptr;
229 }
230
231 QSSGSceneDesc::Node *node = nullptr;
233
234 if (typeName == "Material") {
235 bool isSpecGlossy = addition.contains(u"albedoColor") || addition.contains(u"albedoMap")
236 || addition.contains(u"glossinessMap") || addition.contains(u"glossiness");
237 typeName = isSpecGlossy ? "SpecularGlossyMaterial" : "PrincipledMaterial";
238 }
239
240 if (typeName == "PrincipledMaterial") {
241 node = new QSSGSceneDesc::Node(name, QSSGSceneDesc::Node::Type::Material,
242 QSSGRenderGraphObject::Type::PrincipledMaterial);
243 } else if (typeName == "SpecularGlossyMaterial") {
244 node = new QSSGSceneDesc::Node(name, QSSGSceneDesc::Node::Type::Material,
245 QSSGRenderGraphObject::Type::SpecularGlossyMaterial);
246 } else if (typeName == "Texture") {
247 node = new QSSGSceneDesc::Node(name, QSSGSceneDesc::Node::Type::Texture,
248 QSSGRenderGraphObject::Type::Image2D);
249 } else {
250 qWarning() << "Not supported. Don't know how to create" << typeName;
251 return nullptr;
252 }
253 Q_ASSERT(node);
254 node->scene = scene;
255 for (auto it = addition.constBegin(); it != addition.constEnd(); ++it) {
256 const auto &propertyName = it.key();
257 if (propertyName == u"name" || propertyName == u"type" || propertyName == u"comment" || propertyName == u"command")
258 continue;
259 setProperty(node, it.key(), it.value());
260 }
261
262 if (prevResource) {
263 replaceReferencesToResource(scene->root, prevResource, node);
264 scene->resources.removeOne(prevResource);
265 delete prevResource;
266 }
267
269 return node;
270}
271
273{
274 auto doApply = [scene](const QJsonObject &obj) {
275 QByteArray name = obj.value(u"name").toString().toUtf8();
276 QByteArray typeName = obj.value(u"type").toString().toUtf8();
277 auto command = obj.value(u"command").toString(u"edit"_s);
278 auto nodeType = nodeTypeFromName(typeName);
279 if (command == u"edit") {
280 auto *node = findNode(scene->root, name, nodeType);
281 if (!node)
282 node = findResource(scene, name, nodeType);
283 if (node) {
284 for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) {
285 const auto &propertyName = it.key();
286 if (propertyName == u"name" || propertyName == u"type" || propertyName == u"comment" || propertyName == u"command")
287 continue;
288 setProperty(node, it.key(), it.value());
289 }
290 }
291 } else if (command == u"add") {
293 } else if (command == u"delete") {
294 QSSGSceneDesc::Node *parent = nullptr;
295 auto *node = findNode(scene->root, name, nodeType, &parent);
296 if (node) {
297 deleteTree(node);
298 if (parent)
299 unlinkChild(node, parent);
300 else
301 qWarning("Delete: could not find parent for node");
302 }
303 }
304 };
305
306 const auto editList = changes.value(u"editList").toArray();
307
308 // Do all the adds first, since the edits may depend on them
309 // If adds depend on each other, they need to be in dependency order
310 for (auto edit : editList) {
311 auto obj = edit.toObject();
312 if (obj.value(u"command") == u"add"_s)
313 doApply(obj);
314 }
315
316 for (auto edit : editList) {
317 auto obj = edit.toObject();
318 if (obj.value(u"command") != u"add"_s)
319 doApply(obj);
320 }
321
322}
323
324}
325
IOBluetoothL2CAPChannel * channel
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
QJsonValue value(const QString &key) const
Returns a QJsonValue representing the value for the key key.
\inmodule QtCore\reentrant
Definition qjsonvalue.h:25
QJsonArray toArray() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool removeOne(const AT &t)
Definition qlist.h:598
qsizetype count() const noexcept
Definition qlist.h:398
iterator end()
Definition qset.h:140
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
iterator erase(const_iterator i)
Definition qset.h:145
\inmodule QtCore
Definition qstringview.h:78
QByteArray toUtf8() const
Returns a UTF-8 representation of the string view as a QByteArray.
\inmodule QtCore
Definition qurl.h:94
\inmodule QtCore
Definition qvariant.h:65
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
QSet< QString >::iterator it
static void removeFromAnimation(QSSGSceneDesc::Animation *animation, const NodeSet &nodes)
bool NodeFilter(QSSGSceneDesc::Node *)
void applyEdit(QSSGSceneDesc::Scene *scene, const QJsonObject &changes)
static QSSGSceneDesc::Node::Type nodeTypeFromName(const QByteArrayView &typeName)
static void removeProperty(QSSGSceneDesc::Node *node, const QByteArrayView &name)
static constexpr qsizetype nNodeTypes
static NodeSet flattenTree(QSSGSceneDesc::Node *node, NodeFilter *excludeFunction=nullptr)
static QSSGSceneDesc::NodeList * nodeListFromJson(const QSSGSceneDesc::Scene *scene, const QJsonArray &array)
static QSSGSceneDesc::Node * nodeFromJson(const QSSGSceneDesc::Scene *scene, const QJsonObject &nodeRef)
QSSGSceneDesc::Node * addResource(QSSGSceneDesc::Scene *scene, const QJsonObject &addition)
static void deleteTree(QSSGSceneDesc::Node *node)
static void replaceReferencesToResource(QSSGSceneDesc::Node *node, QSSGSceneDesc::Node *resource, QSSGSceneDesc::Node *replacement)
static const char * typeNames[]
static void unlinkChild(QSSGSceneDesc::Node *child, QSSGSceneDesc::Node *parent)
QSet< QSSGSceneDesc::Node * > NodeSet
static QSSGSceneDesc::Node * findResource(const QSSGSceneDesc::Scene *scene, const QByteArrayView &name, QSSGSceneDesc::Node::Type nodeType)
static QSSGSceneDesc::Node * findNode(QSSGSceneDesc::Node *root, const QByteArrayView name, QSSGSceneDesc::Node::Type type, QSSGSceneDesc::Node **parent=nullptr)
Q_QUICK3DASSETUTILS_EXPORT void applyPropertyValue(const QSSGSceneDesc::Node *node, QObject *obj, QSSGSceneDesc::Property *property)
static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, T &&value)
Q_QUICK3DASSETUTILS_EXPORT void addNode(Node &parent, Node &node)
Combined button and popup list for selecting options.
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
return ret
const char * typeName
GLuint GLuint end
GLenum type
GLuint name
GLfloat n
GLhandleARB obj
[2]
GLenum array
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
ptrdiff_t qsizetype
Definition qtypes.h:165
const char property[13]
Definition qwizard.cpp:101
QList< int > list
[14]
QPropertyAnimation animation
[0]
QGraphicsScene scene
[0]
QLayoutItem * child
[0]
args<< 1<< 2;QJSValue threeAgain=fun.call(args);QString fileName="helloworld.qs";QFile scriptFile(fileName);if(!scriptFile.open(QIODevice::ReadOnly)) QTextStream stream(&scriptFile);QString contents=stream.readAll();scriptFile.close();myEngine.evaluate(contents, fileName);myEngine.globalObject().setProperty("myNumber", 123);...QJSValue myNumberPlusOne=myEngine.evaluate("myNumber + 1");QJSValue result=myEngine.evaluate(...);if(result.isError()) qDebug()<< "Uncaught exception at line"<< result.property("lineNumber").toInt()<< ":"<< result.toString();QPushButton *button=new QPushButton;QJSValue scriptButton=myEngine.newQObject(button);myEngine.globalObject().setProperty("button", scriptButton);myEngine.evaluate("button.checkable = true");qDebug()<< scriptButton.property("checkable").toBool();scriptButton.property("show").call();QJSEngine engine;QObject *myQObject=new QObject();myQObject- setProperty)("dynamicProperty", 3)