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
qssgrtutilities.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qssgrtutilities_p.h"
5
7#include "qssgscenedesc_p.h"
8
9#include <QtCore/qurl.h>
10#include <QtCore/qbuffer.h>
11
12#include <QtGui/qimage.h>
13#include <QtGui/qimagereader.h>
14#include <QtGui/qimagewriter.h>
15#include <QtGui/qquaternion.h>
16
17#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
18
20
21
22// Actually set the property on node->obj, using QMetaProperty::write()
24{
25 auto *obj = qobject_cast<QQuick3DObject *>(o ? o : node->obj);
26 if (!obj)
27 return;
28
29 auto *metaObj = obj->metaObject();
30 int propertyIndex = metaObj->indexOfProperty(property->name);
31 if (propertyIndex < 0) {
32 qWarning() << "QSSGSceneDesc: could not find property" << property->name << "in" << obj;
33 return;
34 }
35 auto metaProp = metaObj->property(propertyIndex);
37
38 auto metaId = property->value.metaType().id();
39 auto *scene = node->scene;
40
41 if (metaId == qMetaTypeId<QSSGSceneDesc::Node *>()) {
42 const auto *valueNode = qvariant_cast<QSSGSceneDesc::Node *>(property->value);
43 QObject *obj = valueNode ? valueNode->obj : nullptr;
45 } else if (metaId == qMetaTypeId<QSSGSceneDesc::Mesh *>()) { // Special handling for mesh nodes.
46 // Mesh nodes does not have an equivalent in the QtQuick3D scene, but is registered
47 // as a source property in the intermediate scene we therefore need to convert it to
48 // be a usable source url now.
49 const auto meshNode = qvariant_cast<const QSSGSceneDesc::Mesh *>(property->value);
50 const auto url = meshNode ? QUrl(QSSGBufferManager::runtimeMeshSourceName(node->scene->id, meshNode->idx)) : QUrl{};
52 } else if (metaId == qMetaTypeId<QUrl>()) {
53 const auto url = qvariant_cast<QUrl>(property->value);
54 // TODO: Use QUrl::resolved() instead??
55 QString workingDir = scene->sourceDir;
56 const QUrl qurl = url.isValid() ? QUrl::fromUserInput(url.path(), workingDir) : QUrl{};
58 } else if (metaId == qMetaTypeId<QSSGSceneDesc::Flag>() && property->call) {
59 // If we have a QSSGSceneDesc::Flag variant, then it came from setProperty(), and the setter function is defined.
60 const auto flag = qvariant_cast<QSSGSceneDesc::Flag>(property->value);
61 property->call->set(*obj, property->name, flag.value);
62 qDebug() << "Flag special case, probably shouldn't happen" << node->name << property->name << property->value << flag.value;
63 return;
64 } else {
65 value = property->value;
66 }
67
68 if (value.metaType().id() == qMetaTypeId<QString>()) {
69 auto str = value.toString();
70 auto propType = metaProp.metaType();
71 if (propType.id() == qMetaTypeId<QVector3D>()) {
72 QStringList l = str.split(u',');
73 if (l.length() != 3) {
74 qWarning() << "Wrong format for QVector3D:" << str;
75 } else {
76 QVector3D vec3(l.at(0).toFloat(), l.at(1).toFloat(), l.at(2).toFloat());
78 }
79 } else if (propType.id() == qMetaTypeId<QVector2D>()) {
80 QStringList l = str.split(u',');
81 if (l.length() != 2) {
82 qWarning() << "Wrong format for QVector2D:" << str;
83 } else {
84 QVector2D vec(l.at(0).toFloat(), l.at(1).toFloat());
86 }
87 } else if (propType.id() == qMetaTypeId<QVector4D>()) {
88 QStringList l = str.split(u',');
89 if (l.length() != 2) {
90 qWarning() << "Wrong format for QVector4D:" << str;
91 } else {
92 QVector4D vec(l.at(0).toFloat(), l.at(1).toFloat(), l.at(2).toFloat(), l.at(3).toFloat());
94 }
95 } else if (propType.id() == qMetaTypeId<QQuaternion>()) {
96 QStringList l = str.split(u',');
97 if (l.length() != 4) {
98 qWarning() << "Wrong format for QQuaternion:" << str;
99 } else {
100 QQuaternion quat(l.at(0).toFloat(), l.at(1).toFloat(), l.at(2).toFloat(), l.at(3).toFloat());
102 }
103 } else {
104 // All other strings are supposed to be in QML-compatible format, so they can be written out directly
105 }
106 } else if (value.metaType().id() == qMetaTypeId<QSSGSceneDesc::NodeList*>()) {
107 auto qmlListVar = metaProp.read(obj);
108 // We have to write explicit code for each list property type, since metatype can't
109 // tell us if we have a QQmlListProperty (if we had known, we could have made a naughty
110 // hack and just static_cast to QQmlListProperty<QObject>
111 if (qmlListVar.metaType().id() == qMetaTypeId<QQmlListProperty<QQuick3DMaterial>>()) {
112 auto qmlList = qvariant_cast<QQmlListProperty<QQuick3DMaterial>>(qmlListVar);
113 auto nodeList = qvariant_cast<QSSGSceneDesc::NodeList*>(value);
114 auto head = reinterpret_cast<QSSGSceneDesc::Node **>(nodeList->head);
115
116 for (int i = 0, end = nodeList->count; i != end; ++i)
117 qmlList.append(&qmlList, qobject_cast<QQuick3DMaterial *>((*(head + i))->obj));
118
119 } else {
120 qWarning() << "Can't handle list property type" << qmlListVar.metaType();
121 }
122 return; //In any case, we can't send NodeList to QMetaProperty::write()
123 }
124
125 // qobject_cast doesn't work on nullptr, so we must convert the pointer manually
126 if ((metaProp.metaType().flags() & QMetaType::PointerToQObject) && value.isNull())
127 value.convert(metaProp.metaType()); //This will return false, but convert to the type anyway
128
129 // Q_ENUMS with '|' is explicitly handled, otherwise uses QVariant::convert. This means
130 // we get implicit qobject_cast and string to:
131 // QMetaType::Bool, QMetaType::QByteArray, QMetaType::QChar, QMetaType::QColor, QMetaType::QDate, QMetaType::QDateTime,
132 // QMetaType::Double, QMetaType::QFont, QMetaType::Int, QMetaType::QKeySequence, QMetaType::LongLong,
133 // QMetaType::QStringList, QMetaType::QTime, QMetaType::UInt, QMetaType::ULongLong, QMetaType::QUuid
134
135 bool success = metaProp.write(obj, value);
136
137 if (!success) {
138 qWarning() << "Failure when setting property" << property->name << "to" << property->value << "maps to" << value
139 << "property metatype:" << metaProp.typeName();
140 }
141}
142
143
144static void setProperties(QQuick3DObject &obj, const QSSGSceneDesc::Node &node, const QString &workingDir = {})
145{
146 using namespace QSSGSceneDesc;
147 const auto &properties = node.properties;
148 auto it = properties.begin();
149 const auto end = properties.end();
150 for (; it != end; ++it) {
151 const auto &v = *it;
152 if (!v->call) {
154 continue;
155 }
156 const auto &var = v->value;
157 if (var.metaType().id() == qMetaTypeId<Node *>()) {
158 const auto *node = qvariant_cast<Node *>(var);
159 v->call->set(obj, v->name, node ? node->obj : nullptr);
160 } else if (var.metaType() == QMetaType::fromType<Mesh *>()) { // Special handling for mesh nodes.
161 // Mesh nodes does not have an equivalent in the QtQuick3D scene, but is registered
162 // as a source property in the intermediate scene we therefore need to convert it to
163 // be a usable source url now.
164 const auto meshNode = qvariant_cast<const Mesh *>(var);
165 const auto url = meshNode ? QUrl(QSSGBufferManager::runtimeMeshSourceName(node.scene->id, meshNode->idx)) : QUrl{};
166 v->call->set(obj, v->name, &url);
167 } else if (var.metaType() == QMetaType::fromType<QUrl>()) {
168 const auto url = qvariant_cast<QUrl>(var);
169 // TODO: Use QUrl::resolved() instead
170 const QUrl qurl = url.isValid() ? QUrl::fromUserInput(url.toString(), workingDir) : QUrl{};
171 v->call->set(obj, v->name, &qurl);
172 } else if (var.metaType().id() == qMetaTypeId<QSSGSceneDesc::Flag>()) {
173 const auto flag = qvariant_cast<QSSGSceneDesc::Flag>(var);
174 v->call->set(obj, v->name, flag.value);
175 } else {
176 v->call->set(obj, v->name, var);
177 }
178 }
179}
180
181template<typename GraphObjectType, typename NodeType>
182GraphObjectType *createRuntimeObject(NodeType &node, QQuick3DObject &parent)
183{
184 GraphObjectType *obj = qobject_cast<GraphObjectType *>(node.obj);
185 if (!obj) {
186 node.obj = qobject_cast<QQuick3DObject *>(obj = new GraphObjectType);
187 obj->setParent(&parent);
188 obj->setParentItem(&parent);
189 }
190 Q_ASSERT(obj == node.obj);
191
192 return obj;
193}
194
195template<>
197{
198 QQuick3DTextureData *obj = qobject_cast<QQuick3DTextureData *>(node.obj);
199 if (!obj) {
200 node.obj = qobject_cast<QQuick3DObject *>(obj = new QQuick3DTextureData);
201 obj->setParent(&parent);
202 obj->setParentItem(&parent);
203
204 const auto &texData = node.data;
205 const bool isCompressed = ((node.flgs & quint8(QSSGSceneDesc::TextureData::Flags::Compressed)) != 0);
206
207 if (!texData.isEmpty()) {
209 if (isCompressed) {
210 QByteArray data = texData;
212 QImageReader imageReader(&readBuffer, node.fmt);
213 image = imageReader.read();
214 if (image.isNull())
215 qWarning() << imageReader.errorString();
216 } else {
217 const auto &size = node.sz;
218 image = QImage(reinterpret_cast<const uchar *>(texData.data()), size.width(), size.height(), QImage::Format::Format_RGBA8888);
219 }
220
221 if (!image.isNull()) {
222 const QPixelFormat pixFormat = image.pixelFormat();
225 if (image.colorCount()) { // a palleted image
226 targetFormat = QImage::Format_RGBA8888;
227 } else if (pixFormat.channelCount() == 1) {
228 targetFormat = QImage::Format_Grayscale8;
229 textureFormat = QQuick3DTextureData::Format::R8;
230 } else if (pixFormat.alphaUsage() == QPixelFormat::IgnoresAlpha) {
231 targetFormat = QImage::Format_RGBX8888;
232 } else if (pixFormat.premultiplied() == QPixelFormat::NotPremultiplied) {
233 targetFormat = QImage::Format_RGBA8888;
234 }
235
236 image.convertTo(targetFormat); // convert to a format mappable to QRhiTexture::Format
237 image.mirror(); // Flip vertically to the conventional Y-up orientation
238
239 const auto bytes = image.sizeInBytes();
240 obj->setSize(image.size());
241 obj->setFormat(textureFormat);
242 obj->setTextureData(QByteArray(reinterpret_cast<const char *>(image.constBits()), bytes));
243 }
244 }
245 }
246
247 return obj;
248}
249
250
251// Resources may refer to other resources and/or nodes, so we first generate all the resources without setting properties,
252// then all the nodes with properties, and finally we set properties for the resources
253// TODO: split this into different functions
254
256 QQuick3DObject &parent, bool traverseChildrenAndSetProperties)
257{
258 using namespace QSSGSceneDesc;
259
260 QQuick3DObject *obj = nullptr;
261 switch (node.nodeType) {
262 case Node::Type::Skeleton:
263 // Skeleton is a resource that also needs to be in the node tree. We don't do that anymore.
264 qWarning("Skeleton runtime import not supported");
265
266 // NOTE: The skeleton is special as it's a resource and a node, the
267 // hierarchical parent is therefore important here.
268 if (!node.obj) {// 1st Phase : 'create Resources'
269 obj = createRuntimeObject<QQuick3DSkeleton>(static_cast<Skeleton &>(node), parent);
270 } else { // 2nd Phase : setParent for the Node hierarchy.
271 obj = qobject_cast<QQuick3DSkeleton *>(node.obj);
272 obj->setParent(&parent);
273 obj->setParentItem(&parent);
274 }
275 break;
276 case Node::Type::Joint:
277 obj = createRuntimeObject<QQuick3DJoint>(static_cast<Joint &>(node), parent);
278 break;
279 case Node::Type::Skin:
280 obj = createRuntimeObject<QQuick3DSkin>(static_cast<Skin &>(node), parent);
281 break;
282 case Node::Type::MorphTarget:
283 obj = createRuntimeObject<QQuick3DMorphTarget>(static_cast<MorphTarget &>(node), parent);
284 break;
285 case Node::Type::Light:
286 {
287 auto &light = static_cast<Light &>(node);
288 if (light.runtimeType == Node::RuntimeType::DirectionalLight)
289 obj = createRuntimeObject<QQuick3DDirectionalLight>(light, parent);
290 else if (light.runtimeType == Node::RuntimeType::PointLight)
291 obj = createRuntimeObject<QQuick3DPointLight>(light, parent);
292 else if (light.runtimeType == Node::RuntimeType::SpotLight)
293 obj = createRuntimeObject<QQuick3DSpotLight>(light, parent);
294 else
295 Q_UNREACHABLE();
296 }
297 break;
298 case Node::Type::Transform:
299 obj = createRuntimeObject<QQuick3DNode>(node, parent);
300 break;
301 case Node::Type::Camera:
302 {
303 auto &camera = static_cast<Camera &>(node);
304 if (camera.runtimeType == Node::RuntimeType::OrthographicCamera)
305 obj = createRuntimeObject<QQuick3DOrthographicCamera>(camera, parent);
306 else if (camera.runtimeType == Node::RuntimeType::PerspectiveCamera)
307 obj = createRuntimeObject<QQuick3DPerspectiveCamera>(camera, parent);
308 else if (camera.runtimeType == Node::RuntimeType::CustomCamera)
309 obj = createRuntimeObject<QQuick3DCustomCamera>(camera, parent);
310 else
311 Q_UNREACHABLE();
312 }
313 break;
314 case Node::Type::Model:
315 obj = createRuntimeObject<QQuick3DModel>(static_cast<Model &>(node), parent);
316 break;
317 case Node::Type::Texture:
318 if (node.runtimeType == Node::RuntimeType::TextureData)
319 obj = createRuntimeObject<QQuick3DTextureData>(static_cast<TextureData &>(node), parent);
320 else if (node.runtimeType == Node::RuntimeType::Image2D)
321 obj = createRuntimeObject<QQuick3DTexture>(static_cast<Texture &>(node), parent);
322 else if (node.runtimeType == Node::RuntimeType::ImageCube)
323 obj = createRuntimeObject<QQuick3DCubeMapTexture>(static_cast<Texture &>(node), parent);
324 else
325 Q_UNREACHABLE();
326 break;
327 case Node::Type::Material:
328 {
329 if (node.runtimeType == Node::RuntimeType::PrincipledMaterial)
330 obj = createRuntimeObject<QQuick3DPrincipledMaterial>(static_cast<Material &>(node), parent);
331 else if (node.runtimeType == Node::RuntimeType::CustomMaterial)
332 obj = createRuntimeObject<QQuick3DCustomMaterial>(static_cast<Material &>(node), parent);
333 else if (node.runtimeType == Node::RuntimeType::SpecularGlossyMaterial)
334 obj = createRuntimeObject<QQuick3DSpecularGlossyMaterial>(static_cast<Material &>(node), parent);
335 else
336 Q_UNREACHABLE();
337 }
338 break;
339 case Node::Type::Mesh:
340 // There's no runtime object for this type, but we need to register the mesh with the
341 // buffer manager (this will happen once the mesh property is processed on the model).
342 break;
343 }
344
345 if (obj && traverseChildrenAndSetProperties) {
346 setProperties(*obj, node);
347 for (auto &chld : node.children)
348 createGraphObject(*chld, *obj);
349 }
350}
351
353{
354 if (!scene.root) {
355 qWarning("Incomplete scene description (missing plugin?)");
356 return nullptr;
357 }
358
359 Q_ASSERT(QQuick3DObjectPrivate::get(&parent)->sceneManager);
360
362
363 auto root = scene.root;
364 for (const auto &resource : scene.resources)
365 createGraphObject(*resource, parent, false);
366
367 createGraphObject(*root, parent);
368
369 // Some resources such as Skin have properties related with the node
370 // hierarchy. Therefore, resources are handled after nodes.
371 for (const auto &resource : scene.resources) {
372 if (resource->obj != nullptr) // A mesh node has no runtime object.
373 setProperties(static_cast<QQuick3DObject &>(*resource->obj), *resource, scene.sourceDir);
374 }
375
376 // Usually it makes sense to only enable 1 timeline at a time
377 // so for now we just enable the first one.
378 bool isFirstAnimation = true;
379 for (const auto &anim: scene.animations) {
380 QSSGQmlUtilities::createTimelineAnimation(*anim, root->obj, isFirstAnimation);
381 if (isFirstAnimation)
382 isFirstAnimation = false;
383 }
384
385 return qobject_cast<QQuick3DNode *>(scene.root->obj);
386}
387
\inmodule QtCore \reentrant
Definition qbuffer.h:16
\inmodule QtCore
Definition qbytearray.h:57
The QImageReader class provides a format independent interface for reading images from files or other...
\inmodule QtGui
Definition qimage.h:37
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGBA8888
Definition qimage.h:59
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_RGBX8888
Definition qimage.h:58
@ Format_Grayscale8
Definition qimage.h:66
int id(int=0) const
Definition qmetatype.h:475
@ PointerToQObject
Definition qmetatype.h:406
\inmodule QtCore
Definition qobject.h:103
\inmodule QtGui
The QQuaternion class represents a quaternion consisting of a vector and scalar.
static QQuick3DObjectPrivate * get(QQuick3DObject *item)
\qmltype Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DObject \inherits QtObject
\qmltype TextureData \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DTextureData
Format
Returns the color format of the texture data assigned in \l textureData property.
static void registerMeshData(const QString &assetId, const QVector< QSSGMesh::Mesh > &meshData)
static QString runtimeMeshSourceName(const QString &assetId, qsizetype meshId)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
\inmodule QtCore
Definition qurl.h:94
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1882
static QUrl fromUserInput(const QString &userInput, const QString &workingDirectory=QString(), UserInputResolutionOptions options=DefaultResolution)
Returns a valid URL from a user supplied userInput string if one can be deduced.
Definition qurl.cpp:3757
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2468
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
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
QMetaType metaType() const
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
QCamera * camera
Definition camera.cpp:19
QString str
[2]
QSet< QString >::iterator it
void createTimelineAnimation(const QSSGSceneDesc::Animation &anim, QObject *parent, bool isEnabled, bool useBinaryKeyframes)
Q_QUICK3DASSETUTILS_EXPORT void applyPropertyValue(const QSSGSceneDesc::Node *node, QObject *obj, QSSGSceneDesc::Property *property)
Q_QUICK3DASSETUTILS_EXPORT QQuick3DNode * createScene(QQuick3DNode &parent, const QSSGSceneDesc::Scene &scene)
Q_QUICK3DASSETUTILS_EXPORT void createGraphObject(QSSGSceneDesc::Node &node, QQuick3DObject &parent, bool traverseChildrenAndSetProperties=true)
Combined button and popup list for selecting options.
Definition image.cpp:4
static const QCssKnownValue properties[NumProperties - 1]
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
constexpr int qMetaTypeId()
Definition qmetatype.h:1405
GLsizei const GLfloat * v
[13]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLhandleARB obj
[2]
GLbitfield GLuint readBuffer
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
GraphObjectType * createRuntimeObject(NodeType &node, QQuick3DObject &parent)
QQuick3DTextureData * createRuntimeObject< QQuick3DTextureData >(QSSGSceneDesc::TextureData &node, QQuick3DObject &parent)
static void setProperties(QQuick3DObject &obj, const QSSGSceneDesc::Node &node, const QString &workingDir={})
unsigned char uchar
Definition qtypes.h:32
unsigned char quint8
Definition qtypes.h:46
const char property[13]
Definition qwizard.cpp:101
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QGraphicsScene scene
[0]
manager head(request, this, [this](QRestReply &reply) { if(reply.isSuccess()) })
[6]