Browse Source

The dots are now zoomable

ssao
A.Olokhtonov 6 months ago
parent
commit
30559b0381
  1. 1
      README.txt
  2. 50
      client/webgl_draw.js
  3. 21
      client/webgl_geometry.js
  4. 40
      client/webgl_shaders.js

1
README.txt

@ -26,6 +26,7 @@ Release:
+ Drag with mouse button 3 + 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] + Investigate skipped inputs on mobile (panning, zooming) [Events were not actually getting skipped. The stroke previews were just not being drawn]
+ Smooth zoom + Smooth zoom
+ Infinite background pattern
- Be able to have multiple "current" strokes per player. In case of bad internet this can happen! - 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 - 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 - Save events to indexeddb (as some kind of a blob), restore on reconnect and page reload

50
client/webgl_draw.js

@ -106,21 +106,8 @@ async function draw(state, context) {
gl.useProgram(context.programs['pattern'].dots); gl.useProgram(context.programs['pattern'].dots);
buffers = context.buffers['pattern']; buffers = context.buffers['pattern'];
locations = context.locations['pattern'].dots; 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.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']);
gl.bufferData(gl.ARRAY_BUFFER, dot_instances, gl.STREAM_DRAW);
gl.enableVertexAttribArray(locations['a_center']); gl.enableVertexAttribArray(locations['a_center']);
gl.vertexAttribPointer(locations['a_center'], 2, gl.FLOAT, false, 2 * 4, 0); gl.vertexAttribPointer(locations['a_center'], 2, gl.FLOAT, false, 2 * 4, 0);
gl.vertexAttribDivisor(locations['a_center'], 1); 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_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom); gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); 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 zoom = state.canvas.zoom;
const t = (state.canvas.zoom - config.pattern_fadeout_min) / (config.pattern_fadeout_max - config.pattern_fadeout_min); 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); 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); gl.clear(gl.DEPTH_BUFFER_BIT);

21
client/webgl_geometry.js

@ -265,7 +265,26 @@ function geometry_gen_circle(cx, cy, r, n) {
return result; 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 result = [];
const width = context.canvas.width; const width = context.canvas.width;
const height = context.canvas.height; const height = context.canvas.height;

40
client/webgl_shaders.js

@ -250,10 +250,8 @@ const tquad_fs_src = `#version 300 es
`; `;
const dots_vs_src = `#version 300 es const dots_vs_src = `#version 300 es
in vec2 a_xy;
in vec2 a_center; // per-instance in vec2 a_center; // per-instance
out float v_dist;
out float v_fadeout; out float v_fadeout;
uniform vec2 u_scale; uniform vec2 u_scale;
@ -262,21 +260,27 @@ const dots_vs_src = `#version 300 es
uniform float u_fadeout; uniform float u_fadeout;
void main() { void main() {
float apron = 2.0; vec2 v = (a_center * u_scale + u_translation) / u_res * 2.0;
vec2 pixel = vec2(2.0) / u_res * apron; vec2 pos;
vec2 outwards = vec2(0.0); vec2 pixel = 2.0 / u_res;
float rscale = apron / u_scale.x;
if (gl_VertexID % 6 == 0) {
if (gl_VertexID % 3 == 0) { pos = v + pixel * vec2(-1.0);
v_dist = 0.0; } else if (gl_VertexID % 6 == 1) {
} else { pos = v + pixel * vec2(1.0, -1.0);
outwards = a_xy; } else if (gl_VertexID % 6 == 2) {
v_dist = 1.0 + rscale; 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 = pos;
vec2 screen02 = ((a_center + a_xy) * u_scale + u_translation) / u_res * 2.0 + outwards * pixel;
screen02.y = 2.0 - screen02.y; screen02.y = 2.0 - screen02.y;
v_fadeout = u_fadeout;
gl_Position = vec4(screen02 - 1.0, 0.0, 1.0); 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 const dots_fs_src = `#version 300 es
precision highp float; precision highp float;
in float v_dist;
in float v_fadeout; in float v_fadeout;
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
void main() { void main() {
float fade = 0.5 * length(fwidth(v_dist)); vec3 color = vec3(0.5);
float alpha = 1.0 - smoothstep(-fade, fade, v_dist - 1.0); FragColor = vec4(color * v_fadeout, v_fadeout);
alpha *= v_fadeout;
vec3 color = vec3(0.8);
FragColor = vec4(color * alpha, alpha);
} }
`; `;

Loading…
Cancel
Save