Revision control
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #ifndef QSSGSCENEDESCRIPTION_P_H
++ #define QSSGSCENEDESCRIPTION_P_H
++
++ #include <QtQuick3DAssetUtils/private/qtquick3dassetutilsglobal_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
++ #include <QtQuick3DUtils/private/qssgmesh_p.h>
++
++ #include <QtCore/qlist.h>
++ #include <QtCore/qhash.h>
++ #include <QtCore/qvariant.h>
++ #include <QtCore/qflags.h>
++ #include <QtQml/qqmllist.h>
++
++ // QtQuick3D
++ #include <QtQuick3D/private/qquick3dobject_p.h>
++ // Materials
++ #include <QtQuick3D/private/qquick3dcustommaterial_p.h>
++ #include <QtQuick3D/private/qquick3ddefaultmaterial_p.h>
++ #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
++ #include <QtQuick3D/private/qquick3dspecularglossymaterial_p.h>
++ #include <QtQuick3D/private/qquick3dmodel_p.h>
++ // cameras
++ #include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
++ #include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
++ #include <QtQuick3D/private/qquick3dcustomcamera_p.h>
++ // Lights
++ #include <QtQuick3D/private/qquick3ddirectionallight_p.h>
++ #include <QtQuick3D/private/qquick3dpointlight_p.h>
++ #include <QtQuick3D/private/qquick3dspotlight_p.h>
++ // Texture
++ #include <QtQuick3D/private/qquick3dtexture_p.h>
++ #include <QtQuick3D/private/qquick3dcubemaptexture_p.h>
++ #include <QtQuick3D/private/qquick3dtexturedata_p.h>
++ //
++ #include <QtQuick3D/private/qquick3dskeleton_p.h>
++ #include <QtQuick3D/private/qquick3djoint_p.h>
++ #include <QtQuick3D/private/qquick3dinstancing_p.h>
++
++ #include <qmetatype.h>
++ #include <QtQuick3DUtils/private/qssginvasivelinkedlist_p.h>
++
++ #include <QtGui/qquaternion.h>
++
++ QT_BEGIN_NAMESPACE
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ namespace QSSGSceneDesc
++ {
++
++ struct Node;
++ struct Animation;
++
++ template<typename T>
++ using rm_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Scene
++ {
++ using ResourceNodes = QVarLengthArray<Node *>;
++ using MeshStorage = QVector<QSSGMesh::Mesh>;
++ using Animations = QVector<Animation *>;
++
++ // Root node, usually an empty 'transform' node.
++ Node *root = nullptr;
++ QString id; // Don't make any assumption about the content of this id...
++ ResourceNodes resources;
++ MeshStorage meshStorage;
++ Animations animations;
++ QString sourceDir;
++ QStringList materialVariants; // names of the asset's material variants, if any
++ mutable quint16 nodeId = 0;
++
++ void reset();
++ void cleanup();
++ };
++
++ struct Q_QUICK3DASSETUTILS_EXPORT PropertyCall
++ {
++ virtual ~PropertyCall() = default;
++ virtual bool set(QQuick3DObject &, const char *, const void *) = 0;
++ virtual bool set(QQuick3DObject &, const char *, const QVariant &) = 0;
++ virtual bool get(const QQuick3DObject &, const void *[]) const = 0;
++ };
++
++ Q_QUICK3DASSETUTILS_EXPORT void destructValue(QVariant &value);
++
++ struct Flag
++ {
++ QMetaEnum me;
++ int value;
++ };
++
++ struct Property
++ {
++ ~Property();
++ enum class Type { Static, Dynamic };
++ QVariant value;
++ QByteArray name;
++ QSSGSceneDesc::PropertyCall *call = nullptr;
++ Type type = Type::Static;
++ };
++
++ inline Property::~Property()
++ {
++ delete call;
++ destructValue(value);
++ }
++
++ Q_QUICK3DASSETUTILS_EXPORT void destructNode(QSSGSceneDesc::Node &node);
++
++ struct NodeList
++ {
++ NodeList(void * const *data, qsizetype n)
++ {
++ const auto size = sizeof(Node *) * n;
++ head = reinterpret_cast<Node **>(malloc(size));
++ memcpy(head, data, size);
++ count = n;
++ }
++ ~NodeList() { if (head) free(head); }
++ Node **head = nullptr;
++ qsizetype count = -1;
++ };
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Node
++ {
++ // Node type
++ enum class Type : quint8
++ {
++ Transform,
++ Camera,
++ Model,
++ Texture,
++ Material,
++ Light,
++ Mesh,
++ Skin,
++ Skeleton,
++ Joint,
++ MorphTarget,
++ Instancing
++ };
++
++ using type = QQuick3DNode;
++ // Runtime type type mapping between this type and the QtQuick3D type
++ using RuntimeType = QSSGRenderGraphObject::Type;
++
++ explicit Node(QByteArray name, Node::Type type, Node::RuntimeType rt)
++ : name(name)
++ , runtimeType(rt)
++ , nodeType(type) {}
++ explicit Node(Node::Type type, Node::RuntimeType rt)
++ : Node(nullptr, type, rt) {}
++
++ virtual ~Node();
++ void cleanupChildren();
++
++ QByteArray name;
++ Scene *scene = nullptr;
++ QObject *obj = nullptr;
++ using ChildList = QList<Node *>;
++ using PropertyList = QList<Property *>;
++ ChildList children;
++ PropertyList properties;
++ quint16 id = 0;
++ RuntimeType runtimeType;
++ Type nodeType;
++ };
++
++ template<typename T>
++ static constexpr bool is_node_v = std::is_base_of_v<Node, T>;
++
++ // Set up type mapping from a QQuick3D type to a SceneDesc type
++ template <typename T> struct TypeMap {};
++ #define QSSG_DECLARE_NODE(NODE) \
++ static_assert(is_node_v<NODE>, #NODE " - does not inherit from Node!"); \
++ template <> struct TypeMap<NODE::type> { using type = QSSGSceneDesc::NODE; };
++
++ template<typename T>
++ using as_scene_type_t = typename T::type;
++ template<typename T>
++ using as_node_type_t = typename TypeMap<T>::type;
++
++ QSSG_DECLARE_NODE(Node)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Texture : Node
++ {
++ using type = QQuick3DTexture;
++ explicit Texture(Node::RuntimeType rt, const QByteArray &name = {});
++ };
++ QSSG_DECLARE_NODE(Texture)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT TextureData : Node
++ {
++ using type = QQuick3DTextureData;
++ enum class Flags : quint8
++ {
++ Compressed = 0x1
++ };
++
++ explicit TextureData(const QByteArray &textureData, QSize size, const QByteArray &format, quint8 flags = 0, QByteArray name = {});
++ QByteArray data;
++ QSize sz;
++ QByteArray fmt;
++ quint8 flgs;
++ };
++ QSSG_DECLARE_NODE(TextureData)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Material : Node
++ {
++ using type = QQuick3DMaterial;
++ explicit Material(Node::RuntimeType rt);
++ };
++ QSSG_DECLARE_NODE(Material)
++
++ // The mesh is a special node, as it's not really a node type but
++ // a handle to a mesh that will be turned into a source URL...
++ struct Q_QUICK3DASSETUTILS_EXPORT Mesh : Node
++ {
++ explicit Mesh(QByteArray name, qsizetype index);
++ qsizetype idx; // idx to the mesh data in the mesh data storage (see Scene).
++ };
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Model : Node
++ {
++ using type = QQuick3DModel;
++ Model();
++ // One list per Scene::materialVariants entry, empty if not switchable
++ QList<QList<Node *>> variantMaterials;
++ QList<Node *> defaultMaterials;
++ };
++ QSSG_DECLARE_NODE(Model)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Camera : Node
++ {
++ using type = QQuick3DCamera;
++ explicit Camera(RuntimeType rt);
++ };
++ QSSG_DECLARE_NODE(Camera)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Light : Node
++ {
++ using type = QQuick3DAbstractLight;
++ explicit Light(RuntimeType rt);
++ };
++ QSSG_DECLARE_NODE(Light)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Skin : Node
++ {
++ using type = QQuick3DSkin;
++ Skin();
++ };
++ QSSG_DECLARE_NODE(Skin)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Skeleton : Node
++ {
++ using type = QQuick3DSkeleton;
++ Skeleton();
++ // Skeleton is a virtual node, which is added for the start of the joint heirarchy.
++ // parent - joint 1 -> parent - skeleton - joint 1
++ // - joint 2 - joint 2
++ // - model 1 - model 1
++ // - camera 1 - camera 1
++ size_t maxIndex = 0;
++ };
++ QSSG_DECLARE_NODE(Skeleton)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Joint : Node
++ {
++ using type = QQuick3DJoint;
++ Joint();
++ };
++ QSSG_DECLARE_NODE(Joint)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT MorphTarget : Node
++ {
++ using type = QQuick3DMorphTarget;
++ MorphTarget();
++ };
++ QSSG_DECLARE_NODE(MorphTarget)
++
++ struct Q_QUICK3DASSETUTILS_EXPORT Instancing : Node
++ {
++ using type = QQuick3DInstancing;
++ Instancing();
++ QByteArray instanceData; // QQuick3DInstancing::InstanceTableEntry array
++ qsizetype instanceCount = 0;
++ };
++ QSSG_DECLARE_NODE(Instancing)
++
++ // We keep our own list data structure, since Qt does not have a variant list where all the
++ // elements have the same type, and using a list of QVariant is very inefficient
++ struct ListView
++ {
++ ~ListView() { if (data) free(data); }
++ QMetaType mt;
++ void *data = nullptr;
++ qsizetype count = -1;
++ };
++
++ Q_QUICK3DASSETUTILS_EXPORT QMetaType listViewMetaType();
++
++ struct Animation
++ {
++ struct KeyPosition
++ {
++ enum class KeyType : quint16
++ {
++ Frame = 0x100,
++ Time = 0x200
++ };
++
++ enum class ValueType : quint8
++ {
++ Number,
++ Vec2,
++ Vec3,
++ Vec4,
++ Quaternion
++ };
++
++ ValueType getValueType() const { return ValueType(0xf & flag); }
++ KeyType getKeyType() const { return KeyType(0xf00 & flag); }
++ QVariant getValue() const {
++ switch (getValueType()) {
++ case ValueType::Number:
++ return value.x();
++ case ValueType::Vec2:
++ return value.toVector2D();
++ case ValueType::Vec3:
++ return value.toVector3D();
++ case ValueType::Vec4:
++ return value;
++ case ValueType::Quaternion:
++ return QQuaternion(value);
++ }
++ return value;
++ }
++ QMetaType::Type getValueQMetaType() const {
++ switch (getValueType()) {
++ case ValueType::Number:
++ return QMetaType::Float;
++ case ValueType::Vec2:
++ return QMetaType::QVector2D;
++ case ValueType::Vec3:
++ return QMetaType::QVector3D;
++ case ValueType::Vec4:
++ return QMetaType::QVector4D;
++ case ValueType::Quaternion:
++ return QMetaType::QQuaternion;
++ }
++ return QMetaType::QVector4D;
++ }
++ QVector4D value;
++ float time = 0.0f;
++ quint16 flag = 0;
++ };
++ using Keys = QList<Animation::KeyPosition *>;
++
++ struct Channel
++ {
++ enum class TargetType : quint8
++ {
++ Property
++ };
++
++ // This is a bit simplistic, but is all we support so let's keep simple.
++ enum class TargetProperty : quint8
++ {
++ Unknown,
++ Position,
++ Rotation,
++ Scale,
++ Weight // for MorphMesh
++ };
++
++ Node *target = nullptr;
++ Keys keys;
++ TargetType targetType = TargetType::Property;
++ TargetProperty targetProperty = TargetProperty::Unknown;
++ };
++ using Channels = QList<Animation::Channel *>;
++
++ Channels channels;
++ // It stores the length of this Animation, every keys in every channels in
++ // an animation will have the same KeyType and it will be a type of
++ // the length
++ float length = 0.0f;
++
++ float framesPerSecond = 0.0f; // for translation back to frames
++
++ QByteArray name;
++ };
++
++ // Add a child node to parent node.
++ Q_QUICK3DASSETUTILS_EXPORT void addNode(Node &parent, Node &node);
++ // Add node to the scene, if a node is already set the new node will
++ // become a child of the root node.
++ Q_QUICK3DASSETUTILS_EXPORT void addNode(Scene &scene, Node &node);
++
++ template<typename> struct ListParam { enum { value = 0 }; };
++ template<typename T> struct ListParam<QList<T>>
++ {
++ using type = T;
++ enum { value = 1 };
++ };
++
++ template <typename T>
++ using listParam_t = typename ListParam<rm_cvref_t<T>>::type;
++
++ template <typename T>
++ struct FuncType
++ {
++ enum { value = 0 };
++ };
++
++ template <typename R, typename... A>
++ struct FuncType<R (*)(A...)>
++ {
++ enum { value = sizeof...(A) == 3 };
++ using Ret = R;
++ using Arg0 = std::tuple_element_t<0, std::tuple<A...>>;
++ using Arg1 = std::tuple_element_t<1, std::tuple<A...>>;
++ using Arg2 = std::tuple_element_t<2, std::tuple<A...>>;
++ using Arg2Base = rm_cvref_t<Arg2>;
++ };
++
++ template <typename R, typename C, typename... A>
++ struct FuncType<R (C::*)(A...)>
++ {
++ enum { value = sizeof... (A) == 1 };
++ using Ret = R;
++ using Class = C;
++ // For now we only care about single argument functions
++ using Arg0 = std::tuple_element_t<0, std::tuple<A...>>;
++ using Arg0Base = rm_cvref_t<Arg0>;
++ };
++
++ template <typename T, typename C>
++ struct FuncType<QQmlListProperty<T> (C::*)()>
++ {
++ enum { value = 2 };
++ using Ret = QQmlListProperty<T>;
++ using Class = C;
++ using Arg0 = void;
++ using Arg0Base = Arg0;
++ };
++
++ template <typename Ret, typename Arg>
++ struct PropertyProxySetter : PropertyCall
++ {
++ using Setter = Ret (*)(QQuick3DObject &, const char *, Arg);
++ constexpr explicit PropertyProxySetter(Setter fn) : call(fn) {}
++ Setter call = nullptr;
++ bool get(const QQuick3DObject &, const void *[]) const override { return false; }
++ bool set(QQuick3DObject &that, const char *name, const void *value) override
++ {
++ if constexpr (std::is_pointer_v<typename FuncType<Setter>::Arg2>)
++ call(that, name, reinterpret_cast<typename FuncType<Setter>::Arg2>(const_cast<void *>(value)));
++ else
++ call(that, name, *reinterpret_cast<typename FuncType<Setter>::Arg2Base *>(const_cast<void *>(value)));
++ return true;
++ }
++ bool set(QQuick3DObject &that, const char *name, const QVariant &var) override
++ {
++ call(that, name, qvariant_cast<typename FuncType<Setter>::Arg2Base>(var));
++ return true;
++ }
++ };
++
++ template <typename Ret, typename Class, typename Arg>
++ struct PropertySetter : PropertyCall
++ {
++ using Setter = Ret (Class::*)(Arg);
++ constexpr explicit PropertySetter(Setter fn) : call(fn) {}
++ Setter call = nullptr;
++ bool get(const QQuick3DObject &, const void *[]) const override { return false; }
++ bool set(QQuick3DObject &that, const char *, const void *value) override
++ {
++ if constexpr (std::is_pointer_v<typename FuncType<Setter>::Arg0>)
++ (qobject_cast<Class *>(&that)->*call)(reinterpret_cast<typename FuncType<Setter>::Arg0>(const_cast<void *>(value)));
++ else {
++ (qobject_cast<Class *>(&that)->*call)(*reinterpret_cast<typename FuncType<Setter>::Arg0Base *>(const_cast<void *>(value)));
++ }
++ return true;
++ }
++ bool set(QQuick3DObject &that, const char *, const QVariant &var) override
++ {
++ (qobject_cast<Class *>(&that)->*call)(qvariant_cast<typename FuncType<Setter>::Arg0Base>(var));
++ return true;
++ }
++ };
++
++ template <typename Ret, typename Class, typename Arg>
++ struct PropertyListSetter : PropertyCall
++ {
++ using Setter = Ret (Class::*)(Arg);
++ using ListT = typename FuncType<Setter>::Arg0Base;
++ using It = listParam_t<ListT>;
++ constexpr explicit PropertyListSetter(Setter fn) : call(fn) {}
++ Setter call = nullptr;
++ bool get(const QQuick3DObject &, const void *[]) const override { return false; }
++ bool set(QQuick3DObject &that, const char *, const void *value) override
++ {
++ if (const auto listView = reinterpret_cast<const ListView *>(value)) {
++ if (listView->count > 0) {
++ const auto begin = reinterpret_cast<It *>(listView->data);
++ const auto end = reinterpret_cast<It *>(listView->data) + listView->count;
++ (qobject_cast<Class *>(&that)->*call)(ListT{begin, end});
++ } else {
++ (qobject_cast<Class *>(&that)->*call)(ListT{});
++ }
++ return true;
++ }
++
++ return false;
++ }
++ bool set(QQuick3DObject &that, const char *, const QVariant &var) override
++ {
++ if (const auto listView = qvariant_cast<const ListView *>(var)) {
++ if (listView->count > 0) {
++ const auto begin = reinterpret_cast<It *>(listView->data);
++ const auto end = reinterpret_cast<It *>(listView->data) + listView->count;
++ (qobject_cast<Class *>(&that)->*call)(ListT{begin, end});
++ } else {
++ (qobject_cast<Class *>(&that)->*call)(ListT{});
++ }
++ }
++ return false;
++ }
++ };
++
++ template <typename Class, typename T, template <typename> typename List>
++ struct PropertyList : PropertyCall
++ {
++ using ListType = List<T>;
++ using ListFunc = ListType (Class::*)();
++ constexpr explicit PropertyList(ListFunc fn) : listfn(fn) {}
++ ListFunc listfn = nullptr;
++
++ static_assert(std::is_same_v<ListType, QQmlListProperty<T>>, "Expected QQmlListProperty!");
++
++ bool get(const QQuick3DObject &, const void *[]) const override { return false; }
++
++
++ void doSet(QQuick3DObject &that, const QSSGSceneDesc::NodeList &nodeList)
++ {
++ ListType list = (qobject_cast<Class *>(&that)->*listfn)();
++ auto head = reinterpret_cast<as_node_type_t<T> **>(nodeList.head);
++ for (int i = 0, end = nodeList.count; i != end; ++i)
++ list.append(&list, qobject_cast<T *>((*(head + i))->obj));
++ }
++
++ bool set(QQuick3DObject &that, const char *, const void *value) override
++ {
++ if (value) {
++ const auto &nodeList = *reinterpret_cast<const QSSGSceneDesc::NodeList *>(value);
++ doSet(that, nodeList);
++ return true;
++ }
++ return false;
++ }
++
++ bool set(QQuick3DObject &that, const char *, const QVariant &var) override
++ {
++ const auto *nodeList = qvariant_cast<const QSSGSceneDesc::NodeList *>(var);
++ if (nodeList) {
++ doSet(that, *nodeList);
++ return true;
++ }
++ return false;
++ }
++ };
++
++ template <typename NodeT>
++ using if_node = typename std::enable_if_t<is_node_v<NodeT>, bool>;
++ template <typename Setter, typename Value>
++ using if_compatible_t = typename std::enable_if_t<std::is_same_v<typename FuncType<Setter>::Arg0Base, rm_cvref_t<Value>>, bool>;
++ template <typename Setter, typename T>
++ using if_compatible_node_list_t = typename std::enable_if_t<std::is_same_v<typename FuncType<Setter>::Ret, QQmlListProperty<as_scene_type_t<T>>>, bool>;
++ template <typename Setter, typename Value>
++ using if_compatible_proxy_t = typename std::enable_if_t<std::is_same_v<typename FuncType<Setter>::Arg2Base, rm_cvref_t<Value>>, bool>;
++
++ // Sets a property on a node, the property is a name map to struct containing a pointer to the value and a function
++ // to set the value on an runtime object (QtQuick3DObject). The type is verified at compile-time, so we can assume
++ // the value is of the right type when setting it at run-time.
++ template<typename Setter, typename T, if_compatible_t<Setter, T> = false>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, T &&value)
++ {
++ Q_ASSERT(node.scene);
++ auto prop = new Property;
++ prop->name = name;
++ prop->call = new PropertySetter(setter);
++ prop->value = QVariant::fromValue(std::forward<T>(value));
++ node.properties.push_back(prop);
++ }
++
++ template<typename Setter, typename T, if_compatible_t<Setter, QFlags<T>> = false>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, QFlags<T> value)
++ {
++ Q_ASSERT(node.scene);
++ auto prop = new Property;
++ prop->name = name;
++ prop->call = new PropertySetter(setter);
++ prop->value = QVariant::fromValue(Flag{ QMetaEnum::fromType<rm_cvref_t<T>>(), value.toInt() });
++ node.properties.push_back(prop);
++ }
++
++
++ template<typename Setter, typename T, if_compatible_t<Setter, QList<T>> = false>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, QList<T> value)
++ {
++ Q_ASSERT(node.scene);
++ static_assert(!std::is_pointer_v<T>, "Type cannot be a pointer!");
++ static_assert(std::is_trivially_destructible_v<T> && std::is_trivially_copy_constructible_v<T>,
++ "List parameter type needs to be trivially constructable and trivially destructible!");
++
++ const auto count = value.size();
++ void *data = nullptr;
++ if (count) {
++ const auto asize = count * sizeof(T);
++ data = malloc(asize); // is freed in ~ListView
++ memcpy(data, value.constData(), asize);
++ }
++ auto prop = new Property;
++ prop->name = name;
++ prop->call = new PropertyListSetter(setter);
++ prop->value = QVariant::fromValue(new ListView{ QMetaType::fromType<rm_cvref_t<T>>(), data, count });
++ node.properties.push_back(prop);
++ }
++
++
++ Q_QUICK3DASSETUTILS_EXPORT QSSGSceneDesc::Property *setProperty(QSSGSceneDesc::Node &node, const char *name, QVariant &&value);
++
++ // Calling this will omit any type checking, so make sure the type is handled correctly
++ // when it gets used later!
++ template<typename Setter>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, QVariant &&value)
++ {
++ Q_ASSERT(node.scene);
++ Property *prop = new Property;
++ prop->name = name;
++ prop->call = new PropertySetter(setter);
++ prop->value = value;
++ node.properties.push_back(prop);
++ }
++
++ template<typename Setter, typename Value, if_compatible_proxy_t<Setter, Value> = true>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, Value &&value, QSSGSceneDesc::Property::Type type = QSSGSceneDesc::Property::Type::Static)
++ {
++ Q_ASSERT(node.scene);
++ static_assert(std::is_trivially_destructible_v<rm_cvref_t<Value>>, "Value needs to be trivially destructible!");
++ Property *prop = new Property;
++ prop->name = name;
++ prop->call = new PropertyProxySetter(setter);
++ prop->value = QVariant::fromValue(value);
++ prop->type = type;
++ node.properties.push_back(prop);
++ }
++
++ template<typename Setter, typename ViewValue, if_compatible_t<Setter, typename ViewValue::type> = false>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, ViewValue view)
++ {
++ Q_ASSERT(node.scene);
++ static_assert(std::is_same_v<typename ViewValue::type, typename FuncType<Setter>::Arg0Base>, "Type cannot be mapped to slot argument");
++ Property *prop = new Property;
++ prop->name = name;
++ prop->call = new PropertySetter(setter);
++ prop->value = QVariant::fromValue(view);
++ node.properties.push_back(prop);
++ }
++
++ template<typename Setter, typename Value, if_compatible_t<Setter, as_scene_type_t<Value> *> = true>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, Value *value)
++ {
++ Q_ASSERT(node.scene);
++ Property *prop = new Property;
++ prop->name = name;
++ prop->call = new PropertySetter(setter);
++ // Always 'Node', the Node class itself contains more fine grain type information.
++ prop->value = QVariant::fromValue(static_cast<Node *>(value));
++ node.properties.push_back(prop);
++ }
++
++ // Overloaded function for setting a type to a property that's a QQmlListProperty.
++ template<typename Setter, typename NodeT, qsizetype Prealloc, if_compatible_node_list_t<Setter, NodeT> = true>
++ static void setProperty(QSSGSceneDesc::Node &node, const char *name, Setter setter, const QVarLengthArray<NodeT *, Prealloc> &list)
++ {
++ Q_ASSERT(node.scene);
++ if (!list.isEmpty()) {
++ NodeList *l = new NodeList(reinterpret_cast<void * const*>(list.constData()), list.count());
++
++ Property *prop = new Property;
++ prop->name = name;
++ prop->call = new PropertyList(setter);
++ prop->value = QVariant::fromValue(l);
++ node.properties.push_back(prop);
++ }
++ }
++
++ } // QSSGSceneDesc
++
++ QT_END_NAMESPACE
++
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Node)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Texture)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Material)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Mesh)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Model)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Camera)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Light)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Skin)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Skeleton)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Joint)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::MorphTarget)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::NodeList)
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Animation)
++
++ Q_DECLARE_METATYPE(QSSGSceneDesc::ListView)
++
++ Q_DECLARE_METATYPE(QSSGSceneDesc::Flag)
++
++ #endif // QSSGSCENEDESCRIPTION_P_H