mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-06-01 18:31:58 +02:00
345 lines
12 KiB
QML
345 lines
12 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import QtQuick.Scene3D 2.6
|
|
import Qt3D.Core 2.6
|
|
import Qt3D.Render 2.6
|
|
import Qt3D.Extras 2.15
|
|
import Qt3D.Input 2.6 as Qt3DInput // to avoid clash with Controls2 Action
|
|
|
|
import Controls 1.0
|
|
import MaterialIcons 2.2
|
|
import Utils 1.0
|
|
|
|
|
|
FocusScope {
|
|
id: root
|
|
|
|
property int renderMode: 2
|
|
readonly property alias library: mediaLibrary
|
|
readonly property alias mainCamera: mainCamera
|
|
|
|
readonly property vector3d defaultCamPosition: Qt.vector3d(12.0, 10.0, -12.0)
|
|
readonly property vector3d defaultCamUpVector: Qt.vector3d(-0.358979, 0.861550, 0.358979) // should be accurate, consistent with camera view center
|
|
readonly property vector3d defaultCamViewCenter: Qt.vector3d(0.0, 0.0, 0.0)
|
|
|
|
readonly property var viewpoint: _reconstruction ? _reconstruction.selectedViewpoint : null
|
|
readonly property bool doSyncViewpointCamera: Viewer3DSettings.syncViewpointCamera && (viewpoint && viewpoint.isReconstructed)
|
|
|
|
// Functions
|
|
function resetCameraPosition() {
|
|
mainCamera.position = defaultCamPosition
|
|
mainCamera.upVector = defaultCamUpVector
|
|
mainCamera.viewCenter = defaultCamViewCenter
|
|
}
|
|
|
|
function load(filepath, label = undefined) {
|
|
mediaLibrary.load(filepath, label)
|
|
}
|
|
|
|
/// View 'attribute' in the 3D Viewer. Media will be loaded if needed.
|
|
/// Returns whether the attribute can be visualized (matching type and extension).
|
|
function view(attribute) {
|
|
if (attribute.desc.type === "File"
|
|
&& Viewer3DSettings.supportedExtensions.indexOf(Filepath.extension(attribute.value)) > - 1) {
|
|
mediaLibrary.view(attribute)
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
/// Solo (i.e display only) the given attribute.
|
|
function solo(attribute) {
|
|
mediaLibrary.solo(mediaLibrary.find(attribute))
|
|
}
|
|
|
|
function clear() {
|
|
mediaLibrary.clear()
|
|
}
|
|
|
|
SystemPalette { id: activePalette }
|
|
|
|
Scene3D {
|
|
id: scene3D
|
|
anchors.fill: parent
|
|
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio // vs. UserAspectRatio
|
|
hoverEnabled: true // If true, will trigger positionChanged events in attached MouseHandler
|
|
aspects: ["logic", "input"]
|
|
focus: true
|
|
|
|
// We cannot use directly an ExifOrientedViewer since this component is not a Loader
|
|
// so we redefine the transform using the ExifOrientation utility functions
|
|
property var orientationTag: (doSyncViewpointCamera && root.viewpoint) ? root.viewpoint.orientation.toString() : "1"
|
|
transform: [
|
|
Rotation {
|
|
angle: ExifOrientation.rotation(scene3D.orientationTag)
|
|
origin.x: scene3D.width * 0.5
|
|
origin.y: scene3D.height * 0.5
|
|
},
|
|
Scale {
|
|
xScale: ExifOrientation.xscale(scene3D.orientationTag)
|
|
origin.x: scene3D.width * 0.5
|
|
origin.y: scene3D.height * 0.5
|
|
}
|
|
]
|
|
|
|
Keys.onPressed: function(event) {
|
|
if (event.key === Qt.Key_F) {
|
|
resetCameraPosition()
|
|
} else if (Qt.Key_1 <= event.key && event.key < Qt.Key_1 + Viewer3DSettings.renderModes.length) {
|
|
Viewer3DSettings.renderMode = event.key - Qt.Key_1
|
|
} else {
|
|
event.accepted = false
|
|
}
|
|
}
|
|
|
|
Entity {
|
|
id: rootEntity
|
|
|
|
Camera {
|
|
id: mainCamera
|
|
projectionType: CameraLens.PerspectiveProjection
|
|
enabled: cameraSelector.camera == mainCamera
|
|
fieldOfView: 45
|
|
nearPlane : 0.01
|
|
farPlane : 10000.0
|
|
position: defaultCamPosition
|
|
upVector: defaultCamUpVector
|
|
viewCenter: defaultCamViewCenter
|
|
aspectRatio: width/height
|
|
}
|
|
|
|
ViewpointCamera {
|
|
id: viewpointCamera
|
|
enabled: cameraSelector.camera === camera
|
|
viewpoint: root.viewpoint
|
|
camera.aspectRatio: width/height
|
|
}
|
|
|
|
Entity {
|
|
components: [
|
|
DirectionalLight{
|
|
color: "white"
|
|
worldDirection: Transformations3DHelper.getRotatedCameraViewVector(cameraSelector.camera.viewVector, cameraSelector.camera.upVector, directionalLightPane.lightPitchValue, directionalLightPane.lightYawValue).normalized()
|
|
}
|
|
]
|
|
}
|
|
|
|
TrackballGizmo {
|
|
beamRadius: 4.0/root.height
|
|
alpha: cameraController.moving ? 1.0 : 0.7
|
|
enabled: Viewer3DSettings.displayGizmo && cameraSelector.camera == mainCamera
|
|
xColor: Colors.red
|
|
yColor: Colors.green
|
|
zColor: Colors.blue
|
|
centerColor: Colors.sysPalette.highlight
|
|
transform: Transform {
|
|
translation: mainCamera.viewCenter
|
|
scale: 0.15 * mainCamera.viewCenter.minus(mainCamera.position).length()
|
|
}
|
|
}
|
|
|
|
DefaultCameraController {
|
|
id: cameraController
|
|
enabled: cameraSelector.camera == mainCamera
|
|
|
|
windowSize {
|
|
width: root.width
|
|
height: root.height
|
|
}
|
|
rotationSpeed: 16
|
|
trackballSize: 0.9
|
|
|
|
camera: mainCamera
|
|
focus: scene3D.activeFocus
|
|
onMousePressed: function(mouse) {
|
|
scene3D.forceActiveFocus()
|
|
}
|
|
onMouseReleased: function(mouse, moved) {
|
|
if (moving)
|
|
return
|
|
if (!moved && mouse.button === Qt.RightButton) {
|
|
contextMenu.popup()
|
|
}
|
|
}
|
|
}
|
|
|
|
components: [
|
|
RenderSettings {
|
|
pickingSettings.pickMethod: PickingSettings.PrimitivePicking // Enables point/edge/triangle picking
|
|
pickingSettings.pickResultMode: PickingSettings.NearestPick
|
|
renderPolicy: RenderSettings.Always
|
|
|
|
activeFrameGraph: RenderSurfaceSelector {
|
|
// Use the whole viewport
|
|
Viewport {
|
|
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
|
|
CameraSelector {
|
|
id: cameraSelector
|
|
camera: doSyncViewpointCamera ? viewpointCamera.camera : mainCamera
|
|
FrustumCulling {
|
|
ClearBuffers {
|
|
clearColor: "transparent"
|
|
buffers : ClearBuffers.ColorDepthBuffer
|
|
RenderStateSet {
|
|
renderStates: [
|
|
DepthTest { depthFunction: DepthTest.Less }
|
|
]
|
|
}
|
|
}
|
|
LayerFilter {
|
|
filterMode: LayerFilter.DiscardAnyMatchingLayers
|
|
layers: Layer {id: drawOnFront}
|
|
}
|
|
LayerFilter {
|
|
filterMode: LayerFilter.AcceptAnyMatchingLayers
|
|
layers: [drawOnFront]
|
|
RenderStateSet {
|
|
renderStates: DepthTest { depthFunction: DepthTest.GreaterOrEqual }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
Qt3DInput.InputSettings { }
|
|
]
|
|
|
|
MediaLibrary {
|
|
id: mediaLibrary
|
|
renderMode: Viewer3DSettings.renderMode
|
|
// Picking to set focus point (camera view center)
|
|
// Only activate it when the 'Control' key is pressed
|
|
pickingEnabled: cameraController.pickingActive
|
|
camera: cameraSelector.camera
|
|
|
|
// Used for TransformGizmo in BoundingBox
|
|
sceneCameraController: cameraController
|
|
frontLayerComponent: drawOnFront
|
|
window: root
|
|
|
|
components: [
|
|
Transform {
|
|
id: transform
|
|
}
|
|
]
|
|
|
|
onClicked: function(pick) {
|
|
if (pick.button === Qt.LeftButton) {
|
|
mainCamera.viewCenter = pick.worldIntersection
|
|
}
|
|
}
|
|
|
|
}
|
|
Locator3D { enabled: Viewer3DSettings.displayOrigin }
|
|
Grid3D { enabled: Viewer3DSettings.displayGrid }
|
|
}
|
|
}
|
|
|
|
// Image overlay when navigating reconstructed cameras
|
|
Loader {
|
|
id: imageOverlayLoader
|
|
anchors.fill: parent
|
|
|
|
active: doSyncViewpointCamera
|
|
visible: Viewer3DSettings.showViewpointImageOverlay
|
|
|
|
sourceComponent: ImageOverlay {
|
|
id: imageOverlay
|
|
source: root.viewpoint.undistortedImageSource
|
|
imageRatio: root.viewpoint.orientedImageSize.width * root.viewpoint.pixelAspectRatio / root.viewpoint.orientedImageSize.height
|
|
uvCenterOffset: root.viewpoint.uvCenterOffset
|
|
showFrame: Viewer3DSettings.showViewpointImageFrame
|
|
imageOpacity: Viewer3DSettings.viewpointImageOverlayOpacity
|
|
}
|
|
}
|
|
|
|
// Media loading overlay
|
|
// (Scene3D is frozen while a media is being loaded)
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
visible: mediaLibrary.loading
|
|
color: Qt.darker(Colors.sysPalette.mid, 1.2)
|
|
opacity: 0.6
|
|
BusyIndicator {
|
|
anchors.centerIn: parent
|
|
running: parent.visible
|
|
}
|
|
}
|
|
|
|
FloatingPane {
|
|
visible: Viewer3DSettings.renderMode == 3
|
|
anchors.bottom: renderModesPanel.top
|
|
GridLayout {
|
|
columns: 2
|
|
rowSpacing: 0
|
|
|
|
RadioButton {
|
|
text: "SHL File"
|
|
autoExclusive: true
|
|
checked: true
|
|
}
|
|
TextField {
|
|
text: Viewer3DSettings.shlFile
|
|
selectByMouse: true
|
|
Layout.minimumWidth: 300
|
|
onEditingFinished: Viewer3DSettings.shlFile = text
|
|
}
|
|
|
|
RadioButton {
|
|
Layout.columnSpan: 2
|
|
autoExclusive: true
|
|
text: "Normals"
|
|
onCheckedChanged: Viewer3DSettings.displayNormals = checked
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Rendering modes
|
|
FloatingPane {
|
|
id: renderModesPanel
|
|
anchors.bottom: parent.bottom
|
|
padding: 4
|
|
Row {
|
|
Repeater {
|
|
model: Viewer3DSettings.renderModes
|
|
|
|
delegate: MaterialToolButton {
|
|
text: modelData["icon"]
|
|
ToolTip.text: modelData["name"] + " (" + (index+1) + ")"
|
|
font.pointSize: 11
|
|
onClicked: Viewer3DSettings.renderMode = index
|
|
checked: Viewer3DSettings.renderMode === index
|
|
checkable: !checked // Hack to disabled check on toggle
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Directional light controller
|
|
DirectionalLightPane {
|
|
id: directionalLightPane
|
|
anchors {
|
|
bottom: parent.bottom
|
|
right: parent.right
|
|
margins: 2
|
|
}
|
|
visible: Viewer3DSettings.displayLightController
|
|
}
|
|
|
|
// Menu
|
|
Menu {
|
|
id: contextMenu
|
|
|
|
MenuItem {
|
|
text: "Fit All"
|
|
onTriggered: mainCamera.viewAll()
|
|
}
|
|
MenuItem {
|
|
text: "Reset View"
|
|
onTriggered: resetCameraPosition()
|
|
}
|
|
}
|
|
}
|