This commit is contained in:
Aitor 2023-12-19 14:35:57 +01:00
parent 77f07f9751
commit e08e2bf64a
6 changed files with 260 additions and 147 deletions

View File

@ -79,6 +79,7 @@
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"draft-js": "^0.11.7", "draft-js": "^0.11.7",
"eventsource-parser": "^1.1.1", "eventsource-parser": "^1.1.1",
"gl-matrix": "^3.4.3",
"highlight.js": "^11.8.0", "highlight.js": "^11.8.0",
"js-beautify": "^1.14.9", "js-beautify": "^1.14.9",
"jszip": "^3.10.1", "jszip": "^3.10.1",

View File

@ -261,7 +261,13 @@
(:y (first selected-shapes)) (:y (first selected-shapes))
(:y selected-frame)) (:y selected-frame))
rule-area-size (/ rules/rule-area-size zoom)] rule-area-size (/ rules/rule-area-size zoom)
;; Aquí podemos configurar como queremos que sea el renderizado:
;; - "gl" Utilizando sólo WebGL2
;; - "svg" Utilizando sólo SVG
;; - "both" Utilizando ambos
renderer "both"]
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only?) (hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only?)
(hooks/setup-viewport-size vport viewport-ref) (hooks/setup-viewport-size vport viewport-ref)
@ -305,54 +311,56 @@
[:& top-bar/top-bar]] [:& top-bar/top-bar]]
[:svg.render-shapes (when (or (= renderer "svg") (= renderer "both"))
{:id "render" [:svg.render-shapes
:xmlns "http://www.w3.org/2000/svg" {:id "render"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlns "http://www.w3.org/2000/svg"
:xmlns:penpot "https://penpot.app/xmlns" :xmlnsXlink "http://www.w3.org/1999/xlink"
:preserveAspectRatio "xMidYMid meet" :xmlns:penpot "https://penpot.app/xmlns"
:key (str "render" page-id) :preserveAspectRatio "xMidYMid meet"
:width (:width vport 0) :key (str "render" page-id)
:height (:height vport 0) :width (:width vport 0)
:view-box (utils/format-viewbox vbox) :height (:height vport 0)
:style {:background-color background :view-box (utils/format-viewbox vbox)
:pointer-events "none"} :style {:background-color background
:fill "none"} :pointer-events "none"}
:fill "none"}
[:defs [:defs
[:linearGradient {:id "frame-placeholder-gradient"} [:linearGradient {:id "frame-placeholder-gradient"}
[:animateTransform [:animateTransform
{:attributeName "gradientTransform" {:attributeName "gradientTransform"
:type "translate" :type "translate"
:from "-1 0" :from "-1 0"
:to "1 0" :to "1 0"
:dur "2s" :dur "2s"
:repeatCount "indefinite"}] :repeatCount "indefinite"}]
[:stop {:offset "0%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}] [:stop {:offset "0%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]
[:stop {:offset "50%" :stop-color (str "color-mix(in srgb-linear, " background " 80%, #777)") :stop-opacity 1}] [:stop {:offset "50%" :stop-color (str "color-mix(in srgb-linear, " background " 80%, #777)") :stop-opacity 1}]
[:stop {:offset "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]] [:stop {:offset "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]]
(when (dbg/enabled? :show-export-metadata) (when (dbg/enabled? :show-export-metadata)
[:& use/export-page {:options options}]) [:& use/export-page {:options options}])
;; We need a "real" background shape so layer transforms work properly in firefox ;; We need a "real" background shape so layer transforms work properly in firefox
[:rect {:width (:width vbox 0) [:rect {:width (:width vbox 0)
:height (:height vbox 0) :height (:height vbox 0)
:x (:x vbox 0) :x (:x vbox 0)
:y (:y vbox 0) :y (:y vbox 0)
:fill background}] :fill background}]
[:& (mf/provider ctx/current-vbox) {:value vbox'} [:& (mf/provider ctx/current-vbox) {:value vbox'}
[:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)} [:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)}
;; Render root shape ;; Render root shape
[:& shapes/root-shape {:key page-id [:& shapes/root-shape {:key page-id
:objects base-objects :objects base-objects
:active-frames @active-frames}]]]] :active-frames @active-frames}]]]])
;; IT's MAGIC! ;; IT's MAGIC!
[gl/canvas {:objects base-objects (when (or (= renderer "gl") (= renderer "both"))
:active-frames @active-frames [gl/canvas {:objects base-objects
:vbox vbox}] :active-frames @active-frames
:vbox vbox}])
[:svg.viewport-controls [:svg.viewport-controls
{:xmlns "http://www.w3.org/2000/svg" {:xmlns "http://www.w3.org/2000/svg"

View File

@ -2,87 +2,129 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require-macros [app.util.gl.macros :refer [slurp]]) (:require-macros [app.util.gl.macros :refer [slurp]])
(:require (:require
["gl-matrix" :as glm]
[app.common.math :as math] [app.common.math :as math]
[app.util.gl :as gl] [app.util.gl :as gl]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def CANVAS_CONTEXT_ID "webgl2") (def CANVAS_CONTEXT_ID "webgl2")
(def default-vertex-shader (slurp "src/app/util/gl/shaders/default.v.glsl")) (def default-vertex-shader (slurp "src/app/util/gl/shaders/default.v.glsl"))
(def default-fragment-shader (slurp "src/app/util/gl/shaders/default.f.glsl")) (def default-fragment-shader (slurp "src/app/util/gl/shaders/default.f.glsl"))
#_(def shaders (js/Map.)) #_(def shaders (js/Map.))
(def programs (js/Map.)) (def programs (js/Map.))
#_(def textures (js/Map.)) #_(def textures (js/Map.))
#_(def framebuffers (js/Map.)) #_(def framebuffers (js/Map.))
(defn parse-color-hex (defn parse-color-hex
[color opacity] "Parses a color string and returns a vector with the RGBA values."
(let [r (str/slice color 1 3) [color opacity]
g (str/slice color 3 5) (let [r (str/slice color 1 3)
b (str/slice color 5 7)] g (str/slice color 3 5)
#js [(/ (js/parseInt r 16) 255.0) b (str/slice color 5 7)]
(/ (js/parseInt g 16) 255.0) #js [(/ (js/parseInt r 16) 255.0)
(/ (js/parseInt b 16) 255.0) (/ (js/parseInt g 16) 255.0)
opacity])) (/ (js/parseInt b 16) 255.0)
opacity]))
(defn parse-color (defn parse-color
[color opacity] "Parses a color string and returns a vector with the RGBA values."
(cond [color opacity]
(str/starts-with? color "#") (cond
(parse-color-hex color opacity) (str/starts-with? color "#")
(parse-color-hex color opacity)
:else :else
#js [0.0 0.0 0.0 1.0])) #js [0.0 0.0 0.0 1.0]))
(defn resize-canvas-to (defn get-object-type-as-int
[canvas width height] "Returns the object type as an integer."
(let [resized-width (not= (.-width canvas) width) [object]
resized-height (not= (.-height canvas) height) (case (:type object)
resized (or resized-width resized-height)] :rect 0
(when resized-width :circle 1
(set! (.-width canvas) width)) :group 2
(when resized-height :path 3
(set! (.-height canvas) height)) :text 4
resized)) :image 5
:svg-raw 6
:bool 7
:frame 8))
(defn resize-canvas (defn resize-canvas-to
[canvas] "Resize canvas to specific coordinates."
(let [width (math/floor (.-clientWidth canvas)) [canvas width height]
height (math/floor (.-clientHeight canvas))] (let [resized-width (not= (.-width canvas) width)
(resize-canvas-to canvas width height))) resized-height (not= (.-height canvas) height)
resized (or resized-width resized-height)]
(when resized-width
(set! (.-width canvas) width))
(when resized-height
(set! (.-height canvas) height))
resized))
(defn prepare-gl (defn resize-canvas
[gl] "Resizes the canvas intrinsic size to the element size."
(let [default-program (gl/create-program-from-sources gl default-vertex-shader default-fragment-shader)] [canvas]
(.set programs "default" default-program))) (let [width (math/floor (.-clientWidth canvas))
height (math/floor (.-clientHeight canvas))]
(resize-canvas-to canvas width height)))
(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)))
(defn render-gl (defn render-gl
"Renders the whole document to the canvas."
[gl objects vbox] [gl objects vbox]
(.clearColor gl 1.0 0.0 1.0 0) (let [projection (.create glm/mat3)
(.clear gl (.-COLOR_BUFFER_BIT gl)) projection (.projection glm/mat3 projection (:width vbox) (:height vbox))]
(.viewport gl 0 0 (.-width (.-canvas gl)) (.-height (.-canvas gl))) (.clearColor gl 1.0 0.0 1.0 0.5)
(.clear gl (.-COLOR_BUFFER_BIT gl))
(.useProgram gl (.get programs "default")) (.viewport gl 0 0 (.-width (.-canvas gl)) (.-height (.-canvas gl)))
(.uniform4f gl (.getUniformLocation gl (.get programs "default") "u_vbox") (:x vbox) (:y vbox) (:width vbox) (:height vbox))
(.enable gl (.-BLEND gl)) ;; Enable alpha blending
(.blendFunc gl (.-SRC_ALPHA gl) (.-ONE_MINUS_SRC_ALPHA gl)) (.enable gl (.-BLEND gl))
(.blendFunc gl (.-SRC_ALPHA gl) (.-ONE_MINUS_SRC_ALPHA gl))
(doseq [[_ object] objects] (.useProgram gl (.get programs "default"))
(let [selrect (:selrect object) (println "---------------> vbox" (:x vbox) (:width vbox) (:y vbox) (:height vbox))
x (:x selrect) (.uniformMatrix3fv gl (.getUniformLocation gl (.get programs "default") "u_projection") false projection)
y (:y selrect) (.uniform4f gl (.getUniformLocation gl (.get programs "default") "u_vbox") (:x vbox) (:y vbox) (:width vbox) (:height vbox))
width (:width selrect)
height (:height selrect)] (doseq [[_ object] objects]
(doseq [fill (reverse (:fills object))] (let [selrect (:selrect object)
(do x (:x selrect)
(.uniform4fv gl (.getUniformLocation gl (.get programs "default") "u_color") (parse-color (:fill-color fill) (:fill-opacity fill))) y (:y selrect)
(.uniform2f gl (.getUniformLocation gl (.get programs "default") "u_size") width height) width (:width selrect)
(.uniform2f gl (.getUniformLocation gl (.get programs "default") "u_position") x y) height (:height selrect)
(.drawArrays gl (.-TRIANGLE_STRIP gl) 0 4)))))) 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)
;; 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)))
(.drawArrays gl (.-TRIANGLE_STRIP gl) 0 4)))))))
(mf/defc canvas (mf/defc canvas
"A canvas element with a WebGL context." "A canvas element with a WebGL context."
@ -91,7 +133,21 @@
(let [objects (unchecked-get props "objects") (let [objects (unchecked-get props "objects")
vbox (unchecked-get props "vbox") vbox (unchecked-get props "vbox")
canvas-ref (mf/use-ref nil) canvas-ref (mf/use-ref nil)
gl-ref (mf/use-ref nil)] gl-ref (mf/use-ref nil)
on-context-lost
(mf/use-fn (fn []
(mf/set-ref-val! gl-ref nil)))
on-context-restore
(mf/use-fn (fn []
(let [canvas (mf/ref-val canvas-ref)]
(when (some? canvas)
(let [gl (.getContext canvas CANVAS_CONTEXT_ID)]
(mf/set-ref-val! gl-ref gl)
(resize-canvas canvas)
(prepare-gl gl)
(render-gl gl objects vbox))))))]
(mf/with-effect [objects vbox] (mf/with-effect [objects vbox]
(let [gl (mf/ref-val gl-ref)] (let [gl (mf/ref-val gl-ref)]
@ -101,11 +157,20 @@
(mf/with-effect [canvas-ref] (mf/with-effect [canvas-ref]
(let [canvas (mf/ref-val canvas-ref)] (let [canvas (mf/ref-val canvas-ref)]
(when (some? canvas) (when (some? canvas)
(.addEventListener canvas "webglcontextlost" on-context-lost)
(.addEventListener canvas "webglcontextrestore" on-context-restore)
(let [gl (.getContext canvas CANVAS_CONTEXT_ID)] (let [gl (.getContext canvas CANVAS_CONTEXT_ID)]
(mf/set-ref-val! gl-ref gl) (mf/set-ref-val! gl-ref gl)
(resize-canvas canvas) (resize-canvas canvas)
(prepare-gl gl) (prepare-gl gl)
(render-gl gl objects vbox))))) (render-gl gl objects vbox))))
;; unmount
(fn []
(let [canvas (mf/ref-val canvas-ref)]
(when (some? canvas)
(.removeEventListener canvas "webglcontextlost" on-context-lost)
(.removeEventListener canvas "webglcontextrestore" on-context-restore)))))
[:canvas {:class (stl/css :canvas) [:canvas {:class (stl/css :canvas)
:ref canvas-ref}])) :ref canvas-ref}]))

View File

@ -1,18 +1,50 @@
#version 300 es #version 300 es
const int type_rect = 0;
const int type_circle = 1;
const int type_group = 2;
const int type_path = 3;
const int type_text = 4;
const int type_image = 5;
const int type_svg_raw = 6;
const int type_bool = 7;
const int type_frame = 8;
precision highp float; precision highp float;
// in vec2 v_texCoord;
// uniform sampler2D u_texture;
uniform vec4 u_color;
out vec4 fragColor; out vec4 fragColor;
in vec2 v_texCoord;
uniform int u_type;
uniform vec4 u_color;
void main() { void main() {
// fragColor = texture(u_framebuffer, v_texCoord) + texture(u_texture, v_texCoord); // Si es un rect o un frame, simplemente asignamos el color al fragColor.
// fragColor = texture(u_texture, v_texCoord); if (u_type == type_rect || u_type == type_frame) {
// Pintamos rosita fragColor = u_color;
fragColor = u_color; // Si es un circulo, comprobamos que el pixel este dentro del circulo, en caso
// fragColor = vec4(1.0, 0.0, 1.0, 1.0); // 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);
} else {
fragColor = u_color;
}
// Para cualquier otro elemento no soportado pintamos una especie de rejilla
// raruna.
} else {
fragColor = vec4(
round(mod(v_texCoord.x, 0.1) * 10.0),
round(mod(v_texCoord.y, .1) * 10.0),
0.0,
1.0
);
}
} }

View File

@ -5,47 +5,46 @@ precision highp float;
uniform vec4 u_vbox; uniform vec4 u_vbox;
uniform vec2 u_position; uniform vec2 u_position;
uniform vec2 u_size; uniform vec2 u_size;
uniform float u_rotation;
uniform mat3 u_projection;
// out vec2 v_texCoord; out vec2 v_texCoord;
vec2 get_vertex_position(int id) { vec2 get_vertex_position(int id) {
if(id == 0) { if(id == 0) {
return vec2(0.0f, 0.0f); return vec2(-1.0, -1.0);
} else if(id == 1) { } else if(id == 1) {
return vec2(1.0f, 0.0f); return vec2(1.0, -1.0);
} else if(id == 2) { } else if(id == 2) {
return vec2(0.0f, 1.0f); return vec2(-1.0, 1.0);
} else if(id == 3) { } else if(id == 3) {
return vec2(1.0f, 1.0f); return vec2(1.0, 1.0);
} else { } else {
return vec2(0.0f, 0.0f); return vec2(0.0f, 0.0f);
} }
} }
/*
vec2 get_tex_position(int id) {
if(id == 0) {
return vec2(0.0f, 1.0f);
} else if(id == 1) {
return vec2(1.0f, 1.0f);
} else if(id == 2) {
return vec2(0.0f, 0.0f);
} else if(id == 3) {
return vec2(1.0f, 0.0f);
} else {
return vec2(0.0f, 0.0f);
}
}
*/
vec2 from(vec2 v, vec2 min, vec2 max) {
return (v - min) / (max - min);
}
void main() { void main() {
vec2 center = u_size * 0.5;
vec2 position = u_position - vec2(u_vbox.xy);
float c = cos(u_rotation);
float s = sin(u_rotation);
mat2 rotation = mat2(c, s, -s, c);
mat2 scale = mat2(
u_size.x * 0.5, 0.0f,
0.0f, u_size.y * 0.5
);
mat2 rotation_scale = rotation * scale;
vec2 vertex = get_vertex_position(gl_VertexID); vec2 vertex = get_vertex_position(gl_VertexID);
vec2 position = vertex * from(u_size, vec2(0), u_vbox.zw) + from(u_position, u_vbox.xy, u_vbox.xy + u_vbox.zw); // 0,1
gl_Position = vec4(mix(vec2(-1.0, 1.0), vec2(1.0, -1.0), position), 0.0f, 1.0f); vec2 vertex_rotated_scaled = rotation_scale * vertex;
// gl_Position = vec4(((get_base_position(gl_VertexID) * u_size + u_position) / u_vbox.zw), 0.0f, 1.0f); vec2 vertex_positioned = center + vertex_rotated_scaled + position;
// v_texCoord = get_tex_position(gl_VertexID);
vec3 projected = u_projection * vec3(vertex_positioned, 1.0);
gl_Position = vec4(projected, 1.0f);
v_texCoord = (vertex + 1.0) * 0.5;
} }

View File

@ -7587,6 +7587,7 @@ __metadata:
draft-js: "npm:^0.11.7" draft-js: "npm:^0.11.7"
eventsource-parser: "npm:^1.1.1" eventsource-parser: "npm:^1.1.1"
gettext-parser: "npm:^7.0.1" gettext-parser: "npm:^7.0.1"
gl-matrix: "npm:^3.4.3"
gulp: "npm:4.0.2" gulp: "npm:4.0.2"
gulp-cached: "npm:^1.1.1" gulp-cached: "npm:^1.1.1"
gulp-concat: "npm:^2.6.1" gulp-concat: "npm:^2.6.1"
@ -7891,6 +7892,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"gl-matrix@npm:^3.4.3":
version: 3.4.3
resolution: "gl-matrix@npm:3.4.3"
checksum: c8ee6e2ce2d089b4ba4ae13ec9d4cb99bf2abe5f68f0cb08d94bbd8bafbec13aacc7230b86539ce5ca01b79226ea8c3194f971f5ca0c81838bc5e4e619dc398e
languageName: node
linkType: hard
"glob-parent@npm:^3.1.0": "glob-parent@npm:^3.1.0":
version: 3.1.0 version: 3.1.0
resolution: "glob-parent@npm:3.1.0" resolution: "glob-parent@npm:3.1.0"