import QtQuick 2.15 import Qt3D.Core 2.15 import Qt3D.Render 2.15 import Qt3D.Extras 2.15 import QtQuick.Scene3D 2.15 import "Materials" import Utils 1.0 /** * MediaLoader provides a single entry point for 3D media loading. * It encapsulates all available plugins/loaders. */ Entity { id: root property url source property bool loading: false property int status: SceneLoader.None property var object: null property int renderMode /// Scene's current camera property Camera camera: null property bool cached: false onSourceChanged: { if (cached) { root.status = SceneLoader.Ready return } // clear previously created object if any if (object) { object.destroy() object = null } var component = undefined status = SceneLoader.Loading if (!Filepath.exists(source)) { status = SceneLoader.None return } switch (Filepath.extension(source)) { case ".abc": case ".json": case ".sfm": if (Viewer3DSettings.supportSfmData) component = sfmDataLoaderEntityComponent break case ".exr": if (Viewer3DSettings.supportDepthMap) component = exrLoaderComponent break case ".obj": case ".stl": default: component = sceneLoaderEntityComponent break } // Media loader available if (component) { object = component.createObject(root, {"source": source}) } } Component { id: sceneLoaderEntityComponent MediaLoaderEntity { id: sceneLoaderEntity objectName: "SceneLoader" components: [ SceneLoader { source: parent.source onStatusChanged: { if (status == SceneLoader.Ready) { textureCount = sceneLoaderPostProcess(sceneLoaderEntity) faceCount = Scene3DHelper.faceCount(sceneLoaderEntity) } root.status = status; } } ] } } Component { id: sfmDataLoaderEntityComponent MediaLoaderEntity { id: sfmDataLoaderEntity Component.onCompleted: { var obj = Viewer3DSettings.sfmDataLoaderComp.createObject(sfmDataLoaderEntity, { 'source': source, 'pointSize': Qt.binding(function() { return 0.01 * Viewer3DSettings.pointSize }), 'locatorScale': Qt.binding(function() { return Viewer3DSettings.cameraScale }), 'cameraPickingEnabled': Qt.binding(function() { return root.enabled }), 'resectionId': Qt.binding(function() { return Viewer3DSettings.resectionId }), 'displayResections': Qt.binding(function() { return Viewer3DSettings.displayResectionIds }) }); obj.statusChanged.connect(function() { if (obj.status === SceneLoader.Ready) { for (var i = 0; i < obj.pointClouds.length; ++i) { vertexCount += Scene3DHelper.vertexCount(obj.pointClouds[i]); } cameraCount = obj.spawnCameraSelectors(); } Viewer3DSettings.resectionIdCount = obj.countResectionIds(); Viewer3DSettings.resectionGroups = obj.countResectionGroups(Viewer3DSettings.resectionIdCount + 1); resectionIdCount = Viewer3DSettings.resectionIdCount resectionGroups = Viewer3DSettings.resectionGroups resectionId = Viewer3DSettings.resectionIdCount root.status = obj.status; }) obj.cameraSelected.connect( function(viewId) { obj.selectedViewId = viewId } ) } } } Component { id: exrLoaderComponent MediaLoaderEntity { id: exrLoaderEntity Component.onCompleted: { var fSize = Filepath.fileSizeMB(source) if (fSize > 500) { // Do not load images that are larger than 500MB console.warn("Viewer3D: Do not load the EXR in 3D as the file size is too large: " + fSize + "MB") root.status = SceneLoader.Error return } // EXR loading strategy: // - [1] as a depth map var obj = Viewer3DSettings.depthMapLoaderComp.createObject( exrLoaderEntity, { 'source': source }) if (obj.status === SceneLoader.Ready) { faceCount = Scene3DHelper.faceCount(obj) root.status = SceneLoader.Ready return } // - [2] as an environment map obj.destroy() root.status = SceneLoader.Loading obj = Qt.createComponent("EnvironmentMapEntity.qml").createObject( exrLoaderEntity, { 'source': source, 'position': Qt.binding(function() { return root.camera.position }) }) obj.statusChanged.connect(function() { root.status = obj.status; }) } } } Component { id: materialSwitcherComponent MaterialSwitcher { } } // Remove automatically created DiffuseMapMaterial and // instantiate a MaterialSwitcher instead. Returns the faceCount function sceneLoaderPostProcess(rootEntity) { var materials = Scene3DHelper.findChildrenByProperty(rootEntity, "diffuse") var entities = [] var texCount = 0 materials.forEach(function(mat) { entities.push(mat.parent) }) entities.forEach(function(entity) { var mats = [] var componentsToRemove = [] // Create as many MaterialSwitcher as individual materials for this entity // NOTE: we let each MaterialSwitcher modify the components of the entity // and therefore remove the default material spawned by the sceneLoader for (var i = 0; i < entity.components.length; ++i) { var comp = entity.components[i] // handle DiffuseMapMaterials created by SceneLoader if (comp.toString().indexOf("QDiffuseMapMaterial") > -1) { // store material definition var m = { "diffuseMap": comp.diffuse.data[0].source, "shininess": comp.shininess, "specular": comp.specular, "ambient": comp.ambient, "mode": root.renderMode } texCount++ mats.push(m) componentsToRemove.push(comp) } if (comp.toString().indexOf("QPhongMaterial") > -1) { // create MaterialSwitcher with default colors mats.push({}) componentsToRemove.push(comp) } } mats.forEach(function(m) { // create a material switcher for each material definition var matSwitcher = materialSwitcherComponent.createObject(entity, m) matSwitcher.mode = Qt.binding(function(){ return root.renderMode }) }) // remove replaced components componentsToRemove.forEach(function(comp) { Scene3DHelper.removeComponent(entity, comp) }) }) return texCount } }