From 2d1d3b60342f5d0622aa4057565a4bdbfdfea051 Mon Sep 17 00:00:00 2001 From: Maciej Samborski Date: Tue, 10 Dec 2024 21:47:42 +0100 Subject: [PATCH] Added Attribute class and color attribute --- graphics.js | 104 +++++++++++++++++++++++ graphics.ts | 139 +++++++++++++++++++++++++++++++ script.js | 196 +++++++++++++++---------------------------- script.ts | 234 +++++++++++++++++----------------------------------- 4 files changed, 384 insertions(+), 289 deletions(-) create mode 100644 graphics.js create mode 100644 graphics.ts diff --git a/graphics.js b/graphics.js new file mode 100644 index 0000000..078d4f1 --- /dev/null +++ b/graphics.js @@ -0,0 +1,104 @@ +function createShader(ctx, type, source) { + var shader = ctx.createShader(type); + if (!shader) { + throw new Error("Couldn't create shader: " + type); + } + ctx.shaderSource(shader, source); + ctx.compileShader(shader); + var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); + if (!success) { + console.log(ctx.getShaderInfoLog(shader)); + ctx.deleteShader(shader); + throw new Error("Couldn't compile shader: " + type); + } + return shader; +} +function createProgram(ctx, vertexShaderSource, fragmentShaderSource) { + var program = ctx.createProgram(); + if (vertexShaderSource !== null) { + const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource); + ctx.attachShader(program, vs); + } + if (fragmentShaderSource !== null) { + const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource); + ctx.attachShader(program, fs); + } + ctx.linkProgram(program); + var success = ctx.getProgramParameter(program, ctx.LINK_STATUS); + if (!success) { + console.log(ctx.getProgramInfoLog(program)); + ctx.deleteProgram(program); + throw new Error("Failed to create program!"); + } + return program; +} +class Graphics { + ctx; + program; + attribs = new Map(); + uniforms = new Map(); + vao; + constructor(ctx, vs, fs) { + this.ctx = ctx; + this.program = createProgram(ctx, vs, fs); + this.vao = ctx.createVertexArray(); + ctx.bindVertexArray(this.vao); + ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.useProgram(this.program); + } + clear(r, g, b, a) { + this.ctx.clearColor(r, g, b, a); + this.ctx.clear(this.ctx.COLOR_BUFFER_BIT); + } + createAttribute(name) { + const attrib = new Attribute(this.ctx, this.program, name); + this.attribs.set(name, attrib); + return attrib; + } + getAttribute(name) { + const attrib = this.attribs.get(name); + if (attrib === undefined) + throw new Error("Tried to get uninitialized attribute: " + name); + return attrib; + } + createUniform(name) { + const loc = this.ctx.getUniformLocation(this.program, name); + if (loc === null) + throw new Error("Couldn't get location for uniform: " + name); + this.uniforms.set(name, loc); + } + getUniform(name) { + const loc = this.uniforms.get(name); + if (loc === undefined) + throw new Error("Tried to get uninitialized uniform: " + name); + return loc; + } +} +class Attribute { + loc; + buffer; + // TODO: maybe use undefined as default value? + size = 0; + type = 0; + normalized = false; + stride = 0; + offset = 0; + constructor(ctx, program, name) { + this.loc = ctx.getAttribLocation(program, name); + this.buffer = ctx.createBuffer(); + ctx.enableVertexAttribArray(this.loc); + } + format(size, type, normalized, stride, offset) { + this.size = size; + this.type = type; + this.normalized = normalized; + this.stride = stride; + this.offset = offset; + } + data(ctx, data, usage) { + ctx.bindBuffer(ctx.ARRAY_BUFFER, this.buffer); + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(data), usage); + ctx.vertexAttribPointer(this.loc, this.size, this.type, this.normalized, this.stride, this.offset); + } +} +export { Graphics, Attribute }; diff --git a/graphics.ts b/graphics.ts new file mode 100644 index 0000000..db646d1 --- /dev/null +++ b/graphics.ts @@ -0,0 +1,139 @@ +function createShader(ctx: WebGL2RenderingContext, type: GLenum, source: string): WebGLShader { + var shader = ctx.createShader(type); + if (!shader) { + throw new Error("Couldn't create shader: " + type); + } + + ctx.shaderSource(shader, source); + ctx.compileShader(shader); + + var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); + if (!success) { + console.log(ctx.getShaderInfoLog(shader)); + ctx.deleteShader(shader); + throw new Error("Couldn't compile shader: " + type); + } + + return shader; +} + +function createProgram(ctx: WebGL2RenderingContext, vertexShaderSource: string | null, fragmentShaderSource: string | null): WebGLProgram { + var program = ctx.createProgram(); + + if (vertexShaderSource !== null) { + const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource); + ctx.attachShader(program, vs); + } + + if (fragmentShaderSource !== null) { + const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource); + ctx.attachShader(program, fs); + } + + ctx.linkProgram(program); + + var success = ctx.getProgramParameter(program, ctx.LINK_STATUS); + if (!success) { + console.log(ctx.getProgramInfoLog(program)); + ctx.deleteProgram(program); + throw new Error("Failed to create program!"); + } + + return program; +} + +class Graphics { + ctx: WebGL2RenderingContext; + program: WebGLProgram; + attribs: Map = new Map(); + uniforms: Map = new Map(); + vao: WebGLVertexArrayObject; + constructor(ctx: WebGL2RenderingContext, vs: string, fs: string) { + this.ctx = ctx; + this.program = createProgram(ctx, vs, fs); + this.vao = ctx.createVertexArray(); + + ctx.bindVertexArray(this.vao); + ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); + ctx.useProgram(this.program); + } + + clear(r: number, g: number, b: number, a: number) { + this.ctx.clearColor(r, g, b, a); + this.ctx.clear(this.ctx.COLOR_BUFFER_BIT); + } + + createAttribute(name: string): Attribute { + const attrib = new Attribute(this.ctx, this.program, name); + this.attribs.set(name, attrib); + return attrib; + } + + getAttribute(name: string): Attribute { + const attrib = this.attribs.get(name); + + if (attrib === undefined) + throw new Error("Tried to get uninitialized attribute: " + name); + + return attrib; + } + + createUniform(name: string) { + const loc = this.ctx.getUniformLocation(this.program, name); + + if (loc === null) + throw new Error("Couldn't get location for uniform: " + name); + + this.uniforms.set(name, loc); + } + + getUniform(name: string): WebGLUniformLocation { + const loc = this.uniforms.get(name); + + if (loc === undefined) + throw new Error("Tried to get uninitialized uniform: " + name); + + return loc; + } +} + +class Attribute { + loc: GLint; + buffer: WebGLBuffer; + + // TODO: maybe use undefined as default value? + size: GLint = 0; + type: GLenum = 0; + normalized: GLboolean = false; + stride: GLsizei = 0; + offset: GLintptr = 0; + + constructor(ctx: WebGL2RenderingContext, program: WebGLProgram, name: string) { + this.loc = ctx.getAttribLocation(program, name); + this.buffer = ctx.createBuffer(); + + ctx.enableVertexAttribArray(this.loc); + } + + format( + size: GLint, + type: GLenum, + normalized: GLboolean, + stride: GLsizei, + offset: GLintptr) + { + this.size = size; + this.type = type; + this.normalized = normalized; + this.stride = stride; + this.offset = offset; + } + + data(ctx: WebGL2RenderingContext, data: Array, usage: GLenum) { + ctx.bindBuffer(ctx.ARRAY_BUFFER, this.buffer); + ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array(data), usage); + ctx.vertexAttribPointer(this.loc, this.size, this.type, this.normalized, this.stride, this.offset); + } +} + +export { Graphics, Attribute } diff --git a/script.js b/script.js index 6759376..d53d3c2 100644 --- a/script.js +++ b/script.js @@ -1,103 +1,72 @@ import { Vec2 } from "./common.js"; -var vertexShaderSource = `#version 300 es +import { Graphics } from "./graphics.js"; +const vertexShader = `#version 300 es - in vec2 a_position; + in vec4 a_color; + out vec4 color; uniform vec2 u_resolution; void main() { vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; + color = a_color; + gl_Position = vec4(clipSpace.xy, 0.0, 1.0); } `; -var fragmentShaderSource = ` #version 300 es +const fragmentShader = `#version 300 es precision highp float; + in vec4 color; out vec4 outColor; - uniform vec4 u_color; void main() { - outColor = u_color; + outColor = color; } `; -function createShader(ctx, type, source) { - var shader = ctx.createShader(type); - if (!shader) { - throw new Error("Couldn't create shader: " + type); - } - ctx.shaderSource(shader, source); - ctx.compileShader(shader); - var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); - if (!success) { - console.log(ctx.getShaderInfoLog(shader)); - ctx.deleteShader(shader); - throw new Error("Couldn't compile shader: " + type); - } - return shader; -} -function createProgram(ctx, vertexShaderSource, fragmentShaderSource) { - var program = ctx.createProgram(); - if (vertexShaderSource !== null) { - const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource); - ctx.attachShader(program, vs); - } - if (fragmentShaderSource !== null) { - const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource); - ctx.attachShader(program, fs); - } - ctx.linkProgram(program); - var success = ctx.getProgramParameter(program, ctx.LINK_STATUS); - if (!success) { - console.log(ctx.getProgramInfoLog(program)); - ctx.deleteProgram(program); - throw new Error("Failed to create program!"); - } - return program; -} function initializeContext(canvasId) { const canvas = document.getElementById(canvasId); const ctx = canvas.getContext("webgl2", { antialias: false }); return ctx; } function drawTriangle(gfx, positions) { - const positionLoc = gfx.attribs.get("a_position"); - if (positionLoc === undefined) - throw new Error("`drawTriangle` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); const points = [ positions[0].x, positions[0].y, positions[1].x, positions[1].y, positions[2].x, positions[2].y, ]; - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + ]; + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); - gfx.ctx.disableVertexAttribArray(positionLoc); } function drawTriangleExts(gfx, position, exts) { - const positionLoc = gfx.attribs.get("a_position"); - if (positionLoc === undefined) - throw new Error("`drawTriangle` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); const points = [ position.x, position.y, position.x + exts.x, position.y, position.x, position.y + exts.y, ]; - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + ]; + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); - gfx.ctx.disableVertexAttribArray(positionLoc); } function drawRectangle(gfx, position, exts) { - const positionLoc = gfx.attribs.get("a_position"); - if (positionLoc === undefined) - throw new Error("`drawRectange` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); const points = [ position.x, position.y, position.x + exts.x, position.y, @@ -106,18 +75,19 @@ function drawRectangle(gfx, position, exts) { position.x + exts.x, position.y, position.x, position.y + exts.y, ]; - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + ]; + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - gfx.ctx.disableVertexAttribArray(positionLoc); } function drawCircle(gfx, position, radius) { - const positionLoc = gfx.attribs.get("a_position"); - if (positionLoc === undefined) - throw new Error("`drawCircle` requires attribute `a_position` to be defined"); const points = new Array(); const precision = 40; const angle = 2.0 * Math.PI / precision; @@ -136,77 +106,29 @@ function drawCircle(gfx, position, radius) { } } function drawLine(gfx, A, B) { - const positionLoc = gfx.attribs.get("a_position"); - if (positionLoc === undefined) - throw new Error("`drawTriangle` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); let points = [ A.x, A.y, B.x, B.y, ]; - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color = [ + 1, 0, 0, 1, + 0, 0, 1, 1, + ]; + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.LINES, 0, 2); - gfx.ctx.disableVertexAttribArray(positionLoc); -} -class Graphics { - ctx; - program; - buffer; - vao; - attribs = new Map(); - uniforms = new Map(); - constructor(ctx) { - this.ctx = ctx; - this.program = createProgram(ctx, vertexShaderSource, fragmentShaderSource); - this.buffer = ctx.createBuffer(); - this.vao = ctx.createVertexArray(); - ctx.bindVertexArray(this.vao); - ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.useProgram(this.program); - } - clear(r, g, b, a) { - this.ctx.clearColor(r, g, b, a); - this.ctx.clear(this.ctx.COLOR_BUFFER_BIT); - } - createAttribute(name) { - const loc = this.ctx.getAttribLocation(this.program, name); - this.attribs.set(name, loc); - } - createUniform(name) { - const loc = this.ctx.getUniformLocation(this.program, name); - if (loc === null) - throw new Error("Couldn't get location for uniform: " + name); - this.uniforms.set(name, loc); - } - getUniform(name) { - const loc = this.uniforms.get(name); - if (loc === undefined) - throw new Error("Tried to get uninitialized uniform: " + name); - return loc; - } } function fullscreenCanvas(id) { const canvas = document.getElementById(id); canvas.width = window.innerWidth; canvas.height = window.innerHeight; } -function draw() { - const canvasId = "game"; - fullscreenCanvas(canvasId); - const ctx = initializeContext(canvasId); - if (ctx === null) - return; - const gfx = new Graphics(ctx); - gfx.createAttribute("a_position"); - gfx.createUniform("u_resolution"); - gfx.createUniform("u_color"); - const position = new Vec2(ctx.canvas.width / 2 - 100, ctx.canvas.height / 2 - 100); +function draw(gfx) { + const position = new Vec2(gfx.ctx.canvas.width / 2 - 100, gfx.ctx.canvas.height / 2 - 100); gfx.clear(0, 0, 0, 0); - ctx.uniform2f(gfx.getUniform("u_resolution"), ctx.canvas.width, ctx.canvas.height); - ctx.uniform4f(gfx.getUniform("u_color"), Math.random(), Math.random(), Math.random(), 1); + gfx.ctx.uniform2f(gfx.getUniform("u_resolution"), gfx.ctx.canvas.width, gfx.ctx.canvas.height); drawRectangle(gfx, position, new Vec2(200, 200)); drawTriangle(gfx, [new Vec2(100, 100), new Vec2(200, 100), new Vec2(100, 200)]); drawTriangleExts(gfx, new Vec2(200, 200), new Vec2(-100, -100)); @@ -214,6 +136,20 @@ function draw() { drawLine(gfx, new Vec2(100, 600), new Vec2(600, 600)); } (() => { - draw(); - window.onresize = draw; + const canvasId = "game"; + fullscreenCanvas(canvasId); + const ctx = initializeContext(canvasId); + if (ctx === null) + return; + const gfx = new Graphics(ctx, vertexShader, fragmentShader); + const a_position = gfx.createAttribute("a_position"); + a_position.format(2, gfx.ctx.FLOAT, false, 0, 0); + const a_color = gfx.createAttribute("a_color"); + a_color.format(4, gfx.ctx.FLOAT, false, 0, 0); + gfx.createUniform("u_resolution"); + draw(gfx); + window.onresize = () => { + fullscreenCanvas(canvasId); + draw(gfx); + }; })(); diff --git a/script.ts b/script.ts index ad42b51..8049952 100644 --- a/script.ts +++ b/script.ts @@ -1,75 +1,35 @@ import { Vec2 } from "./common.js"; +import { Graphics } from "./graphics.js"; -var vertexShaderSource = +const vertexShader = `#version 300 es - in vec2 a_position; + in vec4 a_color; + out vec4 color; uniform vec2 u_resolution; void main() { vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; + color = a_color; + gl_Position = vec4(clipSpace.xy, 0.0, 1.0); } `; -var fragmentShaderSource = - ` #version 300 es +const fragmentShader = + `#version 300 es precision highp float; + in vec4 color; out vec4 outColor; - uniform vec4 u_color; void main() { - outColor = u_color; + outColor = color; } `; -function createShader(ctx: WebGL2RenderingContext, type: GLenum, source: string): WebGLShader { - var shader = ctx.createShader(type); - if (!shader) { - throw new Error("Couldn't create shader: " + type); - } - - ctx.shaderSource(shader, source); - ctx.compileShader(shader); - - var success = ctx.getShaderParameter(shader, ctx.COMPILE_STATUS); - if (!success) { - console.log(ctx.getShaderInfoLog(shader)); - ctx.deleteShader(shader); - throw new Error("Couldn't compile shader: " + type); - } - - return shader; -} - -function createProgram(ctx: WebGL2RenderingContext, vertexShaderSource: string | null, fragmentShaderSource: string | null): WebGLProgram { - var program = ctx.createProgram(); - - if (vertexShaderSource !== null) { - const vs = createShader(ctx, ctx.VERTEX_SHADER, vertexShaderSource); - ctx.attachShader(program, vs); - } - - if (fragmentShaderSource !== null) { - const fs = createShader(ctx, ctx.FRAGMENT_SHADER, fragmentShaderSource); - ctx.attachShader(program, fs); - } - - ctx.linkProgram(program); - - var success = ctx.getProgramParameter(program, ctx.LINK_STATUS); - if (!success) { - console.log(ctx.getProgramInfoLog(program)); - ctx.deleteProgram(program); - throw new Error("Failed to create program!"); - } - - return program; -} - function initializeContext(canvasId: string): WebGL2RenderingContext | null { const canvas = document.getElementById(canvasId) as HTMLCanvasElement; const ctx = canvas.getContext("webgl2", {antialias: false}); @@ -79,10 +39,8 @@ function initializeContext(canvasId: string): WebGL2RenderingContext | null { } function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2]) { - const positionLoc = gfx.attribs.get("a_position"); - - if (positionLoc === undefined) - throw new Error("`drawTriangle` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); const points: Array = [ positions[0].x, positions[0].y, @@ -90,20 +48,20 @@ function drawTriangle(gfx: Graphics, positions: [Vec2, Vec2, Vec2]) { positions[2].x, positions[2].y, ] - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color: Array = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + ]; + + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); - gfx.ctx.disableVertexAttribArray(positionLoc); } function drawTriangleExts(gfx: Graphics, position: Vec2, exts: Vec2) { - const positionLoc = gfx.attribs.get("a_position"); - - if (positionLoc === undefined) - throw new Error("`drawTriangle` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); const points: Array = [ position.x, position.y, @@ -111,20 +69,20 @@ function drawTriangleExts(gfx: Graphics, position: Vec2, exts: Vec2) { position.x, position.y + exts.y, ] - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color: Array = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + ]; + + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3); - gfx.ctx.disableVertexAttribArray(positionLoc); } function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2) { - const positionLoc = gfx.attribs.get("a_position"); - - if (positionLoc === undefined) - throw new Error("`drawRectange` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); const points: Array = [ position.x, position.y, @@ -136,22 +94,22 @@ function drawRectangle(gfx: Graphics, position: Vec2, exts: Vec2) { position.x, position.y + exts.y, ] - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color: Array = [ + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + 1, 0, 0, 1, + 0, 1, 0, 1, + 0, 0, 1, 1, + ]; + + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6); - gfx.ctx.disableVertexAttribArray(positionLoc); } function drawCircle(gfx: Graphics, position: Vec2, radius: number) { - const positionLoc = gfx.attribs.get("a_position"); - - if (positionLoc === undefined) - throw new Error("`drawCircle` requires attribute `a_position` to be defined"); - const points: Array = new Array(); const precision = 40; @@ -173,70 +131,22 @@ function drawCircle(gfx: Graphics, position: Vec2, radius: number) { } function drawLine(gfx: Graphics, A: Vec2, B: Vec2) { - const positionLoc = gfx.attribs.get("a_position"); - - if (positionLoc === undefined) - throw new Error("`drawTriangle` requires attribute `a_position` to be defined"); + const a_position = gfx.getAttribute("a_position"); + const a_color = gfx.getAttribute("a_color"); let points: Array = [ A.x, A.y, B.x, B.y, ]; - gfx.ctx.bindBuffer(gfx.ctx.ARRAY_BUFFER, gfx.buffer); - gfx.ctx.enableVertexAttribArray(positionLoc); - gfx.ctx.vertexAttribPointer(positionLoc, 2, gfx.ctx.FLOAT, false, 0, 0); - gfx.ctx.bufferData(gfx.ctx.ARRAY_BUFFER, new Float32Array(points), gfx.ctx.STATIC_DRAW); - gfx.ctx.bindVertexArray(gfx.vao); + const color: Array = [ + 1, 0, 0, 1, + 0, 0, 1, 1, + ]; + + a_position.data(gfx.ctx, points, gfx.ctx.STATIC_DRAW); + a_color.data(gfx.ctx, color, gfx.ctx.STATIC_DRAW); gfx.ctx.drawArrays(gfx.ctx.LINES, 0, 2); - gfx.ctx.disableVertexAttribArray(positionLoc); -} - -class Graphics { - ctx: WebGL2RenderingContext; - program: WebGLProgram; - buffer: WebGLBuffer; - vao: WebGLVertexArrayObject; - attribs: Map = new Map(); - uniforms: Map = new Map(); - constructor(ctx: WebGL2RenderingContext) { - this.ctx = ctx; - this.program = createProgram(ctx, vertexShaderSource, fragmentShaderSource) - this.buffer = ctx.createBuffer(); - this.vao = ctx.createVertexArray(); - - ctx.bindVertexArray(this.vao); - ctx.viewport(0, 0, ctx.canvas.width, ctx.canvas.height); - ctx.useProgram(this.program); - } - - clear(r: number, g: number, b: number, a: number) { - this.ctx.clearColor(r, g, b, a); - this.ctx.clear(this.ctx.COLOR_BUFFER_BIT); - } - - createAttribute(name: string) { - const loc = this.ctx.getAttribLocation(this.program, name); - this.attribs.set(name, loc); - } - - createUniform(name: string) { - const loc = this.ctx.getUniformLocation(this.program, name); - - if (loc === null) - throw new Error("Couldn't get location for uniform: " + name); - - this.uniforms.set(name, loc); - } - - getUniform(name: string): WebGLUniformLocation { - const loc = this.uniforms.get(name); - - if (loc === undefined) - throw new Error("Tried to get uninitialized uniform: " + name); - - return loc; - } } function fullscreenCanvas(id: string) { @@ -245,23 +155,11 @@ function fullscreenCanvas(id: string) { canvas.height = window.innerHeight; } -function draw() { - const canvasId = "game"; - fullscreenCanvas(canvasId); - const ctx = initializeContext(canvasId); - if (ctx === null) return; - - const gfx = new Graphics(ctx); - - gfx.createAttribute("a_position"); - gfx.createUniform("u_resolution"); - gfx.createUniform("u_color"); - - const position: Vec2 = new Vec2(ctx.canvas.width / 2 - 100, ctx.canvas.height / 2 - 100); +function draw(gfx: Graphics) { + const position: Vec2 = new Vec2(gfx.ctx.canvas.width / 2 - 100, gfx.ctx.canvas.height / 2 - 100); gfx.clear(0, 0, 0, 0); - ctx.uniform2f(gfx.getUniform("u_resolution"), ctx.canvas.width, ctx.canvas.height); - ctx.uniform4f(gfx.getUniform("u_color"), Math.random(), Math.random(), Math.random(), 1); + gfx.ctx.uniform2f(gfx.getUniform("u_resolution"), gfx.ctx.canvas.width, gfx.ctx.canvas.height); drawRectangle(gfx, position, new Vec2(200, 200)) drawTriangle(gfx, [new Vec2(100, 100), new Vec2(200, 100), new Vec2(100, 200)]); drawTriangleExts(gfx, new Vec2(200, 200), new Vec2(-100, -100)); @@ -270,6 +168,24 @@ function draw() { } (() => { - draw(); - window.onresize = draw; + const canvasId = "game"; + fullscreenCanvas(canvasId); + const ctx = initializeContext(canvasId); + if (ctx === null) return; + + const gfx = new Graphics(ctx, vertexShader, fragmentShader); + + const a_position = gfx.createAttribute("a_position"); + a_position.format(2, gfx.ctx.FLOAT, false, 0, 0); + + const a_color = gfx.createAttribute("a_color"); + a_color.format(4, gfx.ctx.FLOAT, false, 0, 0); + + gfx.createUniform("u_resolution"); + + draw(gfx); + window.onresize = () => { + fullscreenCanvas(canvasId); + draw(gfx); + } })();