Browse Source

Fix dynamic data

ssao
A.Olokhtonov 12 months ago
parent
commit
72eedf9b48
  1. 25
      client/bvh.js
  2. 10
      client/client_recv.js
  3. 1
      client/index.html
  4. 7
      client/index.js
  5. 53
      client/webgl_draw.js
  6. 34
      client/webgl_geometry.js
  7. 10
      client/webgl_listeners.js
  8. 52
      client/webgl_shaders.js
  9. 2
      server/send.js

25
client/bvh.js

@ -1,5 +1,3 @@
// TODO: get rid of node_count
//
function bvh_make_leaf(bvh, index, stroke) { function bvh_make_leaf(bvh, index, stroke) {
const leaf = { const leaf = {
'stroke_index': index, 'stroke_index': index,
@ -100,15 +98,12 @@ function bvh_rotate(bvh, index) {
function bvh_add_stroke(bvh, index, stroke) { function bvh_add_stroke(bvh, index, stroke) {
const leaf_index = bvh_make_leaf(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.root = leaf_index;
bvh.node_count++;
return; return;
} }
bvh.node_count++; if (bvh.pqueue.capacity < Math.ceil(bvh.nodes.length * 1.2)) {
if (bvh.pqueue.capacity < Math.ceil(bvh.node_count * 1.2)) {
bvh.pqueue = new MinQueue(bvh.pqueue.capacity * 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].bbox = new_bbox;
bvh.nodes[new_parent].area = (new_bbox.x2 - new_bbox.x1) * (new_bbox.y2 - new_bbox.y1); bvh.nodes[new_parent].area = (new_bbox.x2 - new_bbox.x1) * (new_bbox.y2 - new_bbox.y1);
bvh.node_count++;
// 3. Refit and rotate // 3. Refit and rotate
let refit_index = bvh.nodes[leaf_index].parent_index; let refit_index = bvh.nodes[leaf_index].parent_index;
while (refit_index !== null) { while (refit_index !== null) {
@ -195,18 +188,8 @@ function bvh_intersect_quad(bvh, quad) {
} }
function bvh_clip(state, context) { function bvh_clip(state, context) {
if (state.onscreen_segments === null) { if (state.onscreen_segments.length < Math.ceil(state.total_points * 6 * 1.2)) {
let total_points = 0; state.onscreen_segments = new Uint32Array(state.total_points * 6 * 2);
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);
}
} }
let at = 0; let at = 0;

10
client/client_recv.js

@ -170,10 +170,12 @@ function handle_event(state, context, event, options = {}) {
} }
case EVENT.STROKE: { 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); geometry_clear_player(state, context, event.user_id);
need_draw = true; need_draw = true;
} //}
event.index = state.events.length; event.index = state.events.length;
event.starting_index = state.starting_index; 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.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); geometry_add_stroke(state, context, event, state.events.length, options.skip_bvh === true);
state.stroke_count++; state.stroke_count++;
@ -368,6 +372,8 @@ async function handle_message(state, context, d) {
} }
} }
state.sn = event_count;
bvh_construct(state); bvh_construct(state);
document.getElementById('debug-render-from').max = state.stroke_count; document.getElementById('debug-render-from').max = state.stroke_count;

1
client/index.html

@ -41,7 +41,6 @@
<label><input type="checkbox" id="debug-red">Simple shader</label> <label><input type="checkbox" id="debug-red">Simple shader</label>
<label><input type="checkbox" id="debug-do-prepass">Depth prepass</label> <label><input type="checkbox" id="debug-do-prepass">Depth prepass</label>
<label><input type="checkbox" id="debug-force-clip-off">Force clipping off</label>
<label><input type="checkbox" id="debug-draw-bvh">Draw BVH</label> <label><input type="checkbox" id="debug-draw-bvh">Draw BVH</label>
<div class="flexcol"> <div class="flexcol">

7
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, // 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 // 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); document.addEventListener('DOMContentLoaded', main);
@ -165,10 +167,10 @@ function main() {
'events': [], 'events': [],
'stroke_count': 0, 'stroke_count': 0,
'starting_index': 0, 'starting_index': 0,
'total_points': 0,
'bvh': { 'bvh': {
'nodes': [], 'nodes': [],
'node_count': 0,
'root': null, 'root': null,
'pqueue': new MinQueue(1024), 'pqueue': new MinQueue(1024),
}, },
@ -189,7 +191,7 @@ function main() {
}, },
'players': {}, 'players': {},
'onscreen_segments': null, 'onscreen_segments': new Uint32Array(1024),
'debug': { 'debug': {
'red': false, 'red': false,
@ -198,7 +200,6 @@ function main() {
'limit_to': false, 'limit_to': false,
'render_from': 0, 'render_from': 0,
'render_to': 0, 'render_to': 0,
'force_clip_off': false,
'draw_bvh': false, 'draw_bvh': false,
} }
}; };

53
client/webgl_draw.js

@ -57,7 +57,7 @@ function draw(state, context) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
let index_count; 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) { if (do_clip) {
context.need_index_upload = true; 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.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_stroke_count'], state.stroke_count);
gl.uniform1i(locations['u_debug_mode'], state.debug.red); gl.uniform1i(locations['u_debug_mode'], state.debug.red);
gl.uniform1i(locations['u_shrink'], 1);
gl.enableVertexAttribArray(locations['a_pos']); gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_line']); 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) { if (state.debug.draw_bvh) {
const points = new Float32Array(state.bvh.nodes.length * 6 * 2); 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.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 8, 0);
gl.clear(gl.DEPTH_BUFFER_BIT); gl.clear(gl.DEPTH_BUFFER_BIT);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW); gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2); 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) { if (context.gpu_timer_ext) {
gl.endQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT); gl.endQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT);

