Compare commits

...
9 Commits
6 changed files with 460 additions and 102 deletions

No files matched your search

+4
View File
@@ -1,2 +1,6 @@
/.cpcache/
/.nrepl-port
/.clj-kondo/
/.lsp/
/target/
*.jsa
+14
View File
@@ -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}))
+6 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+422 -99
View File
@@ -1,20 +1,48 @@
(ns nol.core
(:require [clojure.math])
(:import [org.lwjgl.assimp AIColor4D AIFace AIMaterial AIMaterial$Buffer AIMesh AIScene AIVector3D Assimp]
[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,40 +67,254 @@
(throw (RuntimeException. (str "Program link error: " (GL20/glGetProgramInfoLog program)))))
program))
(def vertices (float-array [0.0 1.0 0.0
-1.0 -1.0 0.0
1.0 -1.0 0.0]))
(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)))
(defn- update-loop [window stack data]
(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 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)
(do
(try
(when (and @uploaded-scene @shaders)
(handle-keyboard-state! window)
(let [scene @uploaded-scene
{:keys [program uniforms]} @shaders]
(GL20/glUseProgram program)
(let [color-loc (GL20/glGetUniformLocation program "uColor")]
(doseq [m meshes
:let [[r g b a] (:color (:mesh m))]]
(GL20/glUniform4f color-loc r g b a)
(GL30/glBindVertexArray (:vao m))
(GL11/glDrawElements GL11/GL_TRIANGLES (:indices-count m) GL11/GL_UNSIGNED_INT 0)))
(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))
(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)
@@ -86,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 #_1900 300 #_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")))
@@ -94,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)
@@ -116,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 []
@@ -134,87 +400,144 @@
(.setDaemon true)
(.start))))
(defn extract-mesh [^AIScene scene idx]
(let [meshes (.mMeshes scene)]
(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 (* 3 n))
vert-arr (float-array (* 5 n))
faces (.mFaces mesh)
nf (.mNumFaces mesh)
idx-arr (int-array (* 3 nf))]
idx-arr (int-array (* 3 nf))
uvs (.mTextureCoords mesh 0)]
(dotimes [i n]
(let [v (.get verts i)]
(aset vert-arr (+ (* i 3) 0) (.x v))
(aset vert-arr (+ (* i 3) 1) (.y v))
(aset vert-arr (+ (* i 3) 2) (.z v))))
(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 (.get faces i)
(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)
:color (let [color [(.r mat-color) (.g mat-color) (.b mat-color) (.a mat-color)]]
(if (= color [1.0 1.0 1.0 1.0])
[(rand) (rand) (rand) 1.0]
color))})))
: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 load-model [path]
(let [flags (bit-or Assimp/aiProcess_Triangulate
Assimp/aiProcess_FlipUVs)]
(Assimp/aiImportFile path flags)))
(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"))
(with-gl (stop!))
(def meshes (with-gl
(doall
(for [i (range (.mNumMeshes scene))]
(let [mesh (extract-mesh scene i)
vbo (GL45/glCreateBuffers)
ebo (GL45/glCreateBuffers)
vao (GL45/glCreateVertexArrays)]
(GL45/glNamedBufferStorage vbo (:verts mesh) GL45/GL_DYNAMIC_STORAGE_BIT)
(GL45/glNamedBufferStorage ebo (:indices mesh) GL45/GL_DYNAMIC_STORAGE_BIT)
(GL45/glVertexArrayElementBuffer vao ebo)
(GL45/glVertexArrayVertexBuffer vao 0 vbo 0 (* 3 Float/BYTES))
(GL45/glVertexArrayAttribFormat vao 0 3 GL11/GL_FLOAT false 0)
(GL45/glVertexArrayAttribBinding vao 0 0)
(GL45/glEnableVertexArrayAttrib vao 0)
{:vbo vbo :vao vao :ebo ebo
:mesh mesh
:indices-count (alength (:indices mesh))})))))
(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
(GL11/glClearColor 0.0 0.0 0.0 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))
:-)