Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASD camera movement #7595

Draft
wants to merge 7 commits into
base: editor-dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 71 additions & 30 deletions editor/src/clj/editor/camera.clj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
[editor.types :as types]
[schema.core :as s])
(:import [editor.types AABB Camera Frustum Rect Region]
[javafx.scene.input KeyCode]
[javax.vecmath AxisAngle4d Matrix3d Matrix4d Point2d Point3d Quat4d Tuple2d Tuple3d Tuple4d Vector3d Vector4d]))

(set! *warn-on-reflection* true)
Expand Down Expand Up @@ -384,6 +385,16 @@
(assoc (camera-move camera (.x delta) (.y delta) (.z delta))
:focus-point (doto focus (.add delta)))))

(defn free-look [^Camera camera yaw pitch]
(let [scale-factor 0.25
scaled-yaw (* scale-factor yaw)
scaled-pitch (* scale-factor pitch)
camera-yaw-q (math/euler-y->quat scaled-yaw)
camera-pitch-q (math/euler-x->quat scaled-pitch)
camera-orientation (doto (Quat4d.) (.set 0.0 0.0 0.0 1.0))] ;; there's no .setIdentity for quats?
(.mul camera-orientation camera-yaw-q camera-pitch-q)
(assoc camera :rotation camera-orientation)))

(defn tumble
[^Camera camera last-x last-y evt-x evt-y]
(let [rate 0.005
Expand Down Expand Up @@ -434,7 +445,9 @@

(defn camera-movement
([action]
(camera-movement (:button action) (:shift action) (:control action) (:alt action) (:meta action)))
(if (and (:shift action) (= :mouse-moved (:type action)))
:wasd
(camera-movement (:button action) (:shift action) (:control action) (:alt action) (:meta action))))
([button shift ctrl alt meta]
(let [key [button shift ctrl alt meta]]
(button-interpretation key :idle))))
Expand Down Expand Up @@ -611,34 +624,36 @@
(set-extents fov-x fov-y z-near z-far)
filter-fn)))

(defn handle-input [self action user-data]
(defn- valid-yaw-pitch [yaw pitch update-values last-x last-y x y]
(if (and true update-values (every? some? [last-x last-y]))
(let [dx (- x last-x)
dy (- y last-y)]
[(- yaw dx) (- pitch dy)])
[yaw pitch]))

(defn handle-input [self action _user-data]
(let [viewport (g/node-value self :viewport)
movements-enabled (g/node-value self :movements-enabled)
ui-state (or (g/user-data self ::ui-state) {:movement :idle})
{:keys [last-x last-y]} ui-state
ui-state (or (g/user-data self ::ui-state) {:movement :idle :yaw 0 :pitch 0 :last-x 0 :last-y 0})
{:keys [last-x last-y yaw pitch]} ui-state
{:keys [x y type delta-y]} action
movement (if (= type :mouse-pressed)
(get movements-enabled (camera-movement action) :idle)
(:movement ui-state))
camera (g/node-value self :camera)
camera-type (:type camera)
yaw-pitch (valid-yaw-pitch yaw pitch (and (= camera-type :perspective) (:shift action)) last-x last-y x y)
movement (cond (and (= type :mouse-moved) (:shift action) (= camera-type :perspective)) :wasd
(= type :mouse-pressed) (get movements-enabled (camera-movement action) :idle)
:else (:movement ui-state))
filter-fn (or (:filter-fn camera) identity)
camera (cond-> camera
(and (= type :scroll)
(contains? movements-enabled :dolly))
(dolly (* -0.002 delta-y))

(and (= type :mouse-moved)
(not (= :idle movement)))
(cond->
(= :dolly movement)
(dolly (* -0.002 (- y last-y)))
(= :track movement)
(track viewport last-x last-y x y)
(= :tumble movement)
(tumble last-x last-y x y))

true
filter-fn)]
(and (= type :scroll) (contains? movements-enabled :dolly)) (dolly (* -0.002 delta-y))
(and (= type :mouse-moved) (not (= :idle movement)))
(cond->
(= :wasd movement) (free-look (get yaw-pitch 0) (get yaw-pitch 1))
(= :dolly movement) (dolly (* -0.002 (- y last-y)))
(= :track movement) (track viewport last-x last-y x y)
(= :tumble movement) (tumble last-x last-y x y))
true
filter-fn)]
(g/set-property! self :local-camera camera)
(case type
:scroll (if (contains? movements-enabled :dolly) nil action)
Expand All @@ -648,18 +663,16 @@
:mouse-released (do
(g/user-data-swap! self ::ui-state assoc :last-x nil :last-y nil :movement :idle)
(if (= movement :idle) action nil))
:mouse-moved (if (not (= :idle movement))
(do
(g/user-data-swap! self ::ui-state assoc :last-x x :last-y y)
nil)
action)
:mouse-moved (do (g/user-data-swap! self ::ui-state assoc :last-x x :last-y y :yaw (get yaw-pitch 0) :pitch (get yaw-pitch 1))
(if (not (= :idle movement))
nil
action))
action)))

