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
qquick3dmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dmodel_p.h"
5#include "qquick3dobject_p.h"
7#include "qquick3dnode_p_p.h"
9
10#include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterial_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
14
15#include <QtQuick3DUtils/private/qssgutils_p.h>
16
17#include <QtQml/QQmlFile>
18
20
110
112{
113 disconnect(m_geometryConnection);
114
115 auto matList = materials();
116 qmlClearMaterials(&matList);
117 auto morphList = morphTargets();
118 qmlClearMorphTargets(&morphList);
119}
120
138{
139 return m_source;
140}
141
154QQmlListProperty<QQuick3DMaterial> QQuick3DModel::materials()
155{
156 return QQmlListProperty<QQuick3DMaterial>(this,
157 nullptr,
158 QQuick3DModel::qmlAppendMaterial,
159 QQuick3DModel::qmlMaterialsCount,
160 QQuick3DModel::qmlMaterialAt,
161 QQuick3DModel::qmlClearMaterials);
162}
163
175QQmlListProperty<QQuick3DMorphTarget> QQuick3DModel::morphTargets()
176{
177 return QQmlListProperty<QQuick3DMorphTarget>(this,
178 nullptr,
179 QQuick3DModel::qmlAppendMorphTarget,
180 QQuick3DModel::qmlMorphTargetsCount,
181 QQuick3DModel::qmlMorphTargetAt,
182 QQuick3DModel::qmlClearMorphTargets);
183}
184
195{
196 return m_instancing;
197}
198
209{
210 return m_instanceRoot;
211}
212
213// Source URL's need a bit of translation for the engine because of the
214// use of fragment syntax for specifiying primitives and sub-meshes
215// So we need to check for the fragment before translating to a qmlfile
216
218{
219 QString fragment;
220 if (source.hasFragment()) {
221 // Check if this is an index, or primitive
222 bool isNumber = false;
223 source.fragment().toInt(&isNumber);
224 fragment = QStringLiteral("#") + source.fragment();
225 // If it wasn't an index, then it was a primitive
226 if (!isNumber)
227 return fragment;
228 }
229
230 const QQmlContext *context = qmlContext(contextObject);
231 const auto resolvedUrl = context ? context->resolvedUrl(source) : source;
232 const auto qmlSource = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
233 return (qmlSource.isEmpty() ? source.path() : qmlSource) + fragment;
234}
235
237{
238 m_dirtyAttributes = 0xffffffff;
240}
241
253{
254 return m_castsShadows;
255}
256
271{
272 return m_receivesShadows;
273}
274
284{
285 return m_pickable;
286}
287
296{
297 return m_geometry;
298}
299
314{
315 return m_skeleton;
316}
317
333{
334 return m_skin;
335}
336
337
351QList<QMatrix4x4> QQuick3DModel::inverseBindPoses() const
352{
353 return m_inverseBindPoses;
354}
355
366{
367 return m_bounds;
368}
369
379{
380 return m_depthBias;
381}
382
392{
393 return m_receivesReflections;
394}
395
404{
405 return m_castsReflections;
406}
407
435bool QQuick3DModel::isUsedInBakedLighting() const
436{
437 return m_usedInBakedLighting;
438}
439
473{
474 return m_lightmapBaseResolution;
475}
476
506{
507 return m_bakedLightmap;
508}
509
517{
518 return m_instancingLodMin;
519}
520
528{
529 return m_instancingLodMax;
530}
531
533{
534 if (m_source == source)
535 return;
536
537 m_source = source;
539 markDirty(SourceDirty);
540 if (QQuick3DObjectPrivate::get(this)->sceneManager)
541 QQuick3DObjectPrivate::get(this)->sceneManager->dirtyBoundingBoxList.append(this);
542}
543
544void QQuick3DModel::setCastsShadows(bool castsShadows)
545{
546 if (m_castsShadows == castsShadows)
547 return;
548
549 m_castsShadows = castsShadows;
551 markDirty(ShadowsDirty);
552}
553
554void QQuick3DModel::setReceivesShadows(bool receivesShadows)
555{
556 if (m_receivesShadows == receivesShadows)
557 return;
558
559 m_receivesShadows = receivesShadows;
561 markDirty(ShadowsDirty);
562}
563
564void QQuick3DModel::setPickable(bool isPickable)
565{
566 if (m_pickable == isPickable)
567 return;
568
569 m_pickable = isPickable;
571 markDirty(PickingDirty);
572}
573
575{
576 if (geometry == m_geometry)
577 return;
578
579 // Make sure to disconnect if the geometry gets deleted out from under us
581
582 if (m_geometry)
583 QObject::disconnect(m_geometryConnection);
584 m_geometry = geometry;
585
586 if (m_geometry) {
587 m_geometryConnection
588 = QObject::connect(m_geometry, &QQuick3DGeometry::geometryNodeDirty, this, [this]() {
589 markDirty(GeometryDirty);
590 });
591 }
593 markDirty(GeometryDirty);
594}
595
597{
598 if (skeleton == m_skeleton)
599 return;
600
601 // Make sure to disconnect if the skeleton gets deleted out from under us
603
604 m_skeleton = skeleton;
605
607 markDirty(SkeletonDirty);
608}
609
610void QQuick3DModel::setSkin(QQuick3DSkin *skin)
611{
612 if (skin == m_skin)
613 return;
614
615 // Make sure to disconnect if the skin gets deleted out from under us
616 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DModel::setSkin, skin, m_skin);
617
618 m_skin = skin;
619 emit skinChanged();
620 markDirty(SkinDirty);
621}
622
623void QQuick3DModel::setInverseBindPoses(const QList<QMatrix4x4> &poses)
624{
625 if (m_inverseBindPoses == poses)
626 return;
627
628 m_inverseBindPoses = poses;
630 markDirty(PoseDirty);
631}
632
633
634void QQuick3DModel::setBounds(const QVector3D &min, const QVector3D &max)
635{
636 if (!qFuzzyCompare(m_bounds.maximum(), max)
637 || !qFuzzyCompare(m_bounds.minimum(), min)) {
638 m_bounds.bounds = QSSGBounds3 { min, max };
640 }
641}
642
644{
645 if (m_instancing == instancing)
646 return;
647
648 // Make sure to disconnect if the instance table gets deleted out from under us
650 if (m_instancing)
651 QObject::disconnect(m_instancingConnection);
652 m_instancing = instancing;
653 if (m_instancing) {
654 m_instancingConnection = QObject::connect
656 this, [this]{ markDirty(InstancesDirty);});
657 }
658 markDirty(InstancesDirty);
660}
661
663{
664 if (m_instanceRoot == instanceRoot)
665 return;
666
668
669 m_instanceRoot = instanceRoot;
670 markDirty(InstanceRootDirty);
672}
673
675{
676 if (qFuzzyCompare(bias, m_depthBias))
677 return;
678
679 m_depthBias = bias;
680 markDirty(PropertyDirty);
682}
683
684void QQuick3DModel::setReceivesReflections(bool receivesReflections)
685{
686 if (m_receivesReflections == receivesReflections)
687 return;
688
689 m_receivesReflections = receivesReflections;
690 emit receivesReflectionsChanged();
691 markDirty(ReflectionDirty);
692}
693
694void QQuick3DModel::setCastsReflections(bool castsReflections)
695{
696 if (m_castsReflections == castsReflections)
697 return;
698 m_castsReflections = castsReflections;
699 emit castsReflectionsChanged();
700 markDirty(ReflectionDirty);
701}
702
703void QQuick3DModel::setUsedInBakedLighting(bool enable)
704{
705 if (m_usedInBakedLighting == enable)
706 return;
707
708 m_usedInBakedLighting = enable;
709 emit usedInBakedLightingChanged();
710 markDirty(PropertyDirty);
711}
712
713void QQuick3DModel::setLightmapBaseResolution(int resolution)
714{
715 resolution = qMax(128, resolution);
716 if (m_lightmapBaseResolution == resolution)
717 return;
718
719 m_lightmapBaseResolution = resolution;
720 emit lightmapBaseResolutionChanged();
721 markDirty(PropertyDirty);
722}
723
724void QQuick3DModel::setBakedLightmap(QQuick3DBakedLightmap *bakedLightmap)
725{
726 if (m_bakedLightmap == bakedLightmap)
727 return;
728
729 if (m_bakedLightmap)
730 m_bakedLightmap->disconnect(m_bakedLightmapSignalConnection);
731
732 m_bakedLightmap = bakedLightmap;
733
734 m_bakedLightmapSignalConnection = QObject::connect(m_bakedLightmap, &QQuick3DBakedLightmap::changed, this,
735 [this] { markDirty(PropertyDirty); });
736
737 QObject::connect(m_bakedLightmap, &QObject::destroyed, this,
738 [this]
739 {
740 m_bakedLightmap = nullptr;
741 markDirty(PropertyDirty);
742 });
743
744 emit bakedLightmapChanged();
745 markDirty(PropertyDirty);
746}
747
748void QQuick3DModel::itemChange(ItemChange change, const ItemChangeData &value)
749{
750 if (change == QQuick3DObject::ItemSceneChange)
751 updateSceneManager(value.sceneManager);
752}
753
755{
756 if (!node) {
757 markAllDirty();
758 node = new QSSGRenderModel();
759 }
760
762 int dirtyAttribute = 0;
763
764 auto modelNode = static_cast<QSSGRenderModel *>(node);
765 if (m_dirtyAttributes & SourceDirty)
766 modelNode->meshPath = QSSGRenderPath(translateMeshSource(m_source, this));
767 if (m_dirtyAttributes & PickingDirty)
768 modelNode->setState(QSSGRenderModel::LocalState::Pickable, m_pickable);
769
770 if (m_dirtyAttributes & ShadowsDirty) {
771 modelNode->castsShadows = m_castsShadows;
772 modelNode->receivesShadows = m_receivesShadows;
773 }
774
775 if (m_dirtyAttributes & MaterialsDirty) {
776 if (!m_materials.isEmpty()) {
777 if (modelNode->materials.isEmpty()) {
778 // Easy mode, just add each material
779 for (const Material &material : m_materials) {
780 QSSGRenderGraphObject *graphObject = QQuick3DObjectPrivate::get(material.material)->spatialNode;
781 if (graphObject)
782 modelNode->materials.append(graphObject);
783 else
784 dirtyAttribute |= MaterialsDirty; // We still got dirty materials
785 }
786 } else {
787 // Hard mode, go through each material and see if they match
788 if (modelNode->materials.size() != m_materials.size())
789 modelNode->materials.resize(m_materials.size());
790 for (int i = 0; i < m_materials.size(); ++i) {
791 QSSGRenderGraphObject *graphObject = QQuick3DObjectPrivate::get(m_materials[i].material)->spatialNode;
792 if (modelNode->materials[i] != graphObject)
793 modelNode->materials[i] = graphObject;
794 }
795 }
796 } else {
797 // No materials
798 modelNode->materials.clear();
799 }
800 }
801
802 if (m_dirtyAttributes & MorphTargetsDirty) {
803 if (!m_morphTargets.isEmpty()) {
804 const int numMorphTarget = m_morphTargets.size();
805 if (modelNode->morphTargets.isEmpty()) {
806 // Easy mode, just add each morphTarget
807 for (const auto morphTarget : std::as_const(m_morphTargets)) {
808 QSSGRenderGraphObject *graphObject = QQuick3DObjectPrivate::get(morphTarget)->spatialNode;
809 if (graphObject)
810 modelNode->morphTargets.append(graphObject);
811 else
812 dirtyAttribute |= MorphTargetsDirty; // We still got dirty morphTargets
813 }
814 modelNode->morphWeights.resize(numMorphTarget);
815 modelNode->morphAttributes.resize(numMorphTarget);
816 } else {
817 // Hard mode, go through each morphTarget and see if they match
818 if (modelNode->morphTargets.size() != numMorphTarget) {
819 modelNode->morphTargets.resize(numMorphTarget);
820 modelNode->morphWeights.resize(numMorphTarget);
821 modelNode->morphAttributes.resize(numMorphTarget);
822 }
823 for (int i = 0; i < numMorphTarget; ++i)
824 modelNode->morphTargets[i] = QQuick3DObjectPrivate::get(m_morphTargets.at(i))->spatialNode;
825 }
826 } else {
827 // No morphTargets
828 modelNode->morphTargets.clear();
829 }
830 }
831
832 if (m_dirtyAttributes & quint32(InstancesDirty | InstanceRootDirty)) {
833 // If we have an instance root set we have lower priority and the instance root node should already
834 // have been created.
835 QSSGRenderNode *instanceRootNode = nullptr;
836 if (m_instanceRoot) {
837 if (m_instanceRoot == this)
838 instanceRootNode = modelNode;
839 else
840 instanceRootNode = static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(m_instanceRoot)->spatialNode);
841 }
842 if (instanceRootNode != modelNode->instanceRoot) {
843 modelNode->instanceRoot = instanceRootNode;
845 }
846
847 if (m_instancing) {
848 modelNode->instanceTable = static_cast<QSSGRenderInstanceTable *>(QQuick3DObjectPrivate::get(m_instancing)->spatialNode);
849 } else {
850 modelNode->instanceTable = nullptr;
851 }
852 }
853
854 if (m_dirtyAttributes & GeometryDirty) {
855 if (m_geometry) {
856 modelNode->geometry = static_cast<QSSGRenderGeometry *>(QQuick3DObjectPrivate::get(m_geometry)->spatialNode);
857 setBounds(m_geometry->boundsMin(), m_geometry->boundsMax());
858 } else {
859 modelNode->geometry = nullptr;
861 }
862 }
863
864 if (m_dirtyAttributes & SkeletonDirty) {
865 if (m_skeleton) {
866 modelNode->skeleton = static_cast<QSSGRenderSkeleton *>(QQuick3DObjectPrivate::get(m_skeleton)->spatialNode);
867 if (modelNode->skeleton)
868 modelNode->skeleton->skinningDirty = true;
869 } else {
870 modelNode->skeleton = nullptr;
871 }
872 }
873
874 if (m_dirtyAttributes & SkinDirty) {
875 if (m_skin)
876 modelNode->skin = static_cast<QSSGRenderSkin *>(QQuick3DObjectPrivate::get(m_skin)->spatialNode);
877 else
878 modelNode->skin = nullptr;
879 }
880
881 if (m_dirtyAttributes & LodDirty) {
882 modelNode->instancingLodMin = m_instancingLodMin;
883 modelNode->instancingLodMax = m_instancingLodMax;
884 }
885
886 if (m_dirtyAttributes & PoseDirty) {
887 modelNode->inverseBindPoses = m_inverseBindPoses.toVector();
888 if (modelNode->skeleton)
889 modelNode->skeleton->skinningDirty = true;
890 }
891
892 if (m_dirtyAttributes & PropertyDirty) {
893 modelNode->m_depthBiasSq = QSSGRenderModel::signedSquared(m_depthBias);
894 modelNode->usedInBakedLighting = m_usedInBakedLighting;
895 modelNode->lightmapBaseResolution = uint(m_lightmapBaseResolution);
896 if (m_bakedLightmap && m_bakedLightmap->isEnabled()) {
897 modelNode->lightmapKey = m_bakedLightmap->key();
898 const QString srcPrefix = m_bakedLightmap->loadPrefix();
899 const QString srcPath = srcPrefix.isEmpty() ? QStringLiteral(".") : srcPrefix;
900 const QQmlContext *context = qmlContext(m_bakedLightmap);
901 const QUrl resolvedUrl = context ? context->resolvedUrl(srcPath) : srcPath;
902 modelNode->lightmapLoadPath = QQmlFile::urlToLocalFileOrQrc(resolvedUrl);
903 } else {
904 modelNode->lightmapKey.clear();
905 modelNode->lightmapLoadPath.clear();
906 }
907 modelNode->levelOfDetailBias = m_levelOfDetailBias;
908 }
909
910 if (m_dirtyAttributes & ReflectionDirty) {
911 modelNode->receivesReflections = m_receivesReflections;
912 modelNode->castsReflections = m_castsReflections;
913 }
914
915 m_dirtyAttributes = dirtyAttribute;
916
917 return modelNode;
918}
919
920void QQuick3DModel::markDirty(QQuick3DModel::QSSGModelDirtyType type)
921{
922 if (InstanceRootDirty & quint32(type))
924
925 if (!(m_dirtyAttributes & quint32(type))) {
926 m_dirtyAttributes |= quint32(type);
927 update();
928 }
929}
930
931void QQuick3DModel::updateSceneManager(QQuick3DSceneManager *sceneManager)
932{
933 if (sceneManager) {
934 sceneManager->dirtyBoundingBoxList.append(this);
935 QQuick3DObjectPrivate::refSceneManager(m_skeleton, *sceneManager);
936 QQuick3DObjectPrivate::refSceneManager(m_skin, *sceneManager);
937 QQuick3DObjectPrivate::refSceneManager(m_geometry, *sceneManager);
938 QQuick3DObjectPrivate::refSceneManager(m_instancing, *sceneManager);
939 for (Material &mat : m_materials) {
940 if (!mat.material->parentItem() && !QQuick3DObjectPrivate::get(mat.material)->sceneManager) {
941 if (!mat.refed) {
942 QQuick3DObjectPrivate::refSceneManager(mat.material, *sceneManager);
943 mat.refed = true;
944 }
945 }
946 }
947 } else {
952 for (Material &mat : m_materials) {
953 if (mat.refed) {
955 mat.refed = false;
956 }
957 }
958 }
959}
960
961void QQuick3DModel::onMaterialDestroyed(QObject *object)
962{
963 bool found = false;
964 for (int i = 0; i < m_materials.size(); ++i) {
965 if (m_materials[i].material == object) {
966 m_materials.removeAt(i--);
967 found = true;
968 }
969 }
970 if (found)
971 markDirty(QQuick3DModel::MaterialsDirty);
972}
973
974void QQuick3DModel::qmlAppendMaterial(QQmlListProperty<QQuick3DMaterial> *list, QQuick3DMaterial *material)
975{
976 if (material == nullptr)
977 return;
978 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
979 self->m_materials.push_back({ material, false });
980 self->markDirty(QQuick3DModel::MaterialsDirty);
981
982 if (material->parentItem() == nullptr) {
983 // If the material has no parent, check if it has a hierarchical parent that's a QQuick3DObject
984 // and re-parent it to that, e.g., inline materials
985 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(material->parent());
986 if (parentItem) {
987 material->setParentItem(parentItem);
988 } else { // If no valid parent was found, make sure the material refs our scene manager
989 const auto &sceneManager = QQuick3DObjectPrivate::get(self)->sceneManager;
990 if (sceneManager) {
991 QQuick3DObjectPrivate::get(material)->refSceneManager(*sceneManager);
992 // Have to keep track if we called refSceneManager because we
993 // can end up in double deref attempts when a model is going
994 // away, due to updateSceneManager() being called on
995 // ItemSceneChanged (and also doing deref). We must ensure that
996 // is one deref for each ref.
997 self->m_materials.last().refed = true;
998 }
999 // else: If there's no scene manager, defer until one is set, see itemChange()
1000 }
1001 }
1002
1003 // Make sure materials are removed when destroyed
1004 connect(material, &QQuick3DMaterial::destroyed, self, &QQuick3DModel::onMaterialDestroyed);
1005}
1006
1007QQuick3DMaterial *QQuick3DModel::qmlMaterialAt(QQmlListProperty<QQuick3DMaterial> *list, qsizetype index)
1008{
1009 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1010 return self->m_materials.at(index).material;
1011}
1012
1013qsizetype QQuick3DModel::qmlMaterialsCount(QQmlListProperty<QQuick3DMaterial> *list)
1014{
1015 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1016 return self->m_materials.size();
1017}
1018
1019void QQuick3DModel::qmlClearMaterials(QQmlListProperty<QQuick3DMaterial> *list)
1020{
1021 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1022 for (Material &mat : self->m_materials) {
1023 if (mat.material->parentItem() == nullptr) {
1024 if (mat.refed) {
1025 QQuick3DObjectPrivate::get(mat.material)->derefSceneManager();
1026 mat.refed = false;
1027 }
1028 }
1029 mat.material->disconnect(self, SLOT(onMaterialDestroyed(QObject*)));
1030 }
1031 self->m_materials.clear();
1032 self->markDirty(QQuick3DModel::MaterialsDirty);
1033}
1034
1035void QQuick3DModel::onMorphTargetDestroyed(QObject *object)
1036{
1037 bool found = false;
1038 for (int i = 0; i < m_morphTargets.size(); ++i) {
1039 if (m_morphTargets.at(i) == object) {
1040 m_morphTargets.removeAt(i--);
1041 found = true;
1042 }
1043 }
1044 if (found) {
1045 markDirty(QQuick3DModel::MorphTargetsDirty);
1046 m_numMorphAttribs = 0;
1047 }
1048}
1049
1050void QQuick3DModel::qmlAppendMorphTarget(QQmlListProperty<QQuick3DMorphTarget> *list, QQuick3DMorphTarget *morphTarget)
1051{
1052 if (morphTarget == nullptr)
1053 return;
1054 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1055 if (self->m_numMorphAttribs >= 8) {
1056 qWarning("The number of morph attributes exceeds 8. This morph target will be ignored.");
1057 return;
1058 }
1059 self->m_morphTargets.push_back(morphTarget);
1060 self->m_numMorphAttribs += morphTarget->numAttribs();
1061 if (self->m_numMorphAttribs > 8)
1062 qWarning("The number of morph attributes exceeds 8. This morph target will be supported partially.");
1063
1064 self->markDirty(QQuick3DModel::MorphTargetsDirty);
1065
1066 if (morphTarget->parentItem() == nullptr) {
1067 // If the morphTarget has no parent, check if it has a hierarchical parent that's a QQuick3DObject
1068 // and re-parent it to that, e.g., inline morphTargets
1069 QQuick3DObject *parentItem = qobject_cast<QQuick3DObject *>(morphTarget->parent());
1070 if (parentItem) {
1071 morphTarget->setParentItem(parentItem);
1072 } else { // If no valid parent was found, make sure the morphTarget refs our scene manager
1073 const auto &scenManager = QQuick3DObjectPrivate::get(self)->sceneManager;
1074 if (scenManager)
1075 QQuick3DObjectPrivate::get(morphTarget)->refSceneManager(*scenManager);
1076 // else: If there's no scene manager, defer until one is set, see itemChange()
1077 }
1078 }
1079
1080 // Make sure morphTargets are removed when destroyed
1081 connect(morphTarget, &QQuick3DMorphTarget::destroyed, self, &QQuick3DModel::onMorphTargetDestroyed);
1082}
1083
1084QQuick3DMorphTarget *QQuick3DModel::qmlMorphTargetAt(QQmlListProperty<QQuick3DMorphTarget> *list, qsizetype index)
1085{
1086 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1087 if (index >= self->m_morphTargets.size()) {
1088 qWarning("The index exceeds the range of valid morph targets.");
1089 return nullptr;
1090 }
1091 return self->m_morphTargets.at(index);
1092}
1093
1094qsizetype QQuick3DModel::qmlMorphTargetsCount(QQmlListProperty<QQuick3DMorphTarget> *list)
1095{
1096 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1097 return self->m_morphTargets.size();
1098}
1099
1100void QQuick3DModel::qmlClearMorphTargets(QQmlListProperty<QQuick3DMorphTarget> *list)
1101{
1102 QQuick3DModel *self = static_cast<QQuick3DModel *>(list->object);
1103 for (const auto &morph : std::as_const(self->m_morphTargets)) {
1104 if (morph->parentItem() == nullptr)
1105 QQuick3DObjectPrivate::get(morph)->derefSceneManager();
1106 morph->disconnect(self, SLOT(onMorphTargetDestroyed(QObject*)));
1107 }
1108 self->m_morphTargets.clear();
1109 self->m_numMorphAttribs = 0;
1110 self->markDirty(QQuick3DModel::MorphTargetsDirty);
1111}
1112
1113void QQuick3DModel::setInstancingLodMin(float minDistance)
1114{
1115 if (qFuzzyCompare(m_instancingLodMin, minDistance))
1116 return;
1117 m_instancingLodMin = minDistance;
1118 emit instancingLodMinChanged();
1119 markDirty(LodDirty);
1120}
1121
1122void QQuick3DModel::setInstancingLodMax(float maxDistance)
1123{
1124 if (qFuzzyCompare(m_instancingLodMax, maxDistance))
1125 return;
1126 m_instancingLodMax = maxDistance;
1127 emit instancingLodMaxChanged();
1128 markDirty(LodDirty);
1129}
1130
1156{
1157 return m_levelOfDetailBias;
1158}
1159
1160void QQuick3DModel::setLevelOfDetailBias(float newLevelOfDetailBias)
1161{
1162 if (qFuzzyCompare(m_levelOfDetailBias, newLevelOfDetailBias))
1163 return;
1164 m_levelOfDetailBias = newLevelOfDetailBias;
1165 emit levelOfDetailBiasChanged();
1166 markDirty(QQuick3DModel::PropertyDirty);
1167}
1168
QList< T > toVector() const noexcept
Definition qlist.h:726
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
bool isEnabled() const
\qmltype BakedLightmap \inherits Object \inqmlmodule QtQuick3D
QSSGBounds3 bounds
\qmltype Geometry \inherits Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DGeometry
int const QVector3D const QVector3D & boundsMax
Returns the maximum coordinate of the bounding volume.
int const QVector3D & boundsMin
Returns the minimum coordinate of the bounding volume.
void geometryNodeDirty()
\inmodule QtQuick3D \inherits QQuick3DObject
void receivesShadowsChanged()
bool castsReflections
\qmlproperty bool Model::castsReflections
QQuick3DBakedLightmap * bakedLightmap
\qmlproperty BakedLightmap Model::bakedLightmap
QQuick3DGeometry * geometry
static QString translateMeshSource(const QUrl &source, QObject *contextObject)
QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node) override
QQmlListProperty< QQuick3DMaterial > materials
\qmlproperty List<QtQuick3D::Material> Model::materials
void instanceRootChanged()
QQuick3DSkeleton * skeleton
void pickableChanged()
float instancingLodMin
\qmlproperty real Model::instancingLodMin
void setPickable(bool pickable)
void setCastsShadows(bool castsShadows)
void setInverseBindPoses(const QList< QMatrix4x4 > &poses)
void setInstanceRoot(QQuick3DNode *instanceRoot)
QQuick3DBounds3 bounds
void instancingChanged()
void castsShadowsChanged()
float levelOfDetailBias
\qmlproperty real Model::levelOfDetailBias
void boundsChanged()
QQmlListProperty< QQuick3DMorphTarget > morphTargets
\qmlproperty List<QtQuick3D::MorphTarget> Model::morphTargets
void itemChange(ItemChange, const ItemChangeData &) override
void setInstancing(QQuick3DInstancing *instancing)
QQuick3DModel(QQuick3DNode *parent=nullptr)
\qmltype Model \inherits Node \inqmlmodule QtQuick3D
float instancingLodMax
\qmlproperty real Model::instancingLodMax
void inverseBindPosesChanged()
void geometryChanged()
QQuick3DSkin * skin
\qmlproperty Skin Model::skin
void setGeometry(QQuick3DGeometry *geometry)
void sourceChanged()
void setDepthBias(float bias)
QList< QMatrix4x4 > inverseBindPoses
void setReceivesShadows(bool receivesShadows)
void skeletonChanged()
void setBounds(const QVector3D &min, const QVector3D &max)
QQuick3DNode * instanceRoot
void setSkeleton(QQuick3DSkeleton *skeleton)
void depthBiasChanged()
QQuick3DInstancing * instancing
void setSource(const QUrl &source)
int lightmapBaseResolution
\qmlproperty int Model::lightmapBaseResolution
void markAllDirty() override
bool receivesReflections
\qmlproperty bool Model::receivesReflections
~QQuick3DModel() override
void markAllDirty() override
QSSGRenderGraphObject * updateSpatialNode(QSSGRenderGraphObject *node) override
void refSceneManager(QQuick3DSceneManager &)
static QQuick3DObjectPrivate * get(QQuick3DObject *item)
static void attachWatcher(Context *context, Setter setter, Object3D *newO, Object3D *oldO)
Attach a object-destroyed-watcher to an object that's not owned.
\qmltype Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DObject \inherits QtObject
QQuick3DObject * parent
\qmlproperty Object3D QtQuick3D::Object3D::parent This property holds the parent of the Object3D in a...
void setParentItem(QQuick3DObject *parentItem)
QList< QQuick3DObject * > dirtyBoundingBoxList
Class representing 3D range or axis aligned bounding box.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
void push_back(QChar c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.h:957
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
QString last(qsizetype n) const &
Definition qstring.h:392
\inmodule QtCore
Definition qurl.h:94
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
Combined button and popup list for selecting options.
QString self
Definition language.cpp:58
static void * context
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qWarning
Definition qlogging.h:166
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static bool isNumber(char s)
#define SLOT(a)
Definition qobjectdefs.h:52
GLuint index
[2]
GLuint srcPath
GLenum type
GLboolean enable
GLsizei GLsizei GLchar * source
GLfloat bias
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
static QUrl resolvedUrl(const QUrl &url, const QQmlRefPointer< QQmlContextData > &context)
#define QStringLiteral(str)
#define emit
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
QList< int > list
[14]
myObject disconnect()
[26]
QSSGRenderPath meshPath
static float signedSquared(float val)
void markDirty(DirtyFlag dirtyFlag)
QSSGRenderNode * instanceRoot
Definition moc.h:23