import js.dom import math const ( vert_code = 'attribute vec3 position;uniform mat4 Pmatrix;uniform mat4 Vmatrix;uniform mat4 Mmatrix;attribute vec3 color;varying vec3 vColor;void main(void) {gl_Position = Pmatrix * Vmatrix * Mmatrix * vec4(position,1.);vColor = color;} ' frag_code = 'precision mediump float;varying vec3 vColor;void main(void) {gl_FragColor = vec4(vColor, 1.);} ' vertices = [ f32(-1), -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, -1, ] colors = [ f32(5), 3, 7, 5, 3, 7, 5, 3, 7, 5, 3, 7, 1, 1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 3, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, ] indices = [ u16(0), 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, ] amortization = 0.95 ) fn get_webgl() (JS.HTMLCanvasElement, JS.WebGLRenderingContext) { JS.console.log(dom.document) elem := dom.document.getElementById('myCanvas'.str) or { panic('cannot get canvas') } match elem { JS.HTMLCanvasElement { webgl := elem.getContext('experimental-webgl'.str, js_undefined()) or { panic('context not found') } match webgl { JS.WebGLRenderingContext { return elem, webgl } else { panic('cannot get webgl') } } } else { panic('not an canvas') } } } fn get_projection(angle f64, a f64, z_min f64, z_max f64) []f64 { ang := math.tan((angle * 0.5) * math.pi / 180) return [ 0.5 / ang, 0, 0, 0, 0, 0.5 * a / ang, 0, 0, 0, 0, -(z_max + z_min) / (z_max - z_min), -1, 0, 0, (-2 * z_max * z_min) / (z_max - z_min), 0, ] } fn JS.Math.cos(JS.Number) JS.Number fn JS.Math.sin(JS.Number) JS.Number fn rotate_x(mut m []f64, angle f64) { c := math.cos(angle) s := math.sin(angle) mv1 := m[1] mv5 := m[5] mv9 := m[9] m[1] = m[1] * c - m[2] * s m[5] = m[5] * c - m[6] * s m[9] = m[9] * c - m[10] * s m[2] = m[2] * c + mv1 * s m[6] = m[6] * c + mv5 * s m[10] = m[10] * c + mv9 * s } fn rotate_y(mut m []f64, angle f64) { c := math.cos(angle) s := math.sin(angle) mv0 := m[0] mv4 := m[4] mv8 := m[8] m[0] = c * m[0] + s * m[2] m[4] = c * m[4] + s * m[6] m[8] = c * m[8] + s * m[10] m[2] = c * m[2] - s * mv0 m[6] = c * m[6] - s * mv4 m[10] = c * m[10] - s * mv8 } struct State { mut: drag bool gl JS.WebGLRenderingContext canvas JS.HTMLCanvasElement old_x f64 old_y f64 dx f64 dy f64 theta f64 phi f64 time_old f64 mo_matrix []f64 view_matrix []f64 proj_matrix []f64 pmatrix JS.WebGLUniformLocation vmatrix JS.WebGLUniformLocation mmatrix JS.WebGLUniformLocation index_buffer JS.WebGLBuffer } fn animate(mut state State, time f64) { if !state.drag { state.dx = state.dx * amortization state.dy = state.dy * amortization state.theta += state.dx state.phi += state.dy } state.mo_matrix[0] = 1 state.mo_matrix[1] = 0 state.mo_matrix[2] = 0 state.mo_matrix[3] = 0 state.mo_matrix[4] = 0 state.mo_matrix[5] = 1 state.mo_matrix[6] = 0 state.mo_matrix[7] = 0 state.mo_matrix[8] = 0 state.mo_matrix[9] = 0 state.mo_matrix[10] = 1 state.mo_matrix[11] = 0 state.mo_matrix[12] = 0 state.mo_matrix[13] = 0 state.mo_matrix[14] = 0 state.mo_matrix[15] = 1 // println('${state.theta} ${state.phi}') rotate_x(mut state.mo_matrix, state.phi) rotate_y(mut state.mo_matrix, state.theta) state.time_old = time state.gl.enable(dom.gl_depth_test()) state.gl.clearColor(0.5, 0.5, 0.5, 0.9) state.gl.clearDepth(1.0) state.gl.viewport(0.0, 0.0, state.canvas.width, state.canvas.height) state.gl.clear(JS.Number(int(dom.gl_color_buffer_bit()) | int(dom.gl_depth_buffer_bit()))) state.gl.uniformMatrix4fv(state.pmatrix, JS.Boolean(false), state.proj_matrix.to_number_array()) state.gl.uniformMatrix4fv(state.vmatrix, JS.Boolean(false), state.view_matrix.to_number_array()) state.gl.uniformMatrix4fv(state.mmatrix, JS.Boolean(false), state.mo_matrix.to_number_array()) state.gl.bindBuffer(dom.gl_element_array_buffer(), state.index_buffer) state.gl.drawElements(dom.gl_triangles(), indices.len, dom.gl_unsigned_short(), 0) dom.window().requestAnimationFrame(fn [mut state] (time JS.Number) { animate(mut state, f64(time)) }) } fn main() { canvas, gl := get_webgl() vertex_buffer := gl.createBuffer()? gl.bindBuffer(dom.gl_array_buffer(), vertex_buffer) gl.bufferData(dom.gl_array_buffer(), float32_array(vertices), dom.gl_static_draw()) color_buffer := gl.createBuffer()? gl.bindBuffer(dom.gl_array_buffer(), color_buffer) gl.bufferData(dom.gl_array_buffer(), float32_array(colors), dom.gl_static_draw()) index_buffer := gl.createBuffer()? gl.bindBuffer(dom.gl_element_array_buffer(), index_buffer) gl.bufferData(dom.gl_element_array_buffer(), uint16_array(indices), dom.gl_static_draw()) vert_shader := gl.createShader(dom.gl_vertex_shader())? gl.shaderSource(vert_shader, vert_code.str) gl.compileShader(vert_shader) if !bool(JS.Boolean(gl.getShaderParameter(vert_shader, dom.gl_compile_status()))) { panic('An error occurred when compiling vertex shader: ${string(gl.getShaderInfoLog(vert_shader))}') } frag_shader := gl.createShader(dom.gl_fragment_shader())? gl.shaderSource(frag_shader, frag_code.str) gl.compileShader(frag_shader) if !bool(JS.Boolean(gl.getShaderParameter(frag_shader, dom.gl_compile_status()))) { panic('An error occurred when compiling fragment shader: ${string(gl.getShaderInfoLog(frag_shader))}') } shader_program := gl.createProgram()? gl.attachShader(shader_program, vert_shader) gl.attachShader(shader_program, frag_shader) gl.linkProgram(shader_program) if !bool(JS.Boolean(gl.getProgramParameter(shader_program, dom.gl_link_status()))) { panic('unable to initialize the shader program: ${string(gl.getProgramInfoLog(shader_program))}') } pmatrix := gl.getUniformLocation(shader_program, 'Pmatrix'.str)? vmatrix := gl.getUniformLocation(shader_program, 'Vmatrix'.str)? mmatrix := gl.getUniformLocation(shader_program, 'Mmatrix'.str)? gl.bindBuffer(dom.gl_array_buffer(), vertex_buffer) position := gl.getAttribLocation(shader_program, 'position'.str) gl.vertexAttribPointer(position, JS.Number(3), dom.gl_float(), JS.Boolean(false), JS.Number(0), JS.Number(0)) gl.enableVertexAttribArray(position) gl.bindBuffer(dom.gl_array_buffer(), color_buffer) color := gl.getAttribLocation(shader_program, 'color'.str) gl.vertexAttribPointer(color, JS.Number(3), dom.gl_float(), JS.Boolean(false), JS.Number(0), JS.Number(0)) gl.enableVertexAttribArray(color) gl.useProgram(shader_program) mut proj_matrix := get_projection(40.0, f64(canvas.width) / f64(canvas.height), 1.0, 100.0) mut mo_matrix := [f64(1), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] mut view_matrix := [f64(1), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] view_matrix[14] = view_matrix[14] - 6 mut state := State{false, gl, canvas, 0, 0, 0, 0, 0, 0, 0, mo_matrix, view_matrix, proj_matrix, pmatrix, vmatrix, mmatrix, index_buffer} canvas.addEventListener('mousedown'.str, fn [mut state] (e JS.Event) { state.drag = true match e { JS.MouseEvent { state.old_x = f64(e.pageX) state.old_y = f64(e.pageY) e.preventDefault() } else {} } }, JS.EventListenerOptions{}) canvas.addEventListener('mouseup'.str, fn [mut state] (e JS.Event) { state.drag = false }, JS.EventListenerOptions{}) canvas.addEventListener('mouseout'.str, fn [mut state] (e JS.Event) { state.drag = false }, JS.EventListenerOptions{}) canvas.addEventListener('mousemove'.str, fn [mut state] (e JS.Event) { if !state.drag { return } match e { JS.MouseEvent { state.dx = (f64(e.pageX) - state.old_x) * 2.0 * math.pi / f64(state.canvas.width) state.dy = (f64(e.pageY) - state.old_y) * 2.0 * math.pi / f64(state.canvas.height) state.theta += state.dx state.phi += state.dy state.old_x = f64(e.pageX) state.old_y = f64(e.pageY) e.preventDefault() } else { panic('not a mouse event??') } } }, JS.EventListenerOptions{}) animate(mut state, 0) }