|
|
|
@ -317,6 +317,7 @@ async function draw(state, context, animate, ts) {
@@ -317,6 +317,7 @@ async function draw(state, context, animate, ts) {
|
|
|
|
|
|
|
|
|
|
// Dynamic strokes should be drawn above static strokes
|
|
|
|
|
gl.clear(gl.DEPTH_BUFFER_BIT); |
|
|
|
|
gl.useProgram(pr.program); |
|
|
|
|
|
|
|
|
|
gl.uniform1i(pr.locations['u_stroke_count'], dynamic_stroke_count); |
|
|
|
|
gl.uniform1i(pr.locations['u_stroke_data'], 0); |
|
|
|
@ -353,8 +354,15 @@ async function draw(state, context, animate, ts) {
@@ -353,8 +354,15 @@ async function draw(state, context, animate, ts) {
|
|
|
|
|
gl.enableVertexAttribArray(pr.locations['a_pressure']); |
|
|
|
|
|
|
|
|
|
// Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values)
|
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_a'], 2, gl.FLOAT, false, 2 * 4, 0); |
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_b'], 2, gl.FLOAT, false, 2 * 4, 2 * 4); |
|
|
|
|
if (context.dynamic_instance_ids.size > 1) { |
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_a'], 2, gl.FLOAT, false, 2 * 4, 0); |
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_b'], 2, gl.FLOAT, false, 2 * 4, 2 * 4); |
|
|
|
|
} else { |
|
|
|
|
// A special case where there is no second point. Reuse the first point and handle the zero length segment in the shader
|
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_a'], 2, gl.FLOAT, false, 2 * 4, 0); |
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_b'], 2, gl.FLOAT, false, 2 * 4, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
gl.vertexAttribIPointer(pr.locations['a_stroke_id'], 1, gl.INT, 4, context.dynamic_instance_points.size * 4); |
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_pressure'], 2, gl.UNSIGNED_BYTE, true, 1, context.dynamic_instance_points.size * 4 + context.dynamic_instance_ids.size * 4); |
|
|
|
|
|
|
|
|
@ -422,17 +430,43 @@ async function draw(state, context, animate, ts) {
@@ -422,17 +430,43 @@ async function draw(state, context, animate, ts) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (config.draw_bvh) { |
|
|
|
|
const pr = programs['iquad']; |
|
|
|
|
const bboxes = tv_create(Float32Array, context.clipped_indices.size * 4); |
|
|
|
|
// Debug BVH viz
|
|
|
|
|
for (let i = 0; i < context.clipped_indices.size; ++i) { |
|
|
|
|
const stroke_id = context.clipped_indices.data[i]; |
|
|
|
|
const stroke = state.events[stroke_id]; |
|
|
|
|
const stroke_bbox = state.bvh.nodes[stroke.bvh_node].bbox; |
|
|
|
|
tv_add(bboxes, stroke.bbox.x1); |
|
|
|
|
tv_add(bboxes, stroke.bbox.x2); |
|
|
|
|
tv_add(bboxes, stroke.bbox.y1); |
|
|
|
|
tv_add(bboxes, stroke.bbox.x2); |
|
|
|
|
tv_add(bboxes, stroke.bbox.y2); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const quad_count = bboxes.size / 4; |
|
|
|
|
|
|
|
|
|
gl.useProgram(pr.program); |
|
|
|
|
|
|
|
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_iquads']); |
|
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, tv_data(bboxes), gl.STREAM_DRAW); |
|
|
|
|
|
|
|
|
|
gl.uniform2f(pr.locations['u_res'], context.canvas.width, context.canvas.height); |
|
|
|
|
gl.uniform2f(pr.locations['u_scale'], state.canvas.zoom, state.canvas.zoom); |
|
|
|
|
gl.uniform2f(pr.locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); |
|
|
|
|
|
|
|
|
|
gl.enableVertexAttribArray(pr.locations['a_topleft']); |
|
|
|
|
gl.enableVertexAttribArray(pr.locations['a_bottomright']); |
|
|
|
|
|
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_topleft'], 2, gl.FLOAT, false, 4 * 4, 0); |
|
|
|
|
gl.vertexAttribPointer(pr.locations['a_bottomright'], 2, gl.FLOAT, false, 4 * 4, 2 * 4); |
|
|
|
|
|
|
|
|
|
gl.vertexAttribDivisor(pr.locations['a_topleft'], 1); |
|
|
|
|
gl.vertexAttribDivisor(pr.locations['a_bottomright'], 1); |
|
|
|
|
|
|
|
|
|
// Static draw (everything already bound)
|
|
|
|
|
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, quad_count); |
|
|
|
|
|
|
|
|
|
gl.vertexAttribDivisor(pr.locations['a_topleft'], 0); |
|
|
|
|
gl.vertexAttribDivisor(pr.locations['a_bottomright'], 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
document.getElementById('debug-stats').innerHTML = ` |
|
|
|
|