1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of Qt Quick 3D.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30import QtQuick 2.15
31import QtQuick3D 1.15
32
33Item {
34    id: root
35    property Node controlledObject: undefined
36
37    property real speed: 1
38    property real shiftSpeed: 3
39
40    property real forwardSpeed: 5
41    property real backSpeed: 5
42    property real rightSpeed: 5
43    property real leftSpeed: 5
44    property real upSpeed: 5
45    property real downSpeed: 5
46    property real xSpeed: 0.1
47    property real ySpeed: 0.1
48
49    property bool xInvert: false
50    property bool yInvert: true
51
52    property bool mouseEnabled: true
53    property bool keysEnabled: true
54
55    readonly property bool inputsNeedProcessing: status.moveForward | status.moveBack
56                                                 | status.moveLeft | status.moveRight
57                                                 | status.moveUp | status.moveDown
58                                                 | status.useMouse
59
60    property alias acceptedButtons: dragHandler.acceptedButtons
61
62
63
64    implicitWidth: parent.width
65    implicitHeight: parent.height
66    focus: keysEnabled
67
68    DragHandler {
69        id: dragHandler
70        target: null
71        enabled: mouseEnabled
72        onCentroidChanged: {
73            mouseMoved(Qt.vector2d(centroid.position.x, centroid.position.y));
74        }
75
76        onActiveChanged: {
77            if (active)
78                mousePressed(Qt.vector2d(centroid.position.x, centroid.position.y));
79            else
80                mouseReleased(Qt.vector2d(centroid.position.x, centroid.position.y));
81        }
82    }
83
84    Keys.onPressed: if (keysEnabled) handleKeyPress(event)
85    Keys.onReleased: if (keysEnabled) handleKeyRelease(event)
86
87    function mousePressed(newPos) {
88        status.currentPos = newPos
89        status.lastPos = newPos
90        status.useMouse = true;
91    }
92
93    function mouseReleased(newPos) {
94        status.useMouse = false;
95    }
96
97    function mouseMoved(newPos) {
98        status.currentPos = newPos;
99    }
100
101    function forwardPressed() {
102        status.moveForward = true
103        status.moveBack = false
104    }
105
106    function forwardReleased() {
107        status.moveForward = false
108    }
109
110    function backPressed() {
111        status.moveBack = true
112        status.moveForward = false
113    }
114
115    function backReleased() {
116        status.moveBack = false
117    }
118
119    function rightPressed() {
120        status.moveRight = true
121        status.moveLeft = false
122    }
123
124    function rightReleased() {
125        status.moveRight = false
126    }
127
128    function leftPressed() {
129        status.moveLeft = true
130        status.moveRight = false
131    }
132
133    function leftReleased() {
134        status.moveLeft = false
135    }
136
137    function upPressed() {
138        status.moveUp = true
139        status.moveDown = false
140    }
141
142    function upReleased() {
143        status.moveUp = false
144    }
145
146    function downPressed() {
147        status.moveDown = true
148        status.moveUp = false
149    }
150
151    function downReleased() {
152        status.moveDown = false
153    }
154
155    function shiftPressed() {
156        status.shiftDown = true
157    }
158
159    function shiftReleased() {
160        status.shiftDown = false
161    }
162
163    function handleKeyPress(event)
164    {
165        switch (event.key) {
166        case Qt.Key_W:
167        case Qt.Key_Up:
168            forwardPressed();
169            break;
170        case Qt.Key_S:
171        case Qt.Key_Down:
172            backPressed();
173            break;
174        case Qt.Key_A:
175        case Qt.Key_Left:
176            leftPressed();
177            break;
178        case Qt.Key_D:
179        case Qt.Key_Right:
180            rightPressed();
181            break;
182        case Qt.Key_R:
183        case Qt.Key_PageUp:
184            upPressed();
185            break;
186        case Qt.Key_F:
187        case Qt.Key_PageDown:
188            downPressed();
189            break;
190        case Qt.Key_Shift:
191            shiftPressed();
192            break;
193        }
194    }
195
196    function handleKeyRelease(event)
197    {
198        switch (event.key) {
199        case Qt.Key_W:
200        case Qt.Key_Up:
201            forwardReleased();
202            break;
203        case Qt.Key_S:
204        case Qt.Key_Down:
205            backReleased();
206            break;
207        case Qt.Key_A:
208        case Qt.Key_Left:
209            leftReleased();
210            break;
211        case Qt.Key_D:
212        case Qt.Key_Right:
213            rightReleased();
214            break;
215        case Qt.Key_R:
216        case Qt.Key_PageUp:
217            upReleased();
218            break;
219        case Qt.Key_F:
220        case Qt.Key_PageDown:
221            downReleased();
222            break;
223        case Qt.Key_Shift:
224            shiftReleased();
225            break;
226        }
227    }
228
229    Timer {
230        id: updateTimer
231        interval: 16
232        repeat: true
233        running: root.inputsNeedProcessing
234        onTriggered: {
235            processInputs();
236        }
237    }
238
239    function processInputs()
240    {
241        if (root.inputsNeedProcessing)
242            status.processInput();
243    }
244
245    QtObject {
246        id: status
247
248        property bool moveForward: false
249        property bool moveBack: false
250        property bool moveLeft: false
251        property bool moveRight: false
252        property bool moveUp: false
253        property bool moveDown: false
254        property bool shiftDown: false
255        property bool useMouse: false
256
257        property vector2d lastPos: Qt.vector2d(0, 0)
258        property vector2d currentPos: Qt.vector2d(0, 0)
259
260        function updatePosition(vector, speed, position)
261        {
262            if (shiftDown)
263                speed *= shiftSpeed;
264            else
265                speed *= root.speed
266
267            var direction = vector;
268            var velocity = Qt.vector3d(direction.x * speed,
269                                       direction.y * speed,
270                                       direction.z * speed);
271            controlledObject.position = Qt.vector3d(position.x + velocity.x,
272                                                    position.y + velocity.y,
273                                                    position.z + velocity.z);
274        }
275
276        function negate(vector) {
277            return Qt.vector3d(-vector.x, -vector.y, -vector.z)
278        }
279
280        function processInput() {
281            if (controlledObject == undefined)
282                return;
283
284            if (moveForward)
285                updatePosition(controlledObject.forward, forwardSpeed, controlledObject.position);
286            else if (moveBack)
287                updatePosition(negate(controlledObject.forward), backSpeed, controlledObject.position);
288
289            if (moveRight)
290                updatePosition(controlledObject.right, rightSpeed, controlledObject.position);
291            else if (moveLeft)
292                updatePosition(negate(controlledObject.right), leftSpeed, controlledObject.position);
293
294            if (moveDown)
295                updatePosition(negate(controlledObject.up), downSpeed, controlledObject.position);
296            else if (moveUp)
297                updatePosition(controlledObject.up, upSpeed, controlledObject.position);
298
299            if (useMouse) {
300                // Get the delta
301                var rotationVector = controlledObject.eulerRotation;
302                var delta = Qt.vector2d(lastPos.x - currentPos.x,
303                                        lastPos.y - currentPos.y);
304                // rotate x
305                var rotateX = delta.x * xSpeed
306                if (xInvert)
307                    rotateX = -rotateX;
308                rotationVector.y += rotateX;
309
310                // rotate y
311                var rotateY = delta.y * -ySpeed
312                if (yInvert)
313                    rotateY = -rotateY;
314                rotationVector.x += rotateY;
315                controlledObject.setEulerRotation(rotationVector);
316                lastPos = currentPos;
317            }
318        }
319    }
320}
321