namespace TSE { export abstract class Shader { private _name: string; private _program: WebGLProgram; private _attributes: { [name: string]: number } = {}; private _uniforms: { [name: string]: WebGLUniformLocation } = {}; /** * Create a new Shader. * @param name The name of the Shader. * @param vertexSource The source of the VertexShader * @param fragementSource The source of the FragmentShader */ public constructor(name: string) { this._name = name; } protected setSource(vertexSource: string, fragementSource: string): void { let vertexShader = this.loadShader(vertexSource, gl.VERTEX_SHADER); let fragmentShader = this.loadShader(fragementSource, gl.FRAGMENT_SHADER); this.createProgram(vertexShader, fragmentShader); this.detectAtrributes(); this.detectUniforms(); } /** Use this Shader. */ public use(): void { gl.useProgram(this._program); } private loadShader(source: string, shaderType: number): WebGLShader { let shader: WebGLShader = gl.createShader(shaderType); gl.shaderSource(shader, source); gl.compileShader(shader); const error = gl.getShaderInfoLog(shader); if (error != "") throw new Error("Error compiling shader '" + this._name + "': " + error); return shader; } /** The name of this Shader. */ public get name(): string { return this._name; } /** * Get the Location of an Attribute with the provided Name. * @param name The name of the Attribute. */ public getAttributeLocation(name: string): number { if (this._attributes[name] === undefined) throw new Error(`Unable to find Attribute '${name}' in shader '${this._name}'`); return this._attributes[name]; } /** * Get the Location of an Uniform with the provided Name. * @param name The name of the Uniform. */ public getUniformLocation(name: string): WebGLUniformLocation { if (this._uniforms[name] === undefined) throw new Error(`Unable to find Uniform '${name}' in shader '${this._name}'`); return this._uniforms[name]; } private createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader): void { this._program = gl.createProgram(); gl.attachShader(this._program, vertexShader); gl.attachShader(this._program, fragmentShader); gl.linkProgram(this._program); const error = gl.getProgramInfoLog(this._program); if (error != "") throw new Error("Error linking shader '" + this._name + "': " + error); } private detectAtrributes(): void { let attributeCount = gl.getProgramParameter(this._program, gl.ACTIVE_ATTRIBUTES); for (let i = 0; i < attributeCount; i++) { let attributeInfo: WebGLActiveInfo = gl.getActiveAttrib(this._program, i); if (!attributeInfo) break; this._attributes[attributeInfo.name] = gl.getAttribLocation(this._program, attributeInfo.name); } } private detectUniforms(): void { let uniformCount = gl.getProgramParameter(this._program, gl.ACTIVE_UNIFORMS); for (let i = 0; i < uniformCount; i++) { let uniformInfo: WebGLActiveInfo = gl.getActiveUniform(this._program, i); if (!uniformInfo) break; this._uniforms[uniformInfo.name] = gl.getUniformLocation(this._program, uniformInfo.name); } } } }