(g/defnode CameraController
(property name g/Keyword (default :local-camera))
(property local-camera Camera)
(property movements-enabled g/Any (default #{:dolly :track :tumble}))

(property movements-enabled g/Any (default #{:dolly :track :tumble :wasd}))
(input scene-aabb AABB)
(input viewport Region)

Expand All @@ -668,6 +681,34 @@

(output input-handler Runnable :cached (g/constantly handle-input)))

(defn- key->movement-info [^Camera camera key-mask movement-speed]
(cond (= key-mask :W) {:movement-vec (camera-forward-vector camera)
:movement-dir movement-speed}
(= key-mask :S) {:movement-vec (camera-forward-vector camera)
:movement-dir (- movement-speed)}
(= key-mask :D) {:movement-vec (camera-right-vector camera)
:movement-dir movement-speed}
(= key-mask :A) {:movement-vec (camera-right-vector camera)
:movement-dir (- movement-speed)}
(= key-mask :E) {:movement-vec (Vector3d. 0.0 1.0 0.0)
:movement-dir movement-speed}
(= key-mask :Q) {:movement-vec (Vector3d. 0.0 1.0 0.0)
:movement-dir (- movement-speed)}))

(defn- do-wasd-movement-perspective [^Camera camera key-mask-entry]
(let [camera-key-mask (first key-mask-entry)
camera-movement-speed 0.8
camera-movement-info (key->movement-info camera camera-key-mask camera-movement-speed)
x (* (:movement-dir camera-movement-info) (.getX ^Vector3d (:movement-vec camera-movement-info)))
y (* (:movement-dir camera-movement-info) (.getY ^Vector3d (:movement-vec camera-movement-info)))
z (* (:movement-dir camera-movement-info) (.getZ ^Vector3d (:movement-vec camera-movement-info)))]
(camera-move camera x y z)))

(defn tick-camera [^Camera camera movement-state]
(case (:type camera)
:perspective (reduce do-wasd-movement-perspective camera movement-state)
:orthographic camera))

(defn- lerp [a b t]
(let [d (- b a)]
(+ a (* t d))))
Expand Down
39 changes: 19 additions & 20 deletions editor/src/clj/editor/input.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,8 @@
(ns editor.input
(:require [dynamo.graph :as g]
[schema.core :as s])
(:import [com.defold.editor Start UIUtil]
[com.jogamp.opengl.util.awt TextRenderer]
[editor.types Camera AABB Region]
[java.awt Font]
[java.awt.image BufferedImage]
[javafx.application Platform]
[javafx.collections FXCollections ObservableList]
[javafx.embed.swing SwingFXUtils]
[javafx.event ActionEvent EventHandler EventType]
[javafx.fxml FXMLLoader]
[javafx.scene Scene Node Parent]
[javafx.scene.control Button TitledPane TextArea TreeItem Menu MenuItem MenuBar Tab ProgressBar]
[javafx.scene.image Image ImageView WritableImage PixelWriter]
[javafx.scene.input InputEvent MouseEvent MouseButton ScrollEvent]
[javafx.scene.layout AnchorPane StackPane HBox Priority]
[javafx.stage Stage FileChooser]
[java.io File]
[java.lang Runnable System]
[javax.vecmath Point3d Matrix4d Vector4d Matrix3d Vector3d]))
(:import [javafx.event EventType]
[javafx.scene.input InputEvent MouseEvent KeyEvent MouseButton ScrollEvent]))

(set! *warn-on-reflection* true)

Expand All @@ -46,7 +29,9 @@
MouseEvent/MOUSE_RELEASED :mouse-released
MouseEvent/MOUSE_CLICKED :mouse-clicked
MouseEvent/MOUSE_MOVED :mouse-moved
MouseEvent/MOUSE_DRAGGED :mouse-moved})
MouseEvent/MOUSE_DRAGGED :mouse-moved
KeyEvent/KEY_PRESSED :key-pressed
KeyEvent/KEY_RELEASED :key-released})

(defn translate-action [^EventType jfx-action]
(get action-map jfx-action :undefined))
Expand Down Expand Up @@ -74,6 +59,20 @@
:shift (.isShiftDown scroll-event)
:meta (.isMetaDown scroll-event)
:control (.isControlDown scroll-event)))
:key-pressed (let [key-pressed-event ^KeyEvent jfx-event]
(assoc action
:code (.getCode key-pressed-event)
:alt (.isAltDown key-pressed-event)
:shift (.isShiftDown key-pressed-event)
:meta (.isMetaDown key-pressed-event)
:control (.isControlDown key-pressed-event)))
:key-released (let [key-released-event ^KeyEvent jfx-event]
(assoc action
:code (.getCode key-released-event)
:alt (.isAltDown key-released-event)
:shift (.isShiftDown key-released-event)
:meta (.isMetaDown key-released-event)
:control (.isControlDown key-released-event)))
(let [mouse-event ^MouseEvent jfx-event]
(assoc action
:button (translate-button (.getButton mouse-event))
Expand Down
11 changes: 11 additions & 0 deletions editor/src/clj/editor/math.clj
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@
c (Math/cos ha)]
(Quat4d. 0.0 0.0 s c)))

(defn euler-y->quat ^Quat4d [angle]
(let [ha (* (deg->rad angle) 0.5)
s (Math/sin ha)
c (Math/cos ha)]
(Quat4d. 0 s 0 c)))

