Files
qt-creator/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml

626 lines
22 KiB
QML
Raw Normal View History

/****************************************************************************
**
** Copyright (C) 2019 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.12
import QtQuick.Window 2.12
import QtQuick3D 1.0
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import MouseArea3D 1.0
Window {
id: viewWindow
width: 1024
height: 768
minimumHeight: 200
minimumWidth: 200
visible: true
title: "3D Edit View"
// need all those flags otherwise the title bar disappears after setting WindowStaysOnTopHint flag later
flags: Qt.Window | Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
property alias scene: editView.importScene
property alias showEditLight: btnEditViewLight.toggled
property alias usePerspective: btnPerspective.toggled
property alias globalOrientation: btnLocalGlobal.toggled
property Node selectedNode: null // This is non-null only in single selection case
property var selectedNodes: [] // All selected nodes
property var lightGizmos: []
property var cameraGizmos: []
property var selectionBoxes: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
signal selectionChanged(var selectedNodes)
signal commitObjectProperty(var object, var propName)
signal changeObjectProperty(var object, var propName)
onUsePerspectiveChanged: _generalHelper.storeToolState("usePerspective", usePerspective)
onShowEditLightChanged: _generalHelper.storeToolState("showEditLight", showEditLight)
onGlobalOrientationChanged: _generalHelper.storeToolState("globalOrientation", globalOrientation)
function updateToolStates(toolStates) {
// Init the stored state so we don't unnecessarily reflect changes back to creator
_generalHelper.initToolStates(toolStates);
_generalHelper.restoreWindowState(viewWindow, toolStates);
if ("showEditLight" in toolStates)
showEditLight = toolStates.showEditLight;
if ("usePerspective" in toolStates)
usePerspective = toolStates.usePerspective;
if ("globalOrientation" in toolStates)
globalOrientation = toolStates.globalOrientation;
var groupIndex;
var group;
var i;
if ("groupSelect" in toolStates) {
groupIndex = toolStates.groupSelect;
group = toolbarButtons.buttonGroups["groupSelect"];
for (i = 0; i < group.length; ++i)
group[i].selected = (i === groupIndex);
}
if ("groupTransform" in toolStates) {
groupIndex = toolStates.groupTransform;
group = toolbarButtons.buttonGroups["groupTransform"];
for (i = 0; i < group.length; ++i)
group[i].selected = (i === groupIndex);
}
if ("editCamState" in toolStates)
cameraControl.restoreCameraState(toolStates.editCamState);
}
function ensureSelectionBoxes(count) {
var needMore = count - selectionBoxes.length
if (needMore > 0) {
var component = Qt.createComponent("SelectionBox.qml");
if (component.status === Component.Ready) {
for (var i = 0; i < needMore; ++i) {
var geometryName = _generalHelper.generateUniqueName("SelectionBoxGeometry");
var box = component.createObject(mainSceneHelpers, {"view3D": editView,
"geometryName": geometryName});
selectionBoxes[selectionBoxes.length] = box;
}
}
}
}
function selectObjects(objects) {
// Create selection boxes as necessary. One more box than is actually needed is created, so
// that we always have a previously created box to use for new selection.
// This fixes an occasional visual glitch when creating a new box.
ensureSelectionBoxes(objects.length + 1)
var i;
for (i = 0; i < objects.length; ++i)
selectionBoxes[i].targetNode = objects[i];
for (i = objects.length; i < selectionBoxes.length; ++i)
selectionBoxes[i].targetNode = null;
selectedNodes = objects;
if (objects.length === 0 || objects.length > 1)
selectedNode = null;
else
selectedNode = objects[0];
}
function handleObjectClicked(object, multi) {
var theObject = object;
if (btnSelectGroup.selected) {
while (theObject && theObject.parent !== scene)
theObject = theObject.parent;
}
// Object selection logic:
// Regular click: Clear any multiselection, single-selects the clicked object
// Ctrl-click: No objects selected: Act as single select
// One or more objects selected: Multiselect
// Null object always clears entire selection
var newSelection = [];
if (object !== null) {
if (multi && selectedNodes.length > 0) {
var deselect = false;
for (var i = 0; i < selectedNodes.length; ++i) {
// Multiselecting already selected object clears that object from selection
if (selectedNodes[i] !== object)
newSelection[newSelection.length] = selectedNodes[i];
else
deselect = true;
}
if (!deselect)
newSelection[newSelection.length] = object;
} else {
newSelection[0] = theObject;
}
}
selectObjects(newSelection);
selectionChanged(newSelection);
}
function addLightGizmo(obj)
{
// Insert into first available gizmo
for (var i = 0; i < lightGizmos.length; ++i) {
if (!lightGizmos[i].targetNode) {
lightGizmos[i].targetNode = obj;
return;
}
}
// No free gizmos available, create a new one
var component = Qt.createComponent("LightGizmo.qml");
if (component.status === Component.Ready) {
var gizmo = component.createObject(overlayScene,
{"view3D": overlayView, "targetNode": obj,
"selectedNodes": selectedNodes});
lightGizmos[lightGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
}
}
function addCameraGizmo(obj)
{
// Insert into first available gizmo
for (var i = 0; i < cameraGizmos.length; ++i) {
if (!cameraGizmos[i].targetNode) {
cameraGizmos[i].targetNode = obj;
return;
}
}
// No free gizmos available, create a new one
var component = Qt.createComponent("CameraGizmo.qml");
if (component.status === Component.Ready) {
var geometryName = _generalHelper.generateUniqueName("CameraGeometry");
var gizmo = component.createObject(
overlayScene,
{"view3D": overlayView, "targetNode": obj, "geometryName": geometryName,
"viewPortRect": viewPortRect, "selectedNodes": selectedNodes});
cameraGizmos[cameraGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.viewPortRect = Qt.binding(function() {return viewPortRect;});
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
}
}
// Work-around the fact that the projection matrix for the camera is not calculated until
// the first frame is rendered, so any initial calls to mapFrom3DScene() will fail.
Component.onCompleted: {
selectObjects([]);
_generalHelper.requestOverlayUpdate();
}
onWidthChanged: {
_generalHelper.requestOverlayUpdate();
_generalHelper.storeWindowState(viewWindow);
}
onHeightChanged: {
_generalHelper.requestOverlayUpdate();
_generalHelper.storeWindowState(viewWindow);
}
onXChanged: _generalHelper.storeWindowState(viewWindow);
onYChanged: _generalHelper.storeWindowState(viewWindow);
onWindowStateChanged: _generalHelper.storeWindowState(viewWindow);
Node {
id: overlayScene
PerspectiveCamera {
id: overlayPerspectiveCamera
clipFar: editPerspectiveCamera.clipFar
clipNear: editPerspectiveCamera.clipNear
position: editPerspectiveCamera.position
rotation: editPerspectiveCamera.rotation
}
OrthographicCamera {
id: overlayOrthoCamera
clipFar: editOrthoCamera.clipFar
clipNear: editOrthoCamera.clipNear
position: editOrthoCamera.position
rotation: editOrthoCamera.rotation
scale: editOrthoCamera.scale
}
MouseArea3D {
id: gizmoDragHelper
view3D: overlayView
}
MoveGizmo {
id: moveGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
highlightOnHover: true
targetNode: viewWindow.selectedNode
globalOrientation: viewWindow.globalOrientation
visible: viewWindow.selectedNode && btnMove.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onPositionCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "position")
onPositionMove: viewWindow.changeObjectProperty(viewWindow.selectedNode, "position")
}
ScaleGizmo {
id: scaleGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
highlightOnHover: true
targetNode: viewWindow.selectedNode
visible: viewWindow.selectedNode && btnScale.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onScaleCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "scale")
onScaleChange: viewWindow.changeObjectProperty(viewWindow.selectedNode, "scale")
}
RotateGizmo {
id: rotateGizmo
scale: autoScale.getScale(Qt.vector3d(7, 7, 7))
highlightOnHover: true
targetNode: viewWindow.selectedNode
globalOrientation: viewWindow.globalOrientation
visible: viewWindow.selectedNode && btnRotate.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onRotateCommit: viewWindow.commitObjectProperty(viewWindow.selectedNode, "rotation")
onRotateChange: viewWindow.changeObjectProperty(viewWindow.selectedNode, "rotation")
}
AutoScaleHelper {
id: autoScale
view3D: overlayView
position: moveGizmo.scenePosition
orientation: moveGizmo.orientation
}
Line3D {
id: pivotLine
visible: viewWindow.selectedNode
name: "3D Edit View Pivot Line"
color: "#ddd600"
function flipIfNeeded(vec) {
if (!viewWindow.selectedNode || viewWindow.selectedNode.orientation === Node.LeftHanded)
return vec;
else
return Qt.vector3d(vec.x, vec.y, -vec.z);
}
startPos: viewWindow.selectedNode ? flipIfNeeded(viewWindow.selectedNode.scenePosition)
: Qt.vector3d(0, 0, 0)
Connections {
target: viewWindow
onSelectedNodeChanged: {
pivotLine.endPos = pivotLine.flipIfNeeded(gizmoDragHelper.pivotScenePosition(
viewWindow.selectedNode));
}
}
Connections {
target: viewWindow.selectedNode
onSceneTransformChanged: {
pivotLine.endPos = pivotLine.flipIfNeeded(gizmoDragHelper.pivotScenePosition(
viewWindow.selectedNode));
}
}
Model {
id: pivotCap
source: "#Sphere"
scale: autoScale.getScale(Qt.vector3d(0.03, 0.03, 0.03))
position: pivotLine.startPos
materials: [
DefaultMaterial {
id: lineMat
lighting: DefaultMaterial.NoLighting
cullingMode: Material.DisableCulling
emissiveColor: pivotLine.color
}
]
}
}
}
Rectangle {
anchors.fill: parent
focus: true
gradient: Gradient {
GradientStop { position: 1.0; color: "#222222" }
GradientStop { position: 0.0; color: "#999999" }
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onClicked: {
var pickResult = editView.pick(mouse.x, mouse.y);
handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
mouse.modifiers & Qt.ControlModifier);
if (!pickResult.objectHit)
mouse.accepted = false;
}
}
DropArea {
anchors.fill: parent
}
View3D {
id: editView
anchors.fill: parent
camera: usePerspective ? editPerspectiveCamera : editOrthoCamera
Node {
id: mainSceneHelpers
HelperGrid {
id: helperGrid
lines: 50
step: 50
}
PointLight {
id: editLight
visible: showEditLight
position: usePerspective ? editPerspectiveCamera.position
: editOrthoCamera.position
quadraticFade: 0
linearFade: 0
}
// Initial camera position and rotation should be such that they look at origin.
// Otherwise EditCameraController._lookAtPoint needs to be initialized to correct
// point.
PerspectiveCamera {
id: editPerspectiveCamera
z: -600
y: 600
rotation.x: 45
clipFar: 100000
clipNear: 1
}
OrthographicCamera {
id: editOrthoCamera
z: -600
y: 600
rotation.x: 45
clipFar: 100000
clipNear: -10000
}
}
}
View3D {
id: overlayView
anchors.fill: parent
camera: usePerspective ? overlayPerspectiveCamera : overlayOrthoCamera
importScene: overlayScene
}
Overlay2D {
id: gizmoLabel
targetNode: moveGizmo.visible ? moveGizmo : scaleGizmo
targetView: overlayView
visible: targetNode.dragging
Rectangle {
color: "white"
x: -width / 2
y: -height - 8
width: gizmoLabelText.width + 4
height: gizmoLabelText.height + 4
border.width: 1
Text {
id: gizmoLabelText
text: {
var l = Qt.locale();
var targetProperty;
if (viewWindow.selectedNode) {
if (gizmoLabel.targetNode === moveGizmo)
targetProperty = viewWindow.selectedNode.position;
else
targetProperty = viewWindow.selectedNode.scale;
return qsTr("x:") + Number(targetProperty.x).toLocaleString(l, 'f', 1)
+ qsTr(" y:") + Number(targetProperty.y).toLocaleString(l, 'f', 1)
+ qsTr(" z:") + Number(targetProperty.z).toLocaleString(l, 'f', 1);
} else {
return "";
}
}
anchors.centerIn: parent
}
}
}
EditCameraController {
id: cameraControl
camera: editView.camera
anchors.fill: parent
view3d: editView
}
}
Rectangle { // toolbar
id: toolbar
color: "#9F000000"
width: 35
height: toolbarButtons.height
Column {
id: toolbarButtons
anchors.horizontalCenter: parent.horizontalCenter
spacing: 5
padding: 5
// Button groups must be defined in parent object of buttons
property var buttonGroups: {
"groupSelect": [btnSelectGroup, btnSelectItem],
"groupTransform": [btnMove, btnRotate, btnScale]
}
ToolBarButton {
id: btnSelectItem
selected: true
tooltip: qsTr("Select Item")
shortcut: "Q"
currentShortcut: selected ? "" : shortcut
tool: "item_selection"
buttonGroup: "groupSelect"
}
ToolBarButton {
id: btnSelectGroup
tooltip: qsTr("Select Group")
shortcut: "Q"
currentShortcut: btnSelectItem.currentShortcut === shortcut ? "" : shortcut
tool: "group_selection"
buttonGroup: "groupSelect"
}
Rectangle { // separator
width: 25
height: 1
color: "#f1f1f1"
anchors.horizontalCenter: parent.horizontalCenter
}
ToolBarButton {
id: btnMove
selected: true
tooltip: qsTr("Move current selection")
shortcut: "W"
currentShortcut: shortcut
tool: "move"
buttonGroup: "groupTransform"
}
ToolBarButton {
id: btnRotate
tooltip: qsTr("Rotate current selection")
shortcut: "E"
currentShortcut: shortcut
tool: "rotate"
buttonGroup: "groupTransform"
}
ToolBarButton {
id: btnScale
tooltip: qsTr("Scale current selection")
shortcut: "R"
currentShortcut: shortcut
tool: "scale"
buttonGroup: "groupTransform"
}
Rectangle { // separator
width: 25
height: 1
color: "#f1f1f1"
anchors.horizontalCenter: parent.horizontalCenter
}
ToolBarButton {
id: btnFit
tooltip: qsTr("Fit camera to current selection")
shortcut: "F"
currentShortcut: shortcut
tool: "fit"
togglable: false
onSelectedChanged: {
if (selected) {
var targetNode = viewWindow.selectedNodes.length > 0
? selectionBoxes[0].model : null;
cameraControl.focusObject(targetNode, editView.camera.rotation, true);
}
}
}
}
}
AxisHelper {
anchors.right: parent.right
anchors.top: parent.top
width: 100
height: width
editCameraCtrl: cameraControl
selectedNode : viewWindow.selectedNodes.length ? selectionBoxes[0].model : null
}
Rectangle { // top controls bar
color: "#aa000000"
width: 290
height: btnPerspective.height + 10
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: 100
Row {
padding: 5
anchors.fill: parent
ToggleButton {
id: btnPerspective
width: 105
tooltip: qsTr("Toggle Perspective / Orthographic Projection")
states: [{iconId: "ortho", text: qsTr("Orthographic")}, {iconId: "persp", text: qsTr("Perspective")}]
}
ToggleButton {
id: btnLocalGlobal
width: 65
tooltip: qsTr("Toggle Global / Local Orientation")
states: [{iconId: "local", text: qsTr("Local")}, {iconId: "global", text: qsTr("Global")}]
}
ToggleButton {
id: btnEditViewLight
width: 110
toggleBackground: true
tooltip: qsTr("Toggle Edit Light")
states: [{iconId: "edit_light_off", text: qsTr("Edit Light Off")}, {iconId: "edit_light_on", text: qsTr("Edit Light On")}]
}
}
}
Text {
id: helpText
property string modKey: _generalHelper.isMacOS ? qsTr("Option") : qsTr("Alt")
color: "white"
text: qsTr("Camera controls: ") + modKey
+ qsTr(" + mouse press and drag. Left: Rotate, Middle: Pan, Right/Wheel: Zoom.")
anchors.bottom: parent.bottom
}
}