Compare commits
11
Commits
10e37cc33f
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b667e0197 | ||
|
|
359755aad1 | ||
|
|
734100d52d | ||
|
|
e9a19baa84 | ||
|
|
7dc8ebf8b2 | ||
|
|
beed54fd18 | ||
|
|
fccb0ef878 | ||
|
|
f68619b25e | ||
|
|
44ef432386 | ||
|
|
cffe13054f | ||
|
|
ef02f61771 |
No files matched your search
@@ -1,2 +1,6 @@
|
||||
/.cpcache/
|
||||
/.nrepl-port
|
||||
/.clj-kondo/
|
||||
/.lsp/
|
||||
/target/
|
||||
*.jsa
|
||||
@@ -0,0 +1,14 @@
|
||||
(ns build
|
||||
(:require [clojure.tools.build.api :as b]))
|
||||
|
||||
(def basis (delay (b/create-basis {:project "deps.edn"})))
|
||||
|
||||
(defn uberjar [_]
|
||||
(b/delete {:path "target"})
|
||||
(b/compile-clj {:basis @basis
|
||||
:src-dirs ["src"]
|
||||
:class-dir "target/classes"})
|
||||
(b/uber {:class-dir "target/classes"
|
||||
:uber-file "target/nol.jar"
|
||||
:basis @basis
|
||||
:main 'nol.core}))
|
||||
@@ -5,8 +5,13 @@
|
||||
org.lwjgl/lwjgl$natives-linux {:mvn/version "3.3.4"}
|
||||
org.lwjgl/lwjgl-glfw$natives-linux {:mvn/version "3.3.4"}
|
||||
org.lwjgl/lwjgl-opengl$natives-linux {:mvn/version "3.3.4"}
|
||||
org.lwjgl/lwjgl-stb {:mvn/version "3.3.4"}
|
||||
org.lwjgl/lwjgl-stb$natives-linux {:mvn/version "3.3.4"}
|
||||
org.lwjgl/lwjgl-assimp {:mvn/version "3.3.3"}
|
||||
org.lwjgl/lwjgl-assimp$natives-linux {:mvn/version "3.3.3"}
|
||||
org.joml/joml {:mvn/version "1.10.7"}}
|
||||
:paths ["src"]
|
||||
:aliases {:dev {:jvm-opts ["-Djdk.attach.allowAttachSelf=true"]}}}
|
||||
:aliases {:dev {:jvm-opts ["-Djdk.attach.allowAttachSelf=true"]}
|
||||
:build {:extra-deps {io.github.clojure/tools.build {:mvn/version "0.10.6"}}
|
||||
:jvm-opts ["-Dclojure.compiler.direct-linking=true"]
|
||||
:ns-default build}}}
|
||||
+5
-1
@@ -1,10 +1,14 @@
|
||||
#version 330
|
||||
|
||||
uniform vec4 uColor;
|
||||
uniform sampler2D texture_id;
|
||||
|
||||
in vec2 vUV;
|
||||
out vec4 fragment;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragment = uColor;
|
||||
vec4 tex_color = texture(texture_id, vUV);
|
||||
|
||||
fragment = uColor * tex_color;
|
||||
}
|
||||
+9
-1
@@ -1,8 +1,16 @@
|
||||
#version 330
|
||||
|
||||
uniform mat4 uModel;
|
||||
uniform mat4 uView;
|
||||
uniform mat4 uProjection;
|
||||
|
||||
in vec3 vPos;
|
||||
in vec2 aUV;
|
||||
|
||||
out vec2 vUV;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(vPos, 1.0);
|
||||
vUV = aUV;
|
||||
gl_Position = uProjection * uView * uModel * vec4(vPos, 1.0);
|
||||
}
|
||||
+444
-59
@@ -1,20 +1,48 @@
|
||||
(ns nol.core
|
||||
(:require [clojure.math])
|
||||
(:import [org.lwjgl.assimp Assimp AIFace AIMaterial AIMesh AIScene AIVector3D]
|
||||
[org.lwjgl.glfw GLFW GLFWErrorCallbackI GLFWKeyCallbackI]
|
||||
[org.lwjgl.opengl GL GL11 GL15 GL20 GL33 GL30 GL45]
|
||||
[org.lwjgl.system MemoryUtil MemoryStack]
|
||||
[org.joml Matrix4f]
|
||||
[java.nio FloatBuffer]))
|
||||
(:gen-class)
|
||||
(:require
|
||||
[clojure.math])
|
||||
(:import
|
||||
[java.nio ByteBuffer FloatBuffer IntBuffer]
|
||||
[java.util.concurrent LinkedBlockingQueue]
|
||||
[org.lwjgl PointerBuffer]
|
||||
[org.lwjgl.assimp AIColor4D AIFace AIMaterial AIMatrix4x4 AIMesh AINode AIScene AIString AITexture AIVector3D Assimp]
|
||||
[org.lwjgl.glfw GLFW GLFWKeyCallbackI]
|
||||
[org.lwjgl.opengl GL GL11 GL20 GL30 GL45]
|
||||
[org.lwjgl.stb STBImage]
|
||||
[org.joml Matrix4f Vector2f Vector3f]
|
||||
[org.lwjgl.system MemoryStack MemoryUtil]))
|
||||
|
||||
(def render-queue (java.util.concurrent.LinkedBlockingQueue.))
|
||||
(def ^LinkedBlockingQueue render-queue (LinkedBlockingQueue.))
|
||||
|
||||
(def state (atom {:last-time (GLFW/glfwGetTime) :window nil}))
|
||||
(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.))}))
|
||||
|
||||
(def gl-thread (atom nil))
|
||||
(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
|
||||
:current-mode :static}))
|
||||
|
||||
(defonce gl-thread (atom nil))
|
||||
|
||||
(defmacro with-gl [& body]
|
||||
`(let [result# (promise)]
|
||||
(when-not (and @gl-thread (.isAlive ^Thread @gl-thread))
|
||||
(throw (RuntimeException. "GL thread is not running")))
|
||||
(.put render-queue #(deliver result# (try (do ~@body)
|
||||
(catch Exception e# e#))))
|
||||
(let [r# (deref result#)]
|
||||
@@ -22,10 +50,6 @@
|
||||
(throw r#)
|
||||
r#))))
|
||||
|
||||
(def vertex-shader-text (slurp "shaders/base.vert"))
|
||||
|
||||
(def fragment-shader-text (slurp "shaders/base.frag"))
|
||||
|
||||
(defn- compile-shader [type source]
|
||||
(let [shader (GL20/glCreateShader type)]
|
||||
(GL20/glShaderSource shader ^String source)
|
||||
@@ -43,32 +67,254 @@
|
||||
(throw (RuntimeException. (str "Program link error: " (GL20/glGetProgramInfoLog program)))))
|
||||
program))
|
||||
|
||||
(defn load-model [path]
|
||||
(Assimp/aiImportFile path (bit-or Assimp/aiProcess_Triangulate
|
||||
Assimp/aiProcess_FlipUVs)))
|
||||
(defn ai-matrix->matrix4f [^AIMatrix4x4 m]
|
||||
(Matrix4f. (.a1 m) (.b1 m) (.c1 m) (.d1 m)
|
||||
(.a2 m) (.b2 m) (.c2 m) (.d2 m)
|
||||
(.a3 m) (.b3 m) (.c3 m) (.d3 m)
|
||||
(.a4 m) (.b4 m) (.c4 m) (.d4 m)))
|
||||
|
||||
(def vertices (float-array [0.0 1.0 0.0
|
||||
-1.0 -1.0 0.0
|
||||
1.0 -1.0 0.0]))
|
||||
(defn matrix4f->vectors [^Matrix4f m]
|
||||
[(vector (.m00 m) (.m01 m) (.m02 m) (.m03 m))
|
||||
(vector (.m10 m) (.m11 m) (.m12 m) (.m13 m))
|
||||
(vector (.m20 m) (.m21 m) (.m22 m) (.m23 m))
|
||||
(vector (.m30 m) (.m31 m) (.m32 m) (.m33 m))])
|
||||
|
||||
(defn- update-loop [window stack data]
|
||||
(defn node-matrix4f [^AINode c]
|
||||
(-> c
|
||||
(.mTransformation)
|
||||
(ai-matrix->matrix4f)))
|
||||
|
||||
(defonce loaded-scene (atom nil))
|
||||
(defonce uploaded-scene (atom nil))
|
||||
(defonce shaders (atom nil))
|
||||
|
||||
(defn camera-forward [^double yaw ^double pitch]
|
||||
(Vector3f. (* (Math/cos pitch) (Math/sin yaw))
|
||||
(Math/sin pitch)
|
||||
(- (* (Math/cos pitch) (Math/cos yaw)))))
|
||||
|
||||
(defn camera-right [^double yaw]
|
||||
(Vector3f. (Math/cos yaw) 0.0 (Math/sin yaw)))
|
||||
|
||||
(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))
|
||||
|
||||
(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 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
|
||||
dx (- x2 x1)
|
||||
dy (- y2 y1)]
|
||||
(< threshold (+ (* dx dx) (* dy dy)))))
|
||||
|
||||
(def cam-speed 0.04)
|
||||
|
||||
(defn- handle-keyboard-state! [window]
|
||||
(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-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)
|
||||
ybuf (.mallocDouble stack 1)]
|
||||
(GLFW/glfwGetCursorPos window xbuf ybuf)
|
||||
(Vector2f. (.get xbuf 0) (.get ybuf 0))))
|
||||
|
||||
(defn- handle-mouse-buttons! [^long win button action mods]
|
||||
(cond
|
||||
(and (= button GLFW/GLFW_MOUSE_BUTTON_RIGHT) (= action GLFW/GLFW_PRESS))
|
||||
(let []
|
||||
(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 :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 :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 :current-mode :static :drag-start-pos nil)
|
||||
(GLFW/glfwSetCursor win MemoryUtil/NULL))))
|
||||
|
||||
(defn render-scene [^MemoryStack stack scene node ^Matrix4f mtx]
|
||||
(let [buf (.mallocFloat stack 16)
|
||||
model-mtx (Matrix4f.)]
|
||||
(.mul mtx ^Matrix4f (:transform node) model-mtx)
|
||||
(.get model-mtx buf)
|
||||
(when-let [mesh-indices (seq (:meshes node))]
|
||||
(let [{:keys [gl-meshes gl-textures]} scene
|
||||
{:keys [_program uniforms]} @shaders]
|
||||
(doseq [idx mesh-indices
|
||||
:let [gl-mesh (nth gl-meshes idx)
|
||||
mesh (:mesh gl-mesh)
|
||||
[r g b a] (:color mesh)
|
||||
tex-idx (:texture-idx mesh)]]
|
||||
(GL20/glUniform4f (:color uniforms) r g b a)
|
||||
(GL20/glUniformMatrix4fv ^int (:mm uniforms) false buf)
|
||||
(GL30/glBindVertexArray (:vao gl-mesh))
|
||||
(when tex-idx
|
||||
(let [tex-id (:gl-id (nth gl-textures tex-idx))]
|
||||
(GL45/glActiveTexture GL45/GL_TEXTURE0)
|
||||
(GL11/glBindTexture GL11/GL_TEXTURE_2D tex-id)
|
||||
(GL20/glUniform1i (:tex uniforms) 0)))
|
||||
(GL11/glDrawElements GL11/GL_TRIANGLES (:indices-count gl-mesh) GL11/GL_UNSIGNED_INT 0))))
|
||||
(when-let [children (seq (:children node))]
|
||||
(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)
|
||||
_ (GLFW/glfwGetFramebufferSize window w-buf h-buf)
|
||||
_ (GLFW/glfwGetFramebufferSize ^long window w-buf h-buf)
|
||||
w (.get w-buf 0)
|
||||
h (.get h-buf 0)
|
||||
t (GLFW/glfwGetTime)]
|
||||
(GL11/glViewport 0 0 w h)
|
||||
(GL11/glClear GL11/GL_COLOR_BUFFER_BIT)
|
||||
(GL11/glClear (bit-or GL11/GL_COLOR_BUFFER_BIT GL11/GL_DEPTH_BUFFER_BIT))
|
||||
|
||||
(swap! state assoc :last-time t)
|
||||
(try
|
||||
(when (and @uploaded-scene @shaders)
|
||||
(handle-keyboard-state! window)
|
||||
(let [scene @uploaded-scene
|
||||
{:keys [program uniforms]} @shaders]
|
||||
(GL20/glUseProgram program)
|
||||
(let [vbuf (.mallocFloat stack 16)
|
||||
pbuf (.mallocFloat stack 16)
|
||||
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.)))))
|
||||
(catch Exception e
|
||||
(println "Update Loop GL thread error:" e)))
|
||||
|
||||
(GL30/glBindVertexArray 0)
|
||||
(GL20/glUseProgram 0)
|
||||
|
||||
(GLFW/glfwSwapBuffers window)))
|
||||
|
||||
(defn stop! []
|
||||
(with-gl
|
||||
(GLFW/glfwSetWindowShouldClose
|
||||
(:window @state) true)))
|
||||
(when @uploaded-scene
|
||||
(doseq [{:keys [^int vbo ^int vao ^int ebo]} (:gl-meshes @uploaded-scene)]
|
||||
(GL45/glDeleteBuffers vbo)
|
||||
(GL45/glDeleteBuffers ebo)
|
||||
(GL30/glDeleteVertexArrays vao))
|
||||
(doseq [{:keys [^int gl-id]} (:gl-textures @uploaded-scene)]
|
||||
(GL11/glDeleteTextures gl-id)))
|
||||
(reset! uploaded-scene nil)
|
||||
(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)
|
||||
@@ -78,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 300 #_592 "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")))
|
||||
@@ -86,20 +334,45 @@
|
||||
|
||||
(GLFW/glfwMakeContextCurrent window)
|
||||
(GL/createCapabilities)
|
||||
(GL11/glEnable GL11/GL_DEPTH_TEST)
|
||||
(GL11/glEnable GL11/GL_CULL_FACE)
|
||||
(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]
|
||||
(when (and (= key GLFW/GLFW_KEY_ESCAPE) (= action GLFW/GLFW_PRESS))
|
||||
(swap! state assoc :should-close true)))))
|
||||
|
||||
(let []
|
||||
(try
|
||||
(handle-key-actions! win key scancode action mods)
|
||||
(catch Exception e
|
||||
(println "Key Callback Exception:" e))))))
|
||||
(when (GLFW/glfwRawMouseMotionSupported)
|
||||
(GLFW/glfwSetInputMode window GLFW/GLFW_RAW_MOUSE_MOTION GLFW/GLFW_TRUE))
|
||||
(GLFW/glfwSetCursorPosCallback window
|
||||
(reify org.lwjgl.glfw.GLFWCursorPosCallbackI
|
||||
(invoke [_ win xpos ypos]
|
||||
(try
|
||||
(handle-mouse-move! win xpos ypos)
|
||||
(catch Exception e
|
||||
(println "Cursor Pos Callback Exception:" e))))))
|
||||
(GLFW/glfwSetMouseButtonCallback window
|
||||
(reify org.lwjgl.glfw.GLFWMouseButtonCallbackI
|
||||
(invoke [_ win button action mods]
|
||||
(try
|
||||
(let [mods (cond-> #{}
|
||||
(pos? (bit-and mods GLFW/GLFW_MOD_SHIFT)) (conj :shift)
|
||||
(pos? (bit-and mods GLFW/GLFW_MOD_CONTROL)) (conj :ctrl)
|
||||
(pos? (bit-and mods GLFW/GLFW_MOD_ALT)) (conj :alt)
|
||||
(pos? (bit-and mods GLFW/GLFW_MOD_SUPER)) (conj :super))]
|
||||
(handle-mouse-buttons! win button action mods))
|
||||
(catch Exception e
|
||||
(println "Mouse Button Callback Exception:" e))))))
|
||||
(init window width height)
|
||||
(loop []
|
||||
(while (not (.isEmpty render-queue))
|
||||
((.take render-queue)))
|
||||
(loop []
|
||||
(when-let [f (.poll render-queue)]
|
||||
(f)
|
||||
(recur)))
|
||||
(when-not (or (:should-close @state)
|
||||
(GLFW/glfwWindowShouldClose window))
|
||||
(GLFW/glfwPollEvents)
|
||||
@@ -108,13 +381,14 @@
|
||||
(update-loop window stack {})
|
||||
(finally
|
||||
(MemoryStack/stackPop))))
|
||||
(recur))))
|
||||
(recur)))
|
||||
|
||||
(stop!)
|
||||
(GLFW/glfwDestroyWindow window)
|
||||
(GLFW/glfwTerminate)))
|
||||
|
||||
(defn start! []
|
||||
(when (and @gl-thread (.isAlive @gl-thread))
|
||||
(when (and @gl-thread (.isAlive ^Thread @gl-thread))
|
||||
(throw (RuntimeException. "Already running")))
|
||||
(reset! gl-thread
|
||||
(doto (Thread. (fn []
|
||||
@@ -126,33 +400,144 @@
|
||||
(.setDaemon true)
|
||||
(.start))))
|
||||
|
||||
(defn load-model [^String path]
|
||||
(let [flags (bit-or Assimp/aiProcess_Triangulate
|
||||
Assimp/aiProcess_FlipUVs)]
|
||||
(Assimp/aiImportFile path flags)))
|
||||
|
||||
(defn extract-mesh [^AIScene scene ^long idx]
|
||||
(let [meshes ^PointerBuffer (.mMeshes scene)]
|
||||
(let [mesh (AIMesh/create (long (.get meshes idx)))
|
||||
mat-idx (.mMaterialIndex mesh)
|
||||
mat-ptr ^long (.get (.mMaterials scene) mat-idx)
|
||||
material (AIMaterial/create (long mat-ptr))
|
||||
mat-color (AIColor4D/create)
|
||||
_ (Assimp/aiGetMaterialColor material Assimp/AI_MATKEY_COLOR_DIFFUSE 0 0 ^AIColor4D mat-color)
|
||||
^IntBuffer no-int nil
|
||||
^FloatBuffer no-float nil
|
||||
path (AIString/create)
|
||||
_ (Assimp/aiGetMaterialTexture material Assimp/aiTextureType_DIFFUSE 0 path no-int no-int no-float no-int no-int no-int)
|
||||
data-path (.dataString path)
|
||||
verts (.mVertices mesh)
|
||||
n (.mNumVertices mesh)
|
||||
vert-arr (float-array (* 5 n))
|
||||
faces (.mFaces mesh)
|
||||
nf (.mNumFaces mesh)
|
||||
idx-arr (int-array (* 3 nf))
|
||||
uvs (.mTextureCoords mesh 0)]
|
||||
(dotimes [i n]
|
||||
(let [vert ^AIVector3D (.get verts i)
|
||||
uv (when uvs (.get uvs i))
|
||||
[^float u ^float v] (if uvs [(.x ^AIVector3D uv) (.y ^AIVector3D uv)] [0.0 0.0])]
|
||||
(aset vert-arr (+ (* i 5) 0) (.x vert))
|
||||
(aset vert-arr (+ (* i 5) 1) (.y vert))
|
||||
(aset vert-arr (+ (* i 5) 2) (.z vert))
|
||||
(aset vert-arr (+ (* i 5) 3) u)
|
||||
(aset vert-arr (+ (* i 5) 4) v)))
|
||||
(dotimes [i nf]
|
||||
(let [face ^AIFace (.get faces i)
|
||||
idxs (.mIndices face)]
|
||||
(aset idx-arr (+ (* i 3) 0) (.get idxs 0))
|
||||
(aset idx-arr (+ (* i 3) 1) (.get idxs 1))
|
||||
(aset idx-arr (+ (* i 3) 2) (.get idxs 2))))
|
||||
{:verts vert-arr :indices idx-arr :mat-idx (.mMaterialIndex mesh)
|
||||
:texture-idx (when (.startsWith data-path "*")
|
||||
(parse-long (subs data-path 1)))
|
||||
:color [(.r mat-color) (.g mat-color) (.b mat-color) (.a mat-color)]})))
|
||||
|
||||
(defn traverse [^AINode node]
|
||||
{:node node
|
||||
:meshes (let [buf (.mMeshes node)]
|
||||
(mapv #(.get buf ^int %) (range (.mNumMeshes node))))
|
||||
:transform (node-matrix4f node)
|
||||
:children (mapv #(traverse (AINode/create ^long (.get (.mChildren node) ^int %)))
|
||||
(range (.mNumChildren node)))})
|
||||
|
||||
(defn load-scene [path]
|
||||
(let [scene ^AIScene (load-model path)
|
||||
meshes (doall
|
||||
(for [i (range (.mNumMeshes scene))]
|
||||
(extract-mesh scene i)))
|
||||
textures (let [texs (.mTextures scene)]
|
||||
(doall
|
||||
(for [i (range (.mNumTextures scene))]
|
||||
(let [ai-tex (AITexture/create (.get texs ^int i))
|
||||
buf (.pcDataCompressed ai-tex)
|
||||
w (int-array 1)
|
||||
h (int-array 1)
|
||||
channels (int-array 1)]
|
||||
{:data (STBImage/stbi_load_from_memory buf w h channels 4)
|
||||
:w (aget w 0) :h (aget h 0)}))))
|
||||
hiearchy (traverse (.mRootNode scene))]
|
||||
(Assimp/aiReleaseImport scene)
|
||||
{:meshes meshes :textures textures :hiearchy hiearchy}))
|
||||
|
||||
(defn upload-scene! [scene]
|
||||
(let [gl-meshes
|
||||
(mapv
|
||||
#(let [vbo ^int (GL45/glCreateBuffers)
|
||||
ebo ^int (GL45/glCreateBuffers)
|
||||
vao ^int (GL45/glCreateVertexArrays)]
|
||||
(GL45/glNamedBufferStorage vbo ^floats (:verts %) GL45/GL_DYNAMIC_STORAGE_BIT)
|
||||
(GL45/glNamedBufferStorage ebo ^ints (:indices %) GL45/GL_DYNAMIC_STORAGE_BIT)
|
||||
(GL45/glVertexArrayElementBuffer vao ebo)
|
||||
(GL45/glVertexArrayVertexBuffer vao 0 vbo 0 (* 5 Float/BYTES))
|
||||
|
||||
(GL45/glVertexArrayAttribFormat vao 0 3 GL11/GL_FLOAT false 0)
|
||||
(GL45/glVertexArrayAttribBinding vao 0 0)
|
||||
|
||||
(GL45/glVertexArrayAttribFormat vao 1 2 GL11/GL_FLOAT false (* 3 Float/BYTES))
|
||||
(GL45/glVertexArrayAttribBinding vao 1 0)
|
||||
|
||||
(GL45/glEnableVertexArrayAttrib vao 0)
|
||||
(GL45/glEnableVertexArrayAttrib vao 1)
|
||||
{:vbo vbo :vao vao :ebo ebo
|
||||
:mesh %
|
||||
:indices-count (alength ^ints (:indices %))})
|
||||
(:meshes scene))
|
||||
gl-textures
|
||||
(mapv
|
||||
#(let [w (:w %)
|
||||
h (:h %)
|
||||
tex-id (GL45/glCreateTextures GL11/GL_TEXTURE_2D)]
|
||||
(GL45/glTextureStorage2D tex-id 1 GL11/GL_RGBA8 w h)
|
||||
(GL45/glTextureSubImage2D tex-id 0 0 0 ^int w ^int h GL11/GL_RGBA GL11/GL_UNSIGNED_BYTE ^ByteBuffer (:data %))
|
||||
(GL45/glTextureParameteri tex-id GL11/GL_TEXTURE_MIN_FILTER GL11/GL_LINEAR)
|
||||
(GL45/glTextureParameteri tex-id GL11/GL_TEXTURE_MAG_FILTER GL11/GL_LINEAR)
|
||||
(assoc % :gl-id tex-id))
|
||||
(:textures scene))]
|
||||
(assoc scene :gl-textures gl-textures :gl-meshes gl-meshes)))
|
||||
|
||||
(defn load-shaders! [vert-path frag-path]
|
||||
(let [vert (compile-shader GL20/GL_VERTEX_SHADER (slurp vert-path))
|
||||
frag (compile-shader GL20/GL_FRAGMENT_SHADER (slurp frag-path))
|
||||
program (link-program vert frag)]
|
||||
{:program program
|
||||
:uniforms {:color (GL20/glGetUniformLocation ^int program "uColor")
|
||||
:tex (GL20/glGetUniformLocation ^int program "texture_id")
|
||||
:mm (GL20/glGetUniformLocation ^int program "uModel")
|
||||
:mv (GL20/glGetUniformLocation ^int program "uView")
|
||||
:mp (GL20/glGetUniformLocation ^int program "uProjection")}}))
|
||||
|
||||
(comment
|
||||
(start!)
|
||||
(stop!)
|
||||
(def ^AIScene scene (load-model "assets/model.glb"))
|
||||
(.mNumMeshes scene) ;; 54
|
||||
(.mNumMaterials scene) ;; 17
|
||||
(with-gl (stop!))
|
||||
|
||||
(reset! loaded-scene
|
||||
(do
|
||||
(when @loaded-scene
|
||||
(doseq [tex (:textures @loaded-scene)]
|
||||
(when-let [data (:data tex)]
|
||||
(STBImage/stbi_image_free ^ByteBuffer data))))
|
||||
(load-scene "assets/model.glb")))
|
||||
(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
|
||||
(GL45/glNamedBufferSubData (:vbo @state) 0 ^floats (float-array [ 0.0 0.5 0.0
|
||||
-0.5 -0.5 0.0
|
||||
0.5 -0.5 0.0])))
|
||||
(with-gl
|
||||
(GL45/glNamedBufferSubData (:vbo @state) 0 ^floats (float-array [ 0.0 1.0 0.0
|
||||
-1.0 -1.0 0.0
|
||||
1.0 -1.0 0.0])))
|
||||
|
||||
(def program (with-gl (let [vert (compile-shader GL20/GL_VERTEX_SHADER vertex-shader-text)
|
||||
frag (compile-shader GL20/GL_FRAGMENT_SHADER fragment-shader-text)]
|
||||
(link-program vert frag))))
|
||||
|
||||
(with-gl (GL45/glVertexArrayVertexBuffer vao 0 vbo 0 12))
|
||||
|
||||
(with-gl (GL45/glEnableVertexArrayAttrib vao 0)
|
||||
(GL45/glVertexArrayAttribFormat vao 0 3 GL45/GL_FLOAT false 0)
|
||||
(GL45/glVertexArrayAttribBinding vao 0 0))
|
||||
(GL11/glClearColor 0.2 0.25 0.3 1.0))
|
||||
|
||||
:-)
|
||||
Reference in new issue
Block a user