webgl_game/script.js

242 lines
7.5 KiB
JavaScript

"use strict";
var vertexShaderSource = `#version 300 es
in vec2 a_position;
uniform vec2 u_resolution;
void main() {
vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0;
gl_Position = vec4(clipSpace.xy, 0.0, 1.0);
}
`;
var fragmentShaderSource = ` #version 300 es
precision highp float;
out vec4 outColor;
uniform vec4 u_color;
void main() {
outColor = u_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 createUniform(ctx, program, name) {
const loc = ctx.getUniformLocation(program, name);
return loc;
}
function initializeContext(canvasId) {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext("webgl2", { antialias: false });
return ctx;
}
function drawTriangle(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 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);
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 points = [
position.x, position.y,
position.x + exts.x, position.y,
position.x, position.y + exts.y,
position.x + exts.x, position.y + exts.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);
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 6);
gfx.ctx.disableVertexAttribArray(positionLoc);
}
function drawCircle(gfx, position, radius) {
const precision = 20;
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 angle = 2.0 * Math.PI / precision;
var a = 0;
points.push(position.x);
points.push(position.y);
points.push(position.x);
points.push(position.y + radius);
for (let i = precision; i >= 0; --i) {
var vec = Vec2.angle(a);
a += angle;
}
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);
gfx.ctx.drawArrays(gfx.ctx.TRIANGLES, 0, 3 * precision);
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;
}
}
class Vec2 {
x;
y;
constructor(x, y) {
this.x = x;
this.y = y;
}
add(other) {
this.x += other.x;
this.y += other.y;
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
}
mult(scalar) {
this.x *= scalar;
this.y *= scalar;
}
static angle(angle) {
const eps = 1e-6;
let x = Math.cos(angle);
let y = Math.sin(angle);
if ((x > 0 && x < eps)
|| (x < 0 && x > -eps))
x = 0;
if ((y > 0 && y < eps)
|| (y < 0 && y > -eps))
y = 0;
return new Vec2(x, y);
}
}
class Vec3 {
x;
y;
z;
constructor(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
add(other) {
this.x += other.x;
this.y += other.y;
this.z += other.z;
}
sub(other) {
this.x -= other.x;
this.y -= other.y;
this.z -= other.z;
}
}
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);
gfx.clear(0, 0, 0, 0);
ctx.uniform2f(gfx.getUniform("u_resolution"), ctx.canvas.width, ctx.canvas.height);
ctx.uniform4f(gfx.getUniform("u_color"), 0.2, 0.2, 0.2, 1);
drawRectangle(gfx, position, new Vec2(200, 200));
}
(() => {
draw();
window.onresize = draw;
})();