[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:
Yann Lanthony 2018-01-09 18:54:57 +01:00
parent de49c00e26
commit cc4a24f5f3
4 changed files with 84 additions and 8 deletions

View 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
}

View file

@ -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()
}
}
}

View file

@ -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])
}
}

View file

@ -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