import { assert , log , LogLevel } from './auxiliaries' ; import { Context } from './context' ; import { AbstractObject } from './object' ; /**
 * WebGL shader wrapper encapsulating shader creation, compilation, and deletion. A shader can be attached to multiple
 * Programs for linking, and can be deleted if detached from all (linked) programs. The expected default behavior is to
 * create a shader, attach it to programs, and  discard is immediately after all programs are created (linked). If,
 * however, the source of a shader needs to be changed, e.g., for replacements or other modifications, the shader
 * object should be kept and, on change, all programs that have the shader attached have to be invalidated/relinked
 * manually.
 *
 * ```
 * var frag = new gloperate.Shader(context, context.gl.FRAGMENT_SHADER, 'EmptyFragmentShader');
 * var vert = new gloperate.Shader(context, context.gl.VERTEX_SHADER, 'EmptyVertexShader');
 * vert.initialize('void main() { }');
 * frag.initialize('void main() { }');
 *
 * var prog = new gloperate.Program(context, 'EmptyProgram');
 * prog.initialize([frag, vert]);
 * ```
 */ export class Shader extends AbstractObject < WebGLShader > { see type protected _type : GLenum ; see source protected _source : string ; see compiled protected _compiled = false ; /**
     * Map of replacement strings and the value to replace them with.
     */ protected _replacements : undefined | Map < string , string > ; param context param type param identifier constructor ( context , type , identifier ? ) { super ( context , identifier ) ; const gl = context . gl ; if ( identifier === undefined ) { switch ( type ) { case context . gl . FRAGMENT_SHADER : identifier = 'FragmentShader' ; break ; case context . gl . VERTEX_SHADER : identifier = 'VertexShader' ; break ; default : assert ( false , `expected either a FRAGMENT_SHADER (${ gl . FRAGMENT_SHADER }) ` + `or a VERTEX_SHADER (${ gl . VERTEX_SHADER }), given ${ type }` ) ; } } this . _type = type ; } param source param compile returns protected create ( source ? , compile = true ) { const gl = this . _context . gl ; this . _object = gl . createShader ( this . _type ) ; this . _valid = gl . isShader ( this . _object ) ; this . _compiled = false ; assert ( this . _object instanceof WebGLShader , `expected WebGLShader object to be created` ) ; if ( source ) { this . source = source ; } if ( source && compile ) { this . compile ( ) ; } return this . _object ; } /**
     * Delete the shader object. This should have the reverse effect of `create`.
     */ protected delete ( ) { assert ( this . _object !== undefined , `expected WebGLShader object` ) ; this . _context . gl . deleteShader ( this . _object ) ; this . _object = undefined ; this . _valid = false ; this . _compiled = false ; } /**
     * Triggers recompilation of a shader. This is usually used internally automatically, but exposed here for leaky
     * abstraction. It should not be required to invoke this manually in most cases. The shader object is marked as
     * compiled iff the source compiled successfully. Note that invalidation of all programs this shader is attached
     * to needs to be done manually.
     */ compile ( ) { const gl = this . _context . gl ; let source = this . sourceWithReplacements ; if ( this . _context . isWebGL2 ) { source = `#version 300 es\n${ source }` ; } gl . shaderSource ( this . _object , source ) ; gl . compileShader ( this . _object ) ; this . _compiled = gl . getShaderParameter ( this . _object , gl . COMPILE_STATUS ) ; if ( ! this . _compiled ) { const infoLog = gl . getShaderInfoLog ( this . _object ) ; log ( LogLevel . Error , `compilation of shader '${ this . _identifier }' failed: ${ infoLog }` ) ; } } param searchValue param replaceValue replace ( searchValue , replaceValue ) { if ( this . _replacements === undefined ) { this . _replacements = new Map < string , string > ( ) ; } this . _replacements . set ( searchValue , replaceValue ) ; } /**
     * Either VERTEX_SHADER or FRAGMENT_SHADER.
     */ get type ( ) : GLenum { this . assertInitialized ( ) ; return this . _type ; } /**
     * Allows to change the shader's source. Note that this will not recompile the shader.
     */ set source ( source ) { if ( this . _source === source ) { return ; } this . _source = source ; } /**
     * Read access to the shader's source (without replacements applied).
     */ get source ( ) : string { this . assertInitialized ( ) ; return this . _source ; } returns get sourceWithReplacements ( ) : string { if ( this . _replacements === undefined ) { return this . _source ; } let source = this . _source ; this . _replacements . forEach ( ( replaceValue , searchValue ) => { const searchRegex = searchValue . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) ; source = source . replace ( new RegExp ( searchRegex , 'g' ) , replaceValue ) ; } ) ; return source ; } /**
     * Read access the the shader's compile status. True if last compilation was successful.
     */ get compiled ( ) : boolean { this . assertInitialized ( ) ; return this . _compiled ; } }