Skip to content

Commit ecff89a

Browse files
authored
Merge pull request #2781 from alicevision/dev/phongImageViewerFixes
[qml] PhongImageViewer: Various improvements
2 parents 8ffc283 + c9f0285 commit ecff89a

File tree

2 files changed

+136
-76
lines changed

2 files changed

+136
-76
lines changed

meshroom/ui/qml/Controls/DirectionalLightPane.qml

Lines changed: 58 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import QtQuick
22
import QtQuick.Controls
33
import QtQuick.Layouts
4-
import Qt5Compat.GraphicalEffects // for OpacityMask & Glow
4+
import QtQuick.Shapes
55
import MaterialIcons 2.2
66
import Utils 1.0
77

@@ -13,7 +13,6 @@ import Utils 1.0
1313
* @param lightYawValue - directional light yaw (degrees)
1414
* @param lightPitchValue - directional light pitch (degrees)
1515
*/
16-
1716
FloatingPane {
1817
id: root
1918

@@ -36,9 +35,9 @@ FloatingPane {
3635
// update 2d controller if pitch value changed
3736
onLightPitchValueChanged: { lightBallController.update() }
3837

38+
// pane properties
3939
anchors.margins: 0
4040
padding: 5
41-
radius: 0
4241

4342
ColumnLayout {
4443
anchors.fill: parent
@@ -152,65 +151,72 @@ FloatingPane {
152151
y = controllerRadius * Math.sin(angleRad)
153152
}
154153

155-
// compute distance function for light gradient emulation
156-
var distanceRatio = Math.min(distance, controllerRadius) / controllerRadius
157-
var distanceFunct = distanceRatio * distanceRatio * 0.3
158-
159154
// update light point
160155
lightPoint.x = lightPoint.startOffset + x
161156
lightPoint.y = lightPoint.startOffset + y
162-
163-
// update light gradient
164-
lightGradient.angle = angleRad * (180 / Math.PI) // radians to degrees
165-
lightGradient.horizontalRadius = controllerSize * (1 - distanceFunct)
166-
lightGradient.verticalRadius = controllerSize * (1 + distanceFunct)
167-
lightGradient.horizontalOffset = x * (1 - distanceFunct)
168-
lightGradient.verticalOffset = y * (1 - distanceFunct)
169157
}
170158

171-
RadialGradient {
172-
id: lightGradient
159+
160+
// light ball controller shapes
161+
Shape {
173162
anchors.centerIn: parent
174-
width: controllerSize
175-
height: width
176-
horizontalRadius: controllerSize
177-
verticalRadius: controllerSize
178-
angle: 0
179-
gradient: Gradient {
180-
GradientStop { position: 0.00; color: "#FFFFFFFF" }
181-
GradientStop { position: 0.10; color: "#FFAAAAAA" }
182-
GradientStop { position: 0.50; color: "#FF0C0C0C" }
183-
}
184-
layer.enabled: true
185-
layer.effect: OpacityMask {
186-
id: mask
187-
maskSource: Rectangle {
188-
height: lightGradient.height
189-
width: lightGradient.width
190-
radius: 180 // circle
163+
width: parent.width
164+
height: parent.height
165+
166+
// ball shape
167+
ShapePath {
168+
strokeWidth: 0
169+
170+
// shade gradient
171+
fillGradient: RadialGradient {
172+
centerX: lightPoint.x + lightPoint.radius
173+
centerY: lightPoint.y + lightPoint.radius
174+
centerRadius: controllerSize
175+
focalX: (lightPoint.x - lightPoint.startOffset) * 0.75 + lightPoint.startOffset + lightPoint.radius
176+
focalY: (lightPoint.y - lightPoint.startOffset) * 0.75 + lightPoint.startOffset + lightPoint.radius
177+
focalRadius: 2
178+
GradientStop { position: 0.00; color: "#FFCCCCCC" }
179+
GradientStop { position: 0.05; color: "#FFAAAAAA" }
180+
GradientStop { position: 0.50; color: "#FF0C0C0C" }
191181
}
192-
}
193-
}
194-
195-
Rectangle {
196-
id: lightPoint
197182

198-
property double startOffset : (parent.width - width) * 0.5
183+
// ball circle path
184+
PathRectangle {
185+
x: 0
186+
y: 0
187+
width: controllerSize
188+
height: controllerSize
189+
radius: controllerSize * 0.5 // circle shape
190+
}
191+
}
199192

200-
x: startOffset
201-
y: startOffset
202-
width: controllerRadius / 6
203-
height: width
204-
radius: 180 // circle
205-
color: "white"
206-
}
193+
// light point shape
194+
ShapePath {
195+
strokeWidth: 0
196+
197+
// glow gradient
198+
fillGradient: RadialGradient {
199+
centerX: lightPoint.x + centerRadius
200+
centerY: lightPoint.y + centerRadius
201+
centerRadius: lightPoint.radius
202+
focalX: centerX
203+
focalY: centerY
204+
GradientStop { position: 0.4; color: "#FFFFFFFF" }
205+
GradientStop { position: 0.75; color: "#33FFFFFF" }
206+
GradientStop { position: 1.0; color: "#00FFFFFF" }
207+
}
207208

208-
Glow {
209-
anchors.fill: lightPoint
210-
radius: controllerRadius / 5
211-
samples: 17
212-
color: "white"
213-
source: lightPoint
209+
// point circle path
210+
PathRectangle {
211+
id: lightPoint
212+
readonly property double startOffset : (lightBallController.width - width) * 0.5
213+
x: startOffset
214+
y: startOffset
215+
width: controllerRadius * 0.4
216+
height: width
217+
radius: width * 0.5 // circle shape
218+
}
219+
}
214220
}
215221

216222
MouseArea {

meshroom/ui/qml/Viewer/Viewer2D.qml

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ FocusScope {
642642
xOrigin: imgContainer.width / 2
643643
yOrigin: imgContainer.height / 2
644644

645-
property var activeNode: _reconstruction ? _reconstruction.activeNodes.get('PhotometricStereo').node : null
645+
property var selectedNode: _reconstruction ? _reconstruction.selectedNode : null
646646
property var vp: _reconstruction ? getViewpoint(_reconstruction.selectedViewId) : null
647647
property url sourcePath: getAlbedoFile()
648648
property url normalPath: getNormalFile()
@@ -652,21 +652,39 @@ FocusScope {
652652
property int previousOrientationTag: 1
653653

654654
function getAlbedoFile() {
655-
656-
if(vp && activeNode && activeNode.hasAttribute("albedo")) {
657-
return Filepath.stringToUrl(Filepath.resolve(activeNode.attribute("albedo").value, vp))
655+
// get the image file from an external URL
656+
if (useExternal) {
657+
var externalFile = Filepath.urlToString(sourceExternal)
658+
if(externalFile.includes("_normals"))
659+
return Filepath.stringToUrl(externalFile.replace("_normals", "_albedo"))
660+
return sourceExternal
658661
}
662+
663+
// get the image file from selected node albedo attribute
664+
if(vp && selectedNode && selectedNode.hasAttribute("albedo"))
665+
return Filepath.stringToUrl(Filepath.resolve(selectedNode.attribute("albedo").value, vp))
659666

660-
return getImageFile()
667+
// no valid image file, return empty url
668+
return ""
661669
}
662670

663671
function getNormalFile() {
664-
665-
if(vp && activeNode && activeNode.hasAttribute("normals")) {
666-
return Filepath.stringToUrl(Filepath.resolve(activeNode.attribute("normals").value, vp))
672+
// get the image file from an external URL
673+
if (useExternal) {
674+
var externalFile = Filepath.urlToString(sourceExternal)
675+
if(externalFile.includes("_normals"))
676+
return sourceExternal
677+
if(externalFile.includes("_albedo"))
678+
return Filepath.stringToUrl(externalFile.replace("_albedo", "_normals"))
679+
return "" // invalid external file
667680
}
668681

669-
return getImageFile()
682+
// get the image file from selected node normals attribute
683+
if(vp && selectedNode && selectedNode.hasAttribute("normals"))
684+
return Filepath.stringToUrl(Filepath.resolve(selectedNode.attribute("normals").value, vp))
685+
686+
// no valid image file, return empty url
687+
return ""
670688
}
671689

672690
onWidthChanged: {
@@ -713,14 +731,14 @@ FocusScope {
713731
'gamma': Qt.binding(function() { return hdrImageToolbar.gammaValue }),
714732
'gain': Qt.binding(function() { return hdrImageToolbar.gainValue }),
715733
'channelModeString': Qt.binding(function() { return hdrImageToolbar.channelModeValue }),
716-
'baseColor': Qt.binding(function() { return phongImageViewerToolbar.baseColorValue }),
717-
'textureOpacity': Qt.binding(function() { return phongImageViewerToolbar.textureOpacityValue }),
718-
'ka': Qt.binding(function() { return phongImageViewerToolbar.kaValue }),
719-
'kd': Qt.binding(function() { return phongImageViewerToolbar.kdValue }),
720-
'ks': Qt.binding(function() { return phongImageViewerToolbar.ksValue }),
721-
'shininess': Qt.binding(function() { return phongImageViewerToolbar.shininessValue }),
722-
'lightYaw': Qt.binding(function() { return -directionalLightPane.lightYawValue }), // left handed coordinate system
723-
'lightPitch': Qt.binding(function() { return directionalLightPane.lightPitchValue }),
734+
'baseColor': Qt.binding(function() { return phongImageViewerToolbarLoader.item !== null ? phongImageViewerToolbarLoader.item.baseColorValue : "#ffffff" }),
735+
'textureOpacity': Qt.binding(function() { return phongImageViewerToolbarLoader.item !== null ? phongImageViewerToolbarLoader.item.textureOpacityValue : 0.0}),
736+
'ka': Qt.binding(function() { return phongImageViewerToolbarLoader.item !== null ? phongImageViewerToolbarLoader.item.kaValue : 0.0 }),
737+
'kd': Qt.binding(function() { return phongImageViewerToolbarLoader.item !== null ? phongImageViewerToolbarLoader.item.kdValue : 0.0 }),
738+
'ks': Qt.binding(function() { return phongImageViewerToolbarLoader.item !== null ? phongImageViewerToolbarLoader.item.ksValue : 0.0 }),
739+
'shininess': Qt.binding(function() { return phongImageViewerToolbarLoader.item !== null ? phongImageViewerToolbarLoader.item.shininessValue : 0.0 }),
740+
'lightYaw': Qt.binding(function() { return directionalLightPaneLoader.item !== null ? -directionalLightPaneLoader.item.lightYawValue : 0.0 }), // left handed coordinate system
741+
'lightPitch': Qt.binding(function() { return directionalLightPaneLoader.item !== null ? directionalLightPaneLoader.item.lightPitchValue : 0.0 }),
724742
})
725743
} else {
726744
// Forcing the unload (instead of using Component.onCompleted to load it once and for all) is necessary since Qt 5.14
@@ -1062,6 +1080,38 @@ FocusScope {
10621080
}
10631081
}
10641082
}
1083+
FloatingPane {
1084+
Layout.fillWidth: true
1085+
Layout.fillHeight: false
1086+
Layout.preferredHeight: childrenRect.height
1087+
visible: phongImageViewerLoader.item !== null &&
1088+
phongImageViewerLoader.item.imageStatus === Image.Error &&
1089+
phongImageViewerLoader.sourcePath != ""
1090+
Layout.alignment: Qt.AlignHCenter
1091+
RowLayout {
1092+
anchors.fill: parent
1093+
Label {
1094+
font.pointSize: 8
1095+
text: {
1096+
if (phongImageViewerLoader.item !== null) {
1097+
switch (phongImageViewerLoader.item.status) {
1098+
case 2: // AliceVision.PhongImageViewer.EStatus.MISSING_FILE
1099+
return "Invalid / Missing File(s)"
1100+
case 4: // AliceVision.PhongImageViewer.EStatus.LOADING_ERROR
1101+
return "Error"
1102+
default:
1103+
return ""
1104+
}
1105+
}
1106+
return ""
1107+
}
1108+
horizontalAlignment: Text.AlignHCenter
1109+
verticalAlignment: Text.AlignVCenter
1110+
Layout.fillWidth: true
1111+
Layout.alignment: Qt.AlignHCenter
1112+
}
1113+
}
1114+
}
10651115

10661116
Item {
10671117
id: imgPlaceholder
@@ -1349,25 +1399,29 @@ FocusScope {
13491399
}
13501400
}
13511401

1352-
PhongImageViewerToolbar {
1353-
id: phongImageViewerToolbar
1354-
1402+
Loader {
1403+
id: phongImageViewerToolbarLoader
1404+
active: phongImageViewerLoader.status === Loader.Ready
13551405
anchors {
13561406
bottom: parent.bottom
13571407
left: parent.left
13581408
margins: 2
13591409
}
1360-
visible: root.aliceVisionPluginAvailable && phongImageViewerLoader.active
1410+
sourceComponent: PhongImageViewerToolbar {
1411+
}
13611412
}
13621413

1363-
DirectionalLightPane {
1364-
id: directionalLightPane
1414+
Loader {
1415+
id: directionalLightPaneLoader
1416+
active: phongImageViewerToolbarLoader.status === Loader.Ready
13651417
anchors {
13661418
bottom: parent.bottom
13671419
right: parent.right
13681420
margins: 2
13691421
}
1370-
visible: root.aliceVisionPluginAvailable && phongImageViewerLoader.active && phongImageViewerToolbar.displayLightController
1422+
sourceComponent: DirectionalLightPane {
1423+
visible: phongImageViewerToolbarLoader.item !== null && phongImageViewerToolbarLoader.item.displayLightController
1424+
}
13711425
}
13721426
}
13731427

0 commit comments

Comments
 (0)