gl-tiled
Version:
A Tiled editor renderer for WebGL.
157 lines (127 loc) • 4.91 kB
text/typescript
import { IDictionary } from '../IDictionary';
import { hasOwnKey } from './hasOwnKey';
/**
* Helper class to manage GL shader programs.
*
*/
export class GLProgram
{
/** The underlying GL program. */
program: WebGLProgram;
/** The attribute locations of this program */
attributes: IDictionary<number> = {};
/** The uniform locations of this program */
uniforms: IDictionary<WebGLUniformLocation> = {};
/**
* @param gl The rendering context.
* @param vertexSrc The vertex shader source as an array of strings.
* @param fragmentSrc The fragment shader source as an array of strings.
* @param attributeLocations A key value pair showing which location
* each attribute should sit eg `{ position: 0, uvs: 1 }`.
*/
constructor(gl: WebGLRenderingContext, vertexSrc: string, fragmentSrc: string, attributeLocations?: IDictionary<number>)
{
this.program = GLProgram.compileProgram(
gl,
vertexSrc,
fragmentSrc,
attributeLocations
);
// build a list of attribute locations
const aCount = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES);
for (let i = 0; i < aCount; ++i)
{
const attrib = gl.getActiveAttrib(this.program, i);
if (attrib)
{
this.attributes[attrib.name] = gl.getAttribLocation(this.program, attrib.name);
}
}
// build a list of uniform locations
const uCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < uCount; ++i)
{
const uniform = gl.getActiveUniform(this.program, i);
if (uniform)
{
const name = uniform.name.replace('[0]', '');
const loc = gl.getUniformLocation(this.program, name);
if (loc)
{
this.uniforms[name] = loc;
}
}
}
}
/**
* @param gl The rendering context.
* @param vertexSrc The vertex shader source as an array of strings.
* @param fragmentSrc The fragment shader source as an array of strings.
* @param attributeLocations A key value pair showing which location
* each attribute should sit eg `{ position: 0, uvs: 1 }`.
*/
static compileProgram(gl: WebGLRenderingContext, vertexSrc: string, fragmentSrc: string, attributeLocations?: IDictionary<number>): WebGLProgram
{
const glVertShader = GLProgram.compileShader(gl, gl.VERTEX_SHADER, vertexSrc);
const glFragShader = GLProgram.compileShader(gl, gl.FRAGMENT_SHADER, fragmentSrc);
const program = gl.createProgram();
if (!program)
{
throw new Error('Failed to create WebGL program object.');
}
gl.attachShader(program, glVertShader);
gl.attachShader(program, glFragShader);
// optionally, set the attributes manually for the program rather than letting WebGL decide..
if (attributeLocations)
{
for (const k in attributeLocations)
{
if (!hasOwnKey(attributeLocations, k))
continue;
const location = attributeLocations[k];
if (location)
{
gl.bindAttribLocation(program, location, k);
}
}
}
gl.linkProgram(program);
// if linking fails, then log and cleanup
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
{
const errLog = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
gl.deleteShader(glVertShader);
gl.deleteShader(glFragShader);
throw new Error(`Could not link shader program. Log:\n${errLog}`);
}
// clean up some shaders
gl.deleteShader(glVertShader);
gl.deleteShader(glFragShader);
return program;
}
/**
* Compiles source into a program.
*
* @param gl The rendering context.
* @param type The type, can be either gl.VERTEX_SHADER or gl.FRAGMENT_SHADER.
* @param source The fragment shader source as an array of strings.
*/
static compileShader(gl: WebGLRenderingContext, type: number, source: string): WebGLShader
{
const shader = gl.createShader(type);
if (!shader)
{
throw new Error('Failed to create WebGL shader object.');
}
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
const errLog = gl.getShaderInfoLog(shader);
gl.deleteShader(shader);
throw new Error(`Failed to compile shader. Log:\n${errLog}`);
}
return shader;
}
}