Browse Source

The images are in

ssao
A.Olokhtonov 6 months ago
parent
commit
8a15093147
  1. 1
      .gitignore
  2. 6
      README.txt
  3. 1
      client/client_recv.js
  4. 3
      client/index.js
  5. 41
      client/webgl_draw.js
  6. 64
      client/webgl_geometry.js
  7. 2
      client/webgl_listeners.js
  8. 52
      client/webgl_shaders.js
  9. 4
      server/http.js

1
.gitignore vendored

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
server/images
server/server.log
doca.txt
data/
client/*.dot

6
README.txt

@ -8,8 +8,8 @@ Release: @@ -8,8 +8,8 @@ Release:
+ Do not copy memory to wasm, instead use wasm memory to store data in the first place
+ SIMD for LOD?
+ Multithreading for LOD
+ Textured quads (pictures, code already written in older version)
- Z-prepass fringe bug (also, when do we enable the prepass?)
- Textured quads (pictures, code already written in older version)
- Resize and move pictures (draw handles)
+ Bugs
+ GC stalls!!!
@ -41,6 +41,7 @@ Release: @@ -41,6 +41,7 @@ Release:
- Eraser
- Line drawing
+ Undo
- Undo for images
- Redo
* Polish
+ Use typedvector where appropriate
@ -68,7 +69,8 @@ Bonus: @@ -68,7 +69,8 @@ Bonus:
- Move multiple points
* Customizable background
+ Dots pattern
* Grid pattern
+ Grid pattern
- Menu option
Bonus-bonus:
- Actually infinite canvas (replace floats with something, some kind of fixed point scheme? chunks? multilevel scheme?)

1
client/client_recv.js

@ -372,6 +372,7 @@ function handle_event(state, context, event, options = {}) { @@ -372,6 +372,7 @@ function handle_event(state, context, event, options = {}) {
event.width = bitmap.width;
event.height = bitmap.height;
geometry_add_dummy_stroke(context);
add_image(context, event.image_id, bitmap, p);
// God knows when this will actually complete (it loads the image from the server)

3
client/index.js

@ -104,7 +104,7 @@ let b_fast = true; @@ -104,7 +104,7 @@ let b_fast = true;
function start_spinner(state) {
const str = describeArc(64, 64, 32, a_angel, b_angel);
4
a_angel += speed_a;
b_angel += speed_b;
@ -243,6 +243,7 @@ async function main() { @@ -243,6 +243,7 @@ async function main() {
'buffers': {},
'locations': {},
'textures': {},
'images': [],
'dynamic_serializer': serializer_create(config.initial_dynamic_bytes),
'dynamic_index_serializer': serializer_create(config.initial_dynamic_bytes),

41
client/webgl_draw.js

@ -180,8 +180,6 @@ async function draw(state, context, animate, ts) { @@ -180,8 +180,6 @@ async function draw(state, context, animate, ts) {
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1f(locations['u_fadeout'], 1.0);
// Opacity for major lines goes on a curve 0 / 1 \ 0
// Previous level (major lines)
{
const grid_instances = new Float32Array(geometry_gen_fullscreen_grid_1d(state, context, 32 / zoom_previous, 32 / zoom_previous));
@ -200,7 +198,7 @@ async function draw(state, context, animate, ts) { @@ -200,7 +198,7 @@ async function draw(state, context, animate, ts) {
{
const grid_instances = new Float32Array(geometry_gen_fullscreen_grid_1d(state, context, 32 / zoom_next, 32 / zoom_next));
let t = (zoom_next / zoom - 1) / 7;
t = Math.min(0.1, -t + 1);
t = Math.min(0.1, -t + 1); // slight fade-in
gl.uniform1f(locations['u_fadeout'], t);
@ -211,7 +209,42 @@ async function draw(state, context, animate, ts) { @@ -211,7 +209,42 @@ async function draw(state, context, animate, ts) {
}
}
gl.clear(gl.DEPTH_BUFFER_BIT); // draw strokes above the background pattern
gl.clear(gl.DEPTH_BUFFER_BIT); // draw images above the background pattern
gl.useProgram(context.programs['image']);
buffers = context.buffers['image'];
locations = context.locations['image'];
{
let offset = 0;
const quads = geometry_image_quads(state, context);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_quads']);
gl.bufferData(gl.ARRAY_BUFFER, quads, gl.STATIC_DRAW);
gl.vertexAttribDivisor(locations['a_pos'], 0);
gl.enableVertexAttribArray(locations['a_pos']);
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, 0);
for (const entry of context.images) {
if (state.active_image === entry.key) {
//gl.uniform1i(locations['u_active'], 1);
} else {
//gl.uniform1i(locations['u_active'], 0);
}
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);
gl.uniform1i(locations['u_texture'], 0); // Only 1 active texture for each drawcall
gl.bindTexture(gl.TEXTURE_2D, entry.texture);
gl.drawArrays(gl.TRIANGLES, offset, 6);
offset += 6;
}
}
gl.clear(gl.DEPTH_BUFFER_BIT); // draw strokes above the images
gl.useProgram(context.programs['sdf'].main);
buffers = context.buffers['sdf'];
locations = context.locations['sdf'].main;

64
client/webgl_geometry.js

@ -203,40 +203,22 @@ function add_image(context, image_id, bitmap, p) { @@ -203,40 +203,22 @@ function add_image(context, image_id, bitmap, p) {
const x = p.x;
const y = p.y;
const gl = context.gl;
const id = Object.keys(context.textures['image']).length;
context.textures['image'][id] = {
const id = Object.keys(context.images).length;
const entry = {
'texture': gl.createTexture(),
'image_id': image_id
'key': image_id,
'at': p,
'width': bitmap.width,
'height': bitmap.height,
};
gl.bindTexture(gl.TEXTURE_2D, context.textures['image'][id].texture);
context.images.push(entry);
gl.bindTexture(gl.TEXTURE_2D, entry.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
context.quad_positions.push(...[
x, y,
x, y + bitmap.height,
x + bitmap.width, y + bitmap.height,
x + bitmap.width, y,
x, y,
x + bitmap.width, y + bitmap.height,
]);
context.quad_texcoords.push(...[
0, 0,
0, 1,
1, 1,
1, 0,
0, 0,
1, 1,
]);
context.quad_positions_f32 = new Float32Array(context.quad_positions);
context.quad_texcoords_f32 = new Float32Array(context.quad_texcoords);
}
function move_image(context, image_event) {
@ -365,3 +347,31 @@ function geometry_gen_fullscreen_grid_1d(state, context, step_x, step_y) { @@ -365,3 +347,31 @@ function geometry_gen_fullscreen_grid_1d(state, context, step_x, step_y) {
return result;
}
function geometry_image_quads(state, context) {
const result = new Float32Array(context.images.length * 12);
for (let i = 0; i < context.images.length; ++i) {
const entry = context.images[i];
result[i * 12 + 0] = entry.at.x;
result[i * 12 + 1] = entry.at.y;
result[i * 12 + 2] = entry.at.x + entry.width;
result[i * 12 + 3] = entry.at.y;
result[i * 12 + 4] = entry.at.x;
result[i * 12 + 5] = entry.at.y + entry.height;
result[i * 12 + 6] = entry.at.x + entry.width;
result[i * 12 + 7] = entry.at.y + entry.height;
result[i * 12 + 8] = entry.at.x;
result[i * 12 + 9] = entry.at.y + entry.height;
result[i * 12 + 10] = entry.at.x + entry.width;
result[i * 12 + 11] = entry.at.y;
}
return result;
}

2
client/webgl_listeners.js

@ -653,7 +653,5 @@ async function on_drop(e, state, context) { @@ -653,7 +653,5 @@ async function on_drop(e, state, context) {
const file = e.dataTransfer.files[0];
await insert_image(state, context, file);
schedule_draw(state, context);
return false;
}

52
client/webgl_shaders.js

@ -105,43 +105,32 @@ const sdf_vs_src = `#version 300 es @@ -105,43 +105,32 @@ const sdf_vs_src = `#version 300 es
in vec2 a_a; // point from
in vec2 a_b; // point to
in int a_stroke_id;
in vec2 a_pressure;
uniform vec2 u_scale;
uniform vec2 u_res;
uniform vec2 u_translation;
uniform int u_stroke_count;
uniform int u_stroke_texture_size;
uniform highp usampler2D u_stroke_data;
out vec4 v_line;
out vec2 v_texcoord;
out vec3 v_color;
flat out vec2 v_thickness;
void main() {
vec2 screen02;
float apron = 1.0; // google "futanari inflation rule 34"
int stroke_data_y = a_stroke_id / u_stroke_texture_size;
int stroke_data_x = a_stroke_id % u_stroke_texture_size;
uvec4 stroke_data = texelFetch(u_stroke_data, ivec2(stroke_data_x, stroke_data_y), 0);
float radius = float(stroke_data.w);
vec2 line_dir = normalize(a_b - a_a);
vec2 up_dir = vec2(line_dir.y, -line_dir.x);
vec2 pixel = vec2(2.0) / u_res * apron;
float rscale = apron / u_scale.x;
int vertex_index = gl_VertexID % 6;
vec2 outwards;
vec2 origin;
if (vertex_index == 0) {
// "top left" aka "p1"
origin = a_a;
@ -163,17 +152,13 @@ const sdf_vs_src = `#version 300 es @@ -163,17 +152,13 @@ const sdf_vs_src = `#version 300 es
vec2 pos = origin + normalize(outwards) * radius * 2.0 * max(a_pressure.x, a_pressure.y); // doubling is to account for max possible pressure
screen02 = (pos.xy * u_scale + u_translation) / u_res * 2.0 + outwards * pixel;
v_texcoord = pos.xy + outwards * rscale;
screen02.y = 2.0 - screen02.y;
v_line = vec4(a_a, a_b);
v_thickness = radius * a_pressure; // pressure 0.5 is the "neutral" pressure
v_color = vec3(stroke_data.xyz) / 255.0;
if (a_stroke_id >> 31 != 0) {
screen02 += vec2(100.0); // shift offscreen
}
gl_Position = vec4(screen02 - 1.0, (float(a_stroke_id) / float(u_stroke_count)) * 2.0 - 1.0, 1.0);
}
`;
@ -212,12 +197,11 @@ const sdf_fs_src = `#version 300 es @@ -212,12 +197,11 @@ const sdf_fs_src = `#version 300 es
const tquad_vs_src = `#version 300 es
in vec2 a_pos;
in vec2 a_texcoord;
uniform vec2 u_scale;
uniform vec2 u_res;
uniform vec2 u_translation;
out vec2 v_texcoord;
void main() {
@ -225,7 +209,19 @@ const tquad_vs_src = `#version 300 es @@ -225,7 +209,19 @@ const tquad_vs_src = `#version 300 es
vec2 screen02 = screen01 * 2.0;
screen02.y = 2.0 - screen02.y;
vec2 screen11 = screen02 - 1.0;
v_texcoord = a_texcoord;
int vertex_index = gl_VertexID % 6;
if (vertex_index == 0) {
v_texcoord = vec2(0.0, 0.0);
} else if (vertex_index == 1 || vertex_index == 5) {
v_texcoord = vec2(1.0, 0.0);
} else if (vertex_index == 2 || vertex_index == 4) {
v_texcoord = vec2(0.0, 1.0);
} else {
v_texcoord = vec2(1.0, 1.0);
}
gl_Position = vec4(screen11, 0, 1);
}
`;
@ -236,16 +232,11 @@ const tquad_fs_src = `#version 300 es @@ -236,16 +232,11 @@ const tquad_fs_src = `#version 300 es
in vec2 v_texcoord;
uniform sampler2D u_texture;
uniform bool u_outline;
layout(location = 0) out vec4 FragColor;
void main() {
if (!u_outline) {
FragColor = texture(u_texture, v_texcoord);
} else {
FragColor = mix(texture(u_texture, v_texcoord), vec4(0.7, 0.7, 0.95, 1), 0.5);
}
FragColor = texture(u_texture, v_texcoord);
}
`;
@ -407,6 +398,15 @@ function init_webgl(state, context) { @@ -407,6 +398,15 @@ function init_webgl(state, context) {
'grid': create_program(gl, grid_vs, dots_fs),
};
context.locations['image'] = {
'a_pos': gl.getAttribLocation(context.programs['image'], 'a_pos'),
'u_res': gl.getUniformLocation(context.programs['image'], 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['image'], 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['image'], 'u_translation'),
'u_texture': gl.getUniformLocation(context.programs['image'], 'u_texture'),
};
context.locations['debug'] = {
'a_pos': gl.getAttribLocation(context.programs['debug'], 'a_pos'),
@ -468,6 +468,10 @@ function init_webgl(state, context) { @@ -468,6 +468,10 @@ function init_webgl(state, context) {
'b_packed': gl.createBuffer(),
};
context.buffers['image'] = {
'b_quads': gl.createBuffer(),
};
context.buffers['sdf'] = {
'b_instance': gl.createBuffer(),
'b_dynamic_instance': gl.createBuffer(),

4
server/http.js

@ -9,7 +9,7 @@ export async function route(req) { @@ -9,7 +9,7 @@ export async function route(req) {
const desk_id = url.searchParams.get('deskId') || '0';
const formdata = await req.formData();
const file = formdata.get('file');
const image_id = math.fast_random32();
const image_id = math.crypto_random32();
Bun.write(config.IMAGEDIR + '/' + image_id, file);
@ -17,4 +17,4 @@ export async function route(req) { @@ -17,4 +17,4 @@ export async function route(req) {
} else if (url.pathname === '/api/ping') {
return new Response('pong');
}
}
}

Loading…
Cancel
Save