diff --git a/frontend/src/app/main/ui/workspace/viewport/gl.cljs b/frontend/src/app/main/ui/workspace/viewport/gl.cljs index 3fdba7479..3a1521552 100644 --- a/frontend/src/app/main/ui/workspace/viewport/gl.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/gl.cljs @@ -3,6 +3,7 @@ (:require-macros [app.util.gl.macros :refer [slurp]]) (:require ["gl-matrix" :as glm] + [app.common.data.macros :as dm] [app.common.math :as math] [app.util.gl :as gl] [cuerdas.core :as str] @@ -15,6 +16,7 @@ #_(def shaders (js/Map.)) (def programs (js/Map.)) +(def program-actives (js/Map.)) #_(def textures (js/Map.)) #_(def framebuffers (js/Map.)) @@ -53,6 +55,25 @@ :bool 7 :frame 8)) +(defn get-object-border-radius-as-vec4 + "Returns a vec4 from the object border radius." + [object] + (let [rx (:rx object) + ry (:ry object) + r1 (:r1 object) + r2 (:r2 object) + r3 (:r3 object) + r4 (:r4 object)] + (cond + (or rx ry) + #js [(:rx object) (:ry object) (:rx object) (:ry object)] + + (or r1 r2 r3 r4) + #js [(:r1 object) (:r2 object) (:r3 object) (:r4 object)] + + :else + #js [0.0 0.0 0.0 0.0]))) + (defn resize-canvas-to "Resize canvas to specific coordinates." [canvas width height] @@ -75,28 +96,34 @@ (defn prepare-gl "Prepares the WebGL context for rendering." [gl] - (let [default-program (gl/create-program-from-sources gl default-vertex-shader default-fragment-shader)] - (.set programs "default" default-program))) + (let [default-program (gl/create-program-from-sources gl default-vertex-shader default-fragment-shader) + default-program-actives (gl/get-program-actives gl default-program)] + (js/console.log default-program-actives) + (.set programs "default" default-program) + (.set program-actives "default" default-program-actives))) (defn render-gl "Renders the whole document to the canvas." [gl objects vbox] (let [projection (.create glm/mat3) - projection (.projection glm/mat3 projection (:width vbox) (:height vbox))] + projection (.projection glm/mat3 projection (:width vbox) (:height vbox)) - (.clearColor gl 1.0 0.0 1.0 0.5) + program (.get programs "default") + actives (.get program-actives "default")] + + (.clearColor gl 1.0 0.0 1.0 0.5) (.clear gl (.-COLOR_BUFFER_BIT gl)) (.viewport gl 0 0 (.-width (.-canvas gl)) (.-height (.-canvas gl))) - ;; Enable alpha blending + ;; Enable alpha blending (.enable gl (.-BLEND gl)) (.blendFunc gl (.-SRC_ALPHA gl) (.-ONE_MINUS_SRC_ALPHA gl)) - (.useProgram gl (.get programs "default")) + (.useProgram gl program) (println "---------------> vbox" (:x vbox) (:width vbox) (:y vbox) (:height vbox)) - (.uniformMatrix3fv gl (.getUniformLocation gl (.get programs "default") "u_projection") false projection) - (.uniform4f gl (.getUniformLocation gl (.get programs "default") "u_vbox") (:x vbox) (:y vbox) (:width vbox) (:height vbox)) + (.uniformMatrix3fv gl (gl/get-program-uniform-location actives "u_projection") false projection) + (.uniform4f gl (gl/get-program-uniform-location actives "u_vbox") (:x vbox) (:y vbox) (:width vbox) (:height vbox)) (doseq [[_ object] objects] (let [selrect (:selrect object) @@ -105,25 +132,25 @@ width (:width selrect) height (:height selrect) rotation (:rotation object) - ;; Tengo que encontrar la forma de "reordenar la matriz" para que funcione la - ;; rotación. - ;; transform (:transform object) - ;; {a :a b :b c :c d :d e :e f :f} transform - ;; matrix #_(js/Float32Array. #js [a c 0 b d 0 0 0 1]) - matrix (js/Float32Array. #js [1 0 0 0 1 0 0 0 1]) - fill (first (:fills object))] - (js/console.log "fill" fill) - (js/console.log "matrix" matrix) - (.uniform1i gl (.getUniformLocation gl (.get programs "default") "u_type") (get-object-type-as-int object)) - (.uniform2f gl (.getUniformLocation gl (.get programs "default") "u_size") width height) - (.uniform2f gl (.getUniformLocation gl (.get programs "default") "u_position") x y) - (.uniform1f gl (.getUniformLocation gl (.get programs "default") "u_rotation") (/ (* rotation js/Math.PI) 180.0)) - #_(.uniformMatrix3fv gl (.getUniformLocation gl (.get programs "default") "u_transform") false matrix) + border (get-object-border-radius-as-vec4 object) + type (get-object-type-as-int object)] + (js/console.log border) + (.uniform4fv gl (gl/get-program-uniform-location actives "u_border") border) + (.uniform1i gl (gl/get-program-uniform-location actives "u_type") type) + (.uniform2f gl (gl/get-program-uniform-location actives "u_size") width height) + (.uniform2f gl (gl/get-program-uniform-location actives "u_position") x y) + (.uniform1f gl (gl/get-program-uniform-location actives "u_rotation") (/ (* rotation js/Math.PI) 180.0)) + #_(.uniformMatrix3fv gl (gl/get-program-uniform-location actives "u_transform") false matrix) ;; NOTA: Esto es sólo aplicable en objetos que poseen fills (los textos no ;; poseen fills). (doseq [fill (reverse (:fills object))] (do - (.uniform4fv gl (.getUniformLocation gl (.get programs "default") "u_color") (parse-color (:fill-color fill) (:fill-opacity fill))) + (.uniform4fv gl (gl/get-program-uniform-location actives "u_color") (parse-color (:fill-color fill) (:fill-opacity fill))) + (.drawArrays gl (.-TRIANGLE_STRIP gl) 0 4))) + + (doseq [stroke (reverse (:strokes object))] + (do + (.uniform4fv gl (gl/get-program-uniform-location actives "u_color") (parse-color (:stroke-color stroke) (:stroke-opacity stroke))) (.drawArrays gl (.-TRIANGLE_STRIP gl) 0 4))))))) (mf/defc canvas diff --git a/frontend/src/app/util/gl.cljs b/frontend/src/app/util/gl.cljs index 8e4dd9145..97ff33542 100644 --- a/frontend/src/app/util/gl.cljs +++ b/frontend/src/app/util/gl.cljs @@ -57,13 +57,15 @@ [parameter get-active-name get-location-name] (fn [gl program] (let [count (.getProgramParameter gl program parameter) - get-active (dm/get-prop gl get-active-name) - get-location (dm/get-prop gl get-location-name)] - (into {} (for [index (range count)] - (let [info (get-active gl program index) - name (.-name info) - location (get-location gl program name)] - [name #js {:name name :info info :location location}])))))) + get-active (unchecked-get gl get-active-name) + get-location (unchecked-get gl get-location-name) + actives #js {}] + (doseq [index (range 0 count)] + (let [info (.apply get-active gl #js [program index]) + name (.-name info) + location (.apply get-location gl #js [program name])] + (.defineProperty js/Object actives name #js {:value #js {:name name :info info :location location} :enumerable true :writable false :configurable false}))) + actives))) (def get-program-uniforms (get-program-active-factory (.-ACTIVE_UNIFORMS js/WebGLRenderingContext) "getActiveUniform" "getUniformLocation")) (def get-program-attributes (get-program-active-factory (.-ACTIVE_ATTRIBUTES js/WebGLRenderingContext) "getActiveAttrib" "getAttribLocation")) @@ -75,6 +77,13 @@ attributes (get-program-attributes gl program)] #js { :uniforms uniforms :attributes attributes })) +(defn get-program-uniform-location + "Returns the location of the given uniform in the given program" + [actives name] + (let [uniforms (unchecked-get actives "uniforms") + uniform (unchecked-get uniforms name)] + (unchecked-get uniform "location"))) + ;; ;; Buffers ;; @@ -90,9 +99,19 @@ ;; Framebuffers ;; (defn create-framebuffer - [gl] + [gl attachments] (let [framebuffer (.createFramebuffer gl)] (.bindFramebuffer gl framebuffer) + (doseq [[attachment attachment-info] attachments] + (let [attachment-type (unchecked-get attachment-info "type") + attachment-data (unchecked-get attachment-info "data")] + (cond + (= attachment-type "texture") + (.framebufferTexture2D gl (.-FRAMEBUFFER gl) attachment (.-TEXTURE_2D gl) attachment-data 0) + (= attachment-type "renderbuffer") + (.framebufferRenderbuffer gl (.-FRAMEBUFFER gl) attachment (.-RENDERBUFFER gl) attachment-data) + :else + (throw (js/Error. (dm/str "Unknown attachment type: " attachment-type)))))) (.bindFramebuffer gl nil) framebuffer)) diff --git a/frontend/src/app/util/gl/shaders/default.f.glsl b/frontend/src/app/util/gl/shaders/default.f.glsl index af2abec80..8b7ec74f4 100644 --- a/frontend/src/app/util/gl/shaders/default.f.glsl +++ b/frontend/src/app/util/gl/shaders/default.f.glsl @@ -17,26 +17,45 @@ out vec4 fragColor; in vec2 v_texCoord; uniform int u_type; +uniform vec2 u_size; uniform vec4 u_color; +uniform vec4 u_border; + +bool isRoundedRect(in vec4 border) { + return border.x > 0.0 || border.y > 0.0 || border.z > 0.0 || border.w > 0.0; +} + +// Thanks to Iñigo Quilez for this awesome functions. +// @see https://iquilezles.org/articles/distfunctions2d/ +float sdRoundBox(in vec2 p, in vec2 b, in vec4 r) { + r.xy = (p.x > 0.0f) ? r.xy : r.zw; + r.x = (p.y > 0.0f) ? r.x : r.y; + vec2 q = abs(p) - b + r.x; + return min(max(q.x, q.y), 0.0f) + length(max(q, 0.0f)) - r.x; +} + +float sdCircle(in vec2 p, in float r) { + return length(p) - r; +} void main() { // Si es un rect o un frame, simplemente asignamos el color al fragColor. if (u_type == type_rect || u_type == type_frame) { - fragColor = u_color; - // Si es un circulo, comprobamos que el pixel este dentro del circulo, en caso - // contrario descartamos el pixel. - } else if (u_type == type_circle) { - if (length(v_texCoord - 0.5) > 0.5) { - discard; - } - if(length(v_texCoord - 0.5f) > 0.45f) { - - fragColor = vec4(1.0, 0.0, 0.0, 1.0); - } else if(length(v_texCoord - 0.5f) > 0.4f) { - fragColor = vec4(1.0f, 0.0f, 1.0f, 1.0f); + if (isRoundedRect(u_border)) { + if (sdRoundBox(v_texCoord - 0.5, vec2(0.5), u_border / u_size.x) > 0.0) { + discard; + } + fragColor = u_color; } else { fragColor = u_color; } + // Si es un circulo, comprobamos que el pixel este dentro del circulo, en caso + // contrario descartamos el pixel. + } else if (u_type == type_circle) { + if (sdCircle(v_texCoord - 0.5, 0.5) > 0.0) { + discard; + } + fragColor = u_color; // Para cualquier otro elemento no soportado pintamos una especie de rejilla // raruna. } else {