From 4b667e0197ae9a54b1dda1fdfeac5a313f137be8 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Wed, 1 Jul 2026 13:32:59 +0700 Subject: [PATCH] Clean up camera code a bit --- src/nol/core.clj | 240 +++++++++++++++++++++++++---------------------- 1 file changed, 130 insertions(+), 110 deletions(-) diff --git a/src/nol/core.clj b/src/nol/core.clj index 980c9cb..e3e549c 100644 --- a/src/nol/core.clj +++ b/src/nol/core.clj @@ -15,18 +15,27 @@ (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 :drag-start-pos nil - :camera-pos (Vector3f. 0.0 0.0 10.0) - :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})) + :current-mode :static})) (defonce gl-thread (atom nil)) @@ -79,8 +88,6 @@ (defonce uploaded-scene (atom nil)) (defonce shaders (atom nil)) -(def cam-speed 0.04) - (defn camera-forward [^double yaw ^double pitch] (Vector3f. (* (Math/cos pitch) (Math/sin yaw)) (Math/sin pitch) @@ -92,23 +99,24 @@ (defn camera-up [^Vector3f forward ^Vector3f 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] (= (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] (when (= action GLFW/GLFW_PRESS) (when (= key GLFW/GLFW_KEY_ESCAPE) (swap! state assoc :should-close true)) (when (= key GLFW/GLFW_KEY_R) - (swap! state assoc - :camera-pos (Vector3f. 0.0 0.0 10.0) - :camera-yaw 0.0 - :camera-pitch 0.0 - :focus-point (Vector3f. 0.0 0.0 0.0) - :camera-target (Vector3f.))))) + (swap! state update :current-camera + #(merge % {:pos (Vector3f. 0.0 0.0 10.0) + :yaw 0.0 + :pitch 0.0 + :focus-point (Vector3f. 0.0 0.0 0.0) + :target (Vector3f.)}))))) (defn- drag-detected? [^double x1 ^double y1 ^double x2 ^double y2] (let [threshold 10.0 @@ -116,63 +124,73 @@ dy (- y2 y1)] (< threshold (+ (* dx dx) (* dy dy))))) +(def cam-speed 0.04) + (defn- handle-keyboard-state! [window] - (let [{:keys [camera-yaw camera-pitch ^Vector3f camera-pos]} @state] - (when (key-down? window GLFW/GLFW_KEY_W) - (.add move-dir 0.0 0.0 1.0)) - (when (key-down? window GLFW/GLFW_KEY_S) - (.add move-dir 0.0 0.0 -1.0)) - (when (key-down? window GLFW/GLFW_KEY_A) - (.add move-dir -1.0 0.0 0.0)) - (when (key-down? window GLFW/GLFW_KEY_D) - (.add move-dir 1.0 0.0 0.0)) - (when (key-down? window GLFW/GLFW_KEY_Q) - (.add move-dir 0.0 1.0 0.0)) - (when (key-down? window GLFW/GLFW_KEY_E) - (.add move-dir 0.0 -1.0 0.0)) - (when (> (.length move-dir) 0.0) - (.normalize move-dir) - (let [forward ^Vector3f (camera-forward camera-yaw camera-pitch) - right ^Vector3f (camera-right camera-yaw) - up ^Vector3f (camera-up forward right) - velocity ^Vector3f (-> (Vector3f. right) - (.mul (.x move-dir)) - (.fma (.z move-dir) forward) - (.fma (.y move-dir) up) - (.mul ^double cam-speed))] - (.add camera-pos velocity))) - (.set move-dir 0.0 0.0 0.0))) + (when-let [camera (when (= (:current-mode @state) :fly) + @camera-fly)] + (let [move-dir (Vector3f.) + {:keys [yaw pitch ^Vector3f pos]} camera] + (when (key-down? window GLFW/GLFW_KEY_W) + (.add move-dir 0.0 0.0 1.0)) + (when (key-down? window GLFW/GLFW_KEY_S) + (.add move-dir 0.0 0.0 -1.0)) + (when (key-down? window GLFW/GLFW_KEY_A) + (.add move-dir -1.0 0.0 0.0)) + (when (key-down? window GLFW/GLFW_KEY_D) + (.add move-dir 1.0 0.0 0.0)) + (when (key-down? window GLFW/GLFW_KEY_Q) + (.add move-dir 0.0 1.0 0.0)) + (when (key-down? window GLFW/GLFW_KEY_E) + (.add move-dir 0.0 -1.0 0.0)) + (when (> (.length move-dir) 0.0) + (.normalize move-dir) + (let [forward ^Vector3f (camera-forward yaw pitch) + right ^Vector3f (camera-right yaw) + up ^Vector3f (camera-up forward right) + velocity ^Vector3f (-> (Vector3f. right) + (.mul (.x move-dir)) + (.fma (.z move-dir) forward) + (.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] - (when (:orbit-mode @state) - (let [{:keys [^double orbit-yaw ^double orbit-pitch]} @state - last-orbit-x (double (or (:last-orbit-x @state) xpos)) - last-orbit-y (double (or (:last-orbit-y @state) ypos)) - sensitivity 0.005 - dx (- last-orbit-x xpos) - dy (- last-orbit-y ypos)] - (swap! state assoc - :last-orbit-x xpos - :last-orbit-y ypos - :orbit-yaw (- orbit-yaw (* dx sensitivity)) - :orbit-pitch (-> (- orbit-pitch (* dy sensitivity)) - (max (- (/ Math/PI 2))) - (min (/ Math/PI 2)))))) - #_(when-let [cursor-pos ^Vector2f (:drag-start-pos @state)] - (when (and (drag-detected? xpos ypos (.x cursor-pos) (.y cursor-pos))) - (swap! state assoc :orbit-mode true))) - (when (:fps-mode @state) - (let [{:keys [^double camera-yaw ^double camera-pitch]} @state - last-cursor-x (double (or (:last-cursor-x @state) xpos)) - last-cursor-y (double (or (:last-cursor-y @state) ypos)) - sensitivity 0.001 - dx (- last-cursor-x xpos) - dy (- last-cursor-y ypos)] - (swap! state assoc - :last-cursor-x xpos - :last-cursor-y ypos - :camera-yaw (- camera-yaw (* dx sensitivity)) - :camera-pitch (+ camera-pitch (* dy sensitivity)))))) + (when-let [camera (case (:current-mode @state) + :fly camera-fly + :orbit camera-orbit + :static nil)] + (when (and (= (:type @camera) :orbit)) + (let [{:keys [^double yaw ^double pitch last-x last-y]} @camera + last-x (double (or last-x xpos)) + last-y (double (or last-y ypos)) + sensitivity 0.005 + dx (- last-x xpos) + dy (- last-y ypos) + fwd ^Vector3f (camera-forward yaw pitch)] + (swap! camera assoc + :last-x xpos + :last-y ypos + :yaw (- yaw (* dx sensitivity)) + :pitch (-> (- pitch (* dy sensitivity)) + (max (- (/ Math/PI 2))) + (min (/ Math/PI 2)))))) + #_(when-let [cursor-pos ^Vector2f (:drag-start-pos @state)] + (when (and (drag-detected? xpos ypos (.x cursor-pos) (.y cursor-pos))) + (swap! state assoc :orbit-mode true))) + (when (and (= (:type @camera) :fly)) + (let [{:keys [^double yaw ^double pitch pos last-x last-y]} @camera + last-x (double (or last-x xpos)) + last-y (double (or last-y ypos)) + sensitivity 0.001 + dx (- last-x xpos) + 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] (let [xbuf (.mallocDouble stack 1) @@ -184,23 +202,24 @@ (cond (and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_PRESS)) (let [] - (swap! state assoc :fps-mode true) + (swap! state assoc :current-mode :fly) (GLFW/glfwSetInputMode win GLFW/GLFW_CURSOR GLFW/GLFW_CURSOR_DISABLED)) (and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_RELEASE)) (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)) (and (= button GLFW/GLFW_MOUSE_BUTTON_LEFT) (= action GLFW/GLFW_PRESS)) (do (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))) (and (= button GLFW/GLFW_MOUSE_BUTTON_LEFT) (= action GLFW/GLFW_RELEASE)) (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)))) (defn render-scene [^MemoryStack stack scene node ^Matrix4f mtx] @@ -229,6 +248,13 @@ (doseq [child children] (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] (let [w-buf (.mallocInt stack 1) h-buf (.mallocInt stack 1) @@ -248,34 +274,13 @@ (GL20/glUseProgram program) (let [vbuf (.mallocFloat stack 16) pbuf (.mallocFloat stack 16) - radius 10.0 - {:keys [^double camera-yaw ^double camera-pitch ^Vector3f camera-pos camera-target]} @state - fwd ^Vector3f (camera-forward camera-yaw camera-pitch) - camera-pos (if (:orbit-mode @state) - (let [fwd ^Vector3f (camera-forward (:orbit-yaw @state) (:orbit-pitch @state)) - orbit-pos (Vector3f. ^Vector3f (:focus-point @state))] - (.add orbit-pos (.mul (Vector3f. fwd) 8.0))) - 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) + camera (if (= :orbit (:current-mode @state)) + camera-orbit + camera-fly) + view (camera-view-matrix @camera) + proj (:projection-matrix @state)] + (.get ^Matrix4f view vbuf) + (.get ^Matrix4f proj pbuf) (GL20/glUniformMatrix4fv ^int (:mv uniforms) false vbuf) (GL20/glUniformMatrix4fv ^int (:mp uniforms) false pbuf) (render-scene stack scene (:hiearchy scene) (Matrix4f.))))) @@ -299,6 +304,18 @@ (reset! shaders nil) (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 [] (when-not (GLFW/glfwInit) (throw (RuntimeException. "Failed to init GLFW"))) @@ -307,7 +324,9 @@ (GLFW/glfwWindowHint GLFW/GLFW_CONTEXT_VERSION_MINOR 5) (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) (GLFW/glfwTerminate) (throw (RuntimeException. "Failed to create window"))) @@ -320,8 +339,6 @@ (GL11/glCullFace GL11/GL_BACK) (GLFW/glfwSwapInterval 1) - (swap! state assoc :window window :should-close false) - (GLFW/glfwSetKeyCallback window (reify GLFWKeyCallbackI (invoke [_ win key scancode action mods] @@ -350,6 +367,7 @@ (handle-mouse-buttons! win button action mods)) (catch Exception e (println "Mouse Button Callback Exception:" e)))))) + (init window width height) (loop [] (loop [] (when-let [f (.poll render-queue)] @@ -515,6 +533,8 @@ (reset! uploaded-scene (with-gl (upload-scene! @loaded-scene))) (reset! shaders (with-gl (load-shaders! "shaders/base.vert" "shaders/base.frag"))) + (swap! state assoc :current-state :static) + (with-gl (GL11/glClearColor 0.392 0.584 0.929 1.0)) (with-gl