From 0f8dae0943a7044dbe0b1c828f3ea5a8e7fd3c03 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Fri, 6 Mar 2026 09:05:46 +0700 Subject: [PATCH] Render triangle --- deps.edn | 3 +- shaders/base.frag | 4 +- shaders/base.vert | 10 +--- src/nol/core.clj | 125 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 113 insertions(+), 29 deletions(-) diff --git a/deps.edn b/deps.edn index ca74645..b054c1a 100644 --- a/deps.edn +++ b/deps.edn @@ -8,4 +8,5 @@ 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"]} + :paths ["src"] + :aliases {:dev {:jvm-opts ["-Djdk.attach.allowAttachSelf=true"]}}} diff --git a/shaders/base.frag b/shaders/base.frag index 288b944..5fe6e77 100644 --- a/shaders/base.frag +++ b/shaders/base.frag @@ -1,10 +1,10 @@ #version 330 -in vec3 color; +uniform vec4 uColor; out vec4 fragment; void main() { - fragment = vec4(color, 1.0); + fragment = uColor; } diff --git a/shaders/base.vert b/shaders/base.vert index 49e3abd..af8a967 100644 --- a/shaders/base.vert +++ b/shaders/base.vert @@ -1,14 +1,8 @@ #version 330 -uniform mat4 MVP; - -in vec3 vCol; -in vec2 vPos; - -out vec3 color; +in vec3 vPos; void main() { - gl_Position = MVP * vec4(vPos, 0.0, 1.0); - color = vCol; + gl_Position = vec4(vPos, 1.0); } diff --git a/src/nol/core.clj b/src/nol/core.clj index c5bc3dd..4be88f9 100644 --- a/src/nol/core.clj +++ b/src/nol/core.clj @@ -1,4 +1,5 @@ (ns nol.core + (:require [clojure.math]) (:import [org.lwjgl.assimp Assimp AIScene AIMesh AIVector3D AIFace] [org.lwjgl.glfw GLFW GLFWKeyCallbackI GLFWErrorCallbackI] [org.lwjgl.opengl GL GL45 GL33 GL20 GL15 GL11 GL30] @@ -6,6 +7,21 @@ [org.joml Matrix4f] [java.nio FloatBuffer])) +(def render-queue (java.util.concurrent.LinkedBlockingQueue.)) + +(def state (atom {:last-time (GLFW/glfwGetTime) :window nil})) + +(def gl-thread (atom nil)) + +(defmacro with-gl [& body] + `(let [result# (promise)] + (.put render-queue #(deliver result# (try (do ~@body) + (catch Exception e# e#)))) + (let [r# (deref result#)] + (if (instance? Exception r#) + (throw r#) + r#)))) + (def vertex-shader-text (slurp "shaders/base.vert")) (def fragment-shader-text (slurp "shaders/base.frag")) @@ -27,15 +43,13 @@ (throw (RuntimeException. (str "Program link error: " (GL20/glGetProgramInfoLog program))))) program)) -(def state (atom {:last-time (GLFW/glfwGetTime)})) - (defn load-model [path] (Assimp/aiImportFile path (bit-or Assimp/aiProcess_Triangulate Assimp/aiProcess_FlipUVs))) -(def vertices (float-array [0.0 0.5 0.0 - -0.5 0.5 0.0 - 0.5 -0.5 0.0])) +(def vertices (float-array [0.0 1.0 0.0 + -1.0 -1.0 0.0 + 1.0 -1.0 0.0])) (defn- update-loop [window stack data] (let [w-buf (.mallocInt stack 1) @@ -43,14 +57,31 @@ _ (GLFW/glfwGetFramebufferSize window w-buf h-buf) w (.get w-buf 0) h (.get h-buf 0) - current-time (GLFW/glfwGetTime)] + t (GLFW/glfwGetTime)] (GL11/glViewport 0 0 w h) (GL11/glClear GL11/GL_COLOR_BUFFER_BIT) - (swap! state assoc :last-time current-time) + (swap! state assoc :last-time t) + + (GL45/glUseProgram (:program data)) + (let [loc (GL45/glGetUniformLocation (:program data) "uColor") + t (* t 2)] + (GL45/glUniform4f loc + (* 0.5 (+ 1.0 (clojure.math/sin t))) + (* 0.5 (+ 1.0 (clojure.math/sin (+ t 2.094)))) + (* 0.5 (+ 1.0 (clojure.math/sin (+ t 4.189)))) + 1.0)) + (GL45/glBindVertexArray (:vao data)) + (GL45/glDrawArrays GL45/GL_TRIANGLES 0 3) (GLFW/glfwSwapBuffers window))) +(defn stop! [] + ;; signal the window to close + (with-gl + (GLFW/glfwSetWindowShouldClose + (:window @state) true))) + (defn -main [] (when-not (GLFW/glfwInit) (throw (RuntimeException. "Failed to init GLFW"))) @@ -64,32 +95,90 @@ (GLFW/glfwTerminate) (throw (RuntimeException. "Failed to create window"))) + (GLFW/glfwMakeContextCurrent window) (GL/createCapabilities) (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)) - (GLFW/glfwSetWindowShouldClose win true))))) + (swap! state assoc :should-close true))))) - (loop [] - (when-not (GLFW/glfwWindowShouldClose window) - (GLFW/glfwPollEvents) - (let [stack (MemoryStack/stackPush)] - (try - (update-loop window stack) - (finally - (MemoryStack/stackPop)))) - (recur))) + (let [vbo (GL45/glCreateBuffers) + vao (GL45/glCreateVertexArrays) + data (try + (let [vert (compile-shader GL20/GL_VERTEX_SHADER vertex-shader-text) + frag (compile-shader GL20/GL_FRAGMENT_SHADER fragment-shader-text) + program (link-program vert frag)] + (GL45/glNamedBufferStorage vbo vertices GL45/GL_DYNAMIC_STORAGE_BIT) + (GL45/glVertexArrayVertexBuffer vao 0 vbo 0 12) + (GL45/glEnableVertexArrayAttrib vao 0) + (GL45/glVertexArrayAttribFormat vao 0 3 GL45/GL_FLOAT false 0) + (GL45/glVertexArrayAttribBinding vao 0 0) + {:vbo vbo :vao vao :program program}) + (catch Exception e + (println "Setup error:" (.getMessage e)) + nil))] + (swap! state merge data) + (loop [] + (while (not (.isEmpty render-queue)) + ((.take render-queue))) + (when-not (or (:should-close @state) + (GLFW/glfwWindowShouldClose window)) + (GLFW/glfwPollEvents) + (let [stack (MemoryStack/stackPush)] + (try + (update-loop window stack data) + (finally + (MemoryStack/stackPop)))) + (recur)))) (GLFW/glfwDestroyWindow window) (GLFW/glfwTerminate))) +(defn start! [] + (when (and @gl-thread (.isAlive @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)))) + (comment - (-main) + (start!) + (stop!) (def ^AIScene scene (load-model "assets/model.glb")) (.mNumMeshes scene) ;; 54 (.mNumMaterials scene) ;; 17 + (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)) + :-)