mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-17 08:37:17 +02:00
[ui] 3D Viewer: Alembic support
* optional and only available if AlembicEntity QML plugin is in QML import path * 3D viewer can load a 3D model and an Alembic file at the same time * add contextual menu options to unload model/Alembic
This commit is contained in:
parent
de49c00e26
commit
cc4a24f5f3
4 changed files with 84 additions and 8 deletions
9
meshroom/ui/qml/Viewer/AlembicLoader.qml
Normal file
9
meshroom/ui/qml/Viewer/AlembicLoader.qml
Normal file
|
@ -0,0 +1,9 @@
|
|||
import AlembicEntity 1.0
|
||||
|
||||
/**
|
||||
* Support for Alembic files in Qt3d.
|
||||
* Create this component dynamically to test for AlembicEntity plugin availability.
|
||||
*/
|
||||
AlembicEntity {
|
||||
id: root
|
||||
}
|
|
@ -10,10 +10,15 @@ import Qt3D.Input 2.1 as Qt3DInput // to avoid clash with Controls2 Action
|
|||
FocusScope {
|
||||
id: root
|
||||
|
||||
property alias status: scene.status
|
||||
property alias source: modelLoader.source
|
||||
property alias abcSource: modelLoader.abcSource
|
||||
|
||||
readonly property alias loading: modelLoader.loading
|
||||
|
||||
// Alembic optional support => won't be available if AlembicEntity plugin is not available
|
||||
readonly property Component abcLoaderComp: Qt.createComponent("AlembicLoader.qml")
|
||||
readonly property bool supportAlembic: abcLoaderComp.status == Component.Ready
|
||||
|
||||
// functions
|
||||
function resetCameraCenter() {
|
||||
mainCamera.viewCenter = Qt.vector3d(0.0, 0.0, 0.0);
|
||||
|
@ -104,7 +109,20 @@ FocusScope {
|
|||
|
||||
function clear()
|
||||
{
|
||||
source = 'no_file'
|
||||
clearScene()
|
||||
clearAbc()
|
||||
}
|
||||
|
||||
function clearScene()
|
||||
{
|
||||
source = 'no_file' // only way to force unloading of valid scene
|
||||
source = ''
|
||||
}
|
||||
|
||||
function clearAbc()
|
||||
{
|
||||
abcSource = '' // does not work yet by itself
|
||||
abcLoaderEntity.reload() // need to re-create the alembic loader
|
||||
}
|
||||
|
||||
Scene3D {
|
||||
|
@ -202,9 +220,11 @@ FocusScope {
|
|||
Entity {
|
||||
id: modelLoader
|
||||
property string source
|
||||
property url abcSource
|
||||
// SceneLoader status is not reliable when loading a 3D file
|
||||
property bool loading
|
||||
property bool loading: false
|
||||
onSourceChanged: loading = true
|
||||
onAbcSourceChanged: { if(root.supportAlembic) loading = true }
|
||||
|
||||
components: [scene, transform, picker]
|
||||
|
||||
|
@ -234,17 +254,45 @@ FocusScope {
|
|||
modelLoader.loading = false;
|
||||
if(scene.status == SceneLoader.Ready)
|
||||
{
|
||||
setupMaterialSwitchers(parent)
|
||||
setupMaterialSwitchers(modelLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entity {
|
||||
id: abcLoaderEntity
|
||||
// Instantiate the AlembicEntity dynamically
|
||||
// to avoid import errors if the plugin is not available
|
||||
property Entity abcLoader: undefined
|
||||
|
||||
Component.onCompleted: reload()
|
||||
function reload() {
|
||||
if(!root.supportAlembic) // Alembic plugin not available
|
||||
return
|
||||
|
||||
// destroy previously created entity
|
||||
if(abcLoader != undefined)
|
||||
abcLoader.destroy()
|
||||
|
||||
abcLoader = abcLoaderComp.createObject(abcLoaderEntity, {
|
||||
'url': Qt.binding(function() { return modelLoader.abcSource } ),
|
||||
'particleSize': Qt.binding(function() { return 0.01 * transform.scale })
|
||||
});
|
||||
// urlChanged signal is emitted once the Alembic file is loaded
|
||||
// set the 'loading' property to false when it's emitted
|
||||
// TODO: AlembicEntity should expose a status
|
||||
abcLoader.onUrlChanged.connect(function(){ modelLoader.loading = false })
|
||||
modelLoader.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
Locator3D { enabled: locatorCheckBox.checked }
|
||||
}
|
||||
Grid3D { enabled: gridCheckBox.checked }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Pane {
|
||||
background: Rectangle { color: palette.base; opacity: 0.5; radius: 2 }
|
||||
Column {
|
||||
|
@ -296,5 +344,18 @@ FocusScope {
|
|||
text: "Reset View"
|
||||
onTriggered: resetCameraPosition()
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuItem {
|
||||
height: visible ? implicitHeight : 0
|
||||
text: "Unload Model"
|
||||
visible: root.source != ''
|
||||
onTriggered: clearScene()
|
||||
}
|
||||
MenuItem {
|
||||
height: visible ? implicitHeight : 0
|
||||
text: "Unload Alembic"
|
||||
visible: abcSource != ''
|
||||
onTriggered: clearAbc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,10 @@ Item {
|
|||
// Load a 3D media file in the 3D viewer
|
||||
function load3DMedia(filepath)
|
||||
{
|
||||
viewer3D.source = filepath
|
||||
if(Filepath.extension(filepath) === ".abc")
|
||||
viewer3D.abcSource = filepath
|
||||
else
|
||||
viewer3D.source = filepath
|
||||
}
|
||||
|
||||
SystemPalette { id: palette }
|
||||
|
@ -73,7 +76,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
onDropped: viewer3D.source = drop.urls[0]
|
||||
onDropped: load3DMedia(drop.urls[0])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ ApplicationWindow {
|
|||
font.pointSize: 9
|
||||
|
||||
property variant node: null
|
||||
// supported 3D files extensions
|
||||
readonly property var _3dFileExtensions: ['.obj', '.abc']
|
||||
|
||||
onClosing: {
|
||||
// make sure document is saved before exiting application
|
||||
close.accepted = false
|
||||
|
@ -356,7 +359,7 @@ ApplicationWindow {
|
|||
var attr = node.attributes.at(i)
|
||||
if(attr.isOutput
|
||||
&& attr.desc.type === "File"
|
||||
&& Filepath.extension(attr.value) === ".obj")
|
||||
&& _3dFileExtensions.indexOf(Filepath.extension(attr.value)) > - 1 )
|
||||
{
|
||||
workspaceView.load3DMedia(attr.value)
|
||||
break // only load first model found
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue