Clean up camera code a bit

This commit is contained in:
Joseph Ferano 2026-07-01 13:32:59 +07:00
parent 359755aad1
commit 4b667e0197

View File

@ -15,18 +15,27 @@
(def ^LinkedBlockingQueue render-queue (LinkedBlockingQueue.)) (def ^LinkedBlockingQueue render-queue (LinkedBlockingQueue.))
(defonce camera-fly (atom {:type :fly
:pos (Vector3f.)
:yaw 0.0
:pitch 0.0
:last-x 0.0
:last-y 0.0
:target (Vector3f.)
:view-matrix (.identity (Matrix4f.))}))
(defonce camera-orbit (atom {:type :orbit
:pos (Vector3f.)
:yaw 0.0
:pitch 0.0
:last-x 0.0
:last-y 0.0
:focus-point (Vector3f.)
:view-matrix (.identity (Matrix4f.))}))
(def state (atom {:last-time 0.0 (def state (atom {:last-time 0.0
:drag-start-pos nil :drag-start-pos nil
:camera-pos (Vector3f. 0.0 0.0 10.0) :current-mode :static}))
:camera-yaw 0.0
:camera-pitch 0.0
:orbit-yaw 0.0
:orbit-pitch 0.0
:last-orbit-x 0.0
:last-orbit-y 0.0
:focus-point (Vector3f.)
:camera-target (Vector3f.)
:projection-matrix}))
(defonce gl-thread (atom nil)) (defonce gl-thread (atom nil))
@ -79,8 +88,6 @@
(defonce uploaded-scene (atom nil)) (defonce uploaded-scene (atom nil))
(defonce shaders (atom nil)) (defonce shaders (atom nil))
(def cam-speed 0.04)
(defn camera-forward [^double yaw ^double pitch] (defn camera-forward [^double yaw ^double pitch]
(Vector3f. (* (Math/cos pitch) (Math/sin yaw)) (Vector3f. (* (Math/cos pitch) (Math/sin yaw))
(Math/sin pitch) (Math/sin pitch)
@ -92,23 +99,24 @@
(defn camera-up [^Vector3f forward ^Vector3f right] (defn camera-up [^Vector3f forward ^Vector3f right]
(-> (Vector3f. forward) (.cross right))) (-> (Vector3f. forward) (.cross right)))
(defn get-ndc [^double x ^double y ^long w ^long h]
[(- (* 2 (/ x w)) 1)
(- 1 (* 2 (/ y h)))])
(defn- key-down? [window key] (defn- key-down? [window key]
(= (GLFW/glfwGetKey window key) GLFW/GLFW_PRESS)) (= (GLFW/glfwGetKey window key) GLFW/GLFW_PRESS))
(def ^Vector3f move-dir (Vector3f.))
(def ^Vector3f focus-distance 5.0)
(defn- handle-key-actions! [_win key _scancode action _mods] (defn- handle-key-actions! [_win key _scancode action _mods]
(when (= action GLFW/GLFW_PRESS) (when (= action GLFW/GLFW_PRESS)
(when (= key GLFW/GLFW_KEY_ESCAPE) (when (= key GLFW/GLFW_KEY_ESCAPE)
(swap! state assoc :should-close true)) (swap! state assoc :should-close true))
(when (= key GLFW/GLFW_KEY_R) (when (= key GLFW/GLFW_KEY_R)
(swap! state assoc (swap! state update :current-camera
:camera-pos (Vector3f. 0.0 0.0 10.0) #(merge % {:pos (Vector3f. 0.0 0.0 10.0)
:camera-yaw 0.0 :yaw 0.0
:camera-pitch 0.0 :pitch 0.0
:focus-point (Vector3f. 0.0 0.0 0.0) :focus-point (Vector3f. 0.0 0.0 0.0)
:camera-target (Vector3f.))))) :target (Vector3f.)})))))
(defn- drag-detected? [^double x1 ^double y1 ^double x2 ^double y2] (defn- drag-detected? [^double x1 ^double y1 ^double x2 ^double y2]
(let [threshold 10.0 (let [threshold 10.0
@ -116,63 +124,73 @@
dy (- y2 y1)] dy (- y2 y1)]
(< threshold (+ (* dx dx) (* dy dy))))) (< threshold (+ (* dx dx) (* dy dy)))))
(def cam-speed 0.04)
(defn- handle-keyboard-state! [window] (defn- handle-keyboard-state! [window]
(let [{:keys [camera-yaw camera-pitch ^Vector3f camera-pos]} @state] (when-let [camera (when (= (:current-mode @state) :fly)
(when (key-down? window GLFW/GLFW_KEY_W) @camera-fly)]
(.add move-dir 0.0 0.0 1.0)) (let [move-dir (Vector3f.)
(when (key-down? window GLFW/GLFW_KEY_S) {:keys [yaw pitch ^Vector3f pos]} camera]
(.add move-dir 0.0 0.0 -1.0)) (when (key-down? window GLFW/GLFW_KEY_W)
(when (key-down? window GLFW/GLFW_KEY_A) (.add move-dir 0.0 0.0 1.0))
(.add move-dir -1.0 0.0 0.0)) (when (key-down? window GLFW/GLFW_KEY_S)
(when (key-down? window GLFW/GLFW_KEY_D) (.add move-dir 0.0 0.0 -1.0))
(.add move-dir 1.0 0.0 0.0)) (when (key-down? window GLFW/GLFW_KEY_A)
(when (key-down? window GLFW/GLFW_KEY_Q) (.add move-dir -1.0 0.0 0.0))
(.add move-dir 0.0 1.0 0.0)) (when (key-down? window GLFW/GLFW_KEY_D)
(when (key-down? window GLFW/GLFW_KEY_E) (.add move-dir 1.0 0.0 0.0))
(.add move-dir 0.0 -1.0 0.0)) (when (key-down? window GLFW/GLFW_KEY_Q)
(when (> (.length move-dir) 0.0) (.add move-dir 0.0 1.0 0.0))
(.normalize move-dir) (when (key-down? window GLFW/GLFW_KEY_E)
(let [forward ^Vector3f (camera-forward camera-yaw camera-pitch) (.add move-dir 0.0 -1.0 0.0))
right ^Vector3f (camera-right camera-yaw) (when (> (.length move-dir) 0.0)
up ^Vector3f (camera-up forward right) (.normalize move-dir)
velocity ^Vector3f (-> (Vector3f. right) (let [forward ^Vector3f (camera-forward yaw pitch)
(.mul (.x move-dir)) right ^Vector3f (camera-right yaw)
(.fma (.z move-dir) forward) up ^Vector3f (camera-up forward right)
(.fma (.y move-dir) up) velocity ^Vector3f (-> (Vector3f. right)
(.mul ^double cam-speed))] (.mul (.x move-dir))
(.add camera-pos velocity))) (.fma (.z move-dir) forward)
(.set move-dir 0.0 0.0 0.0))) (.fma (.y move-dir) up)
(.mul ^double cam-speed))]
(.add pos velocity)))
(.set move-dir 0.0 0.0 0.0))))
(defn- handle-mouse-move! [_window ^double xpos ^double ypos] (defn- handle-mouse-move! [_window ^double xpos ^double ypos]
(when (:orbit-mode @state) (when-let [camera (case (:current-mode @state)
(let [{:keys [^double orbit-yaw ^double orbit-pitch]} @state :fly camera-fly
last-orbit-x (double (or (:last-orbit-x @state) xpos)) :orbit camera-orbit
last-orbit-y (double (or (:last-orbit-y @state) ypos)) :static nil)]
sensitivity 0.005 (when (and (= (:type @camera) :orbit))
dx (- last-orbit-x xpos) (let [{:keys [^double yaw ^double pitch last-x last-y]} @camera
dy (- last-orbit-y ypos)] last-x (double (or last-x xpos))
(swap! state assoc last-y (double (or last-y ypos))
:last-orbit-x xpos sensitivity 0.005
:last-orbit-y ypos dx (- last-x xpos)
:orbit-yaw (- orbit-yaw (* dx sensitivity)) dy (- last-y ypos)
:orbit-pitch (-> (- orbit-pitch (* dy sensitivity)) fwd ^Vector3f (camera-forward yaw pitch)]
(max (- (/ Math/PI 2))) (swap! camera assoc
(min (/ Math/PI 2)))))) :last-x xpos
#_(when-let [cursor-pos ^Vector2f (:drag-start-pos @state)] :last-y ypos
(when (and (drag-detected? xpos ypos (.x cursor-pos) (.y cursor-pos))) :yaw (- yaw (* dx sensitivity))
(swap! state assoc :orbit-mode true))) :pitch (-> (- pitch (* dy sensitivity))
(when (:fps-mode @state) (max (- (/ Math/PI 2)))
(let [{:keys [^double camera-yaw ^double camera-pitch]} @state (min (/ Math/PI 2))))))
last-cursor-x (double (or (:last-cursor-x @state) xpos)) #_(when-let [cursor-pos ^Vector2f (:drag-start-pos @state)]
last-cursor-y (double (or (:last-cursor-y @state) ypos)) (when (and (drag-detected? xpos ypos (.x cursor-pos) (.y cursor-pos)))
sensitivity 0.001 (swap! state assoc :orbit-mode true)))
dx (- last-cursor-x xpos) (when (and (= (:type @camera) :fly))
dy (- last-cursor-y ypos)] (let [{:keys [^double yaw ^double pitch pos last-x last-y]} @camera
(swap! state assoc last-x (double (or last-x xpos))
:last-cursor-x xpos last-y (double (or last-y ypos))
:last-cursor-y ypos sensitivity 0.001
:camera-yaw (- camera-yaw (* dx sensitivity)) dx (- last-x xpos)
:camera-pitch (+ camera-pitch (* dy sensitivity)))))) dy (- last-y ypos)]
(swap! camera assoc
:last-x xpos
:last-y ypos
:yaw (- yaw (* dx sensitivity))
:pitch (+ pitch (* dy sensitivity)))))))
(defn- get-current-cursor-pos! ^Vector2f [^MemoryStack stack ^long window] (defn- get-current-cursor-pos! ^Vector2f [^MemoryStack stack ^long window]
(let [xbuf (.mallocDouble stack 1) (let [xbuf (.mallocDouble stack 1)
@ -184,23 +202,24 @@
(cond (cond
(and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_PRESS)) (and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_PRESS))
(let [] (let []
(swap! state assoc :fps-mode true) (swap! state assoc :current-mode :fly)
(GLFW/glfwSetInputMode win GLFW/GLFW_CURSOR GLFW/GLFW_CURSOR_DISABLED)) (GLFW/glfwSetInputMode win GLFW/GLFW_CURSOR GLFW/GLFW_CURSOR_DISABLED))
(and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_RELEASE)) (and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_RELEASE))
(let [] (let []
(swap! state assoc :fps-mode false :last-cursor-x nil :last-cursor-y nil) (swap! state assoc :current-mode :static)
(swap! camera-fly assoc :last-x nil :last-y nil)
(GLFW/glfwSetInputMode win GLFW/GLFW_CURSOR GLFW/GLFW_CURSOR_NORMAL)) (GLFW/glfwSetInputMode win GLFW/GLFW_CURSOR GLFW/GLFW_CURSOR_NORMAL))
(and (= button GLFW/GLFW_MOUSE_BUTTON_LEFT) (= action GLFW/GLFW_PRESS)) (and (= button GLFW/GLFW_MOUSE_BUTTON_LEFT) (= action GLFW/GLFW_PRESS))
(do (do
(when (mods :ctrl) (when (mods :ctrl)
(swap! state assoc :orbit-mode true)) (swap! state assoc :current-mode :orbit))
(swap! state assoc :drag-start-pos (get-current-cursor-pos! (MemoryStack/stackGet) win))) (swap! state assoc :drag-start-pos (get-current-cursor-pos! (MemoryStack/stackGet) win)))
(and (= button GLFW/GLFW_MOUSE_BUTTON_LEFT) (= action GLFW/GLFW_RELEASE)) (and (= button GLFW/GLFW_MOUSE_BUTTON_LEFT) (= action GLFW/GLFW_RELEASE))
(let [] (let []
(swap! state assoc :orbit-mode false :drag-start-pos nil :last-orbit-x nil :last-orbit-y nil) (swap! state assoc :current-mode :static :drag-start-pos nil)
(GLFW/glfwSetCursor win MemoryUtil/NULL)))) (GLFW/glfwSetCursor win MemoryUtil/NULL))))
(defn render-scene [^MemoryStack stack scene node ^Matrix4f mtx] (defn render-scene [^MemoryStack stack scene node ^Matrix4f mtx]
@ -229,6 +248,13 @@
(doseq [child children] (doseq [child children]
(render-scene stack scene child model-mtx))))) (render-scene stack scene child model-mtx)))))
(defn- camera-view-matrix [{:keys [yaw pitch ^Vector3f pos ^Vector3f target ^Matrix4f view-matrix]}]
(-> view-matrix
(.identity)
(.lookAt (.x pos) (.y pos) (.z pos)
(.x target) (.y target) (.z target)
0.0 1.0 0.0)))
(defn- update-loop [window ^MemoryStack stack data] (defn- update-loop [window ^MemoryStack stack data]
(let [w-buf (.mallocInt stack 1) (let [w-buf (.mallocInt stack 1)
h-buf (.mallocInt stack 1) h-buf (.mallocInt stack 1)
@ -248,34 +274,13 @@
(GL20/glUseProgram program) (GL20/glUseProgram program)
(let [vbuf (.mallocFloat stack 16) (let [vbuf (.mallocFloat stack 16)
pbuf (.mallocFloat stack 16) pbuf (.mallocFloat stack 16)
radius 10.0 camera (if (= :orbit (:current-mode @state))
{:keys [^double camera-yaw ^double camera-pitch ^Vector3f camera-pos camera-target]} @state camera-orbit
fwd ^Vector3f (camera-forward camera-yaw camera-pitch) camera-fly)
camera-pos (if (:orbit-mode @state) view (camera-view-matrix @camera)
(let [fwd ^Vector3f (camera-forward (:orbit-yaw @state) (:orbit-pitch @state)) proj (:projection-matrix @state)]
orbit-pos (Vector3f. ^Vector3f (:focus-point @state))] (.get ^Matrix4f view vbuf)
(.add orbit-pos (.mul (Vector3f. fwd) 8.0))) (.get ^Matrix4f proj pbuf)
camera-pos)
target (:camera-target @state)
_ (doto ^Vector3f target
(.set camera-pos)
(.add fwd))
target (if (:orbit-mode @state)
(:focus-point @state)
target)
view (-> (Matrix4f.)
(.identity)
(.lookAt (.x camera-pos) (.y camera-pos) (.z camera-pos) ;; eye (camera position)
(.x ^Vector3f target) (.y ^Vector3f target) (.z ^Vector3f target) ;; center (where to look)
0.0 1.0 0.0)) ;; up
proj (-> (Matrix4f.)
(.identity)
(.perspective (float (Math/toRadians 45))
(float (/ (float w) (float h)))
(float 0.1)
(float 100.0)))]
(.get view vbuf)
(.get proj pbuf)
(GL20/glUniformMatrix4fv ^int (:mv uniforms) false vbuf) (GL20/glUniformMatrix4fv ^int (:mv uniforms) false vbuf)
(GL20/glUniformMatrix4fv ^int (:mp uniforms) false pbuf) (GL20/glUniformMatrix4fv ^int (:mp uniforms) false pbuf)
(render-scene stack scene (:hiearchy scene) (Matrix4f.))))) (render-scene stack scene (:hiearchy scene) (Matrix4f.)))))
@ -299,6 +304,18 @@
(reset! shaders nil) (reset! shaders nil)
(swap! state assoc :should-close true)) (swap! state assoc :should-close true))
(defn- init [window width height]
(swap! state assoc
:window window
:should-close false
:projection-matrix
(-> (Matrix4f.)
(.identity)
(.perspective (float (Math/toRadians 45))
(float (/ (float width) (float height)))
(float 0.1)
(float 100.0)))))
(defn -main [] (defn -main []
(when-not (GLFW/glfwInit) (when-not (GLFW/glfwInit)
(throw (RuntimeException. "Failed to init GLFW"))) (throw (RuntimeException. "Failed to init GLFW")))
@ -307,7 +324,9 @@
(GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MINOR 5) (GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MINOR 5)
(GLFW/glfwWindowHint GLFW/GLFW_OPENGL_PROFILE GLFW/GLFW_OPENGL_CORE_PROFILE) (GLFW/glfwWindowHint GLFW/GLFW_OPENGL_PROFILE GLFW/GLFW_OPENGL_CORE_PROFILE)
(let [window (GLFW/glfwCreateWindow 960 500 #_#_1900 1100 "NOL" MemoryUtil/NULL MemoryUtil/NULL)] (let [width 960 #_1900
height 500 #_1100
window (GLFW/glfwCreateWindow 960 500 "NOL" MemoryUtil/NULL MemoryUtil/NULL)]
(when (= window MemoryUtil/NULL) (when (= window MemoryUtil/NULL)
(GLFW/glfwTerminate) (GLFW/glfwTerminate)
(throw (RuntimeException. "Failed to create window"))) (throw (RuntimeException. "Failed to create window")))
@ -320,8 +339,6 @@
(GL11/glCullFace GL11/GL_BACK) (GL11/glCullFace GL11/GL_BACK)
(GLFW/glfwSwapInterval 1) (GLFW/glfwSwapInterval 1)
(swap! state assoc :window window :should-close false)
(GLFW/glfwSetKeyCallback window (GLFW/glfwSetKeyCallback window
(reify GLFWKeyCallbackI (reify GLFWKeyCallbackI
(invoke [_ win key scancode action mods] (invoke [_ win key scancode action mods]
@ -350,6 +367,7 @@
(handle-mouse-buttons! win button action mods)) (handle-mouse-buttons! win button action mods))
(catch Exception e (catch Exception e
(println "Mouse Button Callback Exception:" e)))))) (println "Mouse Button Callback Exception:" e))))))
(init window width height)
(loop [] (loop []
(loop [] (loop []
(when-let [f (.poll render-queue)] (when-let [f (.poll render-queue)]
@ -515,6 +533,8 @@
(reset! uploaded-scene (with-gl (upload-scene! @loaded-scene))) (reset! uploaded-scene (with-gl (upload-scene! @loaded-scene)))
(reset! shaders (with-gl (load-shaders! "shaders/base.vert" "shaders/base.frag"))) (reset! shaders (with-gl (load-shaders! "shaders/base.vert" "shaders/base.frag")))
(swap! state assoc :current-state :static)
(with-gl (with-gl
(GL11/glClearColor 0.392 0.584 0.929 1.0)) (GL11/glClearColor 0.392 0.584 0.929 1.0))
(with-gl (with-gl