diff --git a/client/bvh.js b/client/bvh.js
index fc270f5..f1f9b61 100644
--- a/client/bvh.js
+++ b/client/bvh.js
@@ -155,9 +155,9 @@ function bvh_add_stroke(bvh, index, stroke) {
}
}
-function bvh_intersect_quad(bvh, quad) {
+function bvh_intersect_quad(bvh, quad, result_buffer) {
if (bvh.root === null) {
- return [];
+ return;
}
const stack = [bvh.root];
@@ -172,20 +172,25 @@ function bvh_intersect_quad(bvh, quad) {
}
if (node.is_leaf) {
- result.push(node.stroke_index);
+ result_buffer.data[result_buffer.count] = node.stroke_index;
+ result_buffer.count += 1;
} else {
stack.push(node.child1, node.child2);
}
}
-
- return result;
}
-function bvh_clip(state, context, lod_level) {
- const lod = context.lods[lod_level];
+function bvh_clip(state, context) {
+ if (state.stroke_count === 0) {
+ return;
+ }
- lod.indices = ser_ensure(lod.indices, lod.total_points * 6 * 4);
- ser_clear(lod.indices);
+ if (context.clipped_indices.cap < state.stroke_count) {
+ context.clipped_indices.cap = round_to_pow2(state.stroke_count, 4096);
+ context.clipped_indices.data = new Uint32Array(context.clipped_indices.cap);
+ }
+
+ context.clipped_indices.count = 0;
const screen_topleft = screen_to_canvas(state, {'x': 0, 'y': 0});
const screen_bottomright = screen_to_canvas(state, {'x': context.canvas.width, 'y': context.canvas.height});
@@ -199,35 +204,9 @@ function bvh_clip(state, context, lod_level) {
'y2': screen_bottomright.y
};
- const stroke_indices = bvh_intersect_quad(state.bvh, screen);
-
- stroke_indices.sort((a, b) => a - b);
-
- for (const i of stroke_indices) {
- if (state.debug.limit_to && i >= state.debug.render_to) break;
-
- const event = state.events[i];
-
- if (!(state.debug.limit_from && i < state.debug.render_from)) {
- if (event.type === EVENT.STROKE && !event.deleted && event.points.length > 0) {
- const points = event.lods[lod_level].points;
-
- for (let j = 0; j < points.length - 1; ++j) {
- const base = event.lods[lod_level].starting_index + j * 4;
-
- // We draw quads as [1, 2, 3, 4, 3, 2]
- ser_u32(lod.indices, base + 0);
- ser_u32(lod.indices, base + 1);
- ser_u32(lod.indices, base + 2);
- ser_u32(lod.indices, base + 3);
- ser_u32(lod.indices, base + 2);
- ser_u32(lod.indices, base + 1);
- }
- }
- }
- }
+ bvh_intersect_quad(state.bvh, screen, context.clipped_indices);
- return lod.indices.offset / 4;
+ new Uint32Array(context.clipped_indices.data.buffer, 0, context.clipped_indices.count).sort(); // we need to draw back to front still!
}
function bvh_construct_rec(bvh, vertical, strokes) {
diff --git a/client/client_recv.js b/client/client_recv.js
index e3743b5..97075bc 100644
--- a/client/client_recv.js
+++ b/client/client_recv.js
@@ -32,22 +32,23 @@ function des_f32(d) {
return value;
}
-function des_f32array(d, count) {
- const result = [];
-
- for (let i = 0; i < count; ++i) {
- const item = d.view.getFloat32(d.offset, true);
- d.offset += 4;
- result.push(item);
+function des_align(d, to) {
+ // TODO: non-stupid version of this
+ while (d.offset % to != 0) {
+ d.offset++;
}
+}
+function des_f32array(d, count) {
+ const result = new Float32Array(d.buffer, d.offset, count);
+ d.offset += 4 * count;
return result;
}
-function des_event(d) {
+function des_event(d, state = null) {
const event = {};
- event.type = des_u8(d);
+ event.type = des_u32(d);
event.user_id = des_u32(d);
switch (event.type) {
@@ -76,18 +77,18 @@ function des_event(d) {
const point_count = des_u16(d);
const width = des_u16(d);
const color = des_u32(d);
+
const coords = des_f32array(d, point_count * 2);
+ event.coords_from = state.coordinates.count;
+ event.coords_to = state.coordinates.count + point_count * 2;
+
+ state.coordinates.data.set(coords, state.coordinates.count);
+ state.coordinates.count += point_count * 2;
+
event.stroke_id = stroke_id;
- event.points = [];
event.lods = [];
- for (let i = 0; i < point_count; ++i) {
- const x = coords[2 * i + 0];
- const y = coords[2 * i + 1];
- event.points.push({'x': x, 'y': y});
- }
-
event.color = color;
event.width = width;
@@ -178,6 +179,8 @@ function handle_event(state, context, event, options = {}) {
need_draw = true;
//}
+ event.index = state.events.length;
+
geometry_add_stroke(state, context, event, state.events.length, options.skip_bvh === true);
state.stroke_count++;
@@ -342,6 +345,9 @@ async function handle_message(state, context, d) {
const event_count = des_u32(d);
const user_count = des_u32(d);
+ const total_points = des_u32(d);
+
+ state.coordinates.data = new Float32Array(round_to_pow2(total_points * 2, 4096));
if (config.debug_print) console.debug(`${event_count} events in init`);
@@ -355,11 +361,13 @@ async function handle_message(state, context, d) {
init_player_defaults(state, user_id, user_color, user_width);
}
+ des_align(d, 4);
+
for (let i = 0; i < event_count; ++i) {
- const event = des_event(d);
+ const event = des_event(d, state);
handle_event(state, context, event, {'skip_bvh': true});
- if (event.type !== EVENT.STROKE || event.points.length > 0) {
+ if (event.type !== EVENT.STROKE || event.coords_to - event.coords_from > 0) {
state.events.push(event);
}
}
diff --git a/client/client_send.js b/client/client_send.js
index 980cdc9..e0a9a15 100644
--- a/client/client_send.js
+++ b/client/client_send.js
@@ -152,6 +152,7 @@ async function send_ack(sn) {
}
async function sync_queue(state) {
+
if (ws === null) {
if (config.debug_print) console.debug('socket has closed, stopping SYNs');
return;
diff --git a/client/index.js b/client/index.js
index 8ec8845..07f9724 100644
--- a/client/index.js
+++ b/client/index.js
@@ -24,7 +24,7 @@ const config = {
initial_offline_timeout: 1000,
default_color: 0x00,
default_width: 8,
- bytes_per_quad: 4 * 4 + 4, // axy, bxy, stroke_id
+ bytes_per_instance: 4 * 4 + 4, // axy, bxy, stroke_id
bytes_per_stroke: 3 + 1, // r, g, b, width
initial_static_bytes: 4096 * 16,
initial_dynamic_bytes: 4096,
@@ -172,6 +172,11 @@ function main() {
'starting_index': 0,
'total_points': 0,
+ 'coordinates': {
+ 'data': null,
+ 'count': 0,
+ },
+
'bvh': {
'nodes': [],
'root': null,
@@ -215,10 +220,19 @@ function main() {
'buffers': {},
'locations': {},
'textures': {},
-
+
'dynamic_serializer': serializer_create(config.initial_dynamic_bytes),
'dynamic_index_serializer': serializer_create(config.initial_dynamic_bytes),
+ // TODO: i seem to have a lot of these, maybe make a few utility functions? similar to serializer, but for pure typedarray
+ 'clipped_indices': {
+ 'data': null,
+ 'count': 0,
+ 'cap': 0,
+ },
+
+ 'instance_data': serializer_create(config.initial_static_bytes),
+
'lods': [],
'stroke_data': serializer_create(config.initial_static_bytes),
diff --git a/client/math.js b/client/math.js
index f6503f3..a4714c7 100644
--- a/client/math.js
+++ b/client/math.js
@@ -1,3 +1,7 @@
+function round_to_pow2(value, multiple) {
+ return (value + multiple - 1) & -multiple;
+}
+
function screen_to_canvas(state, p) {
// should be called with coordinates obtained from MouseEvent.clientX/clientY * window.devicePixelRatio
const xc = (p.x - state.canvas.offset.x) / state.canvas.zoom;
@@ -6,36 +10,39 @@ function screen_to_canvas(state, p) {
return {'x': xc, 'y': yc};
}
-function rdp_find_max(zoom, points, start, end) {
+function rdp_find_max(state, zoom, stroke, start, end) {
const EPS = 1.0 / zoom;
// const EPS = 10.0;
let result = -1;
let max_dist = 0;
- const a = points[start];
- const b = points[end];
+ const ax = state.coordinates.data[stroke.coords_from + start * 2 + 0];
+ const ay = state.coordinates.data[stroke.coords_from + start * 2 + 1];
+ const bx = state.coordinates.data[stroke.coords_from + end * 2 + 0];
+ const by = state.coordinates.data[stroke.coords_from + end * 2 + 1];
- const dx = b.x - a.x;
- const dy = b.y - a.y;
+ const dx = bx - ax;
+ const dy = by - ay;
const dist_ab = Math.sqrt(dx * dx + dy * dy);
const sin_theta = dy / dist_ab;
const cos_theta = dx / dist_ab;
for (let i = start; i < end; ++i) {
- const p = points[i];
+ const px = state.coordinates.data[stroke.coords_from + i * 2 + 0];
+ const py = state.coordinates.data[stroke.coords_from + i * 2 + 1];
- const ox = p.x - a.x;
- const oy = p.y - a.y;
+ const ox = px - ax;
+ const oy = py - ay;
const rx = cos_theta * ox + sin_theta * oy;
const ry = -sin_theta * ox + cos_theta * oy;
- const x = rx + a.x;
- const y = ry + a.y;
+ const x = rx + ax;
+ const y = ry + ay;
- const dist = Math.abs(y - a.y);
+ const dist = Math.abs(y - ay);
if (dist > EPS && dist > max_dist) {
result = i;
@@ -46,45 +53,37 @@ function rdp_find_max(zoom, points, start, end) {
return result;
}
-function process_rdp_r(zoom, mask, points, start, end) {
+function process_rdp_indices_r(state, zoom, mask, stroke, start, end) {
let result = 0;
- const max = rdp_find_max(zoom, points, start, end);
+ const max = rdp_find_max(state, zoom, stroke, start, end);
if (max !== -1) {
mask[max] = 1;
result += 1;
- result += process_rdp_r(zoom, mask, points, start, max);
- result += process_rdp_r(zoom, mask, points, max, end);
+ result += process_rdp_indices_r(state, zoom, mask, stroke, start, max);
+ result += process_rdp_indices_r(state, zoom, mask, stroke, max, end);
}
return result;
}
-function process_rdp(state, zoom, points) {
- if (state.rdp_mask.length < points.length) {
- state.rdp_mask = new Uint8Array(points.length);
+function process_rdp_indices(state, zoom, stroke) {
+ const point_count = (stroke.coords_to - stroke.coords_from) / 2;
+
+ if (state.rdp_mask.length < point_count) {
+ state.rdp_mask = new Uint8Array(point_count);
}
- state.rdp_mask.fill(0, 0, points.length);
+ state.rdp_mask.fill(0, 0, point_count);
const mask = state.rdp_mask;
- const npoints = process_rdp_r(zoom, mask, points, 0, points.length - 1);
+ const npoints = 2 + process_rdp_indices_r(state, zoom, mask, stroke, 0, point_count - 1); // 2 is for the first and last vertex, which do not get included by the recursive functions, but should always be there at any lod level
mask[0] = 1;
- mask[points.length - 1] = 1;
-
- const result = new Array(npoints);
- let j = 0;
-
- for (let i = 0; i < points.length; ++i) {
- if (mask[i] === 1) {
- result[j] = points[i];
- ++j;
- }
- }
+ mask[point_count - 1] = 1;
- return result;
+ return npoints;
}
function process_ewmv(points, round = false) {
@@ -103,9 +102,9 @@ function process_ewmv(points, round = false) {
return result;
}
-function process_stroke(state, zoom, points) {
+function process_stroke(state, zoom, stroke) {
// const result0 = process_ewmv(points);
- const result1 = process_rdp(state, zoom, points, true);
+ const result1 = process_rdp_indices(state, zoom, stroke, true);
return result1;
}
@@ -202,20 +201,23 @@ function segment_interesects_quad(a, b, quad_topleft, quad_bottomright, quad_top
return false;
}
-function stroke_bbox(stroke) {
+function stroke_bbox(state, stroke) {
const radius = stroke.width / 2;
-
- let min_x = stroke.points[0].x - radius;
- let max_x = stroke.points[0].x + radius;
- let min_y = stroke.points[0].y - radius;
- let max_y = stroke.points[0].y + radius;
+ let min_x = state.coordinates.data[stroke.coords_from + 0] - radius;
+ let max_x = state.coordinates.data[stroke.coords_from + 0] + radius;
+
+ let min_y = state.coordinates.data[stroke.coords_from + 1] - radius;
+ let max_y = state.coordinates.data[stroke.coords_from + 1] + radius;
- for (const p of stroke.points) {
- min_x = Math.min(min_x, p.x - radius);
- min_y = Math.min(min_y, p.y - radius);
- max_x = Math.max(max_x, p.x + radius);
- max_y = Math.max(max_y, p.y + radius);
+ for (let i = stroke.coords_from + 2; i < stroke.coords_to; i += 2) {
+ const px = state.coordinates.data[i + 0];
+ const py = state.coordinates.data[i + 1];
+
+ min_x = Math.min(min_x, px - radius);
+ min_y = Math.min(min_y, py - radius);
+ max_x = Math.max(max_x, px + radius);
+ max_y = Math.max(max_y, py + radius);
}
return {'x1': min_x, 'y1': min_y, 'x2': max_x, 'y2': max_y, 'cx': (max_x + min_x) / 2, 'cy': (max_y + min_y) / 2};
@@ -246,6 +248,10 @@ function quad_union(a, b) {
};
}
+function box_area(box) {
+ return (box.x2 - box.x1) * (box.y2 - box.y1);
+}
+
function segments_onscreen(state, context, do_clip) {
// TODO: handle stroke width
diff --git a/client/webgl_draw.js b/client/webgl_draw.js
index 28ecf32..ad082e2 100644
--- a/client/webgl_draw.js
+++ b/client/webgl_draw.js
@@ -48,18 +48,6 @@ function upload_square_rgba16ui_texture(gl, serializer, texture_size) {
function draw(state, context) {
const cpu_before = performance.now();
- let lod_level = -1;
-
- for (let i = context.lods.length - 1; i >= 0; --i) {
- const level = context.lods[i];
- if (state.canvas.zoom <= level.max_zoom) {
- lod_level = i;
- break;
- }
- }
-
- const lod = context.lods[lod_level];
-
state.timers.raf = false;
const gl = context.gl;
@@ -79,19 +67,19 @@ function draw(state, context) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
- gl.bindBuffer(gl.ARRAY_BUFFER, lod.data_buffer);
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, lod.index_buffer);
-
- // static data, per-quad: points, stroke_ids
- // static data, per-stroke (texture): color, width (radius)
- upload_if_needed(gl, gl.ARRAY_BUFFER, lod.segments);
-
locations = context.locations['sdf'].main;
+ buffers = context.buffers['sdf'];
gl.useProgram(context.programs['sdf'].main);
- const segment_count = lod.segments.offset / config.bytes_per_quad;
-
+
+ bvh_clip(state, context);
+ const segment_count = geometry_write_instances(state, context);
+
+ // TODO: maybe have a pool of buffers (pow2?) and select an appropriate one
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_instance']);
+ gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.instance_data.buffer, 0, segment_count * config.bytes_per_instance), gl.DYNAMIC_DRAW);
+
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);
@@ -103,23 +91,26 @@ function draw(state, context) {
gl.enableVertexAttribArray(locations['a_ab']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
- gl.vertexAttribPointer(locations['a_ab'], 4, gl.FLOAT, false, 5 * 4, 0);
- gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, 5 * 4, 4 * 4);
+ gl.vertexAttribPointer(locations['a_ab'], 4, gl.FLOAT, false, config.bytes_per_instance, 0);
+ gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_instance, 4 * 4);
gl.vertexAttribDivisor(locations['a_ab'], 1);
gl.vertexAttribDivisor(locations['a_stroke_id'], 1);
gl.bindTexture(gl.TEXTURE_2D, context.textures['stroke_data']);
+ // TODO: this is stable data, only upload new strokes as they arrive
upload_square_rgba16ui_texture(gl, context.stroke_data, config.stroke_texture_size);
gl.activeTexture(gl.TEXTURE0);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, segment_count); // TODO: based on clipping results
-
+
+ document.getElementById('debug-stats').innerHTML = `
+ Segments onscreen: ${segment_count}
+ Canvas offset: (${state.canvas.offset.x}, ${state.canvas.offset.y})
+ Canvas zoom: ${Math.round(state.canvas.zoom * 100000) / 100000}`;
/*
- const before_clip = performance.now();
- const index_count = bvh_clip(state, context, lod_level);
const after_clip = performance.now();
gl.bindBuffer(gl.ARRAY_BUFFER, lod.data_buffer);
@@ -128,12 +119,7 @@ function draw(state, context) {
upload_if_needed(gl, gl.ARRAY_BUFFER, lod.vertices);
upload_if_needed(gl, gl.ELEMENT_ARRAY_BUFFER, lod.indices);
- document.getElementById('debug-stats').innerHTML = `
- LOD level: ${lod_level}
- Segments onscreen: ${index_count}
- Canvas offset: (${state.canvas.offset.x}, ${state.canvas.offset.y})
- Canvas zoom: ${Math.round(state.canvas.zoom * 100000) / 100000}`;
-
+
if (index_count > 0) {
// DEPTH PREPASS
if (state.debug.do_prepass) {
diff --git a/client/webgl_geometry.js b/client/webgl_geometry.js
index 58c3e3f..ff37c01 100644
--- a/client/webgl_geometry.js
+++ b/client/webgl_geometry.js
@@ -36,40 +36,50 @@ function geometry_prepare_stroke(state) {
};
}
-function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = false) {
- if (!state.online || !stroke || stroke.points.length === 0) return;
-
- stroke.index = state.events.length;
+function geometry_write_instances(state, context) {
+ context.instance_data = ser_ensure(context.instance_data, state.coordinates.count / 2 * config.bytes_per_instance);
+ ser_clear(context.instance_data);
- for (let i = 0; i < config.lod_levels; ++i) {
- const lod = context.lods[i];
+ let segment_count = 0;
- const points = (i > 0 ? process_stroke(state, lod.max_zoom, stroke.points) : stroke.points);
- const segment_serializer = lod.segments = ser_ensure_by(lod.segments, (points.length - 1) * config.bytes_per_quad);
+ for (let i = 0; i < context.clipped_indices.count; ++i) {
+ const stroke_index = context.clipped_indices.data[i];
+ const stroke = state.events[stroke_index];
+ const lod_indices_count = process_stroke(state, state.canvas.zoom, stroke);
- let starting_index = 0;
+ segment_count += lod_indices_count - 1;
- if (state.events.length > 0) {
- const last_stroke = state.events[stroke_index - 1].lods[i];
- starting_index = last_stroke.starting_index + (last_stroke.points.length - 1) * 4;
- }
+ let base_this = 0;
+ let base_next = 0;
- stroke.lods.push({
- 'points': points,
- 'starting_index': starting_index,
- 'width': stroke.width,
- 'color': stroke.color,
- });
+ for (let j = 0; j < lod_indices_count - 1; ++j) {
+ while (state.rdp_mask[base_this] == 0) base_this++;
+ base_next = base_this + 1;
+ while (state.rdp_mask[base_next] == 0) base_next++;
- context.lods[i].total_points += points.length;
+ const ax = state.coordinates.data[stroke.coords_from + base_this * 2 + 0];
+ const ay = state.coordinates.data[stroke.coords_from + base_this * 2 + 1];
+ const bx = state.coordinates.data[stroke.coords_from + base_next * 2 + 0];
+ const by = state.coordinates.data[stroke.coords_from + base_next * 2 + 1];
- push_stroke(segment_serializer, stroke.lods[stroke.lods.length - 1], stroke_index);
+ ser_f32(context.instance_data, ax);
+ ser_f32(context.instance_data, ay);
+ ser_f32(context.instance_data, bx);
+ ser_f32(context.instance_data, by);
+ ser_u32(context.instance_data, stroke_index);
- if (i === 0) {
- stroke.bbox = stroke_bbox(stroke);
- stroke.area = (stroke.bbox.x2 - stroke.bbox.x1) * (stroke.bbox.y2 - stroke.bbox.y1);
+ base_this = base_next;
}
}
+
+ return segment_count;
+}
+
+function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = false) {
+ if (!state.online || !stroke || stroke.coords_to - stroke.coords_from === 0) return;
+
+ stroke.bbox = stroke_bbox(state, stroke);
+ stroke.area = box_area(stroke.bbox);
context.stroke_data = ser_ensure_by(context.stroke_data, config.bytes_per_stroke);
diff --git a/client/webgl_listeners.js b/client/webgl_listeners.js
index 387fe59..ce9c254 100644
--- a/client/webgl_listeners.js
+++ b/client/webgl_listeners.js
@@ -451,7 +451,7 @@ function touchend(e, state, context) {
const stroke = geometry_prepare_stroke(state);
- if (stroke) {
+ if (false && stroke) { // TODO: FIX!
geometry_add_stroke(state, context, stroke, 0); // TODO: stroke index
queue_event(state, stroke_event(state));
geometry_clear_player(state, context, state.me);
diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js
index ad5ca51..2a65f0b 100644
--- a/client/webgl_shaders.js
+++ b/client/webgl_shaders.js
@@ -333,6 +333,7 @@ function init_webgl(state, context) {
context.buffers['sdf'] = {
'b_packed_dynamic': gl.createBuffer(),
'b_packed_dynamic_index': gl.createBuffer(),
+ 'b_instance': gl.createBuffer(),
};
context.textures = {
diff --git a/server/data-local.sqlite b/server/data-local.sqlite
new file mode 100644
index 0000000..ec150a9
Binary files /dev/null and b/server/data-local.sqlite differ
diff --git a/server/recv.js b/server/recv.js
index daa0e65..9dd29b0 100644
--- a/server/recv.js
+++ b/server/recv.js
@@ -127,6 +127,8 @@ function handle_event(session, event) {
'$y': 0,
});
+ desks[session.desk_id].total_points += event.points.length;
+
break;
}
diff --git a/server/send.js b/server/send.js
index 5032630..b040c26 100644
--- a/server/send.js
+++ b/server/send.js
@@ -7,7 +7,7 @@ import { MESSAGE, SESSION, EVENT } from './enums';
import { sessions, desks } from './storage';
function event_size(event) {
- let size = 1 + 4; // type + user_id
+ let size = 4 + 4; // type + user_id
switch (event.type) {
case EVENT.PREDRAW: {
@@ -94,7 +94,7 @@ export async function send_init(ws) {
const desk = desks[desk_id];
let opcode = MESSAGE.INIT;
- let size = 1 + 4 + 4 + 4 + 4; // opcode + user_id + lsn + event count + stroke count + user count
+ let size = 1 + 4 + 4 + 4 + 4 + 4 + 3; // opcode + user_id + lsn + event count + stroke count + user count + total_point_count + align on 4
let session = null;
if (session_id in sessions && sessions[session_id].desk_id == desk_id) {
@@ -142,6 +142,7 @@ export async function send_init(ws) {
ser.u32(s, desk.events.length);
ser.u32(s, user_count);
+ ser.u32(s, desk.total_points);
for (const sid in sessions) {
const other_session = sessions[sid];
@@ -153,6 +154,8 @@ export async function send_init(ws) {
}
}
+ ser.align(s, 4);
+
for (const event of desk.events) {
ser.event(s, event);
}
diff --git a/server/serializer.js b/server/serializer.js
index c7d6d7f..edcbe50 100644
--- a/server/serializer.js
+++ b/server/serializer.js
@@ -36,8 +36,15 @@ export function bytes(s, bytes) {
s.offset += bytes.byteLength;
}
+export function align(s, to) {
+ // TODO: non-stupid version of this
+ while (s.offset % to != 0) {
+ s.offset++;
+ }
+}
+
export function event(s, event) {
- u8(s, event.type);
+ u32(s, event.type); // for alignment reasons
u32(s, event.user_id);
switch (event.type) {
diff --git a/server/storage.js b/server/storage.js
index 61af85c..de99b2b 100644
--- a/server/storage.js
+++ b/server/storage.js
@@ -100,22 +100,25 @@ export function startup() {
const stroke_dict = {};
- for (const stroke of stored_strokes) {
- stroke.points = new Float32Array(stroke.points.buffer);
- stroke_dict[stroke.id] = stroke;
- }
-
for (const desk of stored_desks) {
desks[desk.id] = desk;
desks[desk.id].events = [];
+ desks[desk.id].total_points = 0;
}
-
+
+ for (const stroke of stored_strokes) {
+ stroke.points = new Float32Array(stroke.points.buffer);
+ stroke_dict[stroke.id] = stroke;
+ }
+
for (const event of stored_events) {
if (event.type === EVENT.STROKE) {
const stroke = stroke_dict[event.stroke_id];
event.points = stroke.points;
event.color = stroke.color;
event.width = stroke.width;
+
+ desks[event.desk_id].total_points += stroke.points.length / 2;
}
desks[event.desk_id].events.push(event);