Browse Source

Reuse a single circle geometry. One circle per segment. Still needs last circle

sdf
Aleksey Olokhtonov 3 weeks ago
parent
commit
dc824c12c9
  1. 2
      client/config.js
  2. 21
      client/webgl_draw.js
  3. 26
      client/webgl_geometry.js
  4. 62
      client/webgl_shaders.js

2
client/config.js

@ -12,7 +12,7 @@ const config = { @@ -12,7 +12,7 @@ const config = {
draw_fullnodes: false,
zoom_delta: 0.05,
min_zoom_level: -275,
max_zoom_level: 100,
max_zoom_level: 200,
initial_offline_timeout: 1000,
default_color: 0x00,
default_width: 8,

21
client/webgl_draw.js

@ -273,6 +273,9 @@ async function draw(state, context, animate, ts) { @@ -273,6 +273,9 @@ async function draw(state, context, animate, ts) {
}
}
const circle_segments = 32;
const circle_points = circle_segments * 3;
const circle_data = geometry_line_segments_with_two_circles(circle_segments);
// "Static" data upload
if (segment_count > 0) {
@ -283,14 +286,17 @@ async function draw(state, context, animate, ts) { @@ -283,14 +286,17 @@ async function draw(state, context, animate, ts) {
const total_static_size = context.instance_data_points.size * 4 +
context.instance_data_ids.size * 4 +
context.instance_data_pressures.size;
round_to_pow2(context.instance_data_pressures.size, 4) +
circle_data.length * 4;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_strokes_static']);
gl.bufferData(gl.ARRAY_BUFFER, total_static_size, gl.STREAM_DRAW);
gl.bufferData(gl.ARRAY_BUFFER, total_static_size, gl.DYNAMIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, tv_data(context.instance_data_points));
gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4, tv_data(context.instance_data_ids));
gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4,
tv_data(context.instance_data_pressures));
gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + round_to_pow2(context.instance_data_pressures.size, 4),
circle_data);
gl.bindTexture(gl.TEXTURE_2D, textures['stroke_data']);
upload_square_rgba16ui_texture(gl, context.stroke_data, config.stroke_texture_size);
@ -302,28 +308,35 @@ async function draw(state, context, animate, ts) { @@ -302,28 +308,35 @@ async function draw(state, context, animate, ts) {
gl.uniform1i(pr.locations['u_stroke_data'], 0);
gl.uniform1i(pr.locations['u_stroke_texture_size'], config.stroke_texture_size);
gl.uniform1f(pr.locations['u_fixed_pixel_width'], 0);
gl.uniform1i(pr.locations['u_circle_points'], circle_points);
gl.enableVertexAttribArray(pr.locations['a_pos']);
gl.enableVertexAttribArray(pr.locations['a_a']);
gl.enableVertexAttribArray(pr.locations['a_b']);
gl.enableVertexAttribArray(pr.locations['a_stroke_id']);
gl.enableVertexAttribArray(pr.locations['a_pressure']);
// Circle meshes (shared for all instances)
gl.vertexAttribPointer(pr.locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + round_to_pow2(context.instance_data_pressures.size, 4));
// 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);
gl.vertexAttribIPointer(pr.locations['a_stroke_id'], 1, gl.INT, 4, context.instance_data_points.size * 4);
gl.vertexAttribPointer(pr.locations['a_pressure'], 2, gl.UNSIGNED_BYTE, true, 1, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4);
gl.vertexAttribDivisor(pr.locations['a_pos'], 0);
gl.vertexAttribDivisor(pr.locations['a_a'], 1);
gl.vertexAttribDivisor(pr.locations['a_b'], 1);
gl.vertexAttribDivisor(pr.locations['a_stroke_id'], 1);
gl.vertexAttribDivisor(pr.locations['a_pressure'], 1);
// Static draw (everything already bound)
gl.drawArraysInstanced(gl.TRIANGLES, 0, 32 * 3 + 6 + 32 * 3, segment_count);
gl.drawArraysInstanced(gl.TRIANGLES, 0, circle_points + 6, segment_count);
// I don't really know why I need to do this, but it
// makes background patter drawcall work properly
gl.vertexAttribDivisor(pr.locations['a_pos'], 0);
gl.vertexAttribDivisor(pr.locations['a_a'], 0);
gl.vertexAttribDivisor(pr.locations['a_b'], 0);
gl.vertexAttribDivisor(pr.locations['a_stroke_id'], 0);
@ -332,7 +345,7 @@ async function draw(state, context, animate, ts) { @@ -332,7 +345,7 @@ async function draw(state, context, animate, ts) {
// Dynamic draw (strokes currently being drawn)
if (dynamic_segment_count > 0) {
if (false && dynamic_segment_count > 0) {
const pr = programs['main']; // same as static
// Dynamic strokes should be drawn above static strokes

26
client/webgl_geometry.js

@ -521,3 +521,29 @@ function geometry_generate_handles(state, context, active_image) { @@ -521,3 +521,29 @@ function geometry_generate_handles(state, context, active_image) {
'stroke_data': stroke_data,
};
}
function geometry_line_segments_with_two_circles(circle_segments) {
const results = new Float32Array((circle_segments * 3 + 6) * 2); // triangle fan circle + two triangles, all 2D (x + y)
// Generate circle as triangle fan at 0, 0 with radius 1
// This circle will be offset/scaled in the vertex shader
let last_phi = ((circle_segments - 1) / circle_segments) * 2 * Math.PI;
for (let i = 0; i < circle_segments; ++i) {
const phi = i / circle_segments * 2 * Math.PI;
const x1 = Math.cos(phi);
const y1 = Math.sin(phi);
const x2 = Math.cos(last_phi);
const y2 = Math.sin(last_phi);
results[i * 6 + 0] = x1;
results[i * 6 + 1] = y1;
results[i * 6 + 2] = x2;
results[i * 6 + 3] = y2;
results[i * 6 + 4] = 0;
results[i * 6 + 5] = 0;
last_phi = phi;
}
return results;
}

62
client/webgl_shaders.js

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
const sdf_vs_src = `#version 300 es
in vec2 a_pos; // for the joins/caps these are relative positions. for line segments these are dummy values
in vec2 a_a; // point from
in vec2 a_b; // point to
@ -12,21 +13,20 @@ const sdf_vs_src = `#version 300 es @@ -12,21 +13,20 @@ const sdf_vs_src = `#version 300 es
uniform int u_stroke_texture_size;
uniform highp usampler2D u_stroke_data;
uniform float u_fixed_pixel_width;
uniform int u_circle_points;
out vec3 v_color;
flat out vec2 v_thickness;
void main() {
const float PI = 3.1415926;
vec2 screen02;
int per_segment_points = u_circle_points * 2 + 6;
int vertex_index = gl_VertexID % per_segment_points;
int stroke_data_y = a_stroke_id / u_stroke_texture_size;
int stroke_data_x = a_stroke_id % u_stroke_texture_size;
vec2 line_dir = normalize(a_b - a_a);
vec2 up_dir = vec2(line_dir.y, -line_dir.x);
uvec4 stroke_data = texelFetch(u_stroke_data, ivec2(stroke_data_x, stroke_data_y), 0);
float radius = float(stroke_data.w);
@ -34,54 +34,30 @@ const sdf_vs_src = `#version 300 es @@ -34,54 +34,30 @@ const sdf_vs_src = `#version 300 es
radius = u_fixed_pixel_width / u_scale.x;
}
int vertex_index = gl_VertexID % 198;
vec2 pos;
if (vertex_index < 32 * 3) {
// first cap
float angle1 = float(vertex_index / 3) / 32.0 * PI * 2.0;
float angle2 = float((vertex_index + 1) / 3) / 32.0 * PI * 2.0;
vec2 dir1 = vec2(cos(angle1), sin(angle1));
vec2 dir2 = vec2(cos(angle2), sin(angle2));
if (vertex_index % 3 == 0) {
pos = a_a + dir1 * radius * a_pressure.x * 2.0;
} else if (vertex_index % 3 == 1) {
pos = a_a;
if (vertex_index < u_circle_points) {
pos = a_a + a_pos * radius * a_pressure.x * 2.0;
} else {
pos = a_a + dir2 * radius * a_pressure.x * 2.0;
}
} else if (vertex_index < 102) {
int vertex_index_line = vertex_index - u_circle_points;
vec2 line_dir = normalize(a_b - a_a);
vec2 up_dir = vec2(line_dir.y, -line_dir.x);
// connecting line
if (vertex_index == 96) {
if (vertex_index_line == 0) {
// top left
pos = a_a + up_dir * radius * a_pressure.x * 2.0;
} else if (vertex_index == 97 || vertex_index == 101) {
} else if (vertex_index_line == 1 || vertex_index_line == 5) {
// top right
pos = a_b + up_dir * radius * a_pressure.y * 2.0;
} else if (vertex_index == 98 || vertex_index == 100) {
} else if (vertex_index_line == 2 || vertex_index_line == 4) {
// bottom left
pos = a_a - up_dir * radius * a_pressure.x * 2.0;
} else {
// bottom right
pos = a_b - up_dir * radius * a_pressure.y * 2.0;
}
} else {
// second cap
float angle1 = float((vertex_index - 102) / 3) / 32.0 * PI * 2.0;
float angle2 = float((vertex_index - 101) / 3) / 32.0 * PI * 2.0;
vec2 dir1 = vec2(cos(angle1), sin(angle1));
vec2 dir2 = vec2(cos(angle2), sin(angle2));
if ((vertex_index - 102) % 3 == 0) {
pos = a_b + dir1 * radius * a_pressure.y * 2.0;
} else if ((vertex_index - 102) % 3 == 1) {
pos = a_a;
} else {
pos = a_b + dir2 * radius * a_pressure.y * 2.0;
}
}
screen02 = (pos.xy * u_scale + u_translation) / u_res * 2.0;
@ -89,10 +65,11 @@ const sdf_vs_src = `#version 300 es @@ -89,10 +65,11 @@ const sdf_vs_src = `#version 300 es
v_color = vec3(stroke_data.xyz) / 255.0;
if (a_stroke_id >> 31 != 0) {
/*
if (a_stroke_id >> 31 != 0 && (vertex_index >= u_circle_points)) {
screen02 += vec2(100.0); // shift offscreen
}
*/
gl_Position = vec4(screen02 - 1.0, (float(a_stroke_id) / float(u_stroke_count)) * 2.0 - 1.0, 1.0);
}
`;
@ -346,10 +323,11 @@ function init_webgl(state, context) { @@ -346,10 +323,11 @@ function init_webgl(state, context) {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
//gl.blendEquation(gl.MAX);
/*
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.NOTEQUAL);
*/
context.gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query_webgl2');
if (context.gpu_timer_ext === null) {

Loading…
Cancel
Save