import { injectable } from 'inversify' ; import { uniq } from 'lodash' ; import { extractUniforms } from '../../utils/shader-module' ; import { IModuleParams , IShaderModuleService } from './IShaderModuleService' ; import pickingFrag from './shaders/webgl.picking.frag.glsl' ; import pickingVert from './shaders/webgl.picking.vert.glsl' ; import sdf2dFrag from './shaders/webgl.sdf2d.frag.glsl' ; const precisionRegExp = /precision\s+(high|low|medium)p\s+float/ ; const globalDefaultprecision = '#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n #else\n precision mediump float;\n#endif\n' ; const includeRegExp = /#pragma include (["^+"]?["\ "[a-zA-Z_0-9](.*)"]*?)/g ; @ injectable ( ) export default class ShaderModuleService implements IShaderModuleService { private moduleCache : { [ key ] : IModuleParams ; } = { } ; private rawContentCache : { [ key ] : IModuleParams ; } = { } ; public registerBuiltinModules ( ) { this . destroy ( ) ; this . registerModule ( 'picking' , { vs : pickingVert , fs : pickingFrag } ) ; this . registerModule ( 'sdf2d' , { vs : '' , fs : sdf2dFrag } ) ; } public registerModule ( moduleName , moduleParams ) { if ( this . rawContentCache [ moduleName ] ) { return ; } const { vs = '' , fs = '' , uniforms : declaredUniforms } = moduleParams ; const { content : extractedVS , uniforms : vsUniforms } = extractUniforms ( vs ) ; const { content : extractedFS , uniforms : fsUniforms } = extractUniforms ( fs ) ; this . rawContentCache [ moduleName ] = { fs : extractedFS , uniforms : { ... vsUniforms , ... fsUniforms , ... declaredUniforms , } , vs : extractedVS , } ; } public destroy ( ) { this . moduleCache = { } ; this . rawContentCache = { } ; } public getModule ( moduleName ) { if ( this . moduleCache [ moduleName ] ) { return this . moduleCache [ moduleName ] ; } const rawVS = this . rawContentCache [ moduleName ] . vs || '' ; const rawFS = this . rawContentCache [ moduleName ] . fs || '' ; const { content : vs , includeList : vsIncludeList } = this . processModule ( rawVS , [ ] , 'vs' ) ; const { content : fs , includeList : fsIncludeList } = this . processModule ( rawFS , [ ] , 'fs' ) ; let compiledFs = fs ; const uniforms = uniq ( vsIncludeList . concat ( fsIncludeList ) . concat ( moduleName ) ) . reduce ( ( prev , cur ) => { return { ... prev , ... this . rawContentCache [ cur ] . uniforms , } ; } , { } ) ; /**
         * set default precision for fragment shader
         * https://stackoverflow.com/questions/28540290/why-it-is-necessary-to-set-precision-for-the-fragment-shader
         */ if ( ! precisionRegExp . test ( fs ) ) { compiledFs = globalDefaultprecision + fs ; } this . moduleCache [ moduleName ] = { fs : compiledFs . trim ( ) , uniforms , vs : vs . trim ( ) , } ; return this . moduleCache [ moduleName ] ; } private processModule ( rawContent , includeList , type ) { const compiled = rawContent . replace ( includeRegExp , ( _ , strMatch ) => { const includeOpt = strMatch . split ( ' ' ) ; const includeName = includeOpt [ 0 ] . replace ( /"/g , '' ) ; if ( includeList . indexOf ( includeName ) > - 1 ) { return '' ; } const txt = this . rawContentCache [ includeName ] [ type ] ; includeList . push ( includeName ) ; const { content } = this . processModule ( txt || '' , includeList , type ) ; return content ; } ) ; return { content : compiled , includeList , } ; } }