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",
"draft-js": "^0.11.7",
"eventsource-parser": "^1.1.1",
"gl-matrix": "^3.4.3",
"highlight.js": "^11.8.0",
"js-beautify": "^1.14.9",
"jszip": "^3.10.1",

View File

@@ -261,7 +261,13 @@
(:y (first selected-shapes))
(: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-viewport-size vport viewport-ref)
@@ -305,54 +311,56 @@
[:& top-bar/top-bar]]
[:svg.render-shapes
{:id "render"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot "https://penpot.app/xmlns"
:preserveAspectRatio "xMidYMid meet"
:key (str "render" page-id)
:width (:width vport 0)
:height (:height vport 0)
:view-box (utils/format-viewbox vbox)
:style {:background-color background
:pointer-events "none"}
:fill "none"}
(when (or (= renderer "svg") (= renderer "both"))
[:svg.render-shapes
{:id "render"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot "https://penpot.app/xmlns"
:preserveAspectRatio "xMidYMid meet"
:key (str "render" page-id)
:width (:width vport 0)
:height (:height vport 0)
:view-box (utils/format-viewbox vbox)
:style {:background-color background
:pointer-events "none"}
:fill "none"}
[:defs
[:linearGradient {:id "frame-placeholder-gradient"}
[:animateTransform
{:attributeName "gradientTransform"
:type "translate"
:from "-1 0"
:to "1 0"
:dur "2s"
:repeatCount "indefinite"}]
[: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 "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]]
[:defs
[:linearGradient {:id "frame-placeholder-gradient"}
[:animateTransform
{:attributeName "gradientTransform"
:type "translate"
:from "-1 0"
:to "1 0"
:dur "2s"
:repeatCount "indefinite"}]
[: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 "100%" :stop-color (str "color-mix(in srgb-linear, " background " 90%, #777)") :stop-opacity 1}]]]
(when (dbg/enabled? :show-export-metadata)
[:& use/export-page {:options options}])
(when (dbg/enabled? :show-export-metadata)
[:& use/export-page {:options options}])
;; We need a "real" background shape so layer transforms work properly in firefox
[:rect {:width (:width vbox 0)
:height (:height vbox 0)
:x (:x vbox 0)
:y (:y vbox 0)
:fill background}]
;; We need a "real" background shape so layer transforms work properly in firefox
[:rect {:width (:width vbox 0)
:height (:height vbox 0)
:x (:x vbox 0)
:y (:y vbox 0)
:fill background}]
[:& (mf/provider ctx/current-vbox) {:value vbox'}
[:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)}
;; Render root shape
[:& shapes/root-shape {:key page-id
:objects base-objects
:active-frames @active-frames}]]]]
[:& (mf/provider ctx/current-vbox) {:value vbox'}
[:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)}
;; Render root shape
[:& shapes/root-shape {:key page-id
:objects base-objects
:active-frames @active-frames}]]]])
;; IT's MAGIC!
[gl/canvas {:objects base-objects
:active-frames @active-frames
:vbox vbox}]
(when (or (= renderer "gl") (= renderer "both"))
[gl/canvas {:objects base-objects
:active-frames @active-frames
:vbox vbox}])
[:svg.viewport-controls
{:xmlns "http://www.w3.org/2000/svg"

View File

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

View File

@@ -1,18 +1,50 @@
#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;
// in vec2 v_texCoord;
// uniform sampler2D u_texture;
uniform vec4 u_color;
out vec4 fragColor;
in vec2 v_texCoord;
uniform int u_type;
uniform vec4 u_color;
void main() {
// fragColor = texture(u_framebuffer, v_texCoord) + texture(u_texture, v_texCoord);
// fragColor = texture(u_texture, v_texCoord);
// Pintamos rosita
fragColor = u_color;
// fragColor = vec4(1.0, 0.0, 1.0, 1.0);
// 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);
} 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 vec2 u_position;
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) {
if(id == 0) {
return vec2(0.0f, 0.0f);
return vec2(-1.0, -1.0);
} else if(id == 1) {
return vec2(1.0f, 0.0f);
return vec2(1.0, -1.0);
} else if(id == 2) {
return vec2(0.0f, 1.0f);
return vec2(-1.0, 1.0);
} else if(id == 3) {
return vec2(1.0f, 1.0f);
return vec2(1.0, 1.0);
} else {
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() {
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 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);
// gl_Position = vec4(((get_base_position(gl_VertexID) * u_size + u_position) / u_vbox.zw), 0.0f, 1.0f);
// v_texCoord = get_tex_position(gl_VertexID);
vec2 vertex_rotated_scaled = rotation_scale * vertex;
vec2 vertex_positioned = center + vertex_rotated_scaled + position;
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"
eventsource-parser: "npm:^1.1.1"
gettext-parser: "npm:^7.0.1"
gl-matrix: "npm:^3.4.3"
gulp: "npm:4.0.2"
gulp-cached: "npm:^1.1.1"
gulp-concat: "npm:^2.6.1"
@@ -7891,6 +7892,13 @@ __metadata:
languageName: node
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":
version: 3.1.0
resolution: "glob-parent@npm:3.1.0"