aboutsummaryrefslogtreecommitdiff
path: root/web/pw-visualizer/src/webgl/shader.ts
diff options
context:
space:
mode:
authorIlion Beyst <ilion.beyst@gmail.com>2021-12-29 21:24:57 +0100
committerIlion Beyst <ilion.beyst@gmail.com>2021-12-29 21:25:29 +0100
commit0c6d978442b244ca3f29c1ffdd44b5007ae7ad93 (patch)
treebaae5fa459a49ecd362e548e0649e2f58c669a70 /web/pw-visualizer/src/webgl/shader.ts
parent3eeaab6cec70e7a06a99a1ac2662974f71064bee (diff)
downloadplanetwars.dev-0c6d978442b244ca3f29c1ffdd44b5007ae7ad93.tar.xz
planetwars.dev-0c6d978442b244ca3f29c1ffdd44b5007ae7ad93.zip
separate out visualizer library
Diffstat (limited to 'web/pw-visualizer/src/webgl/shader.ts')
-rw-r--r--web/pw-visualizer/src/webgl/shader.ts327
1 files changed, 327 insertions, 0 deletions
diff --git a/web/pw-visualizer/src/webgl/shader.ts b/web/pw-visualizer/src/webgl/shader.ts
new file mode 100644
index 0000000..942c4c2
--- /dev/null
+++ b/web/pw-visualizer/src/webgl/shader.ts
@@ -0,0 +1,327 @@
+import type { Dictionary } from './util';
+
+function error(msg: string) {
+ console.error(msg);
+}
+
+const defaultShaderType = [
+ "VERTEX_SHADER",
+ "FRAGMENT_SHADER"
+];
+
+/// Create Shader from Source string
+function loadShader(
+ gl: WebGLRenderingContext,
+ shaderSource: string,
+ shaderType: number,
+ opt_errorCallback: any,
+): WebGLShader {
+ var errFn = opt_errorCallback || error;
+ // Create the shader object
+ var shader = gl.createShader(shaderType);
+
+ // Load the shader source
+ gl.shaderSource(shader, shaderSource);
+
+ // Compile the shader
+ gl.compileShader(shader);
+
+ // Check the compile status
+ var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
+ if (!compiled) {
+ // Something went wrong during compilation; get the error
+ var lastError = gl.getShaderInfoLog(shader);
+ errFn("*** Error compiling shader '" + shader + "':" + lastError);
+ gl.deleteShader(shader);
+ return null;
+ }
+
+ return shader;
+}
+
+/// Actually Create Program with Shader's
+function createProgram(
+ gl: WebGLRenderingContext,
+ shaders: WebGLShader[],
+ opt_attribs: string[],
+ opt_locations: number[],
+ opt_errorCallback: any,
+): WebGLProgram {
+ var errFn = opt_errorCallback || error;
+ var program = gl.createProgram();
+ shaders.forEach(function (shader) {
+ gl.attachShader(program, shader);
+ });
+ if (opt_attribs) {
+ opt_attribs.forEach(function (attrib, ndx) {
+ gl.bindAttribLocation(
+ program,
+ opt_locations ? opt_locations[ndx] : ndx,
+ attrib);
+ });
+ }
+ gl.linkProgram(program);
+
+ // Check the link status
+ var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
+ if (!linked) {
+ // something went wrong with the link
+ var lastError = gl.getProgramInfoLog(program);
+ errFn("Error in program linking:" + lastError);
+
+ gl.deleteProgram(program);
+ return null;
+ }
+ return program;
+}
+
+export class ShaderFactory {
+ frag_source: string;
+ vert_source: string;
+
+ static async create_factory(frag_url: string, vert_url: string): Promise<ShaderFactory> {
+ const sources = await Promise.all([
+ fetch(frag_url).then((r) => r.text()),
+ fetch(vert_url).then((r) => r.text()),
+ ]);
+
+ return new ShaderFactory(sources[0], sources[1]);
+ }
+
+ constructor(frag_source: string, vert_source: string ) {
+ this.frag_source = frag_source;
+ this.vert_source = vert_source;
+ }
+
+ create_shader(
+ gl: WebGLRenderingContext,
+ context?: Dictionary<string>,
+ opt_attribs?: string[],
+ opt_locations?: number[],
+ opt_errorCallback?: any,
+ ): Shader {
+ let vert = this.vert_source.slice();
+ let frag = this.frag_source.slice();
+ for (let key in context) {
+ vert = vert.replace(new RegExp("\\$" + key, 'g'), context[key]);
+ frag = frag.replace(new RegExp("\\$" + key, 'g'), context[key]);
+ }
+
+ const shaders = [
+ loadShader(gl, vert, gl.VERTEX_SHADER, opt_errorCallback),
+ loadShader(gl, frag, gl.FRAGMENT_SHADER, opt_errorCallback),
+ ];
+
+ return new Shader(createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback));
+ }
+}
+
+export class Shader {
+ shader: WebGLProgram;
+ uniformCache: Dictionary<WebGLUniformLocation>;
+ attribCache: Dictionary<number>;
+
+ static async createProgramFromUrls(
+ gl: WebGLRenderingContext,
+ vert_url: string,
+ frag_url: string,
+ context?: Dictionary<string>,
+ opt_attribs?: string[],
+ opt_locations?: number[],
+ opt_errorCallback?: any,
+ ): Promise<Shader> {
+ const sources = (await Promise.all([
+ fetch(vert_url).then((r) => r.text()),
+ fetch(frag_url).then((r) => r.text()),
+ ])).map(x => {
+ for (let key in context) {
+ x = x.replace(new RegExp("\\$" + key, 'g'), context[key]);
+ }
+ return x;
+ });
+
+ const shaders = [
+ loadShader(gl, sources[0], 35633, opt_errorCallback),
+ loadShader(gl, sources[1], 35632, opt_errorCallback),
+ ];
+ return new Shader(createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback));
+ }
+
+ constructor(shader: WebGLProgram) {
+ this.shader = shader;
+ this.uniformCache = {};
+ this.attribCache = {};
+ }
+
+ bind(gl: WebGLRenderingContext) {
+ gl.useProgram(this.shader);
+ }
+
+ // Different locations have different types :/
+ getUniformLocation(gl: WebGLRenderingContext, name: string): WebGLUniformLocation {
+ if (this.uniformCache[name] === undefined) {
+ this.uniformCache[name] = gl.getUniformLocation(this.shader, name);
+ }
+
+ return this.uniformCache[name];
+ }
+
+ getAttribLocation(gl: WebGLRenderingContext, name: string): number {
+ if (this.attribCache[name] === undefined) {
+ this.attribCache[name] = gl.getAttribLocation(this.shader, name);
+ }
+
+ return this.attribCache[name];
+ }
+
+ uniform<T extends Uniform>(
+ gl: WebGLRenderingContext,
+ name: string,
+ uniform: T,
+ ) {
+ this.bind(gl);
+ const location = this.getUniformLocation(gl, name);
+ if (location < 0) {
+ console.error("No location found with name " + name);
+ }
+
+ uniform.setUniform(gl, location);
+ }
+
+ clear(gl: WebGLRenderingContext) {
+ gl.deleteProgram(this.shader);
+ }
+}
+
+export interface Uniform {
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation): void;
+}
+
+export class Uniform2fv implements Uniform {
+ data: number[] | Float32Array;
+ constructor(data: number[] | Float32Array) {
+ this.data = data;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform2fv(location, this.data);
+ }
+}
+
+export class Uniform3fv implements Uniform {
+ data: number[] | Float32Array;
+ constructor(data: number[] | Float32Array) {
+ this.data = data;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform3fv(location, this.data);
+ }
+}
+
+export class Uniform3f implements Uniform {
+ x: number;
+ y: number;
+ z: number;
+
+ constructor(x: number, y: number, z: number) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform3f(location, this.x ,this.y, this.z);
+ }
+}
+
+export class Uniform1iv implements Uniform {
+ data: number[] | Int32List;
+ constructor(data: number[] | Int32List) {
+ this.data = data;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform1iv(location, this.data);
+ }
+}
+
+export class Uniform1i implements Uniform {
+ texture: number;
+
+ constructor(texture: number) {
+ this.texture = texture;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform1i(location, this.texture);
+ }
+}
+
+export class Uniform1f implements Uniform {
+ texture: number;
+
+ constructor(texture: number) {
+ this.texture = texture;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform1f(location, this.texture);
+ }
+}
+
+export class Uniform2f implements Uniform {
+ x: number;
+ y: number;
+
+ constructor(xy: number[]) {
+ this.x = xy[0];
+ this.y = xy[1];
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform2f(location, this.x, this.y);
+ }
+}
+
+export class Uniform4f implements Uniform {
+ v0: number;
+ v1: number;
+ v2: number;
+ v3: number;
+
+ constructor(xyzw: number[]) {
+ this.v0 = xyzw[0];
+ this.v1 = xyzw[1];
+ this.v2 = xyzw[2];
+ this.v3 = xyzw[3];
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform4f(location, this.v0, this.v1, this.v2, this.v3);
+ }
+}
+
+export class UniformMatrix3fv implements Uniform {
+ data: number[] | Float32Array;
+ constructor(data: number[] | Float32Array) {
+ this.data = data;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniformMatrix3fv(location, false, this.data);
+ }
+}
+
+export class UniformBool implements Uniform {
+ data: boolean;
+ constructor(data: boolean) {
+ this.data = data;
+ }
+
+ setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
+ gl.uniform1i(location, this.data ? 1 : 0);
+ }
+}
+
+export default Shader; \ No newline at end of file