You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

244 lines
7.8 KiB

function push_point(s, x, y, u, v, r, g, b, type) {
ser_f32(s, x);
ser_f32(s, y);
ser_f32(s, u);
ser_f32(s, v);
// ser_u8(s, Math.floor(Math.random() * 255));
// ser_u8(s, Math.floor(Math.random() * 255));
// ser_u8(s, Math.floor(Math.random() * 255));
ser_u8(s, r);
ser_u8(s, g);
ser_u8(s, b);
ser_u8(s, type);
}
function push_circle(s, cx, cy, radius, r, g, b) {
push_point(s, cx - radius, cy - radius, 0, 0, r, g, b, 1);
push_point(s, cx - radius, cy + radius, 0, 1, r, g, b, 1);
push_point(s, cx + radius, cy - radius, 1, 0, r, g, b, 1);
push_point(s, cx + radius, cy + radius, 1, 1, r, g, b, 1);
push_point(s, cx + radius, cy - radius, 1, 0, r, g, b, 1);
push_point(s, cx - radius, cy + radius, 0, 1, r, g, b, 1);
}
function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, r, g, b) {
push_point(s, p1x, p1y, 0, 0, r, g, b, 0);
push_point(s, p2x, p2y, 0, 1, r, g, b, 0);
push_point(s, p3x, p3y, 1, 0, r, g, b, 0);
push_point(s, p4x, p4y, 1, 1, r, g, b, 0);
push_point(s, p3x, p3y, 1, 0, r, g, b, 0);
push_point(s, p2x, p2y, 0, 1, r, g, b, 0);
}
function push_stroke(s, stroke) {
const stroke_width = stroke.width;
const points = stroke.points;
const color_u32 = stroke.color;
const r = (color_u32 >> 16) & 0xFF;
const g = (color_u32 >> 8) & 0xFF;
const b = color_u32 & 0xFF;
if (points.length === 0) {
return;
}
if (points.length === 1) {
push_circle(s, points[0].x, points[0].y, stroke_width / 2, r, g, b);
return;
}
for (let i = 0; i < points.length - 1; ++i) {
const px = points[i].x;
const py = points[i].y;
const nextpx = points[i + 1].x;
const nextpy = points[i + 1].y;
const d1x = nextpx - px;
const d1y = nextpy - py;
// Perpendicular to (d1x, d1y), points to the LEFT
let perp1x = -d1y;
let perp1y = d1x;
const perpnorm1 = Math.sqrt(perp1x * perp1x + perp1y * perp1y);
perp1x /= perpnorm1;
perp1y /= perpnorm1;
const s1x = px + perp1x * stroke_width / 2;
const s1y = py + perp1y * stroke_width / 2;
const s2x = px - perp1x * stroke_width / 2;
const s2y = py - perp1y * stroke_width / 2;
const s3x = nextpx + perp1x * stroke_width / 2;
const s3y = nextpy + perp1y * stroke_width / 2;
const s4x = nextpx - perp1x * stroke_width / 2;
const s4y = nextpy - perp1y * stroke_width / 2;
push_quad(s, s2x, s2y, s1x, s1y, s4x, s4y, s3x, s3y, r, g, b);
push_circle(s, px, py, stroke_width / 2, r, g, b);
}
const lastp = points[points.length - 1];
push_circle(s, lastp.x, lastp.y, stroke_width / 2, r, g, b);
}
function geometry_prepare_stroke(state) {
if (!state.online) {
return null;
}
return {
'color': state.players[state.me].color,
'width': state.players[state.me].width,
'points': process_stroke(state, state.players[state.me].points),
'user_id': state.me,
};
}
function geometry_add_stroke(state, context, stroke) {
if (!state.online || !stroke) return;
const bytes_left = context.static_stroke_serializer.size - context.static_stroke_serializer.offset;
const bytes_needed = (stroke.points.length * 12 + 6) * config.bytes_per_point;
if (bytes_left < bytes_needed) {
const old_view = context.static_stroke_serializer.strview;
const old_offset = context.static_stroke_serializer.offset;
const new_size = Math.ceil((context.static_stroke_serializer.size + bytes_needed) * 1.62);
context.static_stroke_serializer = serializer_create(new_size);
context.static_stroke_serializer.strview.set(old_view);
context.static_stroke_serializer.offset = old_offset;
}
push_stroke(context.static_stroke_serializer, stroke);
}
function recompute_dynamic_data(state, context) {
let bytes_needed = 0;
for (const player_id in state.players) {
const player = state.players[player_id];
if (player.points.length > 0) {
bytes_needed += (player.points.length * 12 + 6) * config.bytes_per_point;
}
}
if (bytes_needed > context.dynamic_stroke_serializer.size) {
context.dynamic_stroke_serializer = serializer_create(Math.ceil(bytes_needed * 1.62));
} else {
context.dynamic_stroke_serializer.offset = 0;
}
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_stroke_serializer, player);
}
}
}
function geometry_add_point(state, context, player_id, point) {
if (!state.online) return;
state.players[player_id].points.push(point);
recompute_dynamic_data(state, context);
}
function geometry_clear_player(state, context, player_id) {
if (!state.online) return;
state.players[player_id].points.length = 0;
recompute_dynamic_data(state, context);
}
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).length;
context.textures[id] = {
'texture': gl.createTexture(),
'image_id': image_id
};
gl.bindTexture(gl.TEXTURE_2D, context.textures[id].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) {
const x = image_event.x;
const y = image_event.y;
const count = Object.keys(context.textures).length;
for (let id = 0; id < count; ++id) {
const image = context.textures[id];
if (image.image_id === image_event.image_id) {
context.quad_positions[id * 12 + 0] = x;
context.quad_positions[id * 12 + 1] = y;
context.quad_positions[id * 12 + 2] = x;
context.quad_positions[id * 12 + 3] = y + image_event.height;
context.quad_positions[id * 12 + 4] = x + image_event.width;
context.quad_positions[id * 12 + 5] = y + image_event.height;
context.quad_positions[id * 12 + 6] = x + image_event.width;
context.quad_positions[id * 12 + 7] = y;
context.quad_positions[id * 12 + 8] = x;
context.quad_positions[id * 12 + 9] = y;
context.quad_positions[id * 12 + 10] = x + image_event.width;
context.quad_positions[id * 12 + 11] = y + image_event.height;
context.quad_positions_f32 = new Float32Array(context.quad_positions);
break;
}
}
}
function image_at(state, x, y) {
for (let i = state.events.length - 1; i >= 0; --i) {
const event = state.events[i];
if (event.type === EVENT.IMAGE && !event.deleted) {
if ('height' in event && 'width' in event) {
if (event.x <= x && x <= event.x + event.width && event.y <= y && y <= event.y + event.height) {
return event;
}
}
}
}
return null;
}