From c4648adfb549064d391d9d5b38eff0b0577072a8 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Wed, 2 Sep 2020 13:38:39 -0500 Subject: [PATCH] getting basic webgl rendering working --- src/main.onyx | 172 +++++++++++++++++++++++++++++++++++++---- src/shaders/basic.frag | 9 +++ src/shaders/basic.vert | 10 +++ webgl.js | 14 ++++ 4 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 src/shaders/basic.frag create mode 100644 src/shaders/basic.vert diff --git a/src/main.onyx b/src/main.onyx index 9093f5b..25745e8 100644 --- a/src/main.onyx +++ b/src/main.onyx @@ -3,38 +3,176 @@ package main #include_file "core/std/js" #include_file "core/js/webgl" -use package builtin use package core use package gl as gl Vec2 :: struct { - x : i32; - y : i32; + x : f32; + y : f32; } -print_vec2 :: proc (v: Vec2) #add_overload print { - print("Vec2("); - print(v.x); - print(", "); - print(v.y); - print(")"); +// print_vec2 :: proc (v: Vec2) #add_overload print { +// print("Vec2("); +// print(v.x); +// print(", "); +// print(v.y); +// print(")"); +// } + + +GameState :: struct { + dummy : i32; } -main :: proc (args: [] cstring) { - arr : [..] Vec2; - array_init(^arr); - defer array_free(^arr); +update :: proc (gs: ^GameState) { +} + +draw :: proc (gs: ^GameState) { + gl.clearColor(1.0f, 1.0f, 1.0f, 1.0f); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.bindVertexArray(vao); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, NUM_QUADS); + gl.bindVertexArray(-1); +} + +// This procedure is called asynchronously from JS. +// It provides us with the pointer game it in main. +new_frame_callback :: proc (gs: ^GameState) #export { + update(gs); + draw(gs); +} + +NUM_QUADS :: 500 +vao : gl.GLVertexArrayObject + +compile_shader :: proc (shader_type: gl.GLenum, source: cstring) -> gl.GLShader { + shader := gl.createShader(shader_type); + gl.shaderSource(shader, string_make(source)); + gl.compileShader(shader); + + if cast(i32) gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0 { + gl.printShaderInfoLog(shader); + return -1; + } + + return shader; +} + +link_program :: proc (vertex_shader: gl.GLShader, frag_shader: gl.GLShader) -> gl.GLProgram { + program := gl.createProgram(); + gl.attachShader(program, vertex_shader); + gl.attachShader(program, frag_shader); + gl.linkProgram(program); + + if cast(i32) gl.getProgramParameter(program, gl.LINK_STATUS) == 0 { + gl.printProgramInfoLog(program); + return -1; + } + + return program; +} + +setup_drawing :: proc { + vao = gl.createVertexArray(); + gl.bindVertexArray(vao); + + vertex_data : [8] gl.GLfloat; + vertex_data[2 * 0 + 0] = 0.0f; + vertex_data[2 * 0 + 1] = 0.0f; + vertex_data[2 * 1 + 0] = 0.0f; + vertex_data[2 * 1 + 1] = 0.1f; + vertex_data[2 * 2 + 0] = 0.1f; + vertex_data[2 * 2 + 1] = 0.1f; + vertex_data[2 * 3 + 0] = 0.1f; + vertex_data[2 * 3 + 1] = 0.0f; - for i: 0, 100 do array_push(^arr, Vec2.{ i * 2, i * 3 }); + vertex_buffer := gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); + gl.bufferData(gl.ARRAY_BUFFER, Buffer.{ + count = sizeof [8] gl.GLfloat, + data = cast(^void) vertex_data + }, gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 2 * sizeof gl.GLfloat, 0); + + pos_buffer := gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, pos_buffer); + + positions : [..] Vec2; + array_init(^positions, NUM_QUADS); + defer array_free(^positions); + + for i: 0, NUM_QUADS { + array_push(^positions, Vec2.{ + x = cast(f32) random_between(0, 50) / 50.0f, + y = cast(f32) random_between(0, 50) / 50.0f, + }); + } + + gl.bufferData(gl.ARRAY_BUFFER, Buffer.{ + count = NUM_QUADS * sizeof Vec2, + data = cast(^void) positions.data, + }, gl.DYNAMIC_DRAW); + + gl.enableVertexAttribArray(1); + gl.vertexAttribDivisor(1, 1); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 2 * sizeof gl.GLfloat, 0); + + index_data : [6] gl.GLubyte; + index_data[0] = cast(gl.GLubyte) 0; + index_data[1] = cast(gl.GLubyte) 1; + index_data[2] = cast(gl.GLubyte) 2; + index_data[3] = cast(gl.GLubyte) 0; + index_data[4] = cast(gl.GLubyte) 2; + index_data[5] = cast(gl.GLubyte) 3; + + index_buffer := gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, Buffer.{ + count = sizeof [6] gl.GLubyte, + data = cast(^void) index_data, + }, gl.STATIC_DRAW); + + gl.bindVertexArray(-1); + + vertex_shader := compile_shader(gl.VERTEX_SHADER, #file_contents "./src/shaders/basic.vert"); + fragment_shader := compile_shader(gl.FRAGMENT_SHADER, #file_contents "./src/shaders/basic.frag"); + program := link_program(vertex_shader, fragment_shader); + gl.useProgram(program); + + u_proj_loc := gl.getUniformLocation(program, "u_proj"); + proj_mat : [9] gl.GLfloat; + proj_mat[3 * 0 + 0] = 2.0f; + proj_mat[3 * 0 + 1] = 0.0f; + proj_mat[3 * 0 + 2] = 0.0f; + proj_mat[3 * 1 + 0] = 0.0f; + proj_mat[3 * 1 + 1] = -2.0f; + proj_mat[3 * 1 + 2] = 0.0f; + proj_mat[3 * 2 + 0] = -1.0f; + proj_mat[3 * 2 + 1] = 1.0f; + proj_mat[3 * 2 + 2] = 1.0f; + + gl.uniformMatrix3(u_proj_loc, false, proj_mat); +} + + +main :: proc (args: [] cstring) { print("Hello, World!\n"); - print_array(^arr, "\n"); if !gl.init("gamecanvas") { print("Failed to initialize GL canvas."); return; } - gl.clearColor(1.0f, 0.0f, 1.0f, 1.0f); - gl.clear(gl.COLOR_BUFFER_BIT); + setup_drawing(); + + gs := cast(^GameState) calloc(sizeof GameState); + game_launch(gs); } + + + +game_launch :: proc (gs: ^GameState) #foreign "game" "launch" --- + diff --git a/src/shaders/basic.frag b/src/shaders/basic.frag new file mode 100644 index 0000000..5a5229b --- /dev/null +++ b/src/shaders/basic.frag @@ -0,0 +1,9 @@ +#version 300 es + +precision mediump float; + +out vec4 fragColor; + +void main() { + fragColor = vec4(1, 0, 1, 1); +} diff --git a/src/shaders/basic.vert b/src/shaders/basic.vert new file mode 100644 index 0000000..4b52d22 --- /dev/null +++ b/src/shaders/basic.vert @@ -0,0 +1,10 @@ +#version 300 es + +layout(location = 0) in vec2 a_vert_pos; +layout(location = 1) in vec2 a_pos; + +uniform mat3 u_proj; + +void main() { + gl_Position = vec4(u_proj * vec3(a_vert_pos + a_pos, 1), 1); +} \ No newline at end of file diff --git a/webgl.js b/webgl.js index 16b971e..1199e89 100644 --- a/webgl.js +++ b/webgl.js @@ -11,6 +11,7 @@ WebGl_Wasm = { this.renderbuffers = []; this.textures = []; this.uniformlocs = []; + this.vertexArrays = []; this.canvas = document.getElementById(canvasname); if (this.canvas == null) return 0; @@ -34,6 +35,8 @@ WebGl_Wasm = { bindFramebuffer(target, framebuffer) { this.gl.bindFramebuffer(target, this.framebuffers[framebuffer]); }, bindRenderbuffer(target, renderbuffer) { this.gl.bindRenderbuffer(target, this.renderbuffers[renderbuffer]); }, bindTexture(target, texture) { this.gl.bindTexture(target, this.textures[texture]); }, + bindVertexArray(vertexArray) { this.gl.bindVertexArray(this.vertexArrays[vertexArray]); }, + blendColor(red, green, blue, alpha) { this.gl.blendColor(red, green, blue, alpha); }, blendEquation(mode) { this.gl.blendEquation(mode); }, blendEquationSeparate(modeRGB, modeAlpha) { this.gl.blendEquationSeparate(modeRGB, modeAlpha); }, @@ -118,6 +121,13 @@ WebGl_Wasm = { this.textures.push(texture); return this.textures.length - 1; }, + createVertexArray() { + const vao = this.gl.createVertexArray(); + if (vao == null) return -1; + + this.vertexArrays.push(vao); + return this.vertexArrays.length - 1; + }, cullFace(mode) { this.gl.cullFace(mode); }, deleteBuffer(buffer) { this.gl.deleteBuffer(this.buffers[buffer]); }, deleteFramebuffer(framebuffer) { this.gl.deleteFramebuffer(this.framebuffers[framebuffer]); }, @@ -125,6 +135,7 @@ WebGl_Wasm = { deleteRenderbuffer(renderbuffer) { this.gl.deleteRenderbuffer(this.renderbuffers[renderbuffer]); }, deleteShader(shader) { this.gl.deleteShader(this.shaders[shader]); }, deleteTexture(texture) { this.gl.deleteTexture(this.textures[texture]); }, + deleteVertexArray(vertexArray) { this.gl.deleteVertexArray(this.vertexArrays[vertexArray]); }, depthFunc(func) { this.gl.depthFunc(func); }, depthMask(flag) { this.gl.depthMask(flag); }, depthRange(znear, zfar) { this.gl.depthRange(znear, zfar); }, @@ -132,7 +143,9 @@ WebGl_Wasm = { disable(cap) { this.gl.disable(cap); }, disableVertexAttribArray(index) { this.gl.disableVertexAttribArray(index); }, drawArrays(mode, first, count) { this.gl.drawArrays(mode, first, count); }, + drawArraysInstanced(mode, first, count, instanceCount) { this.gl.drawArraysInstanced(mode, first, count, instanceCount); }, drawElements(mode, count, type, offset) { this.gl.drawElements(mode, count, type, offset); }, + drawElementsInstanced(mode, count, type, offset, instanceCount) { this.gl.drawElementsInstanced(mode, count, type, offset, instanceCount); }, enable(cap) { this.gl.enable(cap); }, enableVertexAttribArray(index) { this.gl.enableVertexAttribArray(index); }, finish() { this.gl.finish(); }, @@ -261,6 +274,7 @@ WebGl_Wasm = { vertexAttrib3f(idx, x, y, z) { this.gl.vertexAttrib3f(idx, x, y, z); }, vertexAttrib4f(idx, x, y, z, w) { this.gl.vertexAttrib4f(idx, x, y, z, w); }, vertexAttribPointer(idx, size, type, normalized, stride, offset) { this.gl.vertexAttribPointer(idx, size, type, normalized, stride, offset); }, + vertexAttribDivisor(idx, divisor) { this.gl.vertexAttribDivisor(idx, divisor); }, viewport(x, y, width, height) { this.gl.viewport(x, y, width, height); }, }; -- 2.25.1