diff --git a/client/bvh.js b/client/bvh.js index c913a6f..c499c55 100644 --- a/client/bvh.js +++ b/client/bvh.js @@ -1,5 +1,3 @@ -// TODO: get rid of node_count -// function bvh_make_leaf(bvh, index, stroke) { const leaf = { 'stroke_index': index, @@ -100,15 +98,12 @@ function bvh_rotate(bvh, index) { function bvh_add_stroke(bvh, index, stroke) { const leaf_index = bvh_make_leaf(bvh, index, stroke); - if (bvh.node_count === 0) { + if (bvh.nodes.length === 1) { bvh.root = leaf_index; - bvh.node_count++; return; } - bvh.node_count++; - - if (bvh.pqueue.capacity < Math.ceil(bvh.node_count * 1.2)) { + if (bvh.pqueue.capacity < Math.ceil(bvh.nodes.length * 1.2)) { bvh.pqueue = new MinQueue(bvh.pqueue.capacity * 2); } @@ -152,8 +147,6 @@ function bvh_add_stroke(bvh, index, stroke) { bvh.nodes[new_parent].bbox = new_bbox; bvh.nodes[new_parent].area = (new_bbox.x2 - new_bbox.x1) * (new_bbox.y2 - new_bbox.y1); - bvh.node_count++; - // 3. Refit and rotate let refit_index = bvh.nodes[leaf_index].parent_index; while (refit_index !== null) { @@ -195,18 +188,8 @@ function bvh_intersect_quad(bvh, quad) { } function bvh_clip(state, context) { - if (state.onscreen_segments === null) { - let total_points = 0; - - for (const event of state.events) { - if (event.type === EVENT.STROKE && !event.deleted && event.points.length > 0) { - total_points += event.points.length - 1; - } - } - - if (total_points > 0) { - state.onscreen_segments = new Uint32Array(total_points * 6); - } + if (state.onscreen_segments.length < Math.ceil(state.total_points * 6 * 1.2)) { + state.onscreen_segments = new Uint32Array(state.total_points * 6 * 2); } let at = 0; diff --git a/client/client_recv.js b/client/client_recv.js index 6df8d55..f82d77a 100644 --- a/client/client_recv.js +++ b/client/client_recv.js @@ -170,10 +170,12 @@ function handle_event(state, context, event, options = {}) { } case EVENT.STROKE: { - if (event.user_id != state.me) { + // TODO: @speed do proper local prediction, it's not that hard + + //if (event.user_id != state.me) { geometry_clear_player(state, context, event.user_id); need_draw = true; - } + //} event.index = state.events.length; event.starting_index = state.starting_index; @@ -182,6 +184,8 @@ function handle_event(state, context, event, options = {}) { state.starting_index += (event.points.length - 1) * 4; } + state.total_points += event.points.length; + geometry_add_stroke(state, context, event, state.events.length, options.skip_bvh === true); state.stroke_count++; @@ -368,6 +372,8 @@ async function handle_message(state, context, d) { } } + state.sn = event_count; + bvh_construct(state); document.getElementById('debug-render-from').max = state.stroke_count; diff --git a/client/index.html b/client/index.html index 97c8c3f..f1c1760 100644 --- a/client/index.html +++ b/client/index.html @@ -41,7 +41,6 @@ -
diff --git a/client/index.js b/client/index.js index 317936b..5fb5ae2 100644 --- a/client/index.js +++ b/client/index.js @@ -1,6 +1,8 @@ // NEXT: pan with m3, place dot, cursor size and color, YELLOW and gray, show user cursor, background styles, // view desks, undo, eraser, ruler, images (movable), quadtree for clipping, f5 without redownload, progress bar // +// use returning ID on insert intead of (COLLIDING!) rand_32 +// look into using u64 for point coordinates? fix bad drawings on zoomout document.addEventListener('DOMContentLoaded', main); @@ -165,10 +167,10 @@ function main() { 'events': [], 'stroke_count': 0, 'starting_index': 0, + 'total_points': 0, 'bvh': { 'nodes': [], - 'node_count': 0, 'root': null, 'pqueue': new MinQueue(1024), }, @@ -189,7 +191,7 @@ function main() { }, 'players': {}, - 'onscreen_segments': null, + 'onscreen_segments': new Uint32Array(1024), 'debug': { 'red': false, @@ -198,7 +200,6 @@ function main() { 'limit_to': false, 'render_from': 0, 'render_to': 0, - 'force_clip_off': false, 'draw_bvh': false, } }; diff --git a/client/webgl_draw.js b/client/webgl_draw.js index 56183ce..5e3979c 100644 --- a/client/webgl_draw.js +++ b/client/webgl_draw.js @@ -57,7 +57,7 @@ function draw(state, context) { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); let index_count; - const do_clip = !state.debug.force_clip_off; //(state.canvas.zoom > config.clip_zoom_threshold); + const do_clip = true;//(state.canvas.zoom > config.clip_zoom_threshold); if (do_clip) { context.need_index_upload = true; @@ -137,6 +137,7 @@ function draw(state, context) { gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); gl.uniform1i(locations['u_stroke_count'], state.stroke_count); gl.uniform1i(locations['u_debug_mode'], state.debug.red); + gl.uniform1i(locations['u_shrink'], 1); gl.enableVertexAttribArray(locations['a_pos']); gl.enableVertexAttribArray(locations['a_line']); @@ -160,6 +161,34 @@ function draw(state, context) { } } + // Dynamic data (stroke previews that are currently in progress) + const dynamic_points = context.dynamic_serializer.offset / config.bytes_per_point; + + if (dynamic_points > 0) { + gl.clear(gl.DEPTH_BUFFER_BIT); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_dynamic']); + + gl.enableVertexAttribArray(locations['a_pos']); + gl.enableVertexAttribArray(locations['a_line']); + gl.enableVertexAttribArray(locations['a_color']); + gl.enableVertexAttribArray(locations['a_stroke_id']); + + gl.vertexAttribPointer(locations['a_pos'], 3, gl.FLOAT, false, config.bytes_per_point, 0); + gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3); + gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4); + gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4); + + gl.uniform1i(locations['u_shrink'], 0); + + if (context.need_dynamic_upload) { + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.dynamic_serializer.buffer, 0, context.dynamic_serializer.offset), gl.DYNAMIC_DRAW); + context.need_dynamic_upload = false; + } + + gl.drawArrays(gl.TRIANGLES, 0, dynamic_points); + } + + if (state.debug.draw_bvh) { const points = new Float32Array(state.bvh.nodes.length * 6 * 2); @@ -195,32 +224,10 @@ function draw(state, context) { gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 8, 0); gl.clear(gl.DEPTH_BUFFER_BIT); - gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.drawArrays(gl.TRIANGLES, 0, points.length / 2); } - - /* - if (dynamic_points > 0) { - gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_dynamic']); - - gl.enableVertexAttribArray(locations['a_pos']); - gl.enableVertexAttribArray(locations['a_line']); - gl.enableVertexAttribArray(locations['a_color']); - - gl.vertexAttribPointer(locations['a_pos'], 3, gl.FLOAT, false, config.bytes_per_point, 0); - gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3); - gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4); - - if (context.need_dynamic_upload) { - gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.dynamic_serializer.buffer, 0, context.dynamic_serializer.offset), gl.STATIC_DRAW); - context.need_dynamic_upload = false; - } - - gl.drawArrays(gl.TRIANGLES, 0, dynamic_points); - } - */ - + if (context.gpu_timer_ext) { gl.endQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT); diff --git a/client/webgl_geometry.js b/client/webgl_geometry.js index 33fd2dc..4b56b39 100644 --- a/client/webgl_geometry.js +++ b/client/webgl_geometry.js @@ -13,18 +13,23 @@ function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b, stroke_id) { ser_u32(s, stroke_id); } -function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id) { - push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b, stroke_id); - push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b, stroke_id); - push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b, stroke_id); - push_point(s, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id); +function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id, indexed = true) { + if (indexed) { + push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + } else { + push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b, stroke_id); + } } -function push_stroke(s, stroke, stroke_index) { - // if (stroke.stroke_id !== 1123776468) { - // return; - // } - +function push_stroke(s, stroke, stroke_index, indexed = true) { const stroke_width = stroke.width; const points = stroke.points; const color_u32 = stroke.color; @@ -73,7 +78,8 @@ function push_stroke(s, stroke, stroke_index) { to.x, to.y, stroke_width, r, g, b, - stroke_index + stroke_index, + indexed ); } } @@ -97,7 +103,7 @@ function geometry_prepare_stroke(state) { }; } -function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh) { +function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = false) { if (!state.online || !stroke || stroke.points.length === 0) return; stroke.bbox = stroke_bbox(stroke); @@ -157,8 +163,8 @@ function recompute_dynamic_data(state, context) { for (const player_id in state.players) { // player has the same data as their current stroke: points, color, width const player = state.players[player_id]; - if (player.points.length > 0) { - push_stroke(context.dynamic_serializer, player, 0); // TODO: stroke index ?? + if (player.points.length > 1) { + push_stroke(context.dynamic_serializer, player, 0, false); } } diff --git a/client/webgl_listeners.js b/client/webgl_listeners.js index 3d23714..387fe59 100644 --- a/client/webgl_listeners.js +++ b/client/webgl_listeners.js @@ -26,7 +26,6 @@ function debug_panel_init(state, context) { document.getElementById('debug-do-prepass').checked = state.debug.do_prepass; document.getElementById('debug-limit-from').checked = state.debug.limit_from; document.getElementById('debug-limit-to').checked = state.debug.limit_to; - document.getElementById('debug-force-clip-off').checked = state.debug.force_clip_off; document.getElementById('debug-draw-bvh').checked = state.debug.draw_bvh; document.getElementById('debug-draw-bvh').addEventListener('change', (e) => { @@ -34,11 +33,6 @@ function debug_panel_init(state, context) { schedule_draw(state, context); }); - document.getElementById('debug-force-clip-off').addEventListener('change', (e) => { - state.debug.force_clip_off = e.target.checked; - schedule_draw(state, context); - }); - document.getElementById('debug-red').addEventListener('change', (e) => { state.debug.red = e.target.checked; schedule_draw(state, context); @@ -257,9 +251,9 @@ function mouseup(e, state, context) { const stroke = geometry_prepare_stroke(state); if (stroke) { - geometry_add_stroke(state, context, stroke, 0); // TODO: stroke index? + //geometry_add_stroke(state, context, stroke, 0); // TODO: stroke index? queue_event(state, stroke_event(state)); - geometry_clear_player(state, context, state.me); + //geometry_clear_player(state, context, state.me); schedule_draw(state, context); } diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js index 8790c39..d615e36 100644 --- a/client/webgl_shaders.js +++ b/client/webgl_shaders.js @@ -113,6 +113,7 @@ const sdf_vs_src = `#version 300 es uniform vec2 u_translation; uniform int u_stroke_count; + uniform int u_shrink; out vec4 v_line; out vec2 v_texcoord; @@ -123,31 +124,35 @@ const sdf_vs_src = `#version 300 es void main() { vec2 screen01 = (a_pos.xy * u_scale + u_translation) / u_res; vec2 screen02 = screen01 * 2.0; - - // Inflate quad by 1 pixel - float apron = 2.0; - vec2 line_dir = normalize(a_line.zw - a_line.xy); - vec2 up_dir = vec2(line_dir.y, -line_dir.x); - vec2 pixel = vec2(2.0) / u_res * apron; - - int vertex_index = gl_VertexID % 4; - - if (vertex_index == 0) { - // "top left" aka "p1" - screen02 += up_dir * pixel - line_dir * pixel; - v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale; - } else if (vertex_index == 1) { - // "top right" aka "p2" - screen02 += up_dir * pixel + line_dir * pixel; - v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale; - } else if (vertex_index == 2) { - // "bottom left" aka "p3" - screen02 += -up_dir * pixel - line_dir * pixel; - v_texcoord = a_pos.xy - up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale; + + if (u_shrink == 1) { + // Inflate quad by 2 pixels (change to 1 when I figure out how to fix the prepass shit) + float apron = 2.0; + vec2 line_dir = normalize(a_line.zw - a_line.xy); + vec2 up_dir = vec2(line_dir.y, -line_dir.x); + vec2 pixel = vec2(2.0) / u_res * apron; + + int vertex_index = gl_VertexID % 4; + + if (vertex_index == 0) { + // "top left" aka "p1" + screen02 += up_dir * pixel - line_dir * pixel; + v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale; + } else if (vertex_index == 1) { + // "top right" aka "p2" + screen02 += up_dir * pixel + line_dir * pixel; + v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale; + } else if (vertex_index == 2) { + // "bottom left" aka "p3" + screen02 += -up_dir * pixel - line_dir * pixel; + v_texcoord = a_pos.xy - up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale; + } else { + // "bottom right" aka "p4" + screen02 += -up_dir * pixel + line_dir * pixel; + v_texcoord = a_pos.xy - up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale; + } } else { - // "bottom right" aka "p4" - screen02 += -up_dir * pixel + line_dir * pixel; - v_texcoord = a_pos.xy - up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale; + v_texcoord = a_pos.xy; } screen02.y = 2.0 - screen02.y; @@ -303,6 +308,7 @@ function init_webgl(state, context) { 'u_debug_mode': gl.getUniformLocation(context.programs['sdf'].main, 'u_debug_mode'), 'u_tile_size': gl.getUniformLocation(context.programs['sdf'].main, 'u_tile_size'), 'u_stroke_count': gl.getUniformLocation(context.programs['sdf'].main, 'u_stroke_count'), + 'u_shrink': gl.getUniformLocation(context.programs['sdf'].main, 'u_shrink'), } }; diff --git a/server/recv.js b/server/recv.js index d33ea1e..82b2932 100644 --- a/server/recv.js +++ b/server/recv.js @@ -52,7 +52,7 @@ async function recv_syn(d, session) { '$id': session.id, '$lsn': lsn }); - + send.send_ack(session.ws, lsn); send.sync_desk(session.desk_id); } diff --git a/server/send.js b/server/send.js index 829a600..80907b5 100644 --- a/server/send.js +++ b/server/send.js @@ -206,7 +206,7 @@ async function sync_session(session_id) { } let size = 1 + 4 + 4; // opcode + sn + event count - let count = desk.sn - session.sn; + let count = desk.sn - session.sn; if (count === 0) { if (config.DEBUG_PRINT) console.log('client ACKed all events');