100 lines
2.5 KiB
TypeScript
100 lines
2.5 KiB
TypeScript
import { nextPowerOf2 } from '../../util';
|
|
import { Shaders } from './shaders';
|
|
|
|
import { mat4, vec2 } from 'gl-matrix';
|
|
|
|
export class Renderer {
|
|
public readonly coordScale = 32;
|
|
private readonly viewScale = 2;
|
|
|
|
private readonly gl: WebGLRenderingContext;
|
|
private readonly shaders: Shaders;
|
|
|
|
private readonly center: vec2 = vec2.create();
|
|
private readonly translation: vec2 = vec2.create();
|
|
private readonly viewport: mat4 = mat4.create();
|
|
|
|
constructor(private readonly canvas: HTMLCanvasElement) {
|
|
this.gl = this.mkContext();
|
|
|
|
this.shaders = new Shaders(this.gl);
|
|
|
|
this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
|
|
|
|
this.gl.enable(this.gl.BLEND);
|
|
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
|
|
this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
|
|
}
|
|
|
|
public createBuffer(): WebGLBuffer {
|
|
const ret = this.gl.createBuffer();
|
|
if (!ret)
|
|
throw new Error('unable to create buffer');
|
|
|
|
return ret;
|
|
}
|
|
|
|
public getContext(): WebGLRenderingContext {
|
|
return this.gl;
|
|
}
|
|
|
|
public getVertexPosLoc(): number {
|
|
return this.shaders.vertexPosLoc;
|
|
}
|
|
|
|
public getTextureCoordLoc(): number {
|
|
return this.shaders.textureCoordLoc;
|
|
}
|
|
|
|
public getSamplerLoc(): WebGLUniformLocation {
|
|
return this.shaders.samplerLoc;
|
|
}
|
|
|
|
public setCenter(v: vec2|number[]) {
|
|
this.snapToGrid(this.center, v);
|
|
}
|
|
|
|
public setTranslation(v: vec2|number[]) {
|
|
vec2.sub(this.translation, v, this.center);
|
|
this.snapToGrid(this.translation, this.translation);
|
|
this.gl.uniform2fv(this.shaders.translateLoc, this.translation);
|
|
}
|
|
|
|
public clear(): void {
|
|
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
|
|
|
this.setTranslation([0, 0]);
|
|
}
|
|
|
|
public snapToGrid(out: vec2, a: vec2|number[]): void {
|
|
vec2.scale(out, a, this.coordScale);
|
|
vec2.round(out, out);
|
|
vec2.scale(out, out, 1 / this.coordScale);
|
|
}
|
|
|
|
public resize(): void {
|
|
const w = this.canvas.width;
|
|
const h = this.canvas.height;
|
|
const ws = nextPowerOf2(w);
|
|
const hs = nextPowerOf2(h);
|
|
|
|
this.gl.viewport((w - ws) / 2, (h - hs) / 2, ws, hs);
|
|
this.clear();
|
|
|
|
const scale = this.viewScale * this.coordScale;
|
|
|
|
mat4.identity(this.viewport);
|
|
mat4.scale(this.viewport, this.viewport, [2 * scale / ws, -2 * scale / hs, 1.0]);
|
|
this.gl.uniformMatrix4fv(this.shaders.viewportLoc, false, this.viewport);
|
|
}
|
|
|
|
private mkContext(): WebGLRenderingContext {
|
|
const gl = (
|
|
this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl')
|
|
);
|
|
if (!gl)
|
|
throw new Error('unable to initialize WebGL context');
|
|
|
|
return gl;
|
|
}
|
|
}
|