Compare commits
13
Commits
b3c1fc5edf
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b667e0197 | ||
|
|
359755aad1 | ||
|
|
734100d52d | ||
|
|
e9a19baa84 | ||
|
|
7dc8ebf8b2 | ||
|
|
beed54fd18 | ||
|
|
fccb0ef878 | ||
|
|
f68619b25e | ||
|
|
44ef432386 | ||
|
|
cffe13054f | ||
|
|
ef02f61771 | ||
|
|
10e37cc33f | ||
|
|
0f8dae0943 |
No files matched your search
@@ -1,2 +1,6 @@
|
|||||||
/.cpcache/
|
/.cpcache/
|
||||||
/.nrepl-port
|
/.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,7 +5,13 @@
|
|||||||
org.lwjgl/lwjgl$natives-linux {:mvn/version "3.3.4"}
|
org.lwjgl/lwjgl$natives-linux {:mvn/version "3.3.4"}
|
||||||
org.lwjgl/lwjgl-glfw$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-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 {:mvn/version "3.3.3"}
|
||||||
org.lwjgl/lwjgl-assimp$natives-linux {:mvn/version "3.3.3"}
|
org.lwjgl/lwjgl-assimp$natives-linux {:mvn/version "3.3.3"}
|
||||||
org.joml/joml {:mvn/version "1.10.7"}}
|
org.joml/joml {:mvn/version "1.10.7"}}
|
||||||
:paths ["src"]}
|
:paths ["src"]
|
||||||
|
: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}}}
|
||||||
+6
-2
@@ -1,10 +1,14 @@
|
|||||||
#version 330
|
#version 330
|
||||||
|
|
||||||
in vec3 color;
|
uniform vec4 uColor;
|
||||||
|
uniform sampler2D texture_id;
|
||||||
|
|
||||||
|
in vec2 vUV;
|
||||||
out vec4 fragment;
|
out vec4 fragment;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
fragment = vec4(color, 1.0);
|
vec4 tex_color = texture(texture_id, vUV);
|
||||||
|
|
||||||
|
fragment = uColor * tex_color;
|
||||||
}
|
}
|
||||||
+8
-6
@@ -1,14 +1,16 @@
|
|||||||
#version 330
|
#version 330
|
||||||
|
|
||||||
uniform mat4 MVP;
|
uniform mat4 uModel;
|
||||||
|
uniform mat4 uView;
|
||||||
|
uniform mat4 uProjection;
|
||||||
|
|
||||||
in vec3 vCol;
|
in vec3 vPos;
|
||||||
in vec2 vPos;
|
in vec2 aUV;
|
||||||
|
|
||||||
out vec3 color;
|
out vec2 vUV;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_Position = MVP * vec4(vPos, 0.0, 1.0);
|
vUV = aUV;
|
||||||
color = vCol;
|
gl_Position = uProjection * uView * uModel * vec4(vPos, 1.0);
|
||||||
}
|
}
|
||||||
+477
-29
@@ -1,14 +1,54 @@
|
|||||||
(ns nol.core
|
(ns nol.core
|
||||||
(:import [org.lwjgl.assimp Assimp AIScene AIMesh AIVector3D AIFace]
|
(:gen-class)
|
||||||
[org.lwjgl.glfw GLFW GLFWKeyCallbackI GLFWErrorCallbackI]
|
(:require
|
||||||
[org.lwjgl.opengl GL GL45 GL33 GL20 GL15 GL11 GL30]
|
[clojure.math])
|
||||||
[org.lwjgl.system MemoryStack MemoryUtil]
|
(:import
|
||||||
[org.joml Matrix4f]
|
[java.nio ByteBuffer FloatBuffer IntBuffer]
|
||||||
[java.nio FloatBuffer]))
|
[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 vertex-shader-text (slurp "shaders/base.vert"))
|
(def ^LinkedBlockingQueue render-queue (LinkedBlockingQueue.))
|
||||||
|
|
||||||
(def fragment-shader-text (slurp "shaders/base.frag"))
|
(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
|
||||||
|
: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#)]
|
||||||
|
(if (instance? Exception r#)
|
||||||
|
(throw r#)
|
||||||
|
r#))))
|
||||||
|
|
||||||
(defn- compile-shader [type source]
|
(defn- compile-shader [type source]
|
||||||
(let [shader (GL20/glCreateShader type)]
|
(let [shader (GL20/glCreateShader type)]
|
||||||
@@ -27,30 +67,255 @@
|
|||||||
(throw (RuntimeException. (str "Program link error: " (GL20/glGetProgramInfoLog program)))))
|
(throw (RuntimeException. (str "Program link error: " (GL20/glGetProgramInfoLog program)))))
|
||||||
program))
|
program))
|
||||||
|
|
||||||
(def state (atom {:last-time (GLFW/glfwGetTime)}))
|
(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 load-model [path]
|
(defn matrix4f->vectors [^Matrix4f m]
|
||||||
(Assimp/aiImportFile path (bit-or Assimp/aiProcess_Triangulate
|
[(vector (.m00 m) (.m01 m) (.m02 m) (.m03 m))
|
||||||
Assimp/aiProcess_FlipUVs)))
|
(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))])
|
||||||
|
|
||||||
(def vertices (float-array [0.0 0.5 0.0
|
(defn node-matrix4f [^AINode c]
|
||||||
-0.5 0.5 0.0
|
(-> c
|
||||||
0.5 -0.5 0.0]))
|
(.mTransformation)
|
||||||
|
(ai-matrix->matrix4f)))
|
||||||
|
|
||||||
(defn- update-loop [window stack data]
|
(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)
|
(let [w-buf (.mallocInt stack 1)
|
||||||
h-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)
|
w (.get w-buf 0)
|
||||||
h (.get h-buf 0)
|
h (.get h-buf 0)
|
||||||
current-time (GLFW/glfwGetTime)]
|
t (GLFW/glfwGetTime)]
|
||||||
(GL11/glViewport 0 0 w h)
|
(GL11/glViewport 0 0 w h)
|
||||||
(GL11/glClear GL11/GL_COLOR_BUFFER_BIT)
|
(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 current-time)
|
(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)))
|
(GLFW/glfwSwapBuffers window)))
|
||||||
|
|
||||||
|
(defn stop! []
|
||||||
|
(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 []
|
(defn -main []
|
||||||
(when-not (GLFW/glfwInit)
|
(when-not (GLFW/glfwInit)
|
||||||
(throw (RuntimeException. "Failed to init GLFW")))
|
(throw (RuntimeException. "Failed to init GLFW")))
|
||||||
@@ -59,37 +324,220 @@
|
|||||||
(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 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)
|
(when (= window MemoryUtil/NULL)
|
||||||
(GLFW/glfwTerminate)
|
(GLFW/glfwTerminate)
|
||||||
(throw (RuntimeException. "Failed to create window")))
|
(throw (RuntimeException. "Failed to create window")))
|
||||||
|
|
||||||
|
|
||||||
(GLFW/glfwMakeContextCurrent window)
|
(GLFW/glfwMakeContextCurrent window)
|
||||||
(GL/createCapabilities)
|
(GL/createCapabilities)
|
||||||
|
(GL11/glEnable GL11/GL_DEPTH_TEST)
|
||||||
|
(GL11/glEnable GL11/GL_CULL_FACE)
|
||||||
|
(GL11/glCullFace GL11/GL_BACK)
|
||||||
(GLFW/glfwSwapInterval 1)
|
(GLFW/glfwSwapInterval 1)
|
||||||
|
|
||||||
(GLFW/glfwSetKeyCallback window
|
(GLFW/glfwSetKeyCallback window
|
||||||
(reify GLFWKeyCallbackI
|
(reify GLFWKeyCallbackI
|
||||||
(invoke [_ win key scancode action mods]
|
(invoke [_ win key scancode action mods]
|
||||||
(when (and (= key GLFW/GLFW_KEY_ESCAPE) (= action GLFW/GLFW_PRESS))
|
(try
|
||||||
(GLFW/glfwSetWindowShouldClose win true)))))
|
(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 []
|
(loop []
|
||||||
(when-not (GLFW/glfwWindowShouldClose window)
|
(loop []
|
||||||
|
(when-let [f (.poll render-queue)]
|
||||||
|
(f)
|
||||||
|
(recur)))
|
||||||
|
(when-not (or (:should-close @state)
|
||||||
|
(GLFW/glfwWindowShouldClose window))
|
||||||
(GLFW/glfwPollEvents)
|
(GLFW/glfwPollEvents)
|
||||||
(let [stack (MemoryStack/stackPush)]
|
(let [stack (MemoryStack/stackPush)]
|
||||||
(try
|
(try
|
||||||
(update-loop window stack)
|
(update-loop window stack {})
|
||||||
(finally
|
(finally
|
||||||
(MemoryStack/stackPop))))
|
(MemoryStack/stackPop))))
|
||||||
(recur)))
|
(recur)))
|
||||||
|
|
||||||
|
(stop!)
|
||||||
(GLFW/glfwDestroyWindow window)
|
(GLFW/glfwDestroyWindow window)
|
||||||
(GLFW/glfwTerminate)))
|
(GLFW/glfwTerminate)))
|
||||||
|
|
||||||
|
(defn start! []
|
||||||
|
(when (and @gl-thread (.isAlive ^Thread @gl-thread))
|
||||||
|
(throw (RuntimeException. "Already running")))
|
||||||
|
(reset! gl-thread
|
||||||
|
(doto (Thread. (fn []
|
||||||
|
(try
|
||||||
|
(-main)
|
||||||
|
(catch Exception e
|
||||||
|
(println "GL thread error:" e)))))
|
||||||
|
(.setName "opengl-thread")
|
||||||
|
(.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
|
(comment
|
||||||
(-main)
|
(start!)
|
||||||
(def ^AIScene scene (load-model "assets/model.glb"))
|
(with-gl (stop!))
|
||||||
(.mNumMeshes scene) ;; 54
|
|
||||||
(.mNumMaterials scene) ;; 17
|
(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.2 0.25 0.3 1.0))
|
||||||
|
|
||||||
:-)
|
:-)
|
||||||
Reference in new issue
Block a user