Added Attribute class and color attribute
This commit is contained in:
parent
690360c3f2
commit
2d1d3b6034
|
@ -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 };
|
|
@ -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<string, Attribute> = new Map();
|
||||
uniforms: Map<string, WebGLUniformLocation> = 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<number>, 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 }
|
196
script.js
196
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);
|
||||
};
|
||||
})();
|
||||
|
|
234
script.ts
234
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<number> = [
|
||||
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<number> = [
|
||||
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<number> = [
|
||||
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<number> = [
|
||||
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<number> = [
|
||||
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<number> = [
|
||||
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<Vec2> = new Array<Vec2>();
|
||||
|
||||
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<number> = [
|
||||
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<number> = [
|
||||
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<string, GLint> = new Map();
|
||||
uniforms: Map<string, WebGLUniformLocation> = 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);
|
||||
}
|
||||
})();
|
||||
|
|
Loading…
Reference in New Issue