|
|
|
@ -1,38 +1,25 @@
@@ -1,38 +1,25 @@
|
|
|
|
|
function push_point_xy(s, x, y) { |
|
|
|
|
function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b) { |
|
|
|
|
ser_f32(s, x); |
|
|
|
|
ser_f32(s, y); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function push_point_xyzrgb(s, x, y, z, r, g, b) { |
|
|
|
|
ser_f32(s, x); |
|
|
|
|
ser_f32(s, y); |
|
|
|
|
ser_f32(s, z); |
|
|
|
|
ser_f32(s, thickness); |
|
|
|
|
ser_f32(s, ax); |
|
|
|
|
ser_f32(s, ay); |
|
|
|
|
ser_f32(s, bx); |
|
|
|
|
ser_f32(s, by); |
|
|
|
|
ser_u8(s, r); |
|
|
|
|
ser_u8(s, g); |
|
|
|
|
ser_u8(s, b); |
|
|
|
|
ser_align(s, 4); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function push_quad_xyzrgb(s, p1x, p1y, p4x, p4y, z, r, g, b) { |
|
|
|
|
push_point_xyzrgb(s, p1x, p1y, z, r, g, b); |
|
|
|
|
push_point_xyzrgb(s, p4x, p1y, z, r, g, b); |
|
|
|
|
push_point_xyzrgb(s, p1x, p4y, z, r, g, b); |
|
|
|
|
|
|
|
|
|
push_point_xyzrgb(s, p4x, p4y, z, r, g, b); |
|
|
|
|
push_point_xyzrgb(s, p1x, p4y, z, r, g, b); |
|
|
|
|
push_point_xyzrgb(s, p4x, p1y, z, r, g, b); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const MAX_POINTS_PER_QUAD = 10; |
|
|
|
|
const MAX_QUAD_SIDE = 256; |
|
|
|
|
|
|
|
|
|
function count_stroke_quads(points) { |
|
|
|
|
let min_x, min_y, max_x, max_y; |
|
|
|
|
let points_per_quad = 0; |
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, thickness, r, g, b) { |
|
|
|
|
push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b); |
|
|
|
|
push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b); |
|
|
|
|
push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b); |
|
|
|
|
|
|
|
|
|
return Math.ceil(points.length / MAX_POINTS_PER_QUAD); |
|
|
|
|
push_point(s, p4x, p4y, ax, ay, bx, by, thickness, r, g, b); |
|
|
|
|
push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b); |
|
|
|
|
push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function push_stroke(context, stroke) { |
|
|
|
@ -43,77 +30,78 @@ function push_stroke(context, stroke) {
@@ -43,77 +30,78 @@ function push_stroke(context, stroke) {
|
|
|
|
|
const stroke_width = stroke.width; |
|
|
|
|
const points = stroke.points; |
|
|
|
|
const color_u32 = stroke.color; |
|
|
|
|
const radius = stroke_width / 2; |
|
|
|
|
|
|
|
|
|
const r = (color_u32 >> 16) & 0xFF; |
|
|
|
|
const g = (color_u32 >> 8) & 0xFF; |
|
|
|
|
const b = color_u32 & 0xFF; |
|
|
|
|
|
|
|
|
|
if (points.length === 0) { |
|
|
|
|
if (points.length < 2) { |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let points_from = context.point_serializer.offset / (4 * 2); // 4 is sizeof(f32) btw, just sain'
|
|
|
|
|
const points_start = points_from; |
|
|
|
|
|
|
|
|
|
let min_x, min_y, max_x, max_y; |
|
|
|
|
|
|
|
|
|
min_x = Math.floor(points[0].x - stroke_width / 2); |
|
|
|
|
max_x = Math.ceil(points[0].x + stroke_width / 2); |
|
|
|
|
|
|
|
|
|
min_y = Math.floor(points[0].y - stroke_width / 2); |
|
|
|
|
max_y = Math.ceil(points[0].y + stroke_width / 2); |
|
|
|
|
|
|
|
|
|
let points_per_quad = 0; |
|
|
|
|
const r = (color_u32 >> 16) & 0xFF; |
|
|
|
|
const g = (color_u32 >> 8) & 0xFF; |
|
|
|
|
const b = color_u32 & 0xFF; |
|
|
|
|
|
|
|
|
|
for (let i = 0; i < points.length; ++i) { |
|
|
|
|
const p = points[i]; |
|
|
|
|
for (let i = 0; i < points.length - 1; ++i) { |
|
|
|
|
const from = points[i]; |
|
|
|
|
const to = points[i + 1]; |
|
|
|
|
|
|
|
|
|
min_x = Math.min(min_x, Math.floor(p.x - stroke_width / 2)); |
|
|
|
|
min_y = Math.min(min_y, Math.floor(p.y - stroke_width / 2)); |
|
|
|
|
max_x = Math.max(max_x, Math.ceil(p.x + stroke_width / 2)); |
|
|
|
|
max_y = Math.max(max_y, Math.ceil(p.y + stroke_width / 2)); |
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
push_point_xy(context.point_serializer, p.x, p.y); |
|
|
|
|
const dir1_x = dir_x / len; |
|
|
|
|
const dir1_y = dir_y / len; |
|
|
|
|
|
|
|
|
|
points_per_quad++; |
|
|
|
|
|
|
|
|
|
if (points_per_quad == MAX_POINTS_PER_QUAD) { |
|
|
|
|
let points_to = points_from + MAX_POINTS_PER_QUAD; |
|
|
|
|
const up_x = dir_y / len; |
|
|
|
|
const up_y = -dir_x / len; |
|
|
|
|
|
|
|
|
|
if (points_from > points_start) { |
|
|
|
|
// 1 point overlap to prevent gaps
|
|
|
|
|
ser_u32(context.index_serializer, points_from - 1); |
|
|
|
|
} else { |
|
|
|
|
ser_u32(context.index_serializer, points_from); |
|
|
|
|
} |
|
|
|
|
let p1_x = from.x + (up_x - dir1_x) * radius; |
|
|
|
|
let p1_y = from.y + (up_y - dir1_y) * radius; |
|
|
|
|
|
|
|
|
|
ser_u32(context.index_serializer, points_to); |
|
|
|
|
let p2_x = to.x + (up_x + dir1_x) * radius; |
|
|
|
|
let p2_y = to.y + (up_y + dir1_y) * radius; |
|
|
|
|
|
|
|
|
|
push_quad_xyzrgb(context.quad_serializer, min_x, min_y, max_x, max_y, stroke_width / 2, r, g, b); |
|
|
|
|
let p3_x = from.x + (-up_x - dir1_x) * radius; |
|
|
|
|
let p3_y = from.y + (-up_y - dir1_y) * radius; |
|
|
|
|
|
|
|
|
|
min_x = Math.floor(p.x - stroke_width / 2); |
|
|
|
|
max_x = Math.ceil(p.x + stroke_width / 2); |
|
|
|
|
let p4_x = to.x + (-up_x + dir1_x) * radius; |
|
|
|
|
let p4_y = to.y + (-up_y + dir1_y) * radius; |
|
|
|
|
|
|
|
|
|
min_y = Math.floor(p.y - stroke_width / 2); |
|
|
|
|
max_y = Math.ceil(p.y + stroke_width / 2); |
|
|
|
|
// TODO: floor, ceil corners depending on direction of line
|
|
|
|
|
|
|
|
|
|
points_from = points_to; |
|
|
|
|
points_per_quad = 0; |
|
|
|
|
if (p1_x < p2_x) { |
|
|
|
|
p1_x = Math.floor(p1_x); |
|
|
|
|
p3_x = Math.floor(p3_x); |
|
|
|
|
p2_x = Math.ceil(p2_x); |
|
|
|
|
p4_x = Math.ceil(p4_x); |
|
|
|
|
} else { |
|
|
|
|
p1_x = Math.ceil(p1_x); |
|
|
|
|
p3_x = Math.ceil(p3_x); |
|
|
|
|
p2_x = Math.floor(p2_x); |
|
|
|
|
p4_x = Math.floor(p4_x); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (points_per_quad > 0) { |
|
|
|
|
const points_to = points_from + points_per_quad; |
|
|
|
|
|
|
|
|
|
if (points_from > points_start) { |
|
|
|
|
ser_u32(context.index_serializer, points_from - 1); |
|
|
|
|
if (p1_y < p2_y) { |
|
|
|
|
p1_y = Math.floor(p1_y); |
|
|
|
|
p3_y = Math.floor(p3_y); |
|
|
|
|
p2_y = Math.ceil(p2_y); |
|
|
|
|
p4_y = Math.ceil(p4_y); |
|
|
|
|
} else { |
|
|
|
|
ser_u32(context.index_serializer, points_from); |
|
|
|
|
p1_y = Math.ceil(p1_y); |
|
|
|
|
p3_y = Math.ceil(p3_y); |
|
|
|
|
p2_y = Math.floor(p2_y); |
|
|
|
|
p4_y = Math.floor(p4_y); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ser_u32(context.index_serializer, points_to); |
|
|
|
|
|
|
|
|
|
push_quad_xyzrgb(context.quad_serializer, min_x, min_y, max_x, max_y, stroke_width / 2, r, g, b); |
|
|
|
|
push_quad(context.static_serializer, |
|
|
|
|
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 |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -133,38 +121,12 @@ function geometry_prepare_stroke(state) {
@@ -133,38 +121,12 @@ function geometry_prepare_stroke(state) {
|
|
|
|
|
function geometry_add_stroke(state, context, stroke) { |
|
|
|
|
if (!state.online || !stroke) return; |
|
|
|
|
|
|
|
|
|
const stroke_quads = count_stroke_quads(stroke.points); |
|
|
|
|
// const stroke_quads = Math.ceil(stroke.points.length / MAX_POINTS_PER_QUAD);
|
|
|
|
|
|
|
|
|
|
// Points
|
|
|
|
|
let point_bytes_left = context.point_serializer.size - context.point_serializer.offset; |
|
|
|
|
let point_bytes_needed = stroke.points.length * config.bytes_per_point; |
|
|
|
|
|
|
|
|
|
if (point_bytes_needed % 8192 != 0) { |
|
|
|
|
point_bytes_needed += 8192 - point_bytes_needed % 8192; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (point_bytes_left < point_bytes_needed) { |
|
|
|
|
const extend_points_by = Math.ceil((context.point_serializer.size + point_bytes_needed) * 1.62); |
|
|
|
|
context.point_serializer = ser_extend(context.point_serializer, extend_points_by); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Indices
|
|
|
|
|
const index_bytes_left = context.index_serializer.size - context.index_serializer.offset; |
|
|
|
|
const index_bytes_needed = stroke_quads * (4 * 2); |
|
|
|
|
|
|
|
|
|
if (index_bytes_left < index_bytes_needed) { |
|
|
|
|
const extend_indices_by = Math.ceil((context.index_serializer.size + index_bytes_needed) * 1.62); |
|
|
|
|
context.index_serializer = ser_extend(context.index_serializer, extend_indices_by); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Quads
|
|
|
|
|
const quad_bytes_left = context.quad_serializer.size - context.quad_serializer.offset; |
|
|
|
|
const quad_bytes_needed = stroke_quads * 6 * (4 * 4); |
|
|
|
|
let bytes_left = context.static_serializer.size - context.static_serializer.offset; |
|
|
|
|
let bytes_needed = stroke.points.length * 6 * config.bytes_per_point; |
|
|
|
|
|
|
|
|
|
if (quad_bytes_left < quad_bytes_needed) { |
|
|
|
|
const extend_quads_by = Math.ceil((context.quad_serializer.size + quad_bytes_needed) * 1.62); |
|
|
|
|
context.quad_serializer = ser_extend(context.quad_serializer, extend_quads_by); |
|
|
|
|
if (bytes_left < bytes_needed) { |
|
|
|
|
const extend_to = Math.ceil((context.static_serializer.size + bytes_needed) * 1.62); |
|
|
|
|
context.static_serializer = ser_extend(context.static_serializer, extend_to); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
push_stroke(context, stroke); |
|
|
|
|