(defn euler-x->quat ^Quat4d [angle]
(let [ha (* (deg->rad angle) 0.5)
s (Math/sin ha)
c (Math/cos ha)]
(Quat4d. s 0 0 c)))
(defn quat-components->euler [^double x ^double y ^double z ^double w]
(if (= 0.0 x y)
(let [ha (Math/atan2 z w)]
Expand Down
59 changes: 55 additions & 4 deletions editor/src/clj/editor/scene.clj
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,8 @@
(property input-action-queue g/Any (default []))
(property updatable-states g/Any)

(property camera-movement-state g/Any)

(input input-handlers Runnable :array)
(input picking-rect Rect)
(input tool-info-text g/Str)
Expand Down Expand Up @@ -1219,11 +1221,26 @@
(active? [selection] (selection->movable selection))
(run [selection] (nudge! (selection->movable selection) 10.0 0.0 0.0)))

(defn- handle-key-pressed! [^KeyEvent event]
(defn- handle-camera-wasd-movement [view-id key state-value]
(let [camera-movement-state (g/node-value view-id :camera-movement-state)
camera-movement-state' (if state-value
(assoc camera-movement-state key true)
(dissoc camera-movement-state key))]
(g/transact
(g/set-property view-id :camera-movement-state camera-movement-state'))))

(defn- handle-key-pressed! [^KeyEvent event view-id]
;; Only handle bare key events that cannot be bound to handlers here.
(when (not= ::unhandled
(if (or (.isAltDown event) (.isMetaDown event) (.isShiftDown event) (.isShortcutDown event))
::unhandled
(condp = (.getCode event)
KeyCode/W (handle-camera-wasd-movement view-id :W true)
KeyCode/A (handle-camera-wasd-movement view-id :A true)
KeyCode/S (handle-camera-wasd-movement view-id :S true)
KeyCode/D (handle-camera-wasd-movement view-id :D true)
KeyCode/E (handle-camera-wasd-movement view-id :E true)
KeyCode/Q (handle-camera-wasd-movement view-id :Q true)
::unhandled)
(condp = (.getCode event)
KeyCode/UP (ui/run-command (.getSource event) :up)
KeyCode/DOWN (ui/run-command (.getSource event) :down)
Expand All @@ -1232,6 +1249,19 @@
::unhandled)))
(.consume event)))

(defn- handle-key-released! [^KeyEvent event view-id]
;; Only handle bare key events that cannot be bound to handlers here.
(when (not= ::unhandled
(condp = (.getCode event)
KeyCode/W (handle-camera-wasd-movement view-id :W false)
KeyCode/A (handle-camera-wasd-movement view-id :A false)
KeyCode/S (handle-camera-wasd-movement view-id :S false)
KeyCode/D (handle-camera-wasd-movement view-id :D false)
KeyCode/E (handle-camera-wasd-movement view-id :E false)
KeyCode/Q (handle-camera-wasd-movement view-id :Q false)
::unhandled))
(.consume event)))

(defn register-event-handler! [^Parent parent view-id]
(let [process-events? (atom true)
event-handler (ui/event-handler e
Expand Down Expand Up @@ -1266,7 +1296,10 @@
(.setOnScroll parent event-handler)
(.setOnKeyPressed parent (ui/event-handler e
(when @process-events?
(handle-key-pressed! e))))))
(handle-key-pressed! e view-id))))
(.setOnKeyReleased parent (ui/event-handler e
(when @process-events?
(handle-key-released! e view-id))))))

(defn make-gl-pane! [view-id opts]
(let [image-view (doto (ImageView.)
Expand Down Expand Up @@ -1324,9 +1357,27 @@
(g/update-property! view-id :render-mode render-mode-transitions))))))
scene-view-pane))

(defn- update-scene-camera-fn [view-id]
(let [camera-node (view->camera view-id)
camera-obj (g/node-value camera-node :camera)
camera-movement-state (g/node-value view-id :camera-movement-state)]
(when (not (nil? camera-obj))
(let [camera-obj-augmented (c/tick-camera camera-obj camera-movement-state)]
(g/transact
(g/set-property camera-node :local-camera camera-obj-augmented))))))

(defn- make-scene-view [scene-graph ^Parent parent opts]
(let [view-id (g/make-node! scene-graph SceneView :updatable-states {})
scene-view-pane (make-scene-view-pane view-id opts)]
scene-view-pane (make-scene-view-pane view-id opts)
update-camera-timer (ui/->timer "update-scene-camera" (fn [_ elapsed-time _]
;; TODO: Can I get dt somehow without doing it manually?
(update-scene-camera-fn view-id)))]
(ui/on-closed! (:tab opts) (fn [_]
(ui/timer-stop! update-camera-timer)
#_(ui/kill-event-dispatch! this)
#_(dispose-scene-view! view-id)))

(ui/timer-start! update-camera-timer)
(ui/children! parent [scene-view-pane])
(ui/with-controls scene-view-pane [scene-view-info-label]
(g/set-property! view-id :info-label scene-view-info-label))
Expand Down