|
|
@ -1,42 +1,25 @@ |
|
|
|
function push_stroke(s, stroke, stroke_index) { |
|
|
|
|
|
|
|
const points = stroke.points; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (points.length < 2) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < points.length - 1; ++i) { |
|
|
|
|
|
|
|
const from = points[i]; |
|
|
|
|
|
|
|
const to = points[i + 1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ser_f32(s, from.x); |
|
|
|
|
|
|
|
ser_f32(s, from.y); |
|
|
|
|
|
|
|
ser_f32(s, to.x); |
|
|
|
|
|
|
|
ser_f32(s, to.y); |
|
|
|
|
|
|
|
ser_u32(s, stroke_index); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function geometry_prepare_stroke(state) { |
|
|
|
function geometry_prepare_stroke(state) { |
|
|
|
if (!state.online) { |
|
|
|
if (!state.online) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (state.players[state.me].points.length === 0) { |
|
|
|
const player = state.players[state.me]; |
|
|
|
|
|
|
|
const stroke = player.strokes[player.strokes.length - 1]; // MY OWN player.strokes should never be bigger than 1 element
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (stroke.points.length === 0) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const points = process_stroke2(state.canvas.zoom, state.players[state.me].points); |
|
|
|
const points = process_stroke2(state.canvas.zoom, stroke.points); |
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
return { |
|
|
|
'color': state.players[state.me].color, |
|
|
|
'color': stroke.color, |
|
|
|
'width': state.players[state.me].width, |
|
|
|
'width': stroke.width, |
|
|
|
'points': points, |
|
|
|
'points': points, |
|
|
|
'user_id': state.me, |
|
|
|
'user_id': state.me, |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function geometry_write_instances(state, context, callback) { |
|
|
|
async function geometry_write_instances(state, context, callback) { |
|
|
|
state.stats.rdp_max_count = 0; |
|
|
|
state.stats.rdp_max_count = 0; |
|
|
|
state.stats.rdp_segments = 0; |
|
|
|
state.stats.rdp_segments = 0; |
|
|
@ -56,6 +39,7 @@ function geometry_add_dummy_stroke(context) { |
|
|
|
ser_u16(context.stroke_data, 0); |
|
|
|
ser_u16(context.stroke_data, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Real stroke, add forever
|
|
|
|
function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = false) { |
|
|
|
function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = false) { |
|
|
|
if (!state.online || !stroke || stroke.coords_to - stroke.coords_from === 0 || stroke.deleted) return; |
|
|
|
if (!state.online || !stroke || stroke.coords_to - stroke.coords_from === 0 || stroke.deleted) return; |
|
|
|
|
|
|
|
|
|
|
@ -83,11 +67,13 @@ function recompute_dynamic_data(state, context) { |
|
|
|
|
|
|
|
|
|
|
|
for (const player_id in state.players) { |
|
|
|
for (const player_id in state.players) { |
|
|
|
const player = state.players[player_id]; |
|
|
|
const player = state.players[player_id]; |
|
|
|
if (player.points.length > 0) { |
|
|
|
for (const stroke of player.strokes) { |
|
|
|
total_points += player.points.length; |
|
|
|
if (!stroke.empty && stroke.points.length > 0) { |
|
|
|
|
|
|
|
total_points += stroke.points.length; |
|
|
|
total_strokes += 1; |
|
|
|
total_strokes += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
tv_ensure(context.dynamic_instance_points, round_to_pow2(total_points * 2, 4096)); |
|
|
|
tv_ensure(context.dynamic_instance_points, round_to_pow2(total_points * 2, 4096)); |
|
|
|
tv_ensure(context.dynamic_instance_pressure, round_to_pow2(total_points, 4096)); |
|
|
|
tv_ensure(context.dynamic_instance_pressure, round_to_pow2(total_points, 4096)); |
|
|
@ -106,22 +92,23 @@ function recompute_dynamic_data(state, context) { |
|
|
|
// 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]; |
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < player.points.length; ++i) { |
|
|
|
for (const stroke of player.strokes) { |
|
|
|
const p = player.points[i]; |
|
|
|
if (!stroke.empty && stroke.points.length > 0) { |
|
|
|
|
|
|
|
for (let i = 0; i < stroke.points.length; ++i) { |
|
|
|
|
|
|
|
const p = stroke.points[i]; |
|
|
|
|
|
|
|
|
|
|
|
tv_add(context.dynamic_instance_points, p.x); |
|
|
|
tv_add(context.dynamic_instance_points, p.x); |
|
|
|
tv_add(context.dynamic_instance_points, p.y); |
|
|
|
tv_add(context.dynamic_instance_points, p.y); |
|
|
|
tv_add(context.dynamic_instance_pressure, p.pressure); |
|
|
|
tv_add(context.dynamic_instance_pressure, p.pressure); |
|
|
|
|
|
|
|
|
|
|
|
if (i !== player.points.length - 1) { |
|
|
|
if (i !== stroke.points.length - 1) { |
|
|
|
tv_add(context.dynamic_instance_ids, stroke_index); |
|
|
|
tv_add(context.dynamic_instance_ids, stroke_index); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
tv_add(context.dynamic_instance_ids, stroke_index | (1 << 31)); |
|
|
|
tv_add(context.dynamic_instance_ids, stroke_index | (1 << 31)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (player.points.length > 0) { |
|
|
|
const color_u32 = stroke.color; |
|
|
|
const color_u32 = player.color; |
|
|
|
|
|
|
|
const r = (color_u32 >> 16) & 0xFF; |
|
|
|
const r = (color_u32 >> 16) & 0xFF; |
|
|
|
const g = (color_u32 >> 8) & 0xFF; |
|
|
|
const g = (color_u32 >> 8) & 0xFF; |
|
|
|
const b = color_u32 & 0xFF; |
|
|
|
const b = color_u32 & 0xFF; |
|
|
@ -129,21 +116,45 @@ function recompute_dynamic_data(state, context) { |
|
|
|
ser_u16(context.dynamic_stroke_data, r); |
|
|
|
ser_u16(context.dynamic_stroke_data, r); |
|
|
|
ser_u16(context.dynamic_stroke_data, g); |
|
|
|
ser_u16(context.dynamic_stroke_data, g); |
|
|
|
ser_u16(context.dynamic_stroke_data, b); |
|
|
|
ser_u16(context.dynamic_stroke_data, b); |
|
|
|
ser_u16(context.dynamic_stroke_data, player.width); |
|
|
|
ser_u16(context.dynamic_stroke_data, stroke.width); |
|
|
|
|
|
|
|
|
|
|
|
stroke_index += 1; // TODO: proper player Z order
|
|
|
|
stroke_index += 1; // TODO: proper player Z order
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
context.dynamic_segment_count = total_points; |
|
|
|
context.dynamic_segment_count = total_points; |
|
|
|
context.dynamic_stroke_count = total_strokes; |
|
|
|
context.dynamic_stroke_count = total_strokes; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function geometry_add_point(state, context, player_id, point, is_pen, raw = false) { |
|
|
|
function geometry_start_prestroke(state, player_id) { |
|
|
|
|
|
|
|
if (!state.online) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const player = state.players[player_id]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
player.strokes.push({ |
|
|
|
|
|
|
|
'empty': false, |
|
|
|
|
|
|
|
'points': [], |
|
|
|
|
|
|
|
'head': null, |
|
|
|
|
|
|
|
'color': player.color, |
|
|
|
|
|
|
|
'width': player.width, |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
player.current_prestroke = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function geometry_end_prestroke(state, player_id) { |
|
|
|
|
|
|
|
if (!state.online) return; |
|
|
|
|
|
|
|
const player = state.players[player_id]; |
|
|
|
|
|
|
|
player.current_prestroke = false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function geometry_add_prepoint(state, context, player_id, point, is_pen, raw = false) { |
|
|
|
if (!state.online) return; |
|
|
|
if (!state.online) return; |
|
|
|
|
|
|
|
|
|
|
|
const player = state.players[player_id]; |
|
|
|
const player = state.players[player_id]; |
|
|
|
const points = player.points; |
|
|
|
const stroke = player.strokes[player.strokes.length - 1]; |
|
|
|
|
|
|
|
const points = stroke.points; |
|
|
|
|
|
|
|
|
|
|
|
if (point.pressure < config.min_pressure) { |
|
|
|
if (point.pressure < config.min_pressure) { |
|
|
|
point.pressure = config.min_pressure; |
|
|
|
point.pressure = config.min_pressure; |
|
|
@ -152,30 +163,35 @@ function geometry_add_point(state, context, player_id, point, is_pen, raw = fals |
|
|
|
if (points.length > 0 && !raw) { |
|
|
|
if (points.length > 0 && !raw) { |
|
|
|
// pulled from "perfect-freehand" package. MIT
|
|
|
|
// pulled from "perfect-freehand" package. MIT
|
|
|
|
// https://github.com/steveruizok/perfect-freehand/
|
|
|
|
// https://github.com/steveruizok/perfect-freehand/
|
|
|
|
const streamline = 0.5; |
|
|
|
const streamline = 0.75; |
|
|
|
const t = 0.15 + (1 - streamline) * 0.85 |
|
|
|
const t = 0.15 + (1 - streamline) * 0.85 |
|
|
|
const smooth_pressure = exponential_smoothing(points, point, 3); |
|
|
|
const smooth_pressure = exponential_smoothing(points, point, 3); |
|
|
|
|
|
|
|
|
|
|
|
points.push({ |
|
|
|
points.push({ |
|
|
|
'x': player.dynamic_head.x * t + point.x * (1 - t), |
|
|
|
'x': stroke.head.x * t + point.x * (1 - t), |
|
|
|
'y': player.dynamic_head.y * t + point.y * (1 - t), |
|
|
|
'y': stroke.head.y * t + point.y * (1 - t), |
|
|
|
'pressure': is_pen ? player.dynamic_head.pressure * t + smooth_pressure * (1 - t) : point.pressure, |
|
|
|
'pressure': is_pen ? stroke.head.pressure * t + smooth_pressure * (1 - t) : point.pressure, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (is_pen) { |
|
|
|
if (is_pen) { |
|
|
|
point.pressure = smooth_pressure; |
|
|
|
point.pressure = smooth_pressure; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
state.players[player_id].points.push(point); |
|
|
|
points.push(point); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stroke.head = point; |
|
|
|
|
|
|
|
|
|
|
|
recompute_dynamic_data(state, context); |
|
|
|
recompute_dynamic_data(state, context); |
|
|
|
player.dynamic_head = point; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function geometry_clear_player(state, context, player_id) { |
|
|
|
// Remove prestroke from dynamic data (usually because it's now a real stroke)
|
|
|
|
|
|
|
|
function geometry_clear_oldest_prestroke(state, context, player_id) { |
|
|
|
if (!state.online) return; |
|
|
|
if (!state.online) return; |
|
|
|
state.players[player_id].points.length = 0; |
|
|
|
|
|
|
|
|
|
|
|
const player = state.players[player_id]; |
|
|
|
|
|
|
|
player.strokes.shift(); |
|
|
|
|
|
|
|
|
|
|
|
recompute_dynamic_data(state, context); |
|
|
|
recompute_dynamic_data(state, context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|