34
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); 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) { function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id, indexed = true) {
push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b, stroke_id); if (indexed) {
push_point(s, p2x, p2y, 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, 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);
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, 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) { function push_stroke(s, stroke, stroke_index, indexed = true) {
// if (stroke.stroke_id !== 1123776468) {
// return;
// }
const stroke_width = stroke.width; const stroke_width = stroke.width;
const points = stroke.points; const points = stroke.points;
const color_u32 = stroke.color; const color_u32 = stroke.color;
@ -73,7 +78,8 @@ function push_stroke(s, stroke, stroke_index) {
to.x, to.y, to.x, to.y,
stroke_width, stroke_width,
r, g, b, 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; if (!state.online || !stroke || stroke.points.length === 0) return;
stroke.bbox = stroke_bbox(stroke); stroke.bbox = stroke_bbox(stroke);
@ -157,8 +163,8 @@ function recompute_dynamic_data(state, context) {
for (const player_id in state.players) { for (const player_id in state.players) {
// player has the same data as their current stroke: points, color, width // player has the same data as their current stroke: points, color, width
const player = state.players[player_id]; const player = state.players[player_id];
if (player.points.length > 0) { if (player.points.length > 1) {
push_stroke(context.dynamic_serializer, player, 0); // TODO: stroke index ?? push_stroke(context.dynamic_serializer, player, 0, false);
} }
} }

10
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-do-prepass').checked = state.debug.do_prepass;
document.getElementById('debug-limit-from').checked = state.debug.limit_from; document.getElementById('debug-limit-from').checked = state.debug.limit_from;
document.getElementById('debug-limit-to').checked = state.debug.limit_to; 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').checked = state.debug.draw_bvh;
document.getElementById('debug-draw-bvh').addEventListener('change', (e) => { document.getElementById('debug-draw-bvh').addEventListener('change', (e) => {
@ -34,11 +33,6 @@ function debug_panel_init(state, context) {
schedule_draw(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) => { document.getElementById('debug-red').addEventListener('change', (e) => {
state.debug.red = e.target.checked; state.debug.red = e.target.checked;
schedule_draw(state, context); schedule_draw(state, context);
@ -257,9 +251,9 @@ function mouseup(e, state, context) {
const stroke = geometry_prepare_stroke(state); const stroke = geometry_prepare_stroke(state);
if (stroke) { 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)); queue_event(state, stroke_event(state));
geometry_clear_player(state, context, state.me); //geometry_clear_player(state, context, state.me);
schedule_draw(state, context); schedule_draw(state, context);
} }

52
client/webgl_shaders.js

@ -113,6 +113,7 @@ const sdf_vs_src = `#version 300 es
uniform vec2 u_translation; uniform vec2 u_translation;
uniform int u_stroke_count; uniform int u_stroke_count;
uniform int u_shrink;
out vec4 v_line; out vec4 v_line;
out vec2 v_texcoord; out vec2 v_texcoord;
@ -124,30 +125,34 @@ const sdf_vs_src = `#version 300 es
vec2 screen01 = (a_pos.xy * u_scale + u_translation) / u_res; vec2 screen01 = (a_pos.xy * u_scale + u_translation) / u_res;
vec2 screen02 = screen01 * 2.0; vec2 screen02 = screen01 * 2.0;
// Inflate quad by 1 pixel if (u_shrink == 1) {
float apron = 2.0; // Inflate quad by 2 pixels (change to 1 when I figure out how to fix the prepass shit)
vec2 line_dir = normalize(a_line.zw - a_line.xy); float apron = 2.0;
vec2 up_dir = vec2(line_dir.y, -line_dir.x); vec2 line_dir = normalize(a_line.zw - a_line.xy);
vec2 pixel = vec2(2.0) / u_res * apron; vec2 up_dir = vec2(line_dir.y, -line_dir.x);
vec2 pixel = vec2(2.0) / u_res * apron;
int vertex_index = gl_VertexID % 4;
int vertex_index = gl_VertexID % 4;
if (vertex_index == 0) {
// "top left" aka "p1" if (vertex_index == 0) {
screen02 += up_dir * pixel - line_dir * pixel; // "top left" aka "p1"
v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale; screen02 += up_dir * pixel - line_dir * pixel;
} else if (vertex_index == 1) { v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale;
// "top right" aka "p2" } else if (vertex_index == 1) {
screen02 += up_dir * pixel + line_dir * pixel; // "top right" aka "p2"
v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale; screen02 += up_dir * pixel + line_dir * pixel;
} else if (vertex_index == 2) { v_texcoord = a_pos.xy + up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale;
// "bottom left" aka "p3" } else if (vertex_index == 2) {
screen02 += -up_dir * pixel - line_dir * pixel; // "bottom left" aka "p3"
v_texcoord = a_pos.xy - up_dir * 1.0 / u_scale - line_dir * 1.0 / u_scale; 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 { } else {
// "bottom right" aka "p4" v_texcoord = a_pos.xy;
screen02 += -up_dir * pixel + line_dir * pixel;
v_texcoord = a_pos.xy - up_dir * 1.0 / u_scale + line_dir * 1.0 / u_scale;
} }
screen02.y = 2.0 - screen02.y; 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_debug_mode': gl.getUniformLocation(context.programs['sdf'].main, 'u_debug_mode'),
'u_tile_size': gl.getUniformLocation(context.programs['sdf'].main, 'u_tile_size'), '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_stroke_count': gl.getUniformLocation(context.programs['sdf'].main, 'u_stroke_count'),
'u_shrink': gl.getUniformLocation(context.programs['sdf'].main, 'u_shrink'),
} }
}; };

2
server/send.js

@ -206,7 +206,7 @@ async function sync_session(session_id) {
} }
let size = 1 + 4 + 4; // opcode + sn + event count 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 (count === 0) {
if (config.DEBUG_PRINT) console.log('client ACKed all events'); if (config.DEBUG_PRINT) console.log('client ACKed all events');

Loading…
Cancel
Save