Browse Source

Texture test

infinite
A.Olokhtonov 2 years ago
parent
commit
29f697dceb
  1. 27
      client/tools.js
  2. 1
      client/webgl.html
  3. 53
      client/webgl.js
  4. 18
      client/webgl_geometry.js
  5. 11
      client/webgl_listeners.js
  6. 138
      client/webgl_shaders.js

27
client/tools.js

@ -1,23 +1,26 @@ @@ -1,23 +1,26 @@
function tools_switch(tool) {
if (storage.tools.active_element) {
storage.tools.active_element.classList.remove('active');
function tools_switch(state, tool) {
if (state.tools.active_element) {
state.tools.active_element.classList.remove('active');
}
storage.tools.active = tool;
storage.tools.active_element = document.querySelector(`.tool[data-tool="${tool}"]`);
storage.tools.active_element.classList.add('active');
state.tools.active = tool;
state.tools.active_element = document.querySelector(`.tool[data-tool="${tool}"]`);
state.tools.active_element.classList.add('active');
}
function tools_init() {
function init_tools(state, context) {
const pencil = document.querySelector('.tool[data-tool="pencil"]');
const ruler = document.querySelector('.tool[data-tool="ruler"]');
const eraser = document.querySelector('.tool[data-tool="eraser"]');
const undo = document.querySelector('.tool[data-tool="undo"]');
pencil.addEventListener('click', () => tools_switch('pencil'));
ruler.addEventListener('click', () => tools_switch('ruler'));
eraser.addEventListener('click', () => tools_switch('eraser'));
undo.addEventListener('click', queue_undo);
pencil.addEventListener('click', () => tools_switch(state, 'pencil'));
ruler.addEventListener('click', () => tools_switch(state, 'ruler'));
eraser.addEventListener('click', () => tools_switch(state, 'eraser'));
undo.addEventListener('click', () => {
pop_stroke(state, context);
window.requestAnimationFrame(() => draw(state, context));
});
tools_switch('pencil');
tools_switch(state, 'pencil');
}

1
client/webgl.html

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
<script type="text/javascript" src="math.js?v=4"></script>
<script type="text/javascript" src="aux.js?v=4"></script>
<script type="text/javascript" src="tools.js?v=4"></script>
<script type="text/javascript" src="webgl_geometry.js?v=4"></script>
<script type="text/javascript" src="webgl_shaders.js?v=4"></script>
<script type="text/javascript" src="webgl_listeners.js?v=5"></script>

53
client/webgl.js

@ -2,23 +2,54 @@ document.addEventListener('DOMContentLoaded', main); @@ -2,23 +2,54 @@ document.addEventListener('DOMContentLoaded', main);
function draw(state, context) {
const gl = context.gl;
const locations = context.locations;
const buffers = context.buffers;
const width = window.innerWidth;
const height = window.innerHeight;
let locations;
let buffers;
gl.viewport(0, 0, context.canvas.width, context.canvas.height);
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(context.program);
// Draw images
locations = context.locations['quad'];
buffers = context.buffers['quad'];
gl.useProgram(context.programs['quad']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_color']);
gl.enableVertexAttribArray(locations['a_texcoord']);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_layer'], 0);
gl.uniform1i(locations['u_texture'], 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_pos']);
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, context.quad_positions_f32, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_texcoord']);
gl.vertexAttribPointer(locations['a_texcoord'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, context.quad_texcoords_f32, gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES, 0, context.quad_positions.length / 2);
// Draw strokes
locations = context.locations['stroke'];
buffers = context.buffers['stroke'];
gl.useProgram(context.programs['stroke']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_color']);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_layer'], 1);
const total_pos_size = context.static_positions_f32.byteLength + context.dynamic_positions_f32.byteLength;
const total_color_size = context.static_colors_u8.byteLength + context.dynamic_colors_u8.byteLength;
@ -82,31 +113,43 @@ function main() { @@ -82,31 +113,43 @@ function main() {
'current_stroke': {
'color': 0,
'width': 8,
'points': [],
},
'strokes': [],
'tools': {
'active': null,
'active_element': null,
},
};
const context = {
'canvas': null,
'gl': null,
'program': null,
'programs': {},
'buffers': {},
'locations': {},
'textures': {},
'static_positions': [],
'dynamic_positions': [],
'quad_positions': [],
'quad_texcoords': [],
'static_colors': [],
'dynamic_colors': [],
'static_positions_f32': new Float32Array(0),
'dynamic_positions_f32': new Float32Array(0),
'static_colors_u8': new Uint8Array(0),
'dynamic_colors_u8': new Uint8Array(0),
'quad_positions_f32': new Float32Array(0),
'quad_texcoords_f32': new Float32Array(0),
'bgcolor': {'r': 1.0, 'g': 1.0, 'b': 1.0},
};
init_webgl(state, context);
init_listeners(state, context);
init_tools(state, context);
window.requestAnimationFrame(() => draw(state, context));
}

18
client/webgl_geometry.js

@ -16,7 +16,8 @@ function push_circle_at(positions, cl, r, g, b, c, o) { @@ -16,7 +16,8 @@ function push_circle_at(positions, cl, r, g, b, c, o) {
}
function push_stroke(state, stroke, positions, colors) {
const stroke_width = state.stroke_width;
const starting_length = positions.length;
const stroke_width = stroke.width;
const points = stroke.points;
const color_u32 = stroke.color;
@ -26,6 +27,7 @@ function push_stroke(state, stroke, positions, colors) { @@ -26,6 +27,7 @@ function push_stroke(state, stroke, positions, colors) {
if (points.length < 2) {
// TODO
stroke.popcount = 0;
return;
}
@ -88,6 +90,20 @@ function push_stroke(state, stroke, positions, colors) { @@ -88,6 +90,20 @@ function push_stroke(state, stroke, positions, colors) {
// TODO: angle
push_circle_at(positions, colors, r, g, b, points[points.length - 1], circle_offsets);
stroke.popcount = positions.length - starting_length;
}
function pop_stroke(state, context) {
if (state.strokes.length > 0) {
const popped = state.strokes.pop();
context.static_positions.length -= popped.popcount;
context.static_colors.length -= popped.popcount / 2 * 3;
context.static_positions_f32 = new Float32Array(context.static_positions);
context.static_colors_u8 = new Uint8Array(context.static_colors);
}
}
function add_static_stroke(state, context, stroke) {

11
client/webgl_listeners.js

@ -90,6 +90,7 @@ function mouseup(e, state, context) { @@ -90,6 +90,7 @@ function mouseup(e, state, context) {
if (state.drawing) {
const stroke = {
'color': Math.round(Math.random() * 4294967295),
'width': 8, //Math.round((Math.random() * 20) + 4),
'points': process_stroke(state.current_stroke.points)
};
@ -248,6 +249,11 @@ function touchmove(e, state, context) { @@ -248,6 +249,11 @@ function touchmove(e, state, context) {
const old_finger_midpoint = mid_v2(state.touch.first_finger_position, state.touch.second_finger_position);
const new_finger_midpoint = mid_v2(first_finger_position, second_finger_position);
const new_finger_midpoint_canvas = mid_v2(
screen_to_canvas(state, first_finger_position),
screen_to_canvas(state, second_finger_position)
);
const old_finger_distance = dist_v2(state.touch.first_finger_position, state.touch.second_finger_position);
const new_finger_distance = dist_v2(first_finger_position, second_finger_position);
@ -264,8 +270,8 @@ function touchmove(e, state, context) { @@ -264,8 +270,8 @@ function touchmove(e, state, context) {
const scale_by = new_finger_distance / old_finger_distance;
const dz = state.canvas.zoom * (scale_by - 1.0);
const zoom_offset_x = dz * new_finger_midpoint.x;
const zoom_offset_y = dz * new_finger_midpoint.y;
const zoom_offset_x = dz * new_finger_midpoint_canvas.x;
const zoom_offset_y = dz * new_finger_midpoint_canvas.y;
if (config.min_zoom <= state.canvas.zoom * scale_by && state.canvas.zoom * scale_by <= config.max_zoom) {
state.canvas.zoom *= scale_by;
@ -291,6 +297,7 @@ function touchend(e, state, context) { @@ -291,6 +297,7 @@ function touchend(e, state, context) {
const stroke = {
'color': Math.round(Math.random() * 4294967295),
'width': 8, // Math.round((Math.random() * 20) + 4),
'points': process_stroke(state.current_stroke.points)
};

138
client/webgl_shaders.js

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
const vertex_shader_source = `
const stroke_vs_src = `
attribute vec2 a_pos;
attribute vec3 a_color;
@ -19,7 +19,7 @@ const vertex_shader_source = ` @@ -19,7 +19,7 @@ const vertex_shader_source = `
}
`;
const fragment_shader_source = `
const stroke_fs_src = `
precision mediump float;
varying vec3 v_color;
@ -29,6 +29,39 @@ const fragment_shader_source = ` @@ -29,6 +29,39 @@ const fragment_shader_source = `
}
`;
const tquad_vs_src = `
attribute vec2 a_pos;
attribute vec2 a_texcoord;
uniform vec2 u_scale;
uniform vec2 u_res;
uniform vec2 u_translation;
uniform int u_layer;
varying vec2 v_texcoord;
void main() {
vec2 screen01 = (a_pos * u_scale + u_translation) / u_res;
vec2 screen02 = screen01 * 2.0;
screen02.y = 2.0 - screen02.y;
vec2 screen11 = screen02 - 1.0;
v_texcoord = a_texcoord;
gl_Position = vec4(screen11, u_layer, 1);
}
`;
const tquad_fs_src = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_texcoord);
}
`;
function init_webgl(state, context) {
context.canvas = document.querySelector('#c');
context.gl = context.canvas.getContext('webgl', {
@ -37,25 +70,88 @@ function init_webgl(state, context) { @@ -37,25 +70,88 @@ function init_webgl(state, context) {
'antialias': true,
});
context.gl.enable(context.gl.BLEND);
context.gl.blendFunc(context.gl.ONE, context.gl.ONE_MINUS_SRC_ALPHA);
const vertex_shader = create_shader(context.gl, context.gl.VERTEX_SHADER, vertex_shader_source);
const fragment_shader = create_shader(context.gl, context.gl.FRAGMENT_SHADER, fragment_shader_source);
const program = create_program(context.gl, vertex_shader, fragment_shader)
context.program = program;
context.locations['a_pos'] = context.gl.getAttribLocation(program, 'a_pos');
context.locations['a_color'] = context.gl.getAttribLocation(program, 'a_color');
context.locations['u_res'] = context.gl.getUniformLocation(program, 'u_res');
context.locations['u_scale'] = context.gl.getUniformLocation(program, 'u_scale');
context.locations['u_translation'] = context.gl.getUniformLocation(program, 'u_translation');
context.locations['u_layer'] = context.gl.getUniformLocation(program, 'u_layer');
context.buffers['b_pos'] = context.gl.createBuffer();
context.buffers['b_color'] = context.gl.createBuffer();
const gl = context.gl;
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
const stroke_vs = create_shader(gl, gl.VERTEX_SHADER, stroke_vs_src);
const stroke_fs = create_shader(gl, gl.FRAGMENT_SHADER, stroke_fs_src);
const quad_vs = create_shader(gl, gl.VERTEX_SHADER, tquad_vs_src);
const quad_fs = create_shader(gl, gl.FRAGMENT_SHADER, tquad_fs_src);
context.programs['stroke'] = create_program(gl, stroke_vs, stroke_fs);
context.programs['quad'] = create_program(gl, quad_vs, quad_fs);
context.locations['stroke'] = {
'a_pos': gl.getAttribLocation(context.programs['stroke'], 'a_pos'),
'a_color': gl.getAttribLocation(context.programs['stroke'], 'a_color'),
'u_res': gl.getUniformLocation(context.programs['stroke'], 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['stroke'], 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['stroke'], 'u_translation'),
'u_layer': gl.getUniformLocation(context.programs['stroke'], 'u_layer'),
};
context.locations['quad'] = {
'a_pos': gl.getAttribLocation(context.programs['quad'], 'a_pos'),
'a_texcoord': gl.getAttribLocation(context.programs['quad'], 'a_texcoord'),
'u_res': gl.getUniformLocation(context.programs['quad'], 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['quad'], 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['quad'], 'u_translation'),
'u_layer': gl.getUniformLocation(context.programs['quad'], 'u_layer'),
};
context.buffers['stroke'] = {
'b_pos': context.gl.createBuffer(),
'b_color': context.gl.createBuffer(),
};
context.buffers['quad'] = {
'b_pos': context.gl.createBuffer(),
'b_texcoord': context.gl.createBuffer(),
};
context.textures['test'] = gl.createTexture();
// Fill the texture with a 1x1 blue pixel.
gl.bindTexture(gl.TEXTURE_2D, context.textures['test']);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
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);
var image = new Image();
image.src = "http://192.168.100.2/images/3697505915";
image.addEventListener('load', function() {
// Now that the image has loaded make copy it to the texture.
context.quad_positions = [
100, 100,
100, 100 + image.height,
100 + image.width, 100 + image.height,
100 + image.width, 100,
100, 100,
100 + image.width, 100 + image.height,
];
context.quad_texcoords = [
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);
gl.bindTexture(gl.TEXTURE_2D, context.textures['test']);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,gl.UNSIGNED_BYTE, image);
});
const resize_canvas = (entries) => {
// https://www.khronos.org/webgl/wiki/HandlingHighDPI

Loading…
Cancel
Save