@luma.gl/shadertools
Version:
Shader module system for luma.gl
247 lines (214 loc) • 6.69 kB
JavaScript
import { VERTEX_SHADER, FRAGMENT_SHADER } from './constants';
import { resolveModules } from './resolve-modules';
import { getPlatformShaderDefines, getVersionDefines } from './platform-defines';
import injectShader, { DECLARATION_INJECT_MARKER } from './inject-shader';
import transpileShader from './transpile-shader';
import { assert } from '../utils';
const INJECT_SHADER_DECLARATIONS = "\n\n".concat(DECLARATION_INJECT_MARKER, "\n\n");
const SHADER_TYPE = {
[VERTEX_SHADER]: 'vertex',
[FRAGMENT_SHADER]: 'fragment'
};
const FRAGMENT_SHADER_PROLOGUE = "precision highp float;\n\n";
export function assembleShaders(gl, opts) {
const {
vs,
fs
} = opts;
const modules = resolveModules(opts.modules || []);
return {
gl,
vs: assembleShader(gl, Object.assign({}, opts, {
source: vs,
type: VERTEX_SHADER,
modules
})),
fs: assembleShader(gl, Object.assign({}, opts, {
source: fs,
type: FRAGMENT_SHADER,
modules
})),
getUniforms: assembleGetUniforms(modules)
};
}
function assembleShader(gl, _ref) {
let {
id,
source,
type,
modules,
defines = {},
hookFunctions = [],
inject = {},
transpileToGLSL100 = false,
prologue = true,
log
} = _ref;
assert(typeof source === 'string', 'shader source must be a string');
const isVertex = type === VERTEX_SHADER;
const sourceLines = source.split('\n');
let glslVersion = 100;
let versionLine = '';
let coreSource = source;
if (sourceLines[0].indexOf('#version ') === 0) {
glslVersion = 300;
versionLine = sourceLines[0];
coreSource = sourceLines.slice(1).join('\n');
} else {
versionLine = "#version ".concat(glslVersion);
}
const allDefines = {};
modules.forEach(module => {
Object.assign(allDefines, module.getDefines());
});
Object.assign(allDefines, defines);
let assembledSource = prologue ? "".concat(versionLine, "\n").concat(getShaderName({
id,
source,
type
}), "\n").concat(getShaderType({
type
}), "\n").concat(getPlatformShaderDefines(gl), "\n").concat(getVersionDefines(gl, glslVersion, !isVertex), "\n").concat(getApplicationDefines(allDefines), "\n").concat(isVertex ? '' : FRAGMENT_SHADER_PROLOGUE, "\n") : "".concat(versionLine, "\n");
const hookFunctionMap = normalizeHookFunctions(hookFunctions);
const hookInjections = {};
const declInjections = {};
const mainInjections = {};
for (const key in inject) {
const injection = typeof inject[key] === 'string' ? {
injection: inject[key],
order: 0
} : inject[key];
const match = key.match(/^(v|f)s:(#)?([\w-]+)$/);
if (match) {
const hash = match[2];
const name = match[3];
if (hash) {
if (name === 'decl') {
declInjections[key] = [injection];
} else {
mainInjections[key] = [injection];
}
} else {
hookInjections[key] = [injection];
}
} else {
mainInjections[key] = [injection];
}
}
for (const module of modules) {
if (log) {
module.checkDeprecations(coreSource, log);
}
const moduleSource = module.getModuleSource(type, glslVersion);
assembledSource += moduleSource;
const injections = module.injections[type];
for (const key in injections) {
const match = key.match(/^(v|f)s:#([\w-]+)$/);
if (match) {
const name = match[2];
const injectionType = name === 'decl' ? declInjections : mainInjections;
injectionType[key] = injectionType[key] || [];
injectionType[key].push(injections[key]);
} else {
hookInjections[key] = hookInjections[key] || [];
hookInjections[key].push(injections[key]);
}
}
}
assembledSource += INJECT_SHADER_DECLARATIONS;
assembledSource = injectShader(assembledSource, type, declInjections);
assembledSource += getHookFunctions(hookFunctionMap[type], hookInjections);
assembledSource += coreSource;
assembledSource = injectShader(assembledSource, type, mainInjections);
assembledSource = transpileShader(assembledSource, transpileToGLSL100 ? 100 : glslVersion, isVertex);
return assembledSource;
}
function assembleGetUniforms(modules) {
return function getUniforms(opts) {
const uniforms = {};
for (const module of modules) {
const moduleUniforms = module.getUniforms(opts, uniforms);
Object.assign(uniforms, moduleUniforms);
}
return uniforms;
};
}
function getShaderType(_ref2) {
let {
type
} = _ref2;
return "\n#define SHADER_TYPE_".concat(SHADER_TYPE[type].toUpperCase(), "\n");
}
function getShaderName(_ref3) {
let {
id,
source,
type
} = _ref3;
const injectShaderName = id && typeof id === 'string' && source.indexOf('SHADER_NAME') === -1;
return injectShaderName ? "\n#define SHADER_NAME ".concat(id, "_").concat(SHADER_TYPE[type], "\n\n") : '';
}
function getApplicationDefines() {
let defines = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
let count = 0;
let sourceText = '';
for (const define in defines) {
if (count === 0) {
sourceText += '\n// APPLICATION DEFINES\n';
}
count++;
const value = defines[define];
if (value || Number.isFinite(value)) {
sourceText += "#define ".concat(define.toUpperCase(), " ").concat(defines[define], "\n");
}
}
if (count === 0) {
sourceText += '\n';
}
return sourceText;
}
function getHookFunctions(hookFunctions, hookInjections) {
let result = '';
for (const hookName in hookFunctions) {
const hookFunction = hookFunctions[hookName];
result += "void ".concat(hookFunction.signature, " {\n");
if (hookFunction.header) {
result += " ".concat(hookFunction.header);
}
if (hookInjections[hookName]) {
const injections = hookInjections[hookName];
injections.sort((a, b) => a.order - b.order);
for (const injection of injections) {
result += " ".concat(injection.injection, "\n");
}
}
if (hookFunction.footer) {
result += " ".concat(hookFunction.footer);
}
result += '}\n';
}
return result;
}
function normalizeHookFunctions(hookFunctions) {
const result = {
vs: {},
fs: {}
};
hookFunctions.forEach(hook => {
let opts;
if (typeof hook !== 'string') {
opts = hook;
hook = opts.hook;
} else {
opts = {};
}
hook = hook.trim();
const [stage, signature] = hook.split(':');
const name = hook.replace(/\(.+/, '');
result[stage][name] = Object.assign(opts, {
signature
});
});
return result;
}
//# sourceMappingURL=assemble-shaders.js.map