<commit_before>#+SETUPFILE: ../../src/setup.org

* thi.ng.geom.webgl.buffers

** Attribute buffers

#+BEGIN_SRC clojure :noweb-ref a-buffer
  (defn make-attribute-buffer
    [^WebGLRenderingContext gl target draw-type data]
    (let [buffer (.createBuffer gl)]
      (.bindBuffer gl target buffer)
      (.bufferData gl target data draw-type)
      buffer))

  (defn make-attribute-buffers
    [^WebGLRenderingContext gl mode specs]
    (reduce-kv
     (fn [specs id {:keys [data target] :or {target gl/array-buffer}}]
       (update-in
        specs [id] merge
        {:buffer      (make-attribute-buffer gl target mode data)
         :target      target
         :buffer-mode mode}))
     specs specs))

  (defn make-attribute-buffers-in-spec
    [model-spec ^WebGLRenderingContext gl mode]
    (update-in model-spec [:attribs] #(make-attribute-buffers gl mode %)))

  (defn update-buffer-in-spec
    [^WebGLRenderingContext gl spec id coll]
    (let [{:keys [target data buffer buffer-mode size]} (-> spec :attribs id)]
      (gl/fill-vertex-buffer data coll size)
      (.bindBuffer gl target buffer)
      (.bufferData gl target data buffer-mode)
      gl))

  (defn draw-arrays
    [^WebGLRenderingContext gl
     {:keys [shader attribs uniforms mode num-vertices]
      :or   {mode gl/triangles}}]
    (.useProgram gl (:program shader))
    (loop-kv #(sh/set-uniform shader % %2) uniforms)
    (loop-kv #(sh/set-attribute gl shader % %2) attribs)
    (.drawArrays gl mode 0 num-vertices)
    (loop-kv (fn [id _] (sh/disable-attribute gl shader id)) attribs)
    gl)
#+END_SRC

** Frame buffer

#+BEGIN_SRC clojure :noweb-ref fbo
  (defrecord FBO [^WebGLRenderingContext gl id]
    gl/PBind
    (bind [_] (.bindFramebuffer gl gl/framebuffer id) _)
    (unbind [_] (.bindFramebuffer gl gl/framebuffer nil) _)

    gl/PRelease
    (release [_] (.deleteFramebuffer gl id) _)

    gl/PFramebuffer
    (set-fbo-color-texture
      [_ {:keys [target id]}]
      (.framebufferTexture2D gl gl/framebuffer gl/color-attachment0 target id 0)
      (check-fbo gl)
      _)
    (set-fbo-depth-buffer
      [_ {:keys [id]}]
      (.framebufferRenderbuffer gl gl/framebuffer gl/depth-attachment gl/renderbuffer id)
      (check-fbo gl)
      _))

  (defn- check-fbo
    [^WebGLRenderingContext gl]
    (condp = (.checkFramebufferStatus gl gl/framebuffer)
      gl/framebuffer-unsupported
      (err/throw! "FBO unsupported")
      gl/framebuffer-incomplete-attachment
      (err/throw! "FBO incomplete attachment")
      gl/framebuffer-incomplete-dimensions
      (err/throw! "FBO incomplete dimensions")
      gl/framebuffer-incomplete-missing-attachment
      (err/throw! "FBO incomplete missing attachment")
      gl))

  (defn make-fbo
    [^WebGLRenderingContext gl]
    (FBO. gl (.createFramebuffer gl)))
#+END_SRC

** Render buffer

#+BEGIN_SRC clojure :noweb-ref r-buffer
  (defrecord RenderBuffer [^WebGLRenderingContext gl id format width height]
    gl/PBind
    (bind [_] (.bindRenderbuffer gl gl/renderbuffer id) _)
    (unbind [_] (.bindRenderbuffer gl gl/renderbuffer nil) _)

    gl/PRelease
    (release [_] (.deleteRenderbuffer gl id) _)

    gl/PConfigure
    (configure [_ {:keys [format width height]}]
      (gl/bind _)
      (.renderbufferStorage gl gl/renderbuffer format width height)
      (gl/unbind _)
      (RenderBuffer. gl id format width height)))

  (defn make-render-buffer
    [^WebGLRenderingContext gl & [opts]]
    (let [buf (RenderBuffer. gl (.createRenderbuffer gl) nil nil nil)]
      (if opts (gl/configure buf opts) buf)))

  (defn make-depth-buffer
    [^WebGLRenderingContext gl width height]
    (make-render-buffer gl {:format gl/depth-component16 :width width :height height}))
#+END_SRC

** Textures

#+BEGIN_SRC clojure :noweb-ref tex
  (defrecord Texture2D [^WebGLRenderingContext gl id channels type target width height]
    gl/PBind
    (bind
      [_ unit]
      (.activeTexture gl (+ gl/texture0 unit))
      (.bindTexture gl target id)
      _)
    (unbind
      [_ unit]
      (.activeTexture gl (+ gl/texture0 unit))
      (.bindTexture gl target -1)
      _)

    gl/PRelease
    (release [_] (.deleteTexture gl id) _)

    gl/PConfigure
    (configure
      [_ {:keys [width height filter wrap]}]
      (.texImage2D gl target 0 channels width height 0 channels type nil)
      (when filter (gl/set-texture-filter _ filter))
      (when wrap (gl/set-texture-wrap _ wrap))
      (Texture2D. gl id channels type target width height))

    gl/PTexture
    (set-texture-filter
      [_ type]
      (.texParameteri gl target gl/texture-min-filter type)
      (.texParameteri gl target gl/texture-mag-filter type)
      _)
    (set-texture-wrap
      [_ type]
      (.texParameteri gl target gl/texture-wrap-s type)
      (.texParameteri gl target gl/texture-wrap-t type)
      _))

  (defn make-texture2d
    [gl {:keys [channels type target width height] :as opts}]
    (let [tex (Texture2D.
               gl (.createTexture gl)
               (or channels gl/rgb)
               (or type gl/unsigned-byte)
               (or target gl/texture-2d)
               nil nil)]
      (if (and width height)
        (do (gl/bind tex 0) (gl/configure tex opts))
        tex)))
#+END_SRC

** Complete namespace definition

#+BEGIN_SRC clojure :tangle ../babel/src/thi/ng/geom/webgl/buffers.cljs :noweb yes :mkdirp yes :padline no
  (ns thi.ng.geom.webgl.buffers
    (:require
     [thi.ng.geom.webgl.core :as gl]
     [thi.ng.geom.webgl.arrays :as arrays]
     [thi.ng.geom.webgl.shaders :as sh]
     [thi.ng.geom.webgl.utils :refer [loop-kv]]
     [thi.ng.common.error :as err]))

  (declare check-fbo)

  <<a-buffer>>

  <<fbo>>

  <<r-buffer>>

  <<tex>><commit_msg>[webgl] add make-canvas-texture, rename make-texture2d to make-texture<commit_after>#+SETUPFILE: ../../src/setup.org

* thi.ng.geom.webgl.buffers

** Attribute buffers

#+BEGIN_SRC clojure :noweb-ref a-buffer
  (defn make-attribute-buffer
    [^WebGLRenderingContext gl target draw-type data]
    (let [buffer (.createBuffer gl)]
      (.bindBuffer gl target buffer)
      (.bufferData gl target data draw-type)
      buffer))

  (defn make-attribute-buffers
    [^WebGLRenderingContext gl mode specs]
    (reduce-kv
     (fn [specs id {:keys [data target] :or {target gl/array-buffer}}]
       (update-in
        specs [id] merge
        {:buffer      (make-attribute-buffer gl target mode data)
         :target      target
         :buffer-mode mode}))
     specs specs))

  (defn make-attribute-buffers-in-spec
    [model-spec ^WebGLRenderingContext gl mode]
    (update-in model-spec [:attribs] #(make-attribute-buffers gl mode %)))

  (defn update-buffer-in-spec
    [^WebGLRenderingContext gl spec id coll]
    (let [{:keys [target data buffer buffer-mode size]} (-> spec :attribs id)]
      (gl/fill-vertex-buffer data coll size)
      (.bindBuffer gl target buffer)
      (.bufferData gl target data buffer-mode)
      gl))

  (defn draw-arrays
    [^WebGLRenderingContext gl
     {:keys [shader attribs uniforms mode num-vertices]
      :or   {mode gl/triangles}}]
    (.useProgram gl (:program shader))
    (loop-kv #(sh/set-uniform shader % %2) uniforms)
    (loop-kv #(sh/set-attribute gl shader % %2) attribs)
    (.drawArrays gl mode 0 num-vertices)
    (loop-kv (fn [id _] (sh/disable-attribute gl shader id)) attribs)
    gl)
#+END_SRC

** Frame buffer

#+BEGIN_SRC clojure :noweb-ref fbo
  (defrecord FBO [^WebGLRenderingContext gl id]
    gl/PBind
    (bind [_] (.bindFramebuffer gl gl/framebuffer id) _)
    (unbind [_] (.bindFramebuffer gl gl/framebuffer nil) _)

    gl/PRelease
    (release [_] (.deleteFramebuffer gl id) _)

    gl/PFramebuffer
    (set-fbo-color-texture
      [_ {:keys [target id]}]
      (.framebufferTexture2D gl gl/framebuffer gl/color-attachment0 target id 0)
      (check-fbo gl)
      _)
    (set-fbo-depth-buffer
      [_ {:keys [id]}]
      (.framebufferRenderbuffer gl gl/framebuffer gl/depth-attachment gl/renderbuffer id)
      (check-fbo gl)
      _))

  (defn- check-fbo
    [^WebGLRenderingContext gl]
    (condp = (.checkFramebufferStatus gl gl/framebuffer)
      gl/framebuffer-unsupported
      (err/throw! "FBO unsupported")
      gl/framebuffer-incomplete-attachment
      (err/throw! "FBO incomplete attachment")
      gl/framebuffer-incomplete-dimensions
      (err/throw! "FBO incomplete dimensions")
      gl/framebuffer-incomplete-missing-attachment
      (err/throw! "FBO incomplete missing attachment")
      gl))

  (defn make-fbo
    [^WebGLRenderingContext gl]
    (FBO. gl (.createFramebuffer gl)))
#+END_SRC

** Render buffer

#+BEGIN_SRC clojure :noweb-ref r-buffer
  (defrecord RenderBuffer [^WebGLRenderingContext gl id format width height]
    gl/PBind
    (bind [_] (.bindRenderbuffer gl gl/renderbuffer id) _)
    (unbind [_] (.bindRenderbuffer gl gl/renderbuffer nil) _)

    gl/PRelease
    (release [_] (.deleteRenderbuffer gl id) _)

    gl/PConfigure
    (configure [_ {:keys [format width height]}]
      (gl/bind _)
      (.renderbufferStorage gl gl/renderbuffer format width height)
      (gl/unbind _)
      (RenderBuffer. gl id format width height)))

  (defn make-render-buffer
    [^WebGLRenderingContext gl & [opts]]
    (let [buf (RenderBuffer. gl (.createRenderbuffer gl) nil nil nil)]
      (if opts (gl/configure buf opts) buf)))

  (defn make-depth-buffer
    [^WebGLRenderingContext gl width height]
    (make-render-buffer gl {:format gl/depth-component16 :width width :height height}))
#+END_SRC

** Textures

#+BEGIN_SRC clojure :noweb-ref tex
  (defrecord Texture2D [^WebGLRenderingContext gl id channels type target width height]
    gl/PBind
    (bind
      [_ unit]
      (.activeTexture gl (+ gl/texture0 unit))
      (.bindTexture gl target id)
      _)
    (unbind
      [_ unit]
      (.activeTexture gl (+ gl/texture0 unit))
      (.bindTexture gl target -1)
      _)

    gl/PRelease
    (release [_] (.deleteTexture gl id) _)

    gl/PConfigure
    (configure
      [_ {:keys [width height filter wrap]}]
      (.texImage2D gl target 0 channels width height 0 channels type nil)
      (when filter (gl/set-texture-filter _ filter))
      (when wrap   (gl/set-texture-wrap _ wrap))
      (Texture2D. gl id channels type target width height))

    gl/PTexture
    (set-texture-filter
      [_ type]
      (.texParameteri gl target gl/texture-min-filter type)
      (.texParameteri gl target gl/texture-mag-filter type)
      _)
    (set-texture-wrap
      [_ type]
      (.texParameteri gl target gl/texture-wrap-s type)
      (.texParameteri gl target gl/texture-wrap-t type)
      _))

  (defn make-texture
    [^WebGLRenderingContext gl {:keys [channels type target width height] :as opts}]
    (let [tex (Texture2D.
               gl (.createTexture gl)
               (or channels gl/rgb)
               (or type gl/unsigned-byte)
               (or target gl/texture-2d)
               nil nil)]
      (if (and width height)
        (do (gl/bind tex 0)
            (gl/configure tex opts))
        tex)))

  (defn make-canvas-texture
    [^WebGLRenderingContext gl canvas & [opts]]
    (let [tex (->> opts
                   (merge {:channels gl/rgba
                           :width    (.-width canvas)
                           :height   (.-height canvas)
                           :filter   gl/linear
                           :wrap     gl/clamp-to-edge})
                   (make-texture gl))]
      (.pixelStorei gl gl/unpack-flip-y-webgl 0)
      (.pixelStorei gl gl/unpack-premultiply-alpha-webgl 1)
      (.texImage2D gl gl/texture-2d 0 gl/rgba gl/rgba gl/unsigned-byte canvas)
      tex))
#+END_SRC

** Complete namespace definition

#+BEGIN_SRC clojure :tangle ../babel/src/thi/ng/geom/webgl/buffers.cljs :noweb yes :mkdirp yes :padline no
  (ns thi.ng.geom.webgl.buffers
    (:require
     [thi.ng.geom.webgl.core :as gl]
     [thi.ng.geom.webgl.arrays :as arrays]
     [thi.ng.geom.webgl.shaders :as sh]
     [thi.ng.geom.webgl.utils :refer [loop-kv]]
     [thi.ng.common.error :as err]))

  (declare check-fbo)

  <<a-buffer>>

  <<fbo>>

  <<r-buffer>>

  <<tex>>
{'fraction_non_alphanumeric': 0.12183034608077971, 'fraction_numerical': 0.0037168580160237877, 'mean_word_length': 2.5811889973380655, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6480847', 'n_tokens_mistral': 4154, 'n_tokens_neox': 3910, 'n_words': 1049}