mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-07-18 00:57:16 +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 {
|
FocusScope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias status: scene.status
|
|
||||||
property alias source: modelLoader.source
|
property alias source: modelLoader.source
|
||||||
|
property alias abcSource: modelLoader.abcSource
|
||||||
|
|
||||||
readonly property alias loading: modelLoader.loading
|
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
|
// functions
|
||||||
function resetCameraCenter() {
|
function resetCameraCenter() {
|
||||||
mainCamera.viewCenter = Qt.vector3d(0.0, 0.0, 0.0);
|
mainCamera.viewCenter = Qt.vector3d(0.0, 0.0, 0.0);
|
||||||
|
@ -104,7 +109,20 @@ FocusScope {
|
||||||
|
|
||||||
function clear()
|
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 {
|
Scene3D {
|
||||||
|
@ -202,9 +220,11 @@ FocusScope {
|
||||||
Entity {
|
Entity {
|
||||||
id: modelLoader
|
id: modelLoader
|
||||||
property string source
|
property string source
|
||||||
|
property url abcSource
|
||||||
// SceneLoader status is not reliable when loading a 3D file
|
// SceneLoader status is not reliable when loading a 3D file
|
||||||
property bool loading
|
property bool loading: false
|
||||||
onSourceChanged: loading = true
|
onSourceChanged: loading = true
|
||||||
|
onAbcSourceChanged: { if(root.supportAlembic) loading = true }
|
||||||
|
|
||||||
components: [scene, transform, picker]
|
components: [scene, transform, picker]
|
||||||
|
|
||||||
|
@ -234,17 +254,45 @@ FocusScope {
|
||||||
modelLoader.loading = false;
|
modelLoader.loading = false;
|
||||||
if(scene.status == SceneLoader.Ready)
|
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 }
|
Locator3D { enabled: locatorCheckBox.checked }
|
||||||
}
|
}
|
||||||
Grid3D { enabled: gridCheckBox.checked }
|
Grid3D { enabled: gridCheckBox.checked }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Pane {
|
Pane {
|
||||||
background: Rectangle { color: palette.base; opacity: 0.5; radius: 2 }
|
background: Rectangle { color: palette.base; opacity: 0.5; radius: 2 }
|
||||||
Column {
|
Column {
|
||||||
|
@ -296,5 +344,18 @@ FocusScope {
|
||||||
text: "Reset View"
|
text: "Reset View"
|
||||||
onTriggered: resetCameraPosition()
|
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
|
// Load a 3D media file in the 3D viewer
|
||||||
function load3DMedia(filepath)
|
function load3DMedia(filepath)
|
||||||
{
|
{
|
||||||
viewer3D.source = filepath
|
if(Filepath.extension(filepath) === ".abc")
|
||||||
|
viewer3D.abcSource = filepath
|
||||||
|
else
|
||||||
|
viewer3D.source = filepath
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemPalette { id: palette }
|
SystemPalette { id: palette }
|
||||||
|
@ -73,7 +76,7 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
DropArea {
|
DropArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onDropped: viewer3D.source = drop.urls[0]
|
onDropped: load3DMedia(drop.urls[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ ApplicationWindow {
|
||||||
font.pointSize: 9
|
font.pointSize: 9
|
||||||
|
|
||||||
property variant node: null
|
property variant node: null
|
||||||
|
// supported 3D files extensions
|
||||||
|
readonly property var _3dFileExtensions: ['.obj', '.abc']
|
||||||
|
|
||||||
onClosing: {
|
onClosing: {
|
||||||
// make sure document is saved before exiting application
|
// make sure document is saved before exiting application
|
||||||
close.accepted = false
|
close.accepted = false
|
||||||
|
@ -356,7 +359,7 @@ ApplicationWindow {
|
||||||
var attr = node.attributes.at(i)
|
var attr = node.attributes.at(i)
|
||||||
if(attr.isOutput
|
if(attr.isOutput
|
||||||
&& attr.desc.type === "File"
|
&& attr.desc.type === "File"
|
||||||
&& Filepath.extension(attr.value) === ".obj")
|
&& _3dFileExtensions.indexOf(Filepath.extension(attr.value)) > - 1 )
|
||||||
{
|
{
|
||||||
workspaceView.load3DMedia(attr.value)
|
workspaceView.load3DMedia(attr.value)
|
||||||
break // only load first model found
|
break // only load first model found
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue