From e4b2e45ea6ca8b461c2713ffd17eeb9aa9a1d5d7 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 11 May 2020 17:25:31 +0300 Subject: [PATCH] QmlDesigner: Implement spotlight drag handles Inner cone visualization for spotlight was added, as well as drag handles to adjust inner and outer cone angles and fade. Fade handle adjusts fades in order: quadratic, linear, constant. If a specific fade value is zero, the next one in list is chosen for adjustment. Change-Id: I921936d9782de511558bc6c24cfa0953cce494f0 Fixes: QDS-2038 Reviewed-by: Thomas Hartmann --- .../qmlpuppet/mockfiles/AutoScaleHelper.qml | 12 +- .../mockfiles/DirectionalDraggable.qml | 5 +- .../qml/qmlpuppet/mockfiles/EditView3D.qml | 4 +- .../qml/qmlpuppet/mockfiles/FadeHandle.qml | 127 ++++++++++++++++ .../qml/qmlpuppet/mockfiles/LightGizmo.qml | 142 +++++++++++++----- .../qmlpuppet/mockfiles/SpotLightHandle.qml | 47 +++++- share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc | 1 + 7 files changed, 286 insertions(+), 52 deletions(-) create mode 100644 share/qtcreator/qml/qmlpuppet/mockfiles/FadeHandle.qml diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml index c16c47ddd79..41951d8e13f 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/AutoScaleHelper.qml @@ -31,15 +31,14 @@ Node { id: overlayNode property View3D view3D - property Node target: parent - property bool autoScale: true property Camera camera: view3D.camera + property bool active: true // Read-only property real relativeScale: 1 + onActiveChanged: updateScale() onSceneTransformChanged: updateScale() - onAutoScaleChanged: updateScale() // Trigger delayed update on camera change to ensure camera values are correct onCameraChanged: _generalHelper.requestOverlayUpdate(); @@ -61,11 +60,10 @@ Node { function updateScale() { - if (!autoScale) { - target.scale = Qt.vector3d(1, 1, 1); - } else { + if (active) relativeScale = helper.getRelativeScale(overlayNode); - } + else + relativeScale = 1; } MouseArea3D { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml index f1cf60045ff..1401333627c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/DirectionalDraggable.qml @@ -38,6 +38,7 @@ Model { property MouseArea3D dragHelper: null property alias material: material property real length: 12 + property real offset: 0 readonly property bool hovering: mouseAreaYZ.hovering || mouseAreaXZ.hovering @@ -95,7 +96,7 @@ Model { MouseArea3D { id: mouseAreaYZ view3D: rootModel.view3D - x: 0 + x: rootModel.offset y: -1.5 width: rootModel.length height: 3 @@ -112,7 +113,7 @@ Model { MouseArea3D { id: mouseAreaXZ view3D: rootModel.view3D - x: 0 + x: rootModel.offset y: -1.5 width: rootModel.length height: 3 diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index 9e11bd4e777..c9321790c15 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -457,8 +457,8 @@ Item { view3D: overlayView dragHelper: gizmoDragHelper - onBrightnessCommit: viewRoot.commitObjectProperty(viewRoot.selectedNode, "brightness") - onBrightnessChange: viewRoot.changeObjectProperty(viewRoot.selectedNode, "brightness") + onPropertyValueCommit: viewRoot.commitObjectProperty(targetNode, propName) + onPropertyValueChange: viewRoot.changeObjectProperty(targetNode, propName) } AutoScaleHelper { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/FadeHandle.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/FadeHandle.qml new file mode 100644 index 00000000000..76e52272c27 --- /dev/null +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/FadeHandle.qml @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick3D 1.15 + +DirectionalDraggable { + id: handleRoot + + property string currentLabel + property point currentMousePos + property real fadeScale + property real baseScale: 5 + + scale: autoScaler.getScale(Qt.vector3d(baseScale, baseScale, baseScale)) + length: 3 + offset: -1.5 + + Model { + id: handle + source: "#Sphere" + materials: [ handleRoot.material ] + scale: Qt.vector3d(0.02, 0.02, 0.02) + } + + AutoScaleHelper { + id: autoScaler + active: handleRoot.active + view3D: handleRoot.view3D + } + + property real _q // quadratic fade + property real _l // linear fade + property real _c // constant fade + property real _d: 20 // Divisor from fadeScale calc in lightGizmo + property real _startScale + property real _startFadeScale + property string _currentProp + + signal valueCommit(string propName) + signal valueChange(string propName) + + function updateFade(relativeDistance, screenPos) + { + // Solved from fadeScale equation in LightGizmo + var newValue = 0; + var _x = Math.max(0, (_startFadeScale - (relativeDistance * _startScale)) / 100); + + // Fades capped to range 0-10 because property editor caps them to that range + if (_currentProp === "quadraticFade") { + if (_x === 0) + newValue = 10; + else + newValue = Math.max(0, Math.min(10, -(_c - _d + (_l * _x)) / (_x * _x))); + if (newValue < 0.01) + newValue = 0; // To avoid having tiny positive value when UI shows 0.00 + targetNode.quadraticFade = newValue; + } else if (_currentProp === "linearFade") { + if (_x === 0) + newValue = 10; + else + newValue = Math.max(0, Math.min(10, -(_c - _d) / _x)); + if (newValue < 0.01) + newValue = 0; // To avoid having tiny positive value when UI shows 0.00 + targetNode.linearFade = newValue; + } else { + // Since pure constant fade equates to infinitely long cone, fadeScale calc assumes + // linear fade of one in this case. + newValue = Math.max(0, Math.min(10, _d - _x)); + targetNode.constantFade = newValue; + } + + var l = Qt.locale(); + handleRoot.currentLabel = _currentProp + qsTr(": ") + Number(newValue).toLocaleString(l, 'f', 2); + handleRoot.currentMousePos = screenPos; + } + + onPressed: { + _startScale = autoScaler.relativeScale * baseScale * 2; + _startFadeScale = fadeScale; + _l = targetNode.linearFade; + _c = targetNode.constantFade; + _q = targetNode.quadraticFade; + if (targetNode.quadraticFade === 0) { + if (targetNode.linearFade === 0) { + _currentProp = "constantFade"; + } else { + _currentProp = "linearFade"; + } + } else { + _currentProp = "quadraticFade"; + } + updateFade(0, screenPos); + } + + onDragged: { + updateFade(relativeDistance, screenPos); + handleRoot.valueChange(_currentProp); + } + + onReleased: { + updateFade(relativeDistance, screenPos); + handleRoot.valueCommit(_currentProp); + } +} diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/LightGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/LightGizmo.qml index 36c7c2cb38c..8ac93a85762 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/LightGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/LightGizmo.qml @@ -44,7 +44,7 @@ Node { var c = targetNode.constantFade; var d = 20; // divisor to target intensity value. E.g. 20 = 1/20 = 5% if (l === 0 && q === 0) - l = 1; + l = 1; // For pure constant fade, cone would be infinite, so pretend we have linear fade // Solved from equation in shader: // 1 / d = 1 / (c + (l + q * dist) * dist); if (q === 0) @@ -56,11 +56,14 @@ Node { } } readonly property bool dragging: primaryArrow.dragging + || spotLightHandle.dragging + || spotLightInnerHandle.dragging + || spotLightFadeHandle.dragging property point currentMousePos property string currentLabel - signal brightnessCommit() - signal brightnessChange() + signal propertyValueCommit(string propName) + signal propertyValueChange(string propName) position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0) visible: lightGizmo.targetNode instanceof SpotLight @@ -69,7 +72,7 @@ Node { || lightGizmo.targetNode instanceof PointLight AutoScaleHelper { - id: autoScale + id: autoScaler view3D: lightGizmo.view3D } @@ -89,44 +92,111 @@ Node { rotation: !lightGizmo.targetNode ? Qt.quaternion(1, 0, 0, 0) : lightGizmo.targetNode.sceneRotation - LightModel { - id: spotModel - - property real coneScale: visible - ? lightGizmo.fadeScale * Math.tan(Math.PI * targetNode.coneAngle / 180) - : 1 - - geometryName: "Edit 3D SpotLight" - geometryType: LightGeometry.Spot - material: lightMaterial - visible: lightGizmo.targetNode instanceof SpotLight - scale: Qt.vector3d(coneScale, coneScale, lightGizmo.fadeScale) - } Node { + id: spotParts visible: lightGizmo.targetNode instanceof SpotLight - SpotLightHandle { - id: sphereHandle1 - view3D: lightGizmo.view3D + + LightModel { + id: spotModel + + property real coneXYScale: spotParts.visible && lightGizmo.targetNode + ? lightGizmo.fadeScale * Math.tan(Math.PI * lightGizmo.targetNode.coneAngle / 180) + : 1 + + geometryName: "Edit 3D SpotLight Cone" + geometryType: LightGeometry.Spot material: lightMaterial - position: Qt.vector3d(0, spotModel.scale.y, -spotModel.scale.z) + scale: Qt.vector3d(coneXYScale, coneXYScale, + spotParts.visible && lightGizmo.targetNode && lightGizmo.targetNode.coneAngle > 90 + ? -lightGizmo.fadeScale : lightGizmo.fadeScale) } - SpotLightHandle { - id: sphereHandle2 - view3D: lightGizmo.view3D + + LightModel { + id: spotInnerModel + + property real coneXYScale: spotParts.visible && lightGizmo.targetNode + ? lightGizmo.fadeScale * Math.tan(Math.PI * lightGizmo.targetNode.innerConeAngle / 180) + : 1 + + geometryName: "Edit 3D SpotLight Inner Cone" + geometryType: LightGeometry.Spot material: lightMaterial - position: Qt.vector3d(spotModel.scale.x, 0, -spotModel.scale.z) + scale: Qt.vector3d(coneXYScale, coneXYScale, + spotParts.visible && lightGizmo.targetNode && lightGizmo.targetNode.innerConeAngle > 90 + ? -lightGizmo.fadeScale : lightGizmo.fadeScale) } + SpotLightHandle { - id: sphereHandle3 + id: spotLightHandle view3D: lightGizmo.view3D - material: lightMaterial - position: Qt.vector3d(0, -spotModel.scale.y, -spotModel.scale.z) + color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color + position: lightGizmo.targetNode instanceof SpotLight ? Qt.vector3d(0, spotModel.scale.y, -spotModel.scale.z) + : Qt.vector3d(0, 0, 0) + targetNode: lightGizmo.targetNode instanceof SpotLight ? lightGizmo.targetNode : null + active: lightGizmo.targetNode instanceof SpotLight + dragHelper: lightGizmo.dragHelper + propName: "coneAngle" + propValue: lightGizmo.targetNode instanceof SpotLight ? targetNode.coneAngle : 0 + + onNewValueChanged: targetNode.coneAngle = newValue + onCurrentMousePosChanged: { + lightGizmo.currentMousePos = currentMousePos; + lightGizmo.currentLabel = currentLabel; + } + onValueChange: lightGizmo.propertyValueChange(propName) + onValueCommit: { + if (targetNode.innerConeAngle > targetNode.coneAngle) + targetNode.innerConeAngle = targetNode.coneAngle; + lightGizmo.propertyValueCommit(propName) + lightGizmo.propertyValueCommit(spotLightInnerHandle.propName); + } } + SpotLightHandle { - id: sphereHandle4 + id: spotLightInnerHandle view3D: lightGizmo.view3D - material: lightMaterial - position: Qt.vector3d(-spotModel.scale.x, 0, -spotModel.scale.z) + color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color + position: lightGizmo.targetNode instanceof SpotLight ? Qt.vector3d(0, -spotInnerModel.scale.y, -spotInnerModel.scale.z) + : Qt.vector3d(0, 0, 0) + eulerRotation: Qt.vector3d(180, 0, 0) + targetNode: lightGizmo.targetNode instanceof SpotLight ? lightGizmo.targetNode : null + active: lightGizmo.targetNode instanceof SpotLight + dragHelper: lightGizmo.dragHelper + propName: "innerConeAngle" + propValue: lightGizmo.targetNode instanceof SpotLight ? targetNode.innerConeAngle : 0 + + onNewValueChanged: targetNode.innerConeAngle = newValue + onCurrentMousePosChanged: { + lightGizmo.currentMousePos = currentMousePos; + lightGizmo.currentLabel = currentLabel; + } + onValueChange: lightGizmo.propertyValueChange(propName) + onValueCommit: { + if (targetNode.coneAngle < targetNode.innerConeAngle) + targetNode.coneAngle = targetNode.innerConeAngle; + lightGizmo.propertyValueCommit(propName) + lightGizmo.propertyValueCommit(spotLightHandle.propName); + } + } + + FadeHandle { + id: spotLightFadeHandle + view3D: lightGizmo.view3D + color: (hovering || dragging) ? Qt.rgba(1, 1, 1, 1) : lightGizmo.color + position: lightGizmo.targetNode instanceof SpotLight ? Qt.vector3d(spotModel.scale.x / 2, 0, -spotInnerModel.scale.z / 2) + : Qt.vector3d(0, 0, 0) + eulerRotation: Qt.vector3d(90, 0, 0) + targetNode: lightGizmo.targetNode instanceof SpotLight ? lightGizmo.targetNode : null + active: lightGizmo.targetNode instanceof SpotLight + dragHelper: lightGizmo.dragHelper + fadeScale: lightGizmo.fadeScale + + onCurrentMousePosChanged: { + lightGizmo.currentMousePos = currentMousePos; + lightGizmo.currentLabel = currentLabel; + } + onValueChange: lightGizmo.propertyValueChange(propName) + onValueCommit: lightGizmo.propertyValueCommit(propName) } } @@ -148,7 +218,7 @@ Node { geometryType: LightGeometry.Directional material: lightMaterial visible: lightGizmo.targetNode instanceof DirectionalLight - scale: autoScale.getScale(Qt.vector3d(50, 50, 50)) + scale: autoScaler.getScale(Qt.vector3d(50, 50, 50)) } LightModel { @@ -168,7 +238,7 @@ Node { view3D: lightGizmo.view3D active: lightGizmo.visible dragHelper: lightGizmo.dragHelper - scale: autoScale.getScale(Qt.vector3d(5, 5, 5)) + scale: autoScaler.getScale(Qt.vector3d(5, 5, 5)) length: (lightGizmo.brightnessScale / 10) + 3 property real _startBrightness @@ -177,7 +247,7 @@ Node { { var currentValue = Math.round(Math.max(0, _startBrightness + relativeDistance * 10)); var l = Qt.locale(); - lightGizmo.currentLabel = qsTr("brightness: ") + Number(currentValue).toLocaleString(l, 'f', 0); + lightGizmo.currentLabel = "brightness" + qsTr(": ") + Number(currentValue).toLocaleString(l, 'f', 0); lightGizmo.currentMousePos = screenPos; targetNode.brightness = currentValue; } @@ -189,12 +259,12 @@ Node { onDragged: { updateBrightness(relativeDistance, screenPos); - lightGizmo.brightnessChange(); + lightGizmo.propertyValueChange("brightness"); } onReleased: { updateBrightness(relativeDistance, screenPos); - lightGizmo.brightnessCommit(); + lightGizmo.propertyValueCommit("brightness"); } } diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/SpotLightHandle.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/SpotLightHandle.qml index b50c7555df3..badef266b64 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/SpotLightHandle.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/SpotLightHandle.qml @@ -26,20 +26,57 @@ import QtQuick 2.0 import QtQuick3D 1.15 -Node { +DirectionalDraggable { id: handleRoot - property DefaultMaterial material - property View3D view3D + property string currentLabel + property point currentMousePos + property string propName + property real propValue: 0 + property real newValue: 0 + + scale: autoScaler.getScale(Qt.vector3d(5, 5, 5)) + length: 3 + offset: -1.5 Model { - scale: autoScale.getScale(Qt.vector3d(0.1, 0.1, 0.1)) + id: handle source: "#Sphere" materials: [ handleRoot.material ] + scale: Qt.vector3d(0.02, 0.02, 0.02) } AutoScaleHelper { - id: autoScale + id: autoScaler + active: handleRoot.active view3D: handleRoot.view3D } + + property real _startAngle + + signal valueCommit() + signal valueChange() + + function updateAngle(relativeDistance, screenPos) + { + handleRoot.newValue = Math.round(Math.min(180, Math.max(0, _startAngle + relativeDistance))); + var l = Qt.locale(); + handleRoot.currentLabel = propName + qsTr(": ") + Number(newValue).toLocaleString(l, 'f', 0); + handleRoot.currentMousePos = screenPos; + } + + onPressed: { + _startAngle = propValue; + updateAngle(0, screenPos); + } + + onDragged: { + updateAngle(relativeDistance, screenPos); + handleRoot.valueChange(); + } + + onReleased: { + updateAngle(relativeDistance, screenPos); + handleRoot.valueCommit(); + } } diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc index 06883f61220..0519e2e82a0 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc +++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc @@ -53,5 +53,6 @@ mockfiles/images/point@2x.png mockfiles/images/spot.png mockfiles/images/spot@2x.png + mockfiles/FadeHandle.qml