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.

293 lines
9.0 KiB

function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b, stroke_id) {
ser_f32(s, x);
ser_f32(s, y);
ser_f32(s, thickness);
ser_f32(s, ax);
ser_f32(s, ay);
ser_f32(s, bx);
ser_f32(s, by);
2 years ago
ser_u8(s, r);
ser_u8(s, g);
ser_u8(s, b);
ser_align(s, 4);
ser_u32(s, stroke_id);
2 years ago
}
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_stroke(s, stroke, stroke_index) {
2 years ago
const stroke_width = stroke.width;
2 years ago
const points = stroke.points;
const color_u32 = stroke.color;
const radius = stroke_width / 2;
2 years ago
if (points.length < 2) {
2 years ago
return;
}
const r = (color_u32 >> 16) & 0xFF;
const g = (color_u32 >> 8) & 0xFF;
const b = color_u32 & 0xFF;
for (let i = 0; i < points.length - 1; ++i) {
const from = points[i];
const to = points[i + 1];
const dir_x = to.x - from.x;
const dir_y = to.y - from.y;
const len = Math.sqrt(dir_x * dir_x + dir_y * dir_y);
const dir1_x = dir_x / len;
const dir1_y = dir_y / len;
const up_x = dir_y / len;
const up_y = -dir_x / len;
let p1_x = from.x + (up_x - dir1_x) * radius;
let p1_y = from.y + (up_y - dir1_y) * radius;
let p2_x = to.x + (up_x + dir1_x) * radius;
let p2_y = to.y + (up_y + dir1_y) * radius;
let p3_x = from.x + (-up_x - dir1_x) * radius;
let p3_y = from.y + (-up_y - dir1_y) * radius;
let p4_x = to.x + (-up_x + dir1_x) * radius;
let p4_y = to.y + (-up_y + dir1_y) * radius;
push_quad(s,
p1_x, p1_y,
p2_x, p2_y,
p3_x, p3_y,
p4_x, p4_y,
from.x, from.y,
to.x, to.y,
stroke_width,
r, g, b,
stroke_index
);
}
2 years ago
}
2 years ago
function geometry_prepare_stroke(state) {
if (!state.online) {
return null;
}
if (state.players[state.me].points.length === 0) {
return null;
}
const points = process_stroke(state, state.players[state.me].points);
return {
'color': state.players[state.me].color,
'width': state.players[state.me].width,
'points': points,
'user_id': state.me,
};
}
12 months ago
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;
for (let i = 0; i < config.lod_levels; ++i) {
// TODO: just pass zoom to process_stroke ?
const saved_zoom = state.canvas.zoom;
state.canvas.zoom = Math.pow(0.5, i);
const points = (i > 0 ? process_stroke(state, stroke.points) : stroke.points);
state.canvas.zoom = saved_zoom;
const vertex_serializer = context.lods[i].vertices = ser_ensure_by(context.lods[i].vertices, points.length * 4 * config.bytes_per_point);
/*
event.index = state.events.length;
event.starting_index = state.starting_index;
2 years ago
if (event.points.length > 1) {
state.starting_index += (event.points.length - 1) * 4;
}
state.total_points += event.points.length;
*/
let starting_index = 0;
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;
}
stroke.lods.push({
'points': points,
'starting_index': starting_index,
'width': stroke.width,
'color': stroke.color,
});
context.lods[i].total_points += points.length;
push_stroke(vertex_serializer, stroke.lods[stroke.lods.length - 1], stroke_index);
if (i === 0) {
stroke.bbox = stroke_bbox(stroke);
stroke.area = (stroke.bbox.x2 - stroke.bbox.x1) * (stroke.bbox.y2 - stroke.bbox.y1);
}
}
if (!skip_bvh) bvh_add_stroke(state.bvh, stroke_index, stroke);
2 years ago
}
function geometry_delete_stroke(state, context, stroke_index) {
// NEXT: deleted wrong stroke
let offset = 0;
2 years ago
for (let i = 0; i < stroke_index; ++i) {
const event = state.events[i];
if (event.type === EVENT.STROKE) {
offset += (event.points.length * 12 + 6) * config.bytes_per_point;
}
2 years ago
}
const stroke = state.events[stroke_index];
for (let i = 0; i < stroke.points.length * 12 + 6; ++i) {
context.static_stroke_serializer.view.setUint8(offset + config.bytes_per_point - 1, 125);
offset += config.bytes_per_point;
2 years ago
}
}
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 * 6 * config.bytes_per_point;
}
}
if (bytes_needed > context.dynamic_serializer.size) {
context.dynamic_serializer = serializer_create(Math.ceil(bytes_needed * 1.62));
} else {
context.dynamic_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];
12 months ago
if (player.points.length > 1) {
push_stroke(context.dynamic_serializer, player, 0);
}
}
context.need_dynamic_upload = true;
}
2 years ago
function geometry_add_point(state, context, player_id, point) {
if (!state.online) return;
2 years ago
state.players[player_id].points.push(point);
2 years ago
recompute_dynamic_data(state, context);
2 years ago
}
2 years ago
function geometry_clear_player(state, context, player_id) {
if (!state.online) return;
2 years ago
state.players[player_id].points.length = 0;
recompute_dynamic_data(state, context);
schedule_draw(state, context);
2 years ago
}
function add_image(context, image_id, bitmap, p) {
return; // TODO
2 years ago
const x = p.x;
const y = p.y;
const gl = context.gl;
const id = Object.keys(context.textures['image']).length;
2 years ago
context.textures['image'][id] = {
'texture': gl.createTexture(),
'image_id': image_id
};
2 years ago
gl.bindTexture(gl.TEXTURE_2D, context.textures['image'][id].texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
2 years ago
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['image']).length;
for (let id = 0; id < count; ++id) {
const image = context.textures['image'][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;
}