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,6 +311,7 @@
[:& top-bar/top-bar]] [:& top-bar/top-bar]]
(when (or (= renderer "svg") (= renderer "both"))
[:svg.render-shapes [:svg.render-shapes
{:id "render" {:id "render"
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
@ -347,12 +354,13 @@
;; 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!
(when (or (= renderer "gl") (= renderer "both"))
[gl/canvas {:objects base-objects [gl/canvas {:objects base-objects
:active-frames @active-frames :active-frames @active-frames
:vbox vbox}] :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,6 +2,7 @@
(: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]
@ -18,6 +19,7 @@
#_(def framebuffers (js/Map.)) #_(def framebuffers (js/Map.))
(defn parse-color-hex (defn parse-color-hex
"Parses a color string and returns a vector with the RGBA values."
[color opacity] [color opacity]
(let [r (str/slice color 1 3) (let [r (str/slice color 1 3)
g (str/slice color 3 5) g (str/slice color 3 5)
@ -28,6 +30,7 @@
opacity])) opacity]))
(defn parse-color (defn parse-color
"Parses a color string and returns a vector with the RGBA values."
[color opacity] [color opacity]
(cond (cond
(str/starts-with? color "#") (str/starts-with? color "#")
@ -36,7 +39,22 @@
:else :else
#js [0.0 0.0 0.0 1.0])) #js [0.0 0.0 0.0 1.0]))
(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-to (defn resize-canvas-to
"Resize canvas to specific coordinates."
[canvas width height] [canvas width height]
(let [resized-width (not= (.-width canvas) width) (let [resized-width (not= (.-width canvas) width)
resized-height (not= (.-height canvas) height) resized-height (not= (.-height canvas) height)
@ -48,41 +66,65 @@
resized)) resized))
(defn resize-canvas (defn resize-canvas
"Resizes the canvas intrinsic size to the element size."
[canvas] [canvas]
(let [width (math/floor (.-clientWidth canvas)) (let [width (math/floor (.-clientWidth canvas))
height (math/floor (.-clientHeight canvas))] height (math/floor (.-clientHeight canvas))]
(resize-canvas-to canvas width height))) (resize-canvas-to canvas width height)))
(defn prepare-gl (defn prepare-gl
"Prepares the WebGL context for rendering."
[gl] [gl]
(let [default-program (gl/create-program-from-sources gl default-vertex-shader default-fragment-shader)] (let [default-program (gl/create-program-from-sources gl default-vertex-shader default-fragment-shader)]
(.set programs "default" default-program))) (.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)
projection (.projection glm/mat3 projection (:width vbox) (:height vbox))]
(.clearColor gl 1.0 0.0 1.0 0.5)
(.clear gl (.-COLOR_BUFFER_BIT gl)) (.clear gl (.-COLOR_BUFFER_BIT gl))
(.viewport gl 0 0 (.-width (.-canvas gl)) (.-height (.-canvas gl))) (.viewport gl 0 0 (.-width (.-canvas gl)) (.-height (.-canvas gl)))
(.useProgram gl (.get programs "default")) ;; Enable alpha blending
(.uniform4f gl (.getUniformLocation gl (.get programs "default") "u_vbox") (:x vbox) (:y vbox) (:width vbox) (:height vbox))
(.enable gl (.-BLEND gl)) (.enable gl (.-BLEND gl))
(.blendFunc gl (.-SRC_ALPHA gl) (.-ONE_MINUS_SRC_ALPHA gl)) (.blendFunc gl (.-SRC_ALPHA gl) (.-ONE_MINUS_SRC_ALPHA gl))
(.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] (doseq [[_ object] objects]
(let [selrect (:selrect object) (let [selrect (:selrect object)
x (:x selrect) x (:x selrect)
y (:y selrect) y (:y selrect)
width (:width selrect) width (:width selrect)
height (:height 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))] (doseq [fill (reverse (:fills object))]
(do (do
(.uniform4fv gl (.getUniformLocation gl (.get programs "default") "u_color") (parse-color (:fill-color fill) (:fill-opacity fill))) (.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) (.drawArrays gl (.-TRIANGLE_STRIP gl) 0 4)))))))
(.uniform2f gl (.getUniformLocation gl (.get programs "default") "u_position") x y)
(.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;
// fragColor = vec4(1.0, 0.0, 1.0, 1.0); // 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 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"