From 30559b0381265eecffb0606fe8f94c912db899da Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Mon, 6 May 2024 01:28:52 +0300 Subject: [PATCH] The dots are now zoomable --- README.txt | 1 + client/webgl_draw.js | 50 ++++++++++++++++++++++++---------------- client/webgl_geometry.js | 21 ++++++++++++++++- client/webgl_shaders.js | 40 ++++++++++++++++---------------- 4 files changed, 71 insertions(+), 41 deletions(-) diff --git a/README.txt b/README.txt index 7a99dad..3785770 100644 --- a/README.txt +++ b/README.txt @@ -26,6 +26,7 @@ Release: + Drag with mouse button 3 + Investigate skipped inputs on mobile (panning, zooming) [Events were not actually getting skipped. The stroke previews were just not being drawn] + Smooth zoom + + Infinite background pattern - Be able to have multiple "current" strokes per player. In case of bad internet this can happen! - Do NOT use session id as player id LUL - Save events to indexeddb (as some kind of a blob), restore on reconnect and page reload diff --git a/client/webgl_draw.js b/client/webgl_draw.js index fddde52..9613ed0 100644 --- a/client/webgl_draw.js +++ b/client/webgl_draw.js @@ -106,21 +106,8 @@ async function draw(state, context) { gl.useProgram(context.programs['pattern'].dots); buffers = context.buffers['pattern']; locations = context.locations['pattern'].dots; - - if (state.canvas.zoom >= config.pattern_fadeout_min) { - // Reused data - gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_dot']); - - const one_dot = new Float32Array(geometry_gen_circle(0, 0, 1, 32)); - const dot_instances = new Float32Array(geometry_gen_fullscreen_grid(state, context, 32, 32, 50, 50)); - - gl.bufferData(gl.ARRAY_BUFFER, one_dot, gl.STREAM_DRAW); - gl.enableVertexAttribArray(locations['a_xy']); - gl.vertexAttribPointer(locations['a_xy'], 2, gl.FLOAT, false, 2 * 4, 0); - - // Per-instance data + { gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']); - gl.bufferData(gl.ARRAY_BUFFER, dot_instances, gl.STREAM_DRAW); gl.enableVertexAttribArray(locations['a_center']); gl.vertexAttribPointer(locations['a_center'], 2, gl.FLOAT, false, 2 * 4, 0); gl.vertexAttribDivisor(locations['a_center'], 1); @@ -128,15 +115,38 @@ async function draw(state, context) { gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height); gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom); gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); - - if (config.pattern_fadeout_min <= state.canvas.zoom && state.canvas.zoom <= config.pattern_fadeout_max) { - const t = (state.canvas.zoom - config.pattern_fadeout_min) / (config.pattern_fadeout_max - config.pattern_fadeout_min); + + const zoom = state.canvas.zoom; + const zoom_log2 = Math.log2(zoom); + const zoom_previous = Math.pow(2, Math.floor(zoom_log2)); + const zoom_next = Math.pow(2, Math.ceil(zoom_log2)); + + // Previous level + { + const one_dot = new Float32Array(geometry_gen_quad(0, 0, 1 / zoom_previous)); + const dot_instances = new Float32Array(geometry_gen_fullscreen_grid(state, context, 32 / zoom_previous, 32 / zoom_previous)); + const t = Math.min(1.0, 1.0 - (zoom / zoom_previous) / 2.0); + gl.uniform1f(locations['u_fadeout'], t); - } else { - gl.uniform1f(locations['u_fadeout'], 1); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']); + gl.bufferData(gl.ARRAY_BUFFER, dot_instances, gl.STREAM_DRAW); + + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, dot_instances.length / 2); } - gl.drawArraysInstanced(gl.TRIANGLES, 0, one_dot.length / 2, dot_instances.length / 2); + // Next level + if (zoom_previous != zoom_next) { + const dot_instances = new Float32Array(geometry_gen_fullscreen_grid(state, context, 32 / zoom_next, 32 / zoom_next)); + const t = Math.min(1.0, 1.0 - (zoom_next / zoom) / 2.0); + + gl.uniform1f(locations['u_fadeout'], t); + + gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']); + gl.bufferData(gl.ARRAY_BUFFER, dot_instances, gl.STREAM_DRAW); + + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, dot_instances.length / 2); + } } gl.clear(gl.DEPTH_BUFFER_BIT); diff --git a/client/webgl_geometry.js b/client/webgl_geometry.js index 6e820c5..5d3f86b 100644 --- a/client/webgl_geometry.js +++ b/client/webgl_geometry.js @@ -265,7 +265,26 @@ function geometry_gen_circle(cx, cy, r, n) { return result; } -function geometry_gen_fullscreen_grid(state, context, step_x, step_y, offset_x, offset_y) { +function geometry_gen_quad(cx, cy, r) { + const result = [ + cx - r, + cy - r, + cx + r, + cy - r, + cx - r, + cy + r, + cx + r, + cy + r, + cx - r, + cy + r, + cx + r, + cy - r, + ]; + + return result; +} + +function geometry_gen_fullscreen_grid(state, context, step_x, step_y) { const result = []; const width = context.canvas.width; const height = context.canvas.height; diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js index deec7e8..1bc311b 100644 --- a/client/webgl_shaders.js +++ b/client/webgl_shaders.js @@ -250,10 +250,8 @@ const tquad_fs_src = `#version 300 es `; const dots_vs_src = `#version 300 es - in vec2 a_xy; in vec2 a_center; // per-instance - out float v_dist; out float v_fadeout; uniform vec2 u_scale; @@ -262,21 +260,27 @@ const dots_vs_src = `#version 300 es uniform float u_fadeout; void main() { - float apron = 2.0; - vec2 pixel = vec2(2.0) / u_res * apron; - vec2 outwards = vec2(0.0); - float rscale = apron / u_scale.x; - - if (gl_VertexID % 3 == 0) { - v_dist = 0.0; - } else { - outwards = a_xy; - v_dist = 1.0 + rscale; + vec2 v = (a_center * u_scale + u_translation) / u_res * 2.0; + vec2 pos; + vec2 pixel = 2.0 / u_res; + + if (gl_VertexID % 6 == 0) { + pos = v + pixel * vec2(-1.0); + } else if (gl_VertexID % 6 == 1) { + pos = v + pixel * vec2(1.0, -1.0); + } else if (gl_VertexID % 6 == 2) { + pos = v + pixel * vec2(-1.0, 1.0); + } else if (gl_VertexID % 6 == 3) { + pos = v + pixel * vec2(1.0); + } else if (gl_VertexID % 6 == 4) { + pos = v + pixel * vec2(-1.0, 1.0); + } else if (gl_VertexID % 6 == 5) { + pos = v + pixel * vec2(1.0, -1.0); } - v_fadeout = u_fadeout; - vec2 screen02 = ((a_center + a_xy) * u_scale + u_translation) / u_res * 2.0 + outwards * pixel; + vec2 screen02 = pos; screen02.y = 2.0 - screen02.y; + v_fadeout = u_fadeout; gl_Position = vec4(screen02 - 1.0, 0.0, 1.0); } `; @@ -284,17 +288,13 @@ const dots_vs_src = `#version 300 es const dots_fs_src = `#version 300 es precision highp float; - in float v_dist; in float v_fadeout; layout(location = 0) out vec4 FragColor; void main() { - float fade = 0.5 * length(fwidth(v_dist)); - float alpha = 1.0 - smoothstep(-fade, fade, v_dist - 1.0); - alpha *= v_fadeout; - vec3 color = vec3(0.8); - FragColor = vec4(color * alpha, alpha); + vec3 color = vec3(0.5); + FragColor = vec4(color * v_fadeout, v_fadeout); } `;