(function webpackUniversalModuleDefinition(root, factory) { if (typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if (typeof define === 'function' && define.amd) define([], factory); else if (typeof exports === 'object') exports['luma'] = factory(); else root['luma'] = factory();})(globalThis, function () { "use strict"; var __exports__ = (() => { var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default")); var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; // external-global-plugin:@luma.gl/core var require_core = __commonJS({ "external-global-plugin:@luma.gl/core"(exports, module) { module.exports = globalThis.luma; } }); // bundle.ts var bundle_exports = {}; __export(bundle_exports, { ShaderAssembler: () => ShaderAssembler, ShaderModuleInstance: () => ShaderModuleInstance, _ShaderModuleInstance: () => ShaderModuleInstance, _getDependencyGraph: () => getDependencyGraph, _resolveModules: () => resolveModules, _warp: () => warp, assembleShaderPairGLSL: () => assembleShaderPairGLSL, brightnessContrast: () => brightnessContrast, bulgePinch: () => bulgePinch, capitalize: () => capitalize, colorHalftone: () => colorHalftone, combineInjects: () => combineInjects, convertToVec4: () => convertToVec4, denoise: () => denoise, dirlight: () => dirlight, dirlight1: () => dirlight2, dotScreen: () => dotScreen, edgeWork: () => edgeWork, fp32: () => fp32, fp64: () => fp64, fp64arithmetic: () => fp64arithmetic, fxaa: () => fxaa, generateShaderForModule: () => generateShaderForModule, geometry1: () => geometry, getPassthroughFS: () => getPassthroughFS, getQualifierDetails: () => getQualifierDetails, getShaderInfo: () => getShaderInfo, getShaderLayoutFromWGSL: () => getShaderLayoutFromWGSL, glsl: () => glsl, gouraudLighting: () => gouraudLighting, gouraudMaterial: () => gouraudMaterial, hexagonalPixelate: () => hexagonalPixelate, hueSaturation: () => hueSaturation, ink: () => ink, lighting: () => lighting, lights1: () => lights, magnify: () => magnify, noise: () => noise, normalizeShaderModule: () => normalizeShaderModule, pbr: () => pbr, pbrMaterial: () => pbrMaterial, phongLighting: () => phongLighting, phongMaterial: () => phongMaterial, picking: () => picking, project1: () => project, random: () => random, sepia: () => sepia, swirl: () => swirl, tiltShift: () => tiltShift, triangleBlur: () => triangleBlur, typeToChannelCount: () => typeToChannelCount, typeToChannelSuffix: () => typeToChannelSuffix, vibrance: () => vibrance, vignette: () => vignette, zoomBlur: () => zoomBlur }); __reExport(bundle_exports, __toESM(require_core(), 1)); // src/lib/glsl-utils/highlight.ts var glsl = (x) => `${x}`; // src/lib/utils/assert.ts function assert(condition, message) { if (!condition) { throw new Error(message || "shadertools: assertion failed."); } } // src/lib/filters/prop-types.ts var DEFAULT_PROP_VALIDATORS = { number: { type: "number", validate(value, propType) { return Number.isFinite(value) && typeof propType === "object" && (propType.max === void 0 || value <= propType.max) && (propType.min === void 0 || value >= propType.min); } }, array: { type: "array", validate(value, propType) { return Array.isArray(value) || ArrayBuffer.isView(value); } } }; function makePropValidators(propTypes) { const propValidators = {}; for (const [name, propType] of Object.entries(propTypes)) { propValidators[name] = makePropValidator(propType); } return propValidators; } function getValidatedProperties(properties, propValidators, errorMessage) { const validated = {}; for (const [key, propsValidator] of Object.entries(propValidators)) { if (properties && key in properties && !propsValidator.private) { if (propsValidator.validate) { assert( propsValidator.validate(properties[key], propsValidator), `${errorMessage}: invalid ${key}` ); } validated[key] = properties[key]; } else { validated[key] = propsValidator.value; } } return validated; } function makePropValidator(propType) { let type = getTypeOf(propType); if (type !== "object") { return { value: propType, ...DEFAULT_PROP_VALIDATORS[type], type }; } if (typeof propType === "object") { if (!propType) { return { type: "object", value: null }; } if (propType.type !== void 0) { return { ...propType, ...DEFAULT_PROP_VALIDATORS[propType.type], type: propType.type }; } if (propType.value === void 0) { return { type: "object", value: propType }; } type = getTypeOf(propType.value); return { ...propType, ...DEFAULT_PROP_VALIDATORS[type], type }; } throw new Error("props"); } function getTypeOf(value) { if (Array.isArray(value) || ArrayBuffer.isView(value)) { return "array"; } return typeof value; } // src/module-injectors.ts var MODULE_INJECTORS_VS = glsl`\ #ifdef MODULE_LOGDEPTH logdepth_adjustPosition(gl_Position); #endif `; var MODULE_INJECTORS_FS = glsl`\ #ifdef MODULE_MATERIAL gl_FragColor = material_filterColor(gl_FragColor); #endif #ifdef MODULE_LIGHTING gl_FragColor = lighting_filterColor(gl_FragColor); #endif #ifdef MODULE_FOG gl_FragColor = fog_filterColor(gl_FragColor); #endif #ifdef MODULE_PICKING gl_FragColor = picking_filterHighlightColor(gl_FragColor); gl_FragColor = picking_filterPickingColor(gl_FragColor); #endif #ifdef MODULE_LOGDEPTH logdepth_setFragDepth(); #endif `; // src/lib/shader-assembly/shader-injections.ts var MODULE_INJECTORS = { vertex: MODULE_INJECTORS_VS, fragment: MODULE_INJECTORS_FS }; var REGEX_START_OF_MAIN = /void\s+main\s*\([^)]*\)\s*\{\n?/; var REGEX_END_OF_MAIN = /}\n?[^{}]*$/; var fragments = []; var DECLARATION_INJECT_MARKER = "__LUMA_INJECT_DECLARATIONS__"; function normalizeInjections(injections) { const result = { vertex: {}, fragment: {} }; for (const hook in injections) { let injection = injections[hook]; const stage = getHookStage(hook); if (typeof injection === "string") { injection = { order: 0, injection }; } result[stage][hook] = injection; } return result; } function getHookStage(hook) { const type = hook.slice(0, 2); switch (type) { case "vs": return "vertex"; case "fs": return "fragment"; default: throw new Error(type); } } function injectShader(source, stage, inject, injectStandardStubs = false) { const isVertex = stage === "vertex"; for (const key in inject) { const fragmentData = inject[key]; fragmentData.sort((a, b) => a.order - b.order); fragments.length = fragmentData.length; for (let i = 0, len = fragmentData.length; i < len; ++i) { fragments[i] = fragmentData[i].injection; } const fragmentString = `${fragments.join("\n")} `; switch (key) { case "vs:#decl": if (isVertex) { source = source.replace(DECLARATION_INJECT_MARKER, fragmentString); } break; case "vs:#main-start": if (isVertex) { source = source.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString); } break; case "vs:#main-end": if (isVertex) { source = source.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match); } break; case "fs:#decl": if (!isVertex) { source = source.replace(DECLARATION_INJECT_MARKER, fragmentString); } break; case "fs:#main-start": if (!isVertex) { source = source.replace(REGEX_START_OF_MAIN, (match) => match + fragmentString); } break; case "fs:#main-end": if (!isVertex) { source = source.replace(REGEX_END_OF_MAIN, (match) => fragmentString + match); } break; default: source = source.replace(key, (match) => match + fragmentString); } } source = source.replace(DECLARATION_INJECT_MARKER, ""); if (injectStandardStubs) { source = source.replace(/\}\s*$/, (match) => match + MODULE_INJECTORS[stage]); } return source; } function combineInjects(injects) { const result = {}; assert(Array.isArray(injects) && injects.length > 1); injects.forEach((inject) => { for (const key in inject) { result[key] = result[key] ? `${result[key]} ${inject[key]}` : inject[key]; } }); return result; } // src/lib/shader-module/shader-module-instance.ts var index = 1; var ShaderModuleInstance = class { name; vs; fs; getModuleUniforms; dependencies; deprecations; defines; injections; uniforms = {}; uniformTypes = {}; static instantiateModules(modules) { return modules.map((module) => { if (module instanceof ShaderModuleInstance) { return module; } assert( typeof module !== "string", `Shader module use by name is deprecated. Import shader module '${JSON.stringify( module )}' and use it directly.` ); if (!module.name) { console.warn("shader module has no name"); module.name = `shader-module-${index++}`; } const moduleObject = new ShaderModuleInstance(module); moduleObject.dependencies = ShaderModuleInstance.instantiateModules( module.dependencies || [] ); return moduleObject; }); } constructor(props) { const { name, vs: vs6, fs: fs28, dependencies = [], uniformTypes = {}, uniformPropTypes = {}, getUniforms: getUniforms9, deprecations = [], defines = {}, inject = {} } = props; assert(typeof name === "string"); this.name = name; this.vs = vs6; this.fs = fs28; this.getModuleUniforms = getUniforms9; this.dependencies = ShaderModuleInstance.instantiateModules(dependencies); this.deprecations = this._parseDeprecationDefinitions(deprecations); this.defines = defines; this.injections = normalizeInjections(inject); this.uniformTypes = uniformTypes; if (uniformPropTypes) { this.uniforms = makePropValidators(uniformPropTypes); } } // Extracts the source code chunk for the specified shader type from the named shader module getModuleSource(stage) { let moduleSource; switch (stage) { case "vertex": moduleSource = this.vs || ""; break; case "fragment": moduleSource = this.fs || ""; break; default: assert(false); } const moduleName = this.name.toUpperCase().replace(/[^0-9a-z]/gi, "_"); return `// ----- MODULE ${this.name} --------------- #define MODULE_${moduleName} ${moduleSource} `; } getUniforms(userProps, uniforms) { if (this.getModuleUniforms) { return this.getModuleUniforms(userProps, uniforms); } return getValidatedProperties(userProps, this.uniforms, this.name); } getDefines() { return this.defines; } // Warn about deprecated uniforms or functions checkDeprecations(shaderSource, log3) { this.deprecations.forEach((def) => { if (def.regex?.test(shaderSource)) { if (def.deprecated) { log3.deprecated(def.old, def.new)(); } else { log3.removed(def.old, def.new)(); } } }); } _parseDeprecationDefinitions(deprecations) { deprecations.forEach((def) => { switch (def.type) { case "function": def.regex = new RegExp(`\\b${def.old}\\(`); break; default: def.regex = new RegExp(`${def.type} ${def.old};`); } }); return deprecations; } _defaultGetUniforms(opts = {}) { const uniforms = {}; const propTypes = this.uniforms; for (const key in propTypes) { const propDef = propTypes[key]; if (key in opts && !propDef.private) { if (propDef.validate) { assert(propDef.validate(opts[key], propDef), `${this.name}: invalid ${key}`); } uniforms[key] = opts[key]; } else { uniforms[key] = propDef.value; } } return uniforms; } }; // src/lib/shader-assembly/select-shaders.ts function selectShaders(props) { if (props.source && props.platformInfo.type === "webgpu") { const propsCopy = { ...props, vs: void 0, fs: void 0 }; return propsCopy; } if (!props.vs) { throw new Error("no vertex shader"); } const vs6 = getShaderSource(props.platformInfo, props.vs); let fs28; if (props.fs) { fs28 = getShaderSource(props.platformInfo, props.fs); } return { ...props, vs: vs6, fs: fs28 }; } function getShaderSource(platformInfo, shader) { if (typeof shader === "string") { return shader; } switch (platformInfo.type) { case "webgpu": if (shader?.wgsl) { return shader.wgsl; } throw new Error("WebGPU does not support GLSL shaders"); default: if (shader?.glsl) { return shader.glsl; } throw new Error("WebGL does not support WGSL shaders"); } } // src/lib/shader-assembly/resolve-modules.ts function resolveModules(modules) { const instances = ShaderModuleInstance.instantiateModules(modules); return getShaderDependencies(instances); } function getShaderDependencies(modules) { const moduleMap = {}; const moduleDepth = {}; getDependencyGraph({ modules, level: 0, moduleMap, moduleDepth }); return Object.keys(moduleDepth).sort((a, b) => moduleDepth[b] - moduleDepth[a]).map((name) => moduleMap[name]); } function getDependencyGraph(options) { const { modules, level, moduleMap, moduleDepth } = options; if (level >= 5) { throw new Error("Possible loop in shader dependency graph"); } for (const module of modules) { moduleMap[module.name] = module; if (moduleDepth[module.name] === void 0 || moduleDepth[module.name] < level) { moduleDepth[module.name] = level; } } for (const module of modules) { if (module.dependencies) { getDependencyGraph({ modules: module.dependencies, level: level + 1, moduleMap, moduleDepth }); } } } // src/lib/shader-assembly/platform-defines.ts function getPlatformShaderDefines(platformInfo) { switch (platformInfo?.gpu.toLowerCase()) { case "apple": return glsl`\ #define APPLE_GPU // Apple optimizes away the calculation necessary for emulated fp64 #define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1 #define LUMA_FP32_TAN_PRECISION_WORKAROUND 1 // Intel GPU doesn't have full 32 bits precision in same cases, causes overflow #define LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND 1 `; case "nvidia": return glsl`\ #define NVIDIA_GPU // Nvidia optimizes away the calculation necessary for emulated fp64 #define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1 `; case "intel": return glsl`\ #define INTEL_GPU // Intel optimizes away the calculation necessary for emulated fp64 #define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1 // Intel's built-in 'tan' function doesn't have acceptable precision #define LUMA_FP32_TAN_PRECISION_WORKAROUND 1 // Intel GPU doesn't have full 32 bits precision in same cases, causes overflow #define LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND 1 `; case "amd": return glsl`\ #define AMD_GPU `; default: return glsl`\ #define DEFAULT_GPU // Prevent driver from optimizing away the calculation necessary for emulated fp64 #define LUMA_FP64_CODE_ELIMINATION_WORKAROUND 1 // Headless Chrome's software shader 'tan' function doesn't have acceptable precision #define LUMA_FP32_TAN_PRECISION_WORKAROUND 1 // If the GPU doesn't have full 32 bits precision, will causes overflow #define LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND 1 `; } } // src/lib/shader-transpiler/transpile-glsl-shader.ts function transpileGLSLShader(source, stage) { const sourceGLSLVersion = Number(source.match(/^#version[ \t]+(\d+)/m)?.[1] || 100); if (sourceGLSLVersion !== 300) { throw new Error("luma.gl v9 only supports GLSL 3.00 shader sources"); } switch (stage) { case "vertex": source = convertShader(source, ES300_VERTEX_REPLACEMENTS); return source; case "fragment": source = convertShader(source, ES300_FRAGMENT_REPLACEMENTS); return source; default: throw new Error(stage); } } var ES300_REPLACEMENTS = [ // Fix poorly formatted version directive [/^(#version[ \t]+(100|300[ \t]+es))?[ \t]*\n/, "#version 300 es\n"], // The individual `texture...()` functions were replaced with `texture()` overloads [/\btexture(2D|2DProj|Cube)Lod(EXT)?\(/g, "textureLod("], [/\btexture(2D|2DProj|Cube)(EXT)?\(/g, "texture("] ]; var ES300_VERTEX_REPLACEMENTS = [ ...ES300_REPLACEMENTS, // `attribute` keyword replaced with `in` [makeVariableTextRegExp("attribute"), "in $1"], // `varying` keyword replaced with `out` [makeVariableTextRegExp("varying"), "out $1"] ]; var ES300_FRAGMENT_REPLACEMENTS = [ ...ES300_REPLACEMENTS, // `varying` keyword replaced with `in` [makeVariableTextRegExp("varying"), "in $1"] ]; function convertShader(source, replacements) { for (const [pattern, replacement] of replacements) { source = source.replace(pattern, replacement); } return source; } function makeVariableTextRegExp(qualifier) { return new RegExp(`\\b${qualifier}[ \\t]+(\\w+[ \\t]+\\w+(\\[\\w+\\])?;)`, "g"); } // src/lib/shader-assembly/shader-hooks.ts function getShaderHooks(hookFunctions, hookInjections) { let result = ""; for (const hookName in hookFunctions) { const hookFunction = hookFunctions[hookName]; result += `void ${hookFunction.signature} { `; if (hookFunction.header) { result += ` ${hookFunction.header}`; } if (hookInjections[hookName]) { const injections = hookInjections[hookName]; injections.sort((a, b) => a.order - b.order); for (const injection of injections) { result += ` ${injection.injection} `; } } if (hookFunction.footer) { result += ` ${hookFunction.footer}`; } result += "}\n"; } return result; } function normalizeShaderHooks(hookFunctions) { const result = { vertex: {}, fragment: {} }; for (const hookFunction of hookFunctions) { let opts; let hook; if (typeof hookFunction !== "string") { opts = hookFunction; hook = opts.hook; } else { opts = {}; hook = hookFunction; } hook = hook.trim(); const [shaderStage, signature] = hook.split(":"); const name = hook.replace(/\(.+/, ""); const normalizedHook = Object.assign(opts, { signature }); switch (shaderStage) { case "vs": result.vertex[name] = normalizedHook; break; case "fs": result.fragment[name] = normalizedHook; break; default: throw new Error(shaderStage); } } return result; } // src/lib/glsl-utils/get-shader-info.ts function getShaderInfo(source, defaultName) { return { name: getShaderName(source, defaultName), language: "glsl", version: getShaderVersion(source) }; } function getShaderName(shader, defaultName = "unnamed") { const SHADER_NAME_REGEXP = /#define[^\S\r\n]*SHADER_NAME[^\S\r\n]*([A-Za-z0-9_-]+)\s*/; const match = SHADER_NAME_REGEXP.exec(shader); return match ? match[1] : defaultName; } function getShaderVersion(source) { let version = 100; const words = source.match(/[^\s]+/g); if (words && words.length >= 2 && words[0] === "#version") { const parsedVersion = parseInt(words[1], 10); if (Number.isFinite(parsedVersion)) { version = parsedVersion; } } if (version !== 100 && version !== 300) { throw new Error(`Invalid GLSL version ${version}`); } return version; } // src/lib/shader-assembly/assemble-shaders.ts var INJECT_SHADER_DECLARATIONS = ` ${DECLARATION_INJECT_MARKER} `; var FRAGMENT_SHADER_PROLOGUE = glsl`\ precision highp float; `; function assembleShaderWGSL(options) { const modules = resolveModules(options.modules || []); return { source: assembleWGSLShader(options.platformInfo, { ...options, source: options.source, stage: "vertex", modules }), getUniforms: assembleGetUniforms(modules) }; } function assembleShaderPairWGSL(options) { const modules = resolveModules(options.modules || []); return { vs: assembleWGSLShader(options.platformInfo, { ...options, source: options.vs, stage: "vertex", modules }), fs: assembleWGSLShader(options.platformInfo, { ...options, source: options.fs, stage: "fragment", modules }), getUniforms: assembleGetUniforms(modules) }; } function assembleShaderPairGLSL(options) { const { vs: vs6, fs: fs28 } = options; const modules = resolveModules(options.modules || []); return { vs: assembleGLSLShader(options.platformInfo, { ...options, source: vs6, stage: "vertex", modules }), fs: assembleGLSLShader(options.platformInfo, { ...options, source: fs28, stage: "fragment", modules }), getUniforms: assembleGetUniforms(modules) }; } function assembleWGSLShader(platformInfo, options) { const { // id, source, stage, modules, // defines = {}, hookFunctions = [], inject = {}, log: log3 } = options; assert(typeof source === "string", "shader source must be a string"); const coreSource = source; let assembledSource = ""; const hookFunctionMap = normalizeShaderHooks(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 = /^(v|f)s:(#)?([\w-]+)$/.exec(key); 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]; } } const modulesToInject = platformInfo.type !== "webgpu" ? modules : []; for (const module of modulesToInject) { if (log3) { module.checkDeprecations(coreSource, log3); } const moduleSource = module.getModuleSource(stage, "wgsl"); assembledSource += moduleSource; const injections = module.injections[stage]; for (const key in injections) { const match = /^(v|f)s:#([\w-]+)$/.exec(key); 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, stage, declInjections); assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections); assembledSource += coreSource; assembledSource = injectShader(assembledSource, stage, mainInjections); return assembledSource; } function assembleGLSLShader(platformInfo, options) { const { id, source, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, prologue = true, log: log3 } = options; assert(typeof source === "string", "shader source must be a string"); const sourceVersion = language === "glsl" ? getShaderInfo(source).version : -1; const targetVersion = platformInfo.shaderLanguageVersion; const sourceVersionDirective = sourceVersion === 100 ? "#version 100" : "#version 300 es"; const sourceLines = source.split("\n"); const coreSource = sourceLines.slice(1).join("\n"); const allDefines = {}; modules.forEach((module) => { Object.assign(allDefines, module.getDefines()); }); Object.assign(allDefines, defines); let assembledSource = ""; switch (language) { case "wgsl": break; case "glsl": assembledSource = prologue ? `${sourceVersionDirective} // ----- PROLOGUE ------------------------- ${getShaderNameDefine({ id, source, stage })} ${`#define SHADER_TYPE_${stage.toUpperCase()}`} ${getPlatformShaderDefines(platformInfo)} ${stage === "fragment" ? FRAGMENT_SHADER_PROLOGUE : ""} // ----- APPLICATION DEFINES ------------------------- ${getApplicationDefines(allDefines)} ` : `${sourceVersionDirective} `; break; } const hookFunctionMap = normalizeShaderHooks(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 = /^(v|f)s:(#)?([\w-]+)$/.exec(key); 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 (log3) { module.checkDeprecations(coreSource, log3); } const moduleSource = module.getModuleSource(stage); assembledSource += moduleSource; const injections = module.injections[stage]; for (const key in injections) { const match = /^(v|f)s:#([\w-]+)$/.exec(key); 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 += "// ----- MAIN SHADER SOURCE -------------------------"; assembledSource += INJECT_SHADER_DECLARATIONS; assembledSource = injectShader(assembledSource, stage, declInjections); assembledSource += getShaderHooks(hookFunctionMap[stage], hookInjections); assembledSource += coreSource; assembledSource = injectShader(assembledSource, stage, mainInjections); if (language === "glsl" && sourceVersion !== targetVersion) { assembledSource = transpileGLSLShader(assembledSource, stage); } return assembledSource.trim(); } function assembleGetUniforms(modules) { return function getUniforms9(opts) { const uniforms = {}; for (const module of modules) { const moduleUniforms = module.getUniforms(opts, uniforms); Object.assign(uniforms, moduleUniforms); } return uniforms; }; } function getShaderNameDefine(options) { const { id, source, stage } = options; const injectShaderName = id && source.indexOf("SHADER_NAME") === -1; return injectShaderName ? ` #define SHADER_NAME ${id}_${stage} ` : ""; } function getApplicationDefines(defines = {}) { let sourceText = ""; for (const define in defines) { const value = defines[define]; if (value || Number.isFinite(value)) { sourceText += `#define ${define.toUpperCase()} ${defines[define]} `; } } return sourceText; } // src/lib/shader-assembler.ts var _ShaderAssembler = class { /** Hook functions */ _hookFunctions = []; /** Shader modules */ _defaultModules = []; /** * A default shader assembler instance - the natural place to register default modules and hooks * @returns */ static getDefaultShaderAssembler() { _ShaderAssembler.defaultShaderAssembler = _ShaderAssembler.defaultShaderAssembler || new _ShaderAssembler(); return _ShaderAssembler.defaultShaderAssembler; } /** * Add a default module that does not have to be provided with every call to assembleShaders() */ addDefaultModule(module) { if (!this._defaultModules.find( (m) => m.name === (typeof module === "string" ? module : module.name) )) { this._defaultModules.push(module); } } /** * Remove a default module */ removeDefaultModule(module) { const moduleName = typeof module === "string" ? module : module.name; this._defaultModules = this._defaultModules.filter((m) => m.name !== moduleName); } /** * Register a shader hook * @param hook * @param opts */ addShaderHook(hook, opts) { if (opts) { hook = Object.assign(opts, { hook }); } this._hookFunctions.push(hook); } /** * Assemble a pair of shaders into a single shader program * @param platformInfo * @param props * @returns */ assembleShader(props) { const modules = this._getModuleList(props.modules); const hookFunctions = this._hookFunctions; const options = selectShaders(props); const assembled = assembleShaderWGSL({ platformInfo: props.platformInfo, ...options, modules, hookFunctions }); return { ...assembled, modules }; } /** * Assemble a pair of shaders into a single shader program * @param platformInfo * @param props * @returns */ assembleShaderPair(props) { const options = selectShaders(props); const modules = this._getModuleList(props.modules); const hookFunctions = this._hookFunctions; const { platformInfo } = props; const isWGSL = props.platformInfo.shaderLanguage === "wgsl"; const assembled = isWGSL ? assembleShaderPairWGSL({ platformInfo, ...options, modules, hookFunctions }) : assembleShaderPairGLSL({ platformInfo, ...options, modules, hookFunctions }); return { ...assembled, modules }; } /** * Dedupe and combine with default modules */ _getModuleList(appModules = []) { const modules = new Array( this._defaultModules.length + appModules.length ); const seen = {}; let count = 0; for (let i = 0, len = this._defaultModules.length; i < len; ++i) { const module = this._defaultModules[i]; const name = module.name; modules[count++] = module; seen[name] = true; } for (let i = 0, len = appModules.length; i < len; ++i) { const module = appModules[i]; const name = module.name; if (!seen[name]) { modules[count++] = module; seen[name] = true; } } modules.length = count; return ShaderModuleInstance.instantiateModules(modules); } }; var ShaderAssembler = _ShaderAssembler; /** Default ShaderAssembler instance */ __publicField(ShaderAssembler, "defaultShaderAssembler"); // src/lib/shader-module/normalize-shader-module.ts function normalizeShaderModule(module) { if (!module.normalized) { module.normalized = true; if (module.uniformPropTypes && !module.getUniforms) { const shaderModule = new ShaderModuleInstance(module); module.getUniforms = shaderModule.getUniforms.bind(shaderModule); } } return module; } // src/lib/glsl-utils/shader-utils.ts var FS_GLES = glsl`\ out vec4 transform_output; void main() { transform_output = vec4(0); }`; var FS300 = `#version 300 es ${FS_GLES}`; function getQualifierDetails(line, qualifiers) { qualifiers = Array.isArray(qualifiers) ? qualifiers : [qualifiers]; const words = line.replace(/^\s+/, "").split(/\s+/); const [qualifier, type, definition] = words; if (!qualifiers.includes(qualifier) || !type || !definition) { return null; } const name = definition.split(";")[0]; return { qualifier, type, name }; } function getPassthroughFS(options) { const { input, inputChannels, output } = options || {}; if (!input) { return FS300; } if (!inputChannels) { throw new Error("inputChannels"); } const inputType = channelCountToType(inputChannels); const outputValue = convertToVec4(input, inputChannels); return `#version 300 es in ${inputType} ${input}; out vec4 ${output}; void main() { ${output} = ${outputValue}; }`; } function typeToChannelSuffix(type) { switch (type) { case "float": return "x"; case "vec2": return "xy"; case "vec3": return "xyz"; case "vec4": return "xyzw"; default: throw new Error(type); } } function typeToChannelCount(type) { switch (type) { case "float": return 1; case "vec2": return 2; case "vec3": return 3; case "vec4": return 4; default: throw new Error(type); } } function channelCountToType(channels) { switch (channels) { case 1: return "float"; case 2: return "vec2"; case 3: return "vec3"; case 4: return "vec4"; default: throw new Error(`invalid channels: ${channels}`); } } function convertToVec4(variable, channels) { switch (channels) { case 1: return `vec4(${variable}, 0.0, 0.0, 1.0)`; case 2: return `vec4(${variable}, 0.0, 1.0)`; case 3: return `vec4(${variable}, 1.0)`; case 4: return variable; default: throw new Error(`invalid channels: ${channels}`); } } // src/lib/shader-generator/utils/capitalize.ts function capitalize(str) { return typeof str === "string" ? str.charAt(0).toUpperCase() + str.slice(1) : str; } // src/lib/shader-generator/glsl/generate-glsl.ts function generateGLSLForModule(module, options) { return generateGLSLUniformDeclarations(module, options); } function generateGLSLUniformDeclarations(module, options) { const glsl2 = []; switch (options.uniforms) { case "scoped-interface-blocks": case "unscoped-interface-blocks": glsl2.push(`uniform ${capitalize(module.name)} {`); break; case "uniforms": } for (const [uniformName, uniformFormat] of Object.entries(module.uniformTypes || {})) { const glslUniformType = getGLSLUniformType(uniformFormat); switch (options.uniforms) { case "scoped-interface-blocks": glsl2.push(` ${glslUniformType} ${uniformName};`); break; case "unscoped-interface-blocks": glsl2.push(` ${glslUniformType} ${module.name}_${uniformName};`); break; case "uniforms": glsl2.push(`uniform ${glslUniformType} ${module.name}_${uniformName};`); } } switch (options.uniforms) { case "scoped-interface-blocks": glsl2.push(`} ${module.name};`); break; case "unscoped-interface-blocks": glsl2.push("};"); break; case "uniforms": } glsl2.push(""); return glsl2.join("\n"); } function getGLSLUniformType(uniformFormat) { const UNIFORM_TYPE_TO_GLSL = { f32: "float", i32: "int", u32: "uint", "vec2": "vec2", "vec3": "vec3", "vec4": "vec4", "vec2": "ivec2", "vec3": "ivec3", "vec4": "ivec4", "vec2": "uvec2", "vec3": "uvec3", "vec4": "uvec4", "mat2x2": "mat2", "mat2x3": "mat2x3", "mat2x4": "mat2x4", "mat3x2": "mat3x2", "mat3x3": "mat3", "mat3x4": "mat3x4", "mat4x2": "mat4x2", "mat4x3": "mat4x3", "mat4x4": "mat4" }; const glsl2 = UNIFORM_TYPE_TO_GLSL[uniformFormat]; return glsl2; } // src/lib/shader-generator/wgsl/generate-wgsl.ts function generateWGSLForModule(module, options) { return generateWGSLUniformDeclarations(module, options); } function generateWGSLUniformDeclarations(module, options) { const wgsl = []; wgsl.push(`struct ${capitalize(module.name)} {`); for (const [uniformName, uniformFormat] of Object.entries(module?.uniformTypes || {})) { const wgslUniformType = uniformFormat; wgsl.push(` ${uniformName} : ${wgslUniformType};`); } wgsl.push("};"); wgsl.push(`var ${module.name} : ${capitalize(module.name)};`); return wgsl.join("\n"); } // src/lib/shader-generator/generate-shader.ts function generateShaderForModule(module, options) { switch (options.shaderLanguage) { case "glsl": return generateGLSLForModule(module, options); case "wgsl": return generateWGSLForModule(module, options); } } // src/lib/wgsl/get-shader-layout-wgsl.ts var import_core = __toESM(require_core(), 1); // ../../node_modules/wgsl_reflect/wgsl_reflect.module.js var ParseContext = class { constructor() { this.constants = /* @__PURE__ */ new Map(); this.aliases = /* @__PURE__ */ new Map(); this.structs = /* @__PURE__ */ new Map(); } }; var Node = class { constructor() { } get isAstNode() { return true; } get astNodeType() { return ""; } evaluate(context) { throw new Error("Cannot evaluate node"); } evaluateString(context) { return this.evaluate(context).toString(); } search(callback) { } searchBlock(block, callback) { if (block) { callback(_BlockStart.instance); for (const node of block) { if (node instanceof Array) { this.searchBlock(node, callback); } else { node.search(callback); } } callback(_BlockEnd.instance); } } }; var _BlockStart = class extends Node { }; _BlockStart.instance = new _BlockStart(); var _BlockEnd = class extends Node { }; _BlockEnd.instance = new _BlockEnd(); var Statement = class extends Node { constructor() { super(); } }; var Function = class extends Statement { constructor(name, args, returnType, body, startLine, endLine) { super(); this.calls = /* @__PURE__ */ new Set(); this.name = name; this.args = args; this.returnType = returnType; this.body = body; this.startLine = startLine; this.endLine = endLine; } get astNodeType() { return "function"; } search(callback) { this.searchBlock(this.body, callback); } }; var StaticAssert = class extends Statement { constructor(expression) { super(); this.expression = expression; } get astNodeType() { return "staticAssert"; } search(callback) { this.expression.search(callback); } }; var While = class extends Statement { constructor(condition, body) { super(); this.condition = condition; this.body = body; } get astNodeType() { return "while"; } search(callback) { this.condition.search(callback); this.searchBlock(this.body, callback); } }; var Continuing = class extends Statement { constructor(body) { super(); this.body = body; } get astNodeType() { return "continuing"; } search(callback) { this.searchBlock(this.body, callback); } }; var For = class extends Statement { constructor(init, condition, increment, body) { super(); this.init = init; this.condition = condition; this.increment = increment; this.body = body; } get astNodeType() { return "for"; } search(callback) { var _a2, _b, _c; (_a2 = this.init) === null || _a2 === void 0 ? void 0 : _a2.search(callback); (_b = this.condition) === null || _b === void 0 ? void 0 : _b.search(callback); (_c = this.increment) === null || _c === void 0 ? void 0 : _c.search(callback); this.searchBlock(this.body, callback); } }; var Var = class extends Statement { constructor(name, type, storage, access, value) { super(); this.name = name; this.type = type; this.storage = storage; this.access = access; this.value = value; } get astNodeType() { return "var"; } search(callback) { var _a2; callback(this); (_a2 = this.value) === null || _a2 === void 0 ? void 0 : _a2.search(callback); } }; var Override = class extends Statement { constructor(name, type, value) { super(); this.name = name; this.type = type; this.value = value; } get astNodeType() { return "override"; } search(callback) { var _a2; (_a2 = this.value) === null || _a2 === void 0 ? void 0 : _a2.search(callback); } }; var Let = class extends Statement { constructor(name, type, storage, access, value) { super(); this.name = name; this.type = type; this.storage = storage; this.access = access; this.value = value; } get astNodeType() { return "let"; } search(callback) { var _a2; callback(this); (_a2 = this.value) === null || _a2 === void 0 ? void 0 : _a2.search(callback); } }; var Const = class extends Statement { constructor(name, type, storage, access, value) { super(); this.name = name; this.type = type; this.storage = storage; this.access = access; this.value = value; } get astNodeType() { return "const"; } evaluate(context) { return this.value.evaluate(context); } search(callback) { var _a2; callback(this); (_a2 = this.value) === null || _a2 === void 0 ? void 0 : _a2.search(callback); } }; var IncrementOperator; (function(IncrementOperator2) { IncrementOperator2["increment"] = "++"; IncrementOperator2["decrement"] = "--"; })(IncrementOperator || (IncrementOperator = {})); (function(IncrementOperator2) { function parse(val) { const key = val; if (key == "parse") throw new Error("Invalid value for IncrementOperator"); return IncrementOperator2[key]; } IncrementOperator2.parse = parse; })(IncrementOperator || (IncrementOperator = {})); var Increment = class extends Statement { constructor(operator, variable) { super(); this.operator = operator; this.variable = variable; } get astNodeType() { return "increment"; } search(callback) { this.variable.search(callback); } }; var AssignOperator; (function(AssignOperator2) { AssignOperator2["assign"] = "="; AssignOperator2["addAssign"] = "+="; AssignOperator2["subtractAssin"] = "-="; AssignOperator2["multiplyAssign"] = "*="; AssignOperator2["divideAssign"] = "/="; AssignOperator2["moduloAssign"] = "%="; AssignOperator2["andAssign"] = "&="; AssignOperator2["orAssign"] = "|="; AssignOperator2["xorAssign"] = "^="; AssignOperator2["shiftLeftAssign"] = "<<="; AssignOperator2["shiftRightAssign"] = ">>="; })(AssignOperator || (AssignOperator = {})); (function(AssignOperator2) { function parse(val) { const key = val; if (key == "parse") { throw new Error("Invalid value for AssignOperator"); } return key; } AssignOperator2.parse = parse; })(AssignOperator || (AssignOperator = {})); var Assign = class extends Statement { constructor(operator, variable, value) { super(); this.operator = operator; this.variable = variable; this.value = value; } get astNodeType() { return "assign"; } search(callback) { this.variable.search(callback); this.value.search(callback); } }; var Call = class extends Statement { constructor(name, args) { super(); this.name = name; this.args = args; } get astNodeType() { return "call"; } search(callback) { for (const node of this.args) { node.search(callback); } callback(this); } }; var Loop = class extends Statement { constructor(body, continuing) { super(); this.body = body; this.continuing = continuing; } get astNodeType() { return "loop"; } }; var Switch = class extends Statement { constructor(condition, body) { super(); this.condition = condition; this.body = body; } get astNodeType() { return "body"; } }; var If = class extends Statement { constructor(condition, body, elseif, _else) { super(); this.condition = condition; this.body = body; this.elseif = elseif; this.else = _else; } get astNodeType() { return "if"; } search(callback) { this.condition.search(callback); this.searchBlock(this.body, callback); this.searchBlock(this.elseif, callback); this.searchBlock(this.else, callback); } }; var Return = class extends Statement { constructor(value) { super(); this.value = value; } get astNodeType() { return "return"; } search(callback) { var _a2; (_a2 = this.value) === null || _a2 === void 0 ? void 0 : _a2.search(callback); } }; var Enable = class extends Statement { constructor(name) { super(); this.name = name; } get astNodeType() { return "enable"; } }; var Requires = class extends Statement { constructor(extensions) { super(); this.extensions = extensions; } get astNodeType() { return "requires"; } }; var Diagnostic = class extends Statement { constructor(severity, rule) { super(); this.severity = severity; this.rule = rule; } get astNodeType() { return "diagnostic"; } }; var Alias = class extends Statement { constructor(name, type) { super(); this.name = name; this.type = type; } get astNodeType() { return "alias"; } }; var Discard = class extends Statement { constructor() { super(); } get astNodeType() { return "discard"; } }; var Break = class extends Statement { constructor() { super(); } get astNodeType() { return "break"; } }; var Continue = class extends Statement { constructor() { super(); } get astNodeType() { return "continue"; } }; var Type = class extends Statement { constructor(name) { super(); this.name = name; } get astNodeType() { return "type"; } get isStruct() { return false; } get isArray() { return false; } }; var Struct = class extends Type { constructor(name, members, startLine, endLine) { super(name); this.members = members; this.startLine = startLine; this.endLine = endLine; } get astNodeType() { return "struct"; } get isStruct() { return true; } /// Return the index of the member with the given name, or -1 if not found. getMemberIndex(name) { for (let i = 0; i < this.members.length; i++) { if (this.members[i].name == name) return i; } return -1; } }; var TemplateType = class extends Type { constructor(name, format, access) { super(name); this.format = format; this.access = access; } get astNodeType() { return "template"; } }; var PointerType = class extends Type { constructor(name, storage, type, access) { super(name); this.storage = storage; this.type = type; this.access = access; } get astNodeType() { return "pointer"; } }; var ArrayType = class extends Type { constructor(name, attributes, format, count) { super(name); this.attributes = attributes; this.format = format; this.count = count; } get astNodeType() { return "array"; } get isArray() { return true; } }; var SamplerType = class extends Type { constructor(name, format, access) { super(name); this.format = format; this.access = access; } get astNodeType() { return "sampler"; } }; var Expression = class extends Node { constructor() { super(); } }; var StringExpr = class extends Expression { constructor(value) { super(); this.value = value; } get astNodeType() { return "stringExpr"; } toString() { return this.value; } evaluateString() { return this.value; } }; var CreateExpr = class extends Expression { constructor(type, args) { super(); this.type = type; this.args = args; } get astNodeType() { return "createExpr"; } search(callback) { callback(this); for (const node of this.args) { node.search(callback); } } }; var CallExpr = class extends Expression { constructor(name, args) { super(); this.name = name; this.args = args; } get astNodeType() { return "callExpr"; } evaluate(context) { switch (this.name) { case "abs": return Math.abs(this.args[0].evaluate(context)); case "acos": return Math.acos(this.args[0].evaluate(context)); case "acosh": return Math.acosh(this.args[0].evaluate(context)); case "asin": return Math.asin(this.args[0].evaluate(context)); case "asinh": return Math.asinh(this.args[0].evaluate(context)); case "atan": return Math.atan(this.args[0].evaluate(context)); case "atan2": return Math.atan2(this.args[0].evaluate(context), this.args[1].evaluate(context)); case "atanh": return Math.atanh(this.args[0].evaluate(context)); case "ceil": return Math.ceil(this.args[0].evaluate(context)); case "clamp": return Math.min(Math.max(this.args[0].evaluate(context), this.args[1].evaluate(context)), this.args[2].evaluate(context)); case "cos": return Math.cos(this.args[0].evaluate(context)); case "degrees": return this.args[0].evaluate(context) * 180 / Math.PI; case "distance": return Math.sqrt(Math.pow(this.args[0].evaluate(context) - this.args[1].evaluate(context), 2)); case "dot": case "exp": return Math.exp(this.args[0].evaluate(context)); case "exp2": return Math.pow(2, this.args[0].evaluate(context)); case "floor": return Math.floor(this.args[0].evaluate(context)); case "fma": return this.args[0].evaluate(context) * this.args[1].evaluate(context) + this.args[2].evaluate(context); case "fract": return this.args[0].evaluate(context) - Math.floor(this.args[0].evaluate(context)); case "inverseSqrt": return 1 / Math.sqrt(this.args[0].evaluate(context)); case "log": return Math.log(this.args[0].evaluate(context)); case "log2": return Math.log2(this.args[0].evaluate(context)); case "max": return Math.max(this.args[0].evaluate(context), this.args[1].evaluate(context)); case "min": return Math.min(this.args[0].evaluate(context), this.args[1].evaluate(context)); case "mix": return this.args[0].evaluate(context) * (1 - this.args[2].evaluate(context)) + this.args[1].evaluate(context) * this.args[2].evaluate(context); case "modf": return this.args[0].evaluate(context) - Math.floor(this.args[0].evaluate(context)); case "pow": return Math.pow(this.args[0].evaluate(context), this.args[1].evaluate(context)); case "radians": return this.args[0].evaluate(context) * Math.PI / 180; case "round": return Math.round(this.args[0].evaluate(context)); case "sign": return Math.sign(this.args[0].evaluate(context)); case "sin": return Math.sin(this.args[0].evaluate(context)); case "sinh": return Math.sinh(this.args[0].evaluate(context)); case "saturate": return Math.min(Math.max(this.args[0].evaluate(context), 0), 1); case "smoothstep": return this.args[0].evaluate(context) * this.args[0].evaluate(context) * (3 - 2 * this.args[0].evaluate(context)); case "sqrt": return Math.sqrt(this.args[0].evaluate(context)); case "step": return this.args[0].evaluate(context) < this.args[1].evaluate(context) ? 0 : 1; case "tan": return Math.tan(this.args[0].evaluate(context)); case "tanh": return Math.tanh(this.args[0].evaluate(context)); case "trunc": return Math.trunc(this.args[0].evaluate(context)); default: throw new Error("Non const function: " + this.name); } } search(callback) { for (const node of this.args) { node.search(callback); } callback(this); } }; var VariableExpr = class extends Expression { constructor(name) { super(); this.name = name; } get astNodeType() { return "varExpr"; } search(callback) { callback(this); if (this.postfix) { this.postfix.search(callback); } } evaluate(context) { const constant = context.constants.get(this.name); if (!constant) { throw new Error("Cannot evaluate node"); } return constant.evaluate(context); } }; var ConstExpr = class extends Expression { constructor(name, initializer) { super(); this.name = name; this.initializer = initializer; } get astNodeType() { return "constExpr"; } evaluate(context) { var _a2, _b; if (this.initializer instanceof CreateExpr) { const property = (_a2 = this.postfix) === null || _a2 === void 0 ? void 0 : _a2.evaluateString(context); const type = (_b = this.initializer.type) === null || _b === void 0 ? void 0 : _b.name; const struct = context.structs.get(type); const memberIndex = struct === null || struct === void 0 ? void 0 : struct.getMemberIndex(property); if (memberIndex != -1) { const value = this.initializer.args[memberIndex].evaluate(context); return value; } console.log(memberIndex); } return this.initializer.evaluate(context); } search(callback) { this.initializer.search(callback); } }; var LiteralExpr = class extends Expression { constructor(value) { super(); this.value = value; } get astNodeType() { return "literalExpr"; } evaluate() { return this.value; } }; var BitcastExpr = class extends Expression { constructor(type, value) { super(); this.type = type; this.value = value; } get astNodeType() { return "bitcastExpr"; } search(callback) { this.value.search(callback); } }; var TypecastExpr = class extends Expression { constructor(type, args) { super(); this.type = type; this.args = args; } get astNodeType() { return "typecastExpr"; } evaluate(context) { return this.args[0].evaluate(context); } search(callback) { this.searchBlock(this.args, callback); } }; var GroupingExpr = class extends Expression { constructor(contents) { super(); this.contents = contents; } get astNodeType() { return "groupExpr"; } evaluate(context) { return this.contents[0].evaluate(context); } search(callback) { this.searchBlock(this.contents, callback); } }; var ArrayIndex = class extends Expression { constructor(index2) { super(); this.index = index2; } search(callback) { this.index.search(callback); } }; var Operator = class extends Expression { constructor() { super(); } }; var UnaryOperator = class extends Operator { constructor(operator, right) { super(); this.operator = operator; this.right = right; } get astNodeType() { return "unaryOp"; } evaluate(context) { switch (this.operator) { case "+": return this.right.evaluate(context); case "-": return -this.right.evaluate(context); case "!": return this.right.evaluate(context) ? 0 : 1; case "~": return ~this.right.evaluate(context); default: throw new Error("Unknown unary operator: " + this.operator); } } search(callback) { this.right.search(callback); } }; var BinaryOperator = class extends Operator { constructor(operator, left, right) { super(); this.operator = operator; this.left = left; this.right = right; } get astNodeType() { return "binaryOp"; } evaluate(context) { switch (this.operator) { case "+": return this.left.evaluate(context) + this.right.evaluate(context); case "-": return this.left.evaluate(context) - this.right.evaluate(context); case "*": return this.left.evaluate(context) * this.right.evaluate(context); case "/": return this.left.evaluate(context) / this.right.evaluate(context); case "%": return this.left.evaluate(context) % this.right.evaluate(context); case "==": return this.left.evaluate(context) == this.right.evaluate(context) ? 1 : 0; case "!=": return this.left.evaluate(context) != this.right.evaluate(context) ? 1 : 0; case "<": return this.left.evaluate(context) < this.right.evaluate(context) ? 1 : 0; case ">": return this.left.evaluate(context) > this.right.evaluate(context) ? 1 : 0; case "<=": return this.left.evaluate(context) <= this.right.evaluate(context) ? 1 : 0; case ">=": return this.left.evaluate(context) >= this.right.evaluate(context) ? 1 : 0; case "&&": return this.left.evaluate(context) && this.right.evaluate(context) ? 1 : 0; case "||": return this.left.evaluate(context) || this.right.evaluate(context) ? 1 : 0; default: throw new Error(`Unknown operator ${this.operator}`); } } search(callback) { this.left.search(callback); this.right.search(callback); } }; var SwitchCase = class extends Node { constructor() { super(); } }; var Case = class extends SwitchCase { constructor(selector, body) { super(); this.selector = selector; this.body = body; } get astNodeType() { return "case"; } search(callback) { this.searchBlock(this.body, callback); } }; var Default = class extends SwitchCase { constructor(body) { super(); this.body = body; } get astNodeType() { return "default"; } search(callback) { this.searchBlock(this.body, callback); } }; var Argument = class extends Node { constructor(name, type, attributes) { super(); this.name = name; this.type = type; this.attributes = attributes; } get astNodeType() { return "argument"; } }; var ElseIf = class extends Node { constructor(condition, body) { super(); this.condition = condition; this.body = body; } get astNodeType() { return "elseif"; } search(callback) { this.condition.search(callback); this.searchBlock(this.body, callback); } }; var Member = class extends Node { constructor(name, type, attributes) { super(); this.name = name; this.type = type; this.attributes = attributes; } get astNodeType() { return "member"; } }; var Attribute = class extends Node { constructor(name, value) { super(); this.name = name; this.value = value; } get astNodeType() { return "attribute"; } }; var _a; var TokenClass; (function(TokenClass2) { TokenClass2[TokenClass2["token"] = 0] = "token"; TokenClass2[TokenClass2["keyword"] = 1] = "keyword"; TokenClass2[TokenClass2["reserved"] = 2] = "reserved"; })(TokenClass || (TokenClass = {})); var TokenType = class { constructor(name, type, rule) { this.name = name; this.type = type; this.rule = rule; } toString() { return this.name; } }; var TokenTypes = class { }; _a = TokenTypes; TokenTypes.none = new TokenType("", TokenClass.reserved, ""); TokenTypes.eof = new TokenType("EOF", TokenClass.token, ""); TokenTypes.reserved = { asm: new TokenType("asm", TokenClass.reserved, "asm"), bf16: new TokenType("bf16", TokenClass.reserved, "bf16"), do: new TokenType("do", TokenClass.reserved, "do"), enum: new TokenType("enum", TokenClass.reserved, "enum"), f16: new TokenType("f16", TokenClass.reserved, "f16"), f64: new TokenType("f64", TokenClass.reserved, "f64"), handle: new TokenType("handle", TokenClass.reserved, "handle"), i8: new TokenType("i8", TokenClass.reserved, "i8"), i16: new TokenType("i16", TokenClass.reserved, "i16"), i64: new TokenType("i64", TokenClass.reserved, "i64"), mat: new TokenType("mat", TokenClass.reserved, "mat"), premerge: new TokenType("premerge", TokenClass.reserved, "premerge"), regardless: new TokenType("regardless", TokenClass.reserved, "regardless"), typedef: new TokenType("typedef", TokenClass.reserved, "typedef"), u8: new TokenType("u8", TokenClass.reserved, "u8"), u16: new TokenType("u16", TokenClass.reserved, "u16"), u64: new TokenType("u64", TokenClass.reserved, "u64"), unless: new TokenType("unless", TokenClass.reserved, "unless"), using: new TokenType("using", TokenClass.reserved, "using"), vec: new TokenType("vec", TokenClass.reserved, "vec"), void: new TokenType("void", TokenClass.reserved, "void") }; TokenTypes.keywords = { array: new TokenType("array", TokenClass.keyword, "array"), atomic: new TokenType("atomic", TokenClass.keyword, "atomic"), bool: new TokenType("bool", TokenClass.keyword, "bool"), f32: new TokenType("f32", TokenClass.keyword, "f32"), i32: new TokenType("i32", TokenClass.keyword, "i32"), mat2x2: new TokenType("mat2x2", TokenClass.keyword, "mat2x2"), mat2x3: new TokenType("mat2x3", TokenClass.keyword, "mat2x3"), mat2x4: new TokenType("mat2x4", TokenClass.keyword, "mat2x4"), mat3x2: new TokenType("mat3x2", TokenClass.keyword, "mat3x2"), mat3x3: new TokenType("mat3x3", TokenClass.keyword, "mat3x3"), mat3x4: new TokenType("mat3x4", TokenClass.keyword, "mat3x4"), mat4x2: new TokenType("mat4x2", TokenClass.keyword, "mat4x2"), mat4x3: new TokenType("mat4x3", TokenClass.keyword, "mat4x3"), mat4x4: new TokenType("mat4x4", TokenClass.keyword, "mat4x4"), ptr: new TokenType("ptr", TokenClass.keyword, "ptr"), sampler: new TokenType("sampler", TokenClass.keyword, "sampler"), sampler_comparison: new TokenType("sampler_comparison", TokenClass.keyword, "sampler_comparison"), struct: new TokenType("struct", TokenClass.keyword, "struct"), texture_1d: new TokenType("texture_1d", TokenClass.keyword, "texture_1d"), texture_2d: new TokenType("texture_2d", TokenClass.keyword, "texture_2d"), texture_2d_array: new TokenType("texture_2d_array", TokenClass.keyword, "texture_2d_array"), texture_3d: new TokenType("texture_3d", TokenClass.keyword, "texture_3d"), texture_cube: new TokenType("texture_cube", TokenClass.keyword, "texture_cube"), texture_cube_array: new TokenType("texture_cube_array", TokenClass.keyword, "texture_cube_array"), texture_multisampled_2d: new TokenType("texture_multisampled_2d", TokenClass.keyword, "texture_multisampled_2d"), texture_storage_1d: new TokenType("texture_storage_1d", TokenClass.keyword, "texture_storage_1d"), texture_storage_2d: new TokenType("texture_storage_2d", TokenClass.keyword, "texture_storage_2d"), texture_storage_2d_array: new TokenType("texture_storage_2d_array", TokenClass.keyword, "texture_storage_2d_array"), texture_storage_3d: new TokenType("texture_storage_3d", TokenClass.keyword, "texture_storage_3d"), texture_depth_2d: new TokenType("texture_depth_2d", TokenClass.keyword, "texture_depth_2d"), texture_depth_2d_array: new TokenType("texture_depth_2d_array", TokenClass.keyword, "texture_depth_2d_array"), texture_depth_cube: new TokenType("texture_depth_cube", TokenClass.keyword, "texture_depth_cube"), texture_depth_cube_array: new TokenType("texture_depth_cube_array", TokenClass.keyword, "texture_depth_cube_array"), texture_depth_multisampled_2d: new TokenType("texture_depth_multisampled_2d", TokenClass.keyword, "texture_depth_multisampled_2d"), texture_external: new TokenType("texture_external", TokenClass.keyword, "texture_external"), u32: new TokenType("u32", TokenClass.keyword, "u32"), vec2: new TokenType("vec2", TokenClass.keyword, "vec2"), vec3: new TokenType("vec3", TokenClass.keyword, "vec3"), vec4: new TokenType("vec4", TokenClass.keyword, "vec4"), bitcast: new TokenType("bitcast", TokenClass.keyword, "bitcast"), block: new TokenType("block", TokenClass.keyword, "block"), break: new TokenType("break", TokenClass.keyword, "break"), case: new TokenType("case", TokenClass.keyword, "case"), continue: new TokenType("continue", TokenClass.keyword, "continue"), continuing: new TokenType("continuing", TokenClass.keyword, "continuing"), default: new TokenType("default", TokenClass.keyword, "default"), diagnostic: new TokenType("diagnostic", TokenClass.keyword, "diagnostic"), discard: new TokenType("discard", TokenClass.keyword, "discard"), else: new TokenType("else", TokenClass.keyword, "else"), enable: new TokenType("enable", TokenClass.keyword, "enable"), fallthrough: new TokenType("fallthrough", TokenClass.keyword, "fallthrough"), false: new TokenType("false", TokenClass.keyword, "false"), fn: new TokenType("fn", TokenClass.keyword, "fn"), for: new TokenType("for", TokenClass.keyword, "for"), function: new TokenType("function", TokenClass.keyword, "function"), if: new TokenType("if", TokenClass.keyword, "if"), let: new TokenType("let", TokenClass.keyword, "let"), const: new TokenType("const", TokenClass.keyword, "const"), loop: new TokenType("loop", TokenClass.keyword, "loop"), while: new TokenType("while", TokenClass.keyword, "while"), private: new TokenType("private", TokenClass.keyword, "private"), read: new TokenType("read", TokenClass.keyword, "read"), read_write: new TokenType("read_write", TokenClass.keyword, "read_write"), return: new TokenType("return", TokenClass.keyword, "return"), requires: new TokenType("requires", TokenClass.keyword, "requires"), storage: new TokenType("storage", TokenClass.keyword, "storage"), switch: new TokenType("switch", TokenClass.keyword, "switch"), true: new TokenType("true", TokenClass.keyword, "true"), alias: new TokenType("alias", TokenClass.keyword, "alias"), type: new TokenType("type", TokenClass.keyword, "type"), uniform: new TokenType("uniform", TokenClass.keyword, "uniform"), var: new TokenType("var", TokenClass.keyword, "var"), override: new TokenType("override", TokenClass.keyword, "override"), workgroup: new TokenType("workgroup", TokenClass.keyword, "workgroup"), write: new TokenType("write", TokenClass.keyword, "write"), r8unorm: new TokenType("r8unorm", TokenClass.keyword, "r8unorm"), r8snorm: new TokenType("r8snorm", TokenClass.keyword, "r8snorm"), r8uint: new TokenType("r8uint", TokenClass.keyword, "r8uint"), r8sint: new TokenType("r8sint", TokenClass.keyword, "r8sint"), r16uint: new TokenType("r16uint", TokenClass.keyword, "r16uint"), r16sint: new TokenType("r16sint", TokenClass.keyword, "r16sint"), r16float: new TokenType("r16float", TokenClass.keyword, "r16float"), rg8unorm: new TokenType("rg8unorm", TokenClass.keyword, "rg8unorm"), rg8snorm: new TokenType("rg8snorm", TokenClass.keyword, "rg8snorm"), rg8uint: new TokenType("rg8uint", TokenClass.keyword, "rg8uint"), rg8sint: new TokenType("rg8sint", TokenClass.keyword, "rg8sint"), r32uint: new TokenType("r32uint", TokenClass.keyword, "r32uint"), r32sint: new TokenType("r32sint", TokenClass.keyword, "r32sint"), r32float: new TokenType("r32float", TokenClass.keyword, "r32float"), rg16uint: new TokenType("rg16uint", TokenClass.keyword, "rg16uint"), rg16sint: new TokenType("rg16sint", TokenClass.keyword, "rg16sint"), rg16float: new TokenType("rg16float", TokenClass.keyword, "rg16float"), rgba8unorm: new TokenType("rgba8unorm", TokenClass.keyword, "rgba8unorm"), rgba8unorm_srgb: new TokenType("rgba8unorm_srgb", TokenClass.keyword, "rgba8unorm_srgb"), rgba8snorm: new TokenType("rgba8snorm", TokenClass.keyword, "rgba8snorm"), rgba8uint: new TokenType("rgba8uint", TokenClass.keyword, "rgba8uint"), rgba8sint: new TokenType("rgba8sint", TokenClass.keyword, "rgba8sint"), bgra8unorm: new TokenType("bgra8unorm", TokenClass.keyword, "bgra8unorm"), bgra8unorm_srgb: new TokenType("bgra8unorm_srgb", TokenClass.keyword, "bgra8unorm_srgb"), rgb10a2unorm: new TokenType("rgb10a2unorm", TokenClass.keyword, "rgb10a2unorm"), rg11b10float: new TokenType("rg11b10float", TokenClass.keyword, "rg11b10float"), rg32uint: new TokenType("rg32uint", TokenClass.keyword, "rg32uint"), rg32sint: new TokenType("rg32sint", TokenClass.keyword, "rg32sint"), rg32float: new TokenType("rg32float", TokenClass.keyword, "rg32float"), rgba16uint: new TokenType("rgba16uint", TokenClass.keyword, "rgba16uint"), rgba16sint: new TokenType("rgba16sint", TokenClass.keyword, "rgba16sint"), rgba16float: new TokenType("rgba16float", TokenClass.keyword, "rgba16float"), rgba32uint: new TokenType("rgba32uint", TokenClass.keyword, "rgba32uint"), rgba32sint: new TokenType("rgba32sint", TokenClass.keyword, "rgba32sint"), rgba32float: new TokenType("rgba32float", TokenClass.keyword, "rgba32float"), static_assert: new TokenType("static_assert", TokenClass.keyword, "static_assert") // WGSL grammar has a few keywords that have different token names than the strings they // represent. Aliasing them here. /*int32: new TokenType("i32", TokenClass.keyword, "i32"), uint32: new TokenType("u32", TokenClass.keyword, "u32"), float32: new TokenType("f32", TokenClass.keyword, "f32"), pointer: new TokenType("ptr", TokenClass.keyword, "ptr"),*/ }; TokenTypes.tokens = { decimal_float_literal: new TokenType("decimal_float_literal", TokenClass.token, /((-?[0-9]*\.[0-9]+|-?[0-9]+\.[0-9]*)((e|E)(\+|-)?[0-9]+)?f?)|(-?[0-9]+(e|E)(\+|-)?[0-9]+f?)|([0-9]+f)/), hex_float_literal: new TokenType("hex_float_literal", TokenClass.token, /-?0x((([0-9a-fA-F]*\.[0-9a-fA-F]+|[0-9a-fA-F]+\.[0-9a-fA-F]*)((p|P)(\+|-)?[0-9]+f?)?)|([0-9a-fA-F]+(p|P)(\+|-)?[0-9]+f?))/), int_literal: new TokenType("int_literal", TokenClass.token, /-?0x[0-9a-fA-F]+|0i?|-?[1-9][0-9]*i?/), uint_literal: new TokenType("uint_literal", TokenClass.token, /0x[0-9a-fA-F]+u|0u|[1-9][0-9]*u/), ident: new TokenType("ident", TokenClass.token, /[_a-zA-Z][0-9a-zA-Z_]*/), and: new TokenType("and", TokenClass.token, "&"), and_and: new TokenType("and_and", TokenClass.token, "&&"), arrow: new TokenType("arrow ", TokenClass.token, "->"), attr: new TokenType("attr", TokenClass.token, "@"), forward_slash: new TokenType("forward_slash", TokenClass.token, "/"), bang: new TokenType("bang", TokenClass.token, "!"), bracket_left: new TokenType("bracket_left", TokenClass.token, "["), bracket_right: new TokenType("bracket_right", TokenClass.token, "]"), brace_left: new TokenType("brace_left", TokenClass.token, "{"), brace_right: new TokenType("brace_right", TokenClass.token, "}"), colon: new TokenType("colon", TokenClass.token, ":"), comma: new TokenType("comma", TokenClass.token, ","), equal: new TokenType("equal", TokenClass.token, "="), equal_equal: new TokenType("equal_equal", TokenClass.token, "=="), not_equal: new TokenType("not_equal", TokenClass.token, "!="), greater_than: new TokenType("greater_than", TokenClass.token, ">"), greater_than_equal: new TokenType("greater_than_equal", TokenClass.token, ">="), shift_right: new TokenType("shift_right", TokenClass.token, ">>"), less_than: new TokenType("less_than", TokenClass.token, "<"), less_than_equal: new TokenType("less_than_equal", TokenClass.token, "<="), shift_left: new TokenType("shift_left", TokenClass.token, "<<"), modulo: new TokenType("modulo", TokenClass.token, "%"), minus: new TokenType("minus", TokenClass.token, "-"), minus_minus: new TokenType("minus_minus", TokenClass.token, "--"), period: new TokenType("period", TokenClass.token, "."), plus: new TokenType("plus", TokenClass.token, "+"), plus_plus: new TokenType("plus_plus", TokenClass.token, "++"), or: new TokenType("or", TokenClass.token, "|"), or_or: new TokenType("or_or", TokenClass.token, "||"), paren_left: new TokenType("paren_left", TokenClass.token, "("), paren_right: new TokenType("paren_right", TokenClass.token, ")"), semicolon: new TokenType("semicolon", TokenClass.token, ";"), star: new TokenType("star", TokenClass.token, "*"), tilde: new TokenType("tilde", TokenClass.token, "~"), underscore: new TokenType("underscore", TokenClass.token, "_"), xor: new TokenType("xor", TokenClass.token, "^"), plus_equal: new TokenType("plus_equal", TokenClass.token, "+="), minus_equal: new TokenType("minus_equal", TokenClass.token, "-="), times_equal: new TokenType("times_equal", TokenClass.token, "*="), division_equal: new TokenType("division_equal", TokenClass.token, "/="), modulo_equal: new TokenType("modulo_equal", TokenClass.token, "%="), and_equal: new TokenType("and_equal", TokenClass.token, "&="), or_equal: new TokenType("or_equal", TokenClass.token, "|="), xor_equal: new TokenType("xor_equal", TokenClass.token, "^="), shift_right_equal: new TokenType("shift_right_equal", TokenClass.token, ">>="), shift_left_equal: new TokenType("shift_left_equal", TokenClass.token, "<<=") }; TokenTypes.simpleTokens = { "@": _a.tokens.attr, "{": _a.tokens.brace_left, "}": _a.tokens.brace_right, ":": _a.tokens.colon, ",": _a.tokens.comma, "(": _a.tokens.paren_left, ")": _a.tokens.paren_right, ";": _a.tokens.semicolon }; TokenTypes.literalTokens = { "&": _a.tokens.and, "&&": _a.tokens.and_and, "->": _a.tokens.arrow, "/": _a.tokens.forward_slash, "!": _a.tokens.bang, "[": _a.tokens.bracket_left, "]": _a.tokens.bracket_right, "=": _a.tokens.equal, "==": _a.tokens.equal_equal, "!=": _a.tokens.not_equal, ">": _a.tokens.greater_than, ">=": _a.tokens.greater_than_equal, ">>": _a.tokens.shift_right, "<": _a.tokens.less_than, "<=": _a.tokens.less_than_equal, "<<": _a.tokens.shift_left, "%": _a.tokens.modulo, "-": _a.tokens.minus, "--": _a.tokens.minus_minus, ".": _a.tokens.period, "+": _a.tokens.plus, "++": _a.tokens.plus_plus, "|": _a.tokens.or, "||": _a.tokens.or_or, "*": _a.tokens.star, "~": _a.tokens.tilde, "_": _a.tokens.underscore, "^": _a.tokens.xor, "+=": _a.tokens.plus_equal, "-=": _a.tokens.minus_equal, "*=": _a.tokens.times_equal, "/=": _a.tokens.division_equal, "%=": _a.tokens.modulo_equal, "&=": _a.tokens.and_equal, "|=": _a.tokens.or_equal, "^=": _a.tokens.xor_equal, ">>=": _a.tokens.shift_right_equal, "<<=": _a.tokens.shift_left_equal }; TokenTypes.regexTokens = { decimal_float_literal: _a.tokens.decimal_float_literal, hex_float_literal: _a.tokens.hex_float_literal, int_literal: _a.tokens.int_literal, uint_literal: _a.tokens.uint_literal, ident: _a.tokens.ident }; TokenTypes.storage_class = [ _a.keywords.function, _a.keywords.private, _a.keywords.workgroup, _a.keywords.uniform, _a.keywords.storage ]; TokenTypes.access_mode = [ _a.keywords.read, _a.keywords.write, _a.keywords.read_write ]; TokenTypes.sampler_type = [ _a.keywords.sampler, _a.keywords.sampler_comparison ]; TokenTypes.sampled_texture_type = [ _a.keywords.texture_1d, _a.keywords.texture_2d, _a.keywords.texture_2d_array, _a.keywords.texture_3d, _a.keywords.texture_cube, _a.keywords.texture_cube_array ]; TokenTypes.multisampled_texture_type = [ _a.keywords.texture_multisampled_2d ]; TokenTypes.storage_texture_type = [ _a.keywords.texture_storage_1d, _a.keywords.texture_storage_2d, _a.keywords.texture_storage_2d_array, _a.keywords.texture_storage_3d ]; TokenTypes.depth_texture_type = [ _a.keywords.texture_depth_2d, _a.keywords.texture_depth_2d_array, _a.keywords.texture_depth_cube, _a.keywords.texture_depth_cube_array, _a.keywords.texture_depth_multisampled_2d ]; TokenTypes.texture_external_type = [_a.keywords.texture_external]; TokenTypes.any_texture_type = [ ..._a.sampled_texture_type, ..._a.multisampled_texture_type, ..._a.storage_texture_type, ..._a.depth_texture_type, ..._a.texture_external_type ]; TokenTypes.texel_format = [ _a.keywords.r8unorm, _a.keywords.r8snorm, _a.keywords.r8uint, _a.keywords.r8sint, _a.keywords.r16uint, _a.keywords.r16sint, _a.keywords.r16float, _a.keywords.rg8unorm, _a.keywords.rg8snorm, _a.keywords.rg8uint, _a.keywords.rg8sint, _a.keywords.r32uint, _a.keywords.r32sint, _a.keywords.r32float, _a.keywords.rg16uint, _a.keywords.rg16sint, _a.keywords.rg16float, _a.keywords.rgba8unorm, _a.keywords.rgba8unorm_srgb, _a.keywords.rgba8snorm, _a.keywords.rgba8uint, _a.keywords.rgba8sint, _a.keywords.bgra8unorm, _a.keywords.bgra8unorm_srgb, _a.keywords.rgb10a2unorm, _a.keywords.rg11b10float, _a.keywords.rg32uint, _a.keywords.rg32sint, _a.keywords.rg32float, _a.keywords.rgba16uint, _a.keywords.rgba16sint, _a.keywords.rgba16float, _a.keywords.rgba32uint, _a.keywords.rgba32sint, _a.keywords.rgba32float ]; TokenTypes.const_literal = [ _a.tokens.int_literal, _a.tokens.uint_literal, _a.tokens.decimal_float_literal, _a.tokens.hex_float_literal, _a.keywords.true, _a.keywords.false ]; TokenTypes.literal_or_ident = [ _a.tokens.ident, _a.tokens.int_literal, _a.tokens.uint_literal, _a.tokens.decimal_float_literal, _a.tokens.hex_float_literal ]; TokenTypes.element_count_expression = [ _a.tokens.int_literal, _a.tokens.uint_literal, _a.tokens.ident ]; TokenTypes.template_types = [ _a.keywords.vec2, _a.keywords.vec3, _a.keywords.vec4, _a.keywords.mat2x2, _a.keywords.mat2x3, _a.keywords.mat2x4, _a.keywords.mat3x2, _a.keywords.mat3x3, _a.keywords.mat3x4, _a.keywords.mat4x2, _a.keywords.mat4x3, _a.keywords.mat4x4, _a.keywords.atomic, _a.keywords.bitcast, ..._a.any_texture_type ]; TokenTypes.attribute_name = [_a.tokens.ident, _a.keywords.block, _a.keywords.diagnostic]; TokenTypes.assignment_operators = [ _a.tokens.equal, _a.tokens.plus_equal, _a.tokens.minus_equal, _a.tokens.times_equal, _a.tokens.division_equal, _a.tokens.modulo_equal, _a.tokens.and_equal, _a.tokens.or_equal, _a.tokens.xor_equal, _a.tokens.shift_right_equal, _a.tokens.shift_left_equal ]; TokenTypes.increment_operators = [ _a.tokens.plus_plus, _a.tokens.minus_minus ]; var Token = class { constructor(type, lexeme, line) { this.type = type; this.lexeme = lexeme; this.line = line; } toString() { return this.lexeme; } isTemplateType() { return TokenTypes.template_types.indexOf(this.type) != -1; } isArrayType() { return this.type == TokenTypes.keywords.array; } isArrayOrTemplateType() { return this.isArrayType() || this.isTemplateType(); } }; var WgslScanner = class { constructor(source) { this._tokens = []; this._start = 0; this._current = 0; this._line = 1; this._source = source !== null && source !== void 0 ? source : ""; } /// Scan all tokens from the source. scanTokens() { while (!this._isAtEnd()) { this._start = this._current; if (!this.scanToken()) { throw `Invalid syntax at line ${this._line}`; } } this._tokens.push(new Token(TokenTypes.eof, "", this._line)); return this._tokens; } /// Scan a single token from the source. scanToken() { let lexeme = this._advance(); if (lexeme == "\n") { this._line++; return true; } if (this._isWhitespace(lexeme)) { return true; } if (lexeme == "/") { if (this._peekAhead() == "/") { while (lexeme != "\n") { if (this._isAtEnd()) { return true; } lexeme = this._advance(); } this._line++; return true; } else if (this._peekAhead() == "*") { this._advance(); let commentLevel = 1; while (commentLevel > 0) { if (this._isAtEnd()) { return true; } lexeme = this._advance(); if (lexeme == "\n") { this._line++; } else if (lexeme == "*") { if (this._peekAhead() == "/") { this._advance(); commentLevel--; if (commentLevel == 0) { return true; } } } else if (lexeme == "/") { if (this._peekAhead() == "*") { this._advance(); commentLevel++; } } } return true; } } const simpleToken = TokenTypes.simpleTokens[lexeme]; if (simpleToken) { this._addToken(simpleToken); return true; } let matchType = TokenTypes.none; const isAlpha = this._isAlpha(lexeme); const isUnderscore = lexeme === "_"; if (this._isAlphaNumeric(lexeme)) { let nextChar = this._peekAhead(); while (this._isAlphaNumeric(nextChar)) { lexeme += this._advance(); nextChar = this._peekAhead(); } } if (isAlpha) { const matchedType = TokenTypes.keywords[lexeme]; if (matchedType) { this._addToken(matchedType); return true; } } if (isAlpha || isUnderscore) { this._addToken(TokenTypes.tokens.ident); return true; } for (; ; ) { let matchedType = this._findType(lexeme); const nextLexeme = this._peekAhead(); if (lexeme == ">" && (nextLexeme == ">" || nextLexeme == "=")) { let foundLessThan = false; let ti = this._tokens.length - 1; for (let count = 0; count < 5 && ti >= 0; ++count, --ti) { if (this._tokens[ti].type === TokenTypes.tokens.less_than) { if (ti > 0 && this._tokens[ti - 1].isArrayOrTemplateType()) { foundLessThan = true; } break; } } if (foundLessThan) { this._addToken(matchedType); return true; } } if (matchedType === TokenTypes.none) { let lookAheadLexeme = lexeme; let lookAhead = 0; const maxLookAhead = 2; for (let li = 0; li < maxLookAhead; ++li) { lookAheadLexeme += this._peekAhead(li); matchedType = this._findType(lookAheadLexeme); if (matchedType !== TokenTypes.none) { lookAhead = li; break; } } if (matchedType === TokenTypes.none) { if (matchType === TokenTypes.none) { return false; } this._current--; this._addToken(matchType); return true; } lexeme = lookAheadLexeme; this._current += lookAhead + 1; } matchType = matchedType; if (this._isAtEnd()) { break; } lexeme += this._advance(); } if (matchType === TokenTypes.none) { return false; } this._addToken(matchType); return true; } _findType(lexeme) { for (const name in TokenTypes.regexTokens) { const type2 = TokenTypes.regexTokens[name]; if (this._match(lexeme, type2.rule)) { return type2; } } const type = TokenTypes.literalTokens[lexeme]; if (type) { return type; } return TokenTypes.none; } _match(lexeme, rule) { const match = rule.exec(lexeme); return match && match.index == 0 && match[0] == lexeme; } _isAtEnd() { return this._current >= this._source.length; } _isAlpha(c) { return c >= "a" && c <= "z" || c >= "A" && c <= "Z"; } _isAlphaNumeric(c) { return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c == "_" || c >= "0" && c <= "9"; } _isWhitespace(c) { return c == " " || c == " " || c == "\r"; } _advance(amount = 0) { let c = this._source[this._current]; amount = amount || 0; amount++; this._current += amount; return c; } _peekAhead(offset = 0) { offset = offset || 0; if (this._current + offset >= this._source.length) { return "\0"; } return this._source[this._current + offset]; } _addToken(type) { const text = this._source.substring(this._start, this._current); this._tokens.push(new Token(type, text, this._line)); } }; var WgslParser = class { constructor() { this._tokens = []; this._current = 0; this._currentLine = 0; this._context = new ParseContext(); this._deferArrayCountEval = []; } parse(tokensOrCode) { this._initialize(tokensOrCode); this._deferArrayCountEval.length = 0; const statements = []; while (!this._isAtEnd()) { const statement = this._global_decl_or_directive(); if (!statement) { break; } statements.push(statement); } if (this._deferArrayCountEval.length > 0) { for (const arrayDecl of this._deferArrayCountEval) { const arrayType = arrayDecl["arrayType"]; const countNode = arrayDecl["countNode"]; if (countNode instanceof VariableExpr) { const variable = countNode; const name = variable.name; const constant = this._context.constants.get(name); if (constant) { try { const count = constant.evaluate(this._context); arrayType.count = count; } catch (e) { } } } } this._deferArrayCountEval.length = 0; } return statements; } _initialize(tokensOrCode) { if (tokensOrCode) { if (typeof tokensOrCode == "string") { const scanner = new WgslScanner(tokensOrCode); this._tokens = scanner.scanTokens(); } else { this._tokens = tokensOrCode; } } else { this._tokens = []; } this._current = 0; } _error(token, message) { return { token, message, toString: function() { return `${message}`; } }; } _isAtEnd() { return this._current >= this._tokens.length || this._peek().type == TokenTypes.eof; } _match(types) { if (types instanceof TokenType) { if (this._check(types)) { this._advance(); return true; } return false; } for (let i = 0, l = types.length; i < l; ++i) { const type = types[i]; if (this._check(type)) { this._advance(); return true; } } return false; } _consume(types, message) { if (this._check(types)) { return this._advance(); } throw this._error(this._peek(), message); } _check(types) { if (this._isAtEnd()) { return false; } const tk = this._peek(); if (types instanceof Array) { const t = tk.type; const index2 = types.indexOf(t); return index2 != -1; } return tk.type == types; } _advance() { var _a2, _b; this._currentLine = (_b = (_a2 = this._peek()) === null || _a2 === void 0 ? void 0 : _a2.line) !== null && _b !== void 0 ? _b : -1; if (!this._isAtEnd()) { this._current++; } return this._previous(); } _peek() { return this._tokens[this._current]; } _previous() { return this._tokens[this._current - 1]; } _global_decl_or_directive() { while (this._match(TokenTypes.tokens.semicolon) && !this._isAtEnd()) ; if (this._match(TokenTypes.keywords.alias)) { const type = this._type_alias(); this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); return type; } if (this._match(TokenTypes.keywords.diagnostic)) { const directive = this._diagnostic(); this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); return directive; } if (this._match(TokenTypes.keywords.requires)) { const requires = this._requires_directive(); this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); return requires; } if (this._match(TokenTypes.keywords.enable)) { const enable = this._enable_directive(); this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); return enable; } const attrs = this._attribute(); if (this._check(TokenTypes.keywords.var)) { const _var = this._global_variable_decl(); if (_var != null) { _var.attributes = attrs; } this._consume(TokenTypes.tokens.semicolon, "Expected ';'."); return _var; } if (this._check(TokenTypes.keywords.override)) { const _override = this._override_variable_decl(); if (_override != null) { _override.attributes = attrs; } this._consume(TokenTypes.tokens.semicolon, "Expected ';'."); return _override; } if (this._check(TokenTypes.keywords.let)) { const _let = this._global_let_decl(); if (_let != null) { _let.attributes = attrs; } this._consume(TokenTypes.tokens.semicolon, "Expected ';'."); return _let; } if (this._check(TokenTypes.keywords.const)) { const _const = this._global_const_decl(); if (_const != null) { _const.attributes = attrs; } this._consume(TokenTypes.tokens.semicolon, "Expected ';'."); return _const; } if (this._check(TokenTypes.keywords.struct)) { const _struct = this._struct_decl(); if (_struct != null) { _struct.attributes = attrs; } return _struct; } if (this._check(TokenTypes.keywords.fn)) { const _fn = this._function_decl(); if (_fn != null) { _fn.attributes = attrs; } return _fn; } return null; } _function_decl() { if (!this._match(TokenTypes.keywords.fn)) { return null; } const startLine = this._currentLine; const name = this._consume(TokenTypes.tokens.ident, "Expected function name.").toString(); this._consume(TokenTypes.tokens.paren_left, "Expected '(' for function arguments."); const args = []; if (!this._check(TokenTypes.tokens.paren_right)) { do { if (this._check(TokenTypes.tokens.paren_right)) { break; } const argAttrs = this._attribute(); const name2 = this._consume(TokenTypes.tokens.ident, "Expected argument name.").toString(); this._consume(TokenTypes.tokens.colon, "Expected ':' for argument type."); const typeAttrs = this._attribute(); const type = this._type_decl(); if (type != null) { type.attributes = typeAttrs; args.push(new Argument(name2, type, argAttrs)); } } while (this._match(TokenTypes.tokens.comma)); } this._consume(TokenTypes.tokens.paren_right, "Expected ')' after function arguments."); let _return = null; if (this._match(TokenTypes.tokens.arrow)) { const attrs = this._attribute(); _return = this._type_decl(); if (_return != null) { _return.attributes = attrs; } } const body = this._compound_statement(); const endLine = this._currentLine; return new Function(name, args, _return, body, startLine, endLine); } _compound_statement() { const statements = []; this._consume(TokenTypes.tokens.brace_left, "Expected '{' for block."); while (!this._check(TokenTypes.tokens.brace_right)) { const statement = this._statement(); if (statement !== null) { statements.push(statement); } } this._consume(TokenTypes.tokens.brace_right, "Expected '}' for block."); return statements; } _statement() { while (this._match(TokenTypes.tokens.semicolon) && !this._isAtEnd()) ; if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } if (this._check(TokenTypes.keywords.if)) { return this._if_statement(); } if (this._check(TokenTypes.keywords.switch)) { return this._switch_statement(); } if (this._check(TokenTypes.keywords.loop)) { return this._loop_statement(); } if (this._check(TokenTypes.keywords.for)) { return this._for_statement(); } if (this._check(TokenTypes.keywords.while)) { return this._while_statement(); } if (this._check(TokenTypes.keywords.continuing)) { return this._continuing_statement(); } if (this._check(TokenTypes.keywords.static_assert)) { return this._static_assert_statement(); } if (this._check(TokenTypes.tokens.brace_left)) { return this._compound_statement(); } let result = null; if (this._check(TokenTypes.keywords.return)) { result = this._return_statement(); } else if (this._check([ TokenTypes.keywords.var, TokenTypes.keywords.let, TokenTypes.keywords.const ])) { result = this._variable_statement(); } else if (this._match(TokenTypes.keywords.discard)) { result = new Discard(); } else if (this._match(TokenTypes.keywords.break)) { result = new Break(); } else if (this._match(TokenTypes.keywords.continue)) { result = new Continue(); } else { result = this._increment_decrement_statement() || this._func_call_statement() || this._assignment_statement(); } if (result != null) { this._consume(TokenTypes.tokens.semicolon, "Expected ';' after statement."); } return result; } _static_assert_statement() { if (!this._match(TokenTypes.keywords.static_assert)) { return null; } const expression = this._optional_paren_expression(); return new StaticAssert(expression); } _while_statement() { if (!this._match(TokenTypes.keywords.while)) { return null; } const condition = this._optional_paren_expression(); if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } const block = this._compound_statement(); return new While(condition, block); } _continuing_statement() { if (!this._match(TokenTypes.keywords.continuing)) { return null; } const block = this._compound_statement(); return new Continuing(block); } _for_statement() { if (!this._match(TokenTypes.keywords.for)) { return null; } this._consume(TokenTypes.tokens.paren_left, "Expected '('."); const init = !this._check(TokenTypes.tokens.semicolon) ? this._for_init() : null; this._consume(TokenTypes.tokens.semicolon, "Expected ';'."); const condition = !this._check(TokenTypes.tokens.semicolon) ? this._short_circuit_or_expression() : null; this._consume(TokenTypes.tokens.semicolon, "Expected ';'."); const increment = !this._check(TokenTypes.tokens.paren_right) ? this._for_increment() : null; this._consume(TokenTypes.tokens.paren_right, "Expected ')'."); if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } const body = this._compound_statement(); return new For(init, condition, increment, body); } _for_init() { return this._variable_statement() || this._func_call_statement() || this._assignment_statement(); } _for_increment() { return this._func_call_statement() || this._increment_decrement_statement() || this._assignment_statement(); } _variable_statement() { if (this._check(TokenTypes.keywords.var)) { const _var = this._variable_decl(); if (_var === null) { throw this._error(this._peek(), "Variable declaration expected."); } let value = null; if (this._match(TokenTypes.tokens.equal)) { value = this._short_circuit_or_expression(); } return new Var(_var.name, _var.type, _var.storage, _var.access, value); } if (this._match(TokenTypes.keywords.let)) { const name = this._consume(TokenTypes.tokens.ident, "Expected name for let.").toString(); let type = null; if (this._match(TokenTypes.tokens.colon)) { const typeAttrs = this._attribute(); type = this._type_decl(); if (type != null) { type.attributes = typeAttrs; } } this._consume(TokenTypes.tokens.equal, "Expected '=' for let."); const value = this._short_circuit_or_expression(); return new Let(name, type, null, null, value); } if (this._match(TokenTypes.keywords.const)) { const name = this._consume(TokenTypes.tokens.ident, "Expected name for const.").toString(); let type = null; if (this._match(TokenTypes.tokens.colon)) { const typeAttrs = this._attribute(); type = this._type_decl(); if (type != null) { type.attributes = typeAttrs; } } this._consume(TokenTypes.tokens.equal, "Expected '=' for const."); const value = this._short_circuit_or_expression(); return new Const(name, type, null, null, value); } return null; } _increment_decrement_statement() { const savedPos = this._current; const _var = this._unary_expression(); if (_var == null) { return null; } if (!this._check(TokenTypes.increment_operators)) { this._current = savedPos; return null; } const token = this._consume(TokenTypes.increment_operators, "Expected increment operator"); return new Increment(token.type === TokenTypes.tokens.plus_plus ? IncrementOperator.increment : IncrementOperator.decrement, _var); } _assignment_statement() { let _var = null; if (this._check(TokenTypes.tokens.brace_right)) { return null; } let isUnderscore = this._match(TokenTypes.tokens.underscore); if (!isUnderscore) { _var = this._unary_expression(); } if (!isUnderscore && _var == null) { return null; } const type = this._consume(TokenTypes.assignment_operators, "Expected assignment operator."); const value = this._short_circuit_or_expression(); return new Assign(AssignOperator.parse(type.lexeme), _var, value); } _func_call_statement() { if (!this._check(TokenTypes.tokens.ident)) { return null; } const savedPos = this._current; const name = this._consume(TokenTypes.tokens.ident, "Expected function name."); const args = this._argument_expression_list(); if (args === null) { this._current = savedPos; return null; } return new Call(name.lexeme, args); } _loop_statement() { if (!this._match(TokenTypes.keywords.loop)) { return null; } if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } this._consume(TokenTypes.tokens.brace_left, "Expected '{' for loop."); const statements = []; let statement = this._statement(); while (statement !== null) { if (Array.isArray(statement)) { for (let s of statement) { statements.push(s); } } else { statements.push(statement); } statement = this._statement(); } let continuing = null; if (this._match(TokenTypes.keywords.continuing)) { continuing = this._compound_statement(); } this._consume(TokenTypes.tokens.brace_right, "Expected '}' for loop."); return new Loop(statements, continuing); } _switch_statement() { if (!this._match(TokenTypes.keywords.switch)) { return null; } const condition = this._optional_paren_expression(); if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } this._consume(TokenTypes.tokens.brace_left, "Expected '{' for switch."); const body = this._switch_body(); if (body == null || body.length == 0) { throw this._error(this._previous(), "Expected 'case' or 'default'."); } this._consume(TokenTypes.tokens.brace_right, "Expected '}' for switch."); return new Switch(condition, body); } _switch_body() { const cases = []; if (this._match(TokenTypes.keywords.case)) { const selector = this._case_selectors(); this._match(TokenTypes.tokens.colon); if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } this._consume(TokenTypes.tokens.brace_left, "Exected '{' for switch case."); const body = this._case_body(); this._consume(TokenTypes.tokens.brace_right, "Exected '}' for switch case."); cases.push(new Case(selector, body)); } if (this._match(TokenTypes.keywords.default)) { this._match(TokenTypes.tokens.colon); if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } this._consume(TokenTypes.tokens.brace_left, "Exected '{' for switch default."); const body = this._case_body(); this._consume(TokenTypes.tokens.brace_right, "Exected '}' for switch default."); cases.push(new Default(body)); } if (this._check([TokenTypes.keywords.default, TokenTypes.keywords.case])) { const _cases = this._switch_body(); cases.push(_cases[0]); } return cases; } _case_selectors() { const selectors = [ this._shift_expression() //?.evaluate(this._context).toString() ?? "", ]; while (this._match(TokenTypes.tokens.comma)) { selectors.push(this._shift_expression()); } return selectors; } _case_body() { if (this._match(TokenTypes.keywords.fallthrough)) { this._consume(TokenTypes.tokens.semicolon, "Expected ';'"); return []; } let statement = this._statement(); if (statement == null) { return []; } if (!(statement instanceof Array)) { statement = [statement]; } const nextStatement = this._case_body(); if (nextStatement.length == 0) { return statement; } return [...statement, nextStatement[0]]; } _if_statement() { if (!this._match(TokenTypes.keywords.if)) { return null; } const condition = this._optional_paren_expression(); if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } const block = this._compound_statement(); let elseif = []; if (this._match_elseif()) { if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } elseif = this._elseif_statement(elseif); } let _else = null; if (this._match(TokenTypes.keywords.else)) { if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } _else = this._compound_statement(); } return new If(condition, block, elseif, _else); } _match_elseif() { if (this._tokens[this._current].type === TokenTypes.keywords.else && this._tokens[this._current + 1].type === TokenTypes.keywords.if) { this._advance(); this._advance(); return true; } return false; } _elseif_statement(elseif = []) { const condition = this._optional_paren_expression(); const block = this._compound_statement(); elseif.push(new ElseIf(condition, block)); if (this._match_elseif()) { if (this._check(TokenTypes.tokens.attr)) { this._attribute(); } this._elseif_statement(elseif); } return elseif; } _return_statement() { if (!this._match(TokenTypes.keywords.return)) { return null; } const value = this._short_circuit_or_expression(); return new Return(value); } _short_circuit_or_expression() { let expr = this._short_circuit_and_expr(); while (this._match(TokenTypes.tokens.or_or)) { expr = new BinaryOperator(this._previous().toString(), expr, this._short_circuit_and_expr()); } return expr; } _short_circuit_and_expr() { let expr = this._inclusive_or_expression(); while (this._match(TokenTypes.tokens.and_and)) { expr = new BinaryOperator(this._previous().toString(), expr, this._inclusive_or_expression()); } return expr; } _inclusive_or_expression() { let expr = this._exclusive_or_expression(); while (this._match(TokenTypes.tokens.or)) { expr = new BinaryOperator(this._previous().toString(), expr, this._exclusive_or_expression()); } return expr; } _exclusive_or_expression() { let expr = this._and_expression(); while (this._match(TokenTypes.tokens.xor)) { expr = new BinaryOperator(this._previous().toString(), expr, this._and_expression()); } return expr; } _and_expression() { let expr = this._equality_expression(); while (this._match(TokenTypes.tokens.and)) { expr = new BinaryOperator(this._previous().toString(), expr, this._equality_expression()); } return expr; } _equality_expression() { const expr = this._relational_expression(); if (this._match([TokenTypes.tokens.equal_equal, TokenTypes.tokens.not_equal])) { return new BinaryOperator(this._previous().toString(), expr, this._relational_expression()); } return expr; } _relational_expression() { let expr = this._shift_expression(); while (this._match([ TokenTypes.tokens.less_than, TokenTypes.tokens.greater_than, TokenTypes.tokens.less_than_equal, TokenTypes.tokens.greater_than_equal ])) { expr = new BinaryOperator(this._previous().toString(), expr, this._shift_expression()); } return expr; } _shift_expression() { let expr = this._additive_expression(); while (this._match([TokenTypes.tokens.shift_left, TokenTypes.tokens.shift_right])) { expr = new BinaryOperator(this._previous().toString(), expr, this._additive_expression()); } return expr; } _additive_expression() { let expr = this._multiplicative_expression(); while (this._match([TokenTypes.tokens.plus, TokenTypes.tokens.minus])) { expr = new BinaryOperator(this._previous().toString(), expr, this._multiplicative_expression()); } return expr; } _multiplicative_expression() { let expr = this._unary_expression(); while (this._match([ TokenTypes.tokens.star, TokenTypes.tokens.forward_slash, TokenTypes.tokens.modulo ])) { expr = new BinaryOperator(this._previous().toString(), expr, this._unary_expression()); } return expr; } _unary_expression() { if (this._match([ TokenTypes.tokens.minus, TokenTypes.tokens.bang, TokenTypes.tokens.tilde, TokenTypes.tokens.star, TokenTypes.tokens.and ])) { return new UnaryOperator(this._previous().toString(), this._unary_expression()); } return this._singular_expression(); } _singular_expression() { const expr = this._primary_expression(); const p = this._postfix_expression(); if (p) { expr.postfix = p; } return expr; } _postfix_expression() { if (this._match(TokenTypes.tokens.bracket_left)) { const expr = this._short_circuit_or_expression(); this._consume(TokenTypes.tokens.bracket_right, "Expected ']'."); const arrayIndex = new ArrayIndex(expr); const p = this._postfix_expression(); if (p) { arrayIndex.postfix = p; } return arrayIndex; } if (this._match(TokenTypes.tokens.period)) { const name = this._consume(TokenTypes.tokens.ident, "Expected member name."); const p = this._postfix_expression(); const expr = new StringExpr(name.lexeme); if (p) { expr.postfix = p; } return expr; } return null; } _getStruct(name) { if (this._context.aliases.has(name)) { const alias = this._context.aliases.get(name).type; return alias; } if (this._context.structs.has(name)) { const struct = this._context.structs.get(name); return struct; } return null; } _primary_expression() { if (this._match(TokenTypes.tokens.ident)) { const name = this._previous().toString(); if (this._check(TokenTypes.tokens.paren_left)) { const args2 = this._argument_expression_list(); const struct = this._getStruct(name); if (struct != null) { return new CreateExpr(struct, args2); } return new CallExpr(name, args2); } if (this._context.constants.has(name)) { const c = this._context.constants.get(name); return new ConstExpr(name, c.value); } return new VariableExpr(name); } if (this._match(TokenTypes.const_literal)) { return new LiteralExpr(parseFloat(this._previous().toString())); } if (this._check(TokenTypes.tokens.paren_left)) { return this._paren_expression(); } if (this._match(TokenTypes.keywords.bitcast)) { this._consume(TokenTypes.tokens.less_than, "Expected '<'."); const type2 = this._type_decl(); this._consume(TokenTypes.tokens.greater_than, "Expected '>'."); const value = this._paren_expression(); return new BitcastExpr(type2, value); } const type = this._type_decl(); const args = this._argument_expression_list(); return new TypecastExpr(type, args); } _argument_expression_list() { if (!this._match(TokenTypes.tokens.paren_left)) { return null; } const args = []; do { if (this._check(TokenTypes.tokens.paren_right)) { break; } const arg = this._short_circuit_or_expression(); args.push(arg); } while (this._match(TokenTypes.tokens.comma)); this._consume(TokenTypes.tokens.paren_right, "Expected ')' for agument list"); return args; } _optional_paren_expression() { this._match(TokenTypes.tokens.paren_left); const expr = this._short_circuit_or_expression(); this._match(TokenTypes.tokens.paren_right); return new GroupingExpr([expr]); } _paren_expression() { this._consume(TokenTypes.tokens.paren_left, "Expected '('."); const expr = this._short_circuit_or_expression(); this._consume(TokenTypes.tokens.paren_right, "Expected ')'."); return new GroupingExpr([expr]); } _struct_decl() { if (!this._match(TokenTypes.keywords.struct)) { return null; } const startLine = this._currentLine; const name = this._consume(TokenTypes.tokens.ident, "Expected name for struct.").toString(); this._consume(TokenTypes.tokens.brace_left, "Expected '{' for struct body."); const members = []; while (!this._check(TokenTypes.tokens.brace_right)) { const memberAttrs = this._attribute(); const memberName = this._consume(TokenTypes.tokens.ident, "Expected variable name.").toString(); this._consume(TokenTypes.tokens.colon, "Expected ':' for struct member type."); const typeAttrs = this._attribute(); const memberType = this._type_decl(); if (memberType != null) { memberType.attributes = typeAttrs; } if (!this._check(TokenTypes.tokens.brace_right)) this._consume(TokenTypes.tokens.comma, "Expected ',' for struct member."); else this._match(TokenTypes.tokens.comma); members.push(new Member(memberName, memberType, memberAttrs)); } this._consume(TokenTypes.tokens.brace_right, "Expected '}' after struct body."); const endLine = this._currentLine; const structNode = new Struct(name, members, startLine, endLine); this._context.structs.set(name, structNode); return structNode; } _global_variable_decl() { const _var = this._variable_decl(); if (_var && this._match(TokenTypes.tokens.equal)) { _var.value = this._const_expression(); } return _var; } _override_variable_decl() { const _override = this._override_decl(); if (_override && this._match(TokenTypes.tokens.equal)) { _override.value = this._const_expression(); } return _override; } _global_const_decl() { if (!this._match(TokenTypes.keywords.const)) { return null; } const name = this._consume(TokenTypes.tokens.ident, "Expected variable name"); let type = null; if (this._match(TokenTypes.tokens.colon)) { const attrs = this._attribute(); type = this._type_decl(); if (type != null) { type.attributes = attrs; } } let value = null; if (this._match(TokenTypes.tokens.equal)) { const valueExpr = this._short_circuit_or_expression(); if (valueExpr instanceof CreateExpr) { value = valueExpr; } else if (valueExpr instanceof ConstExpr && valueExpr.initializer instanceof CreateExpr) { value = valueExpr.initializer; } else { try { const constValue = valueExpr.evaluate(this._context); value = new LiteralExpr(constValue); } catch (_a2) { value = valueExpr; } } } const c = new Const(name.toString(), type, "", "", value); this._context.constants.set(c.name, c); return c; } _global_let_decl() { if (!this._match(TokenTypes.keywords.let)) { return null; } const name = this._consume(TokenTypes.tokens.ident, "Expected variable name"); let type = null; if (this._match(TokenTypes.tokens.colon)) { const attrs = this._attribute(); type = this._type_decl(); if (type != null) { type.attributes = attrs; } } let value = null; if (this._match(TokenTypes.tokens.equal)) { value = this._const_expression(); } return new Let(name.toString(), type, "", "", value); } _const_expression() { if (this._match(TokenTypes.const_literal)) { return new StringExpr(this._previous().toString()); } const type = this._type_decl(); this._consume(TokenTypes.tokens.paren_left, "Expected '('."); let args = []; while (!this._check(TokenTypes.tokens.paren_right)) { args.push(this._const_expression()); if (!this._check(TokenTypes.tokens.comma)) { break; } this._advance(); } this._consume(TokenTypes.tokens.paren_right, "Expected ')'."); return new CreateExpr(type, args); } _variable_decl() { if (!this._match(TokenTypes.keywords.var)) { return null; } let storage = ""; let access = ""; if (this._match(TokenTypes.tokens.less_than)) { storage = this._consume(TokenTypes.storage_class, "Expected storage_class.").toString(); if (this._match(TokenTypes.tokens.comma)) access = this._consume(TokenTypes.access_mode, "Expected access_mode.").toString(); this._consume(TokenTypes.tokens.greater_than, "Expected '>'."); } const name = this._consume(TokenTypes.tokens.ident, "Expected variable name"); let type = null; if (this._match(TokenTypes.tokens.colon)) { const attrs = this._attribute(); type = this._type_decl(); if (type != null) { type.attributes = attrs; } } return new Var(name.toString(), type, storage, access, null); } _override_decl() { if (!this._match(TokenTypes.keywords.override)) { return null; } const name = this._consume(TokenTypes.tokens.ident, "Expected variable name"); let type = null; if (this._match(TokenTypes.tokens.colon)) { const attrs = this._attribute(); type = this._type_decl(); if (type != null) { type.attributes = attrs; } } return new Override(name.toString(), type, null); } _diagnostic() { this._consume(TokenTypes.tokens.paren_left, "Expected '('"); const severity = this._consume(TokenTypes.tokens.ident, "Expected severity control name."); this._consume(TokenTypes.tokens.comma, "Expected ','"); const rule = this._consume(TokenTypes.tokens.ident, "Expected diagnostic rule name."); this._consume(TokenTypes.tokens.paren_right, "Expected ')'"); return new Diagnostic(severity.toString(), rule.toString()); } _enable_directive() { const name = this._consume(TokenTypes.tokens.ident, "identity expected."); return new Enable(name.toString()); } _requires_directive() { const extensions = [this._consume(TokenTypes.tokens.ident, "identity expected.").toString()]; while (this._match(TokenTypes.tokens.comma)) { const name = this._consume(TokenTypes.tokens.ident, "identity expected."); extensions.push(name.toString()); } return new Requires(extensions); } _type_alias() { const name = this._consume(TokenTypes.tokens.ident, "identity expected."); this._consume(TokenTypes.tokens.equal, "Expected '=' for type alias."); let aliasType = this._type_decl(); if (aliasType === null) { throw this._error(this._peek(), "Expected Type for Alias."); } if (this._context.aliases.has(aliasType.name)) { aliasType = this._context.aliases.get(aliasType.name).type; } const aliasNode = new Alias(name.toString(), aliasType); this._context.aliases.set(aliasNode.name, aliasNode); return aliasNode; } _type_decl() { if (this._check([ TokenTypes.tokens.ident, ...TokenTypes.texel_format, TokenTypes.keywords.bool, TokenTypes.keywords.f32, TokenTypes.keywords.i32, TokenTypes.keywords.u32 ])) { const type2 = this._advance(); const typeName = type2.toString(); if (this._context.structs.has(typeName)) { return this._context.structs.get(typeName); } if (this._context.aliases.has(typeName)) { return this._context.aliases.get(typeName).type; } return new Type(type2.toString()); } let type = this._texture_sampler_types(); if (type) { return type; } if (this._check(TokenTypes.template_types)) { let type2 = this._advance().toString(); let format = null; let access = null; if (this._match(TokenTypes.tokens.less_than)) { format = this._type_decl(); access = null; if (this._match(TokenTypes.tokens.comma)) { access = this._consume(TokenTypes.access_mode, "Expected access_mode for pointer").toString(); } this._consume(TokenTypes.tokens.greater_than, "Expected '>' for type."); } return new TemplateType(type2, format, access); } if (this._match(TokenTypes.keywords.ptr)) { let pointer = this._previous().toString(); this._consume(TokenTypes.tokens.less_than, "Expected '<' for pointer."); const storage = this._consume(TokenTypes.storage_class, "Expected storage_class for pointer"); this._consume(TokenTypes.tokens.comma, "Expected ',' for pointer."); const decl = this._type_decl(); let access = null; if (this._match(TokenTypes.tokens.comma)) { access = this._consume(TokenTypes.access_mode, "Expected access_mode for pointer").toString(); } this._consume(TokenTypes.tokens.greater_than, "Expected '>' for pointer."); return new PointerType(pointer, storage.toString(), decl, access); } const attrs = this._attribute(); if (this._match(TokenTypes.keywords.array)) { let format = null; let countInt = -1; const array = this._previous(); let countNode = null; if (this._match(TokenTypes.tokens.less_than)) { format = this._type_decl(); if (this._context.aliases.has(format.name)) { format = this._context.aliases.get(format.name).type; } let count = ""; if (this._match(TokenTypes.tokens.comma)) { countNode = this._shift_expression(); try { count = countNode.evaluate(this._context).toString(); countNode = null; } catch (e) { count = "1"; } } this._consume(TokenTypes.tokens.greater_than, "Expected '>' for array."); countInt = count ? parseInt(count) : 0; } const arrayType = new ArrayType(array.toString(), attrs, format, countInt); if (countNode) { this._deferArrayCountEval.push({ arrayType, countNode }); } return arrayType; } return null; } _texture_sampler_types() { if (this._match(TokenTypes.sampler_type)) { return new SamplerType(this._previous().toString(), null, null); } if (this._match(TokenTypes.depth_texture_type)) { return new SamplerType(this._previous().toString(), null, null); } if (this._match(TokenTypes.sampled_texture_type) || this._match(TokenTypes.multisampled_texture_type)) { const sampler = this._previous(); this._consume(TokenTypes.tokens.less_than, "Expected '<' for sampler type."); const format = this._type_decl(); this._consume(TokenTypes.tokens.greater_than, "Expected '>' for sampler type."); return new SamplerType(sampler.toString(), format, null); } if (this._match(TokenTypes.storage_texture_type)) { const sampler = this._previous(); this._consume(TokenTypes.tokens.less_than, "Expected '<' for sampler type."); const format = this._consume(TokenTypes.texel_format, "Invalid texel format.").toString(); this._consume(TokenTypes.tokens.comma, "Expected ',' after texel format."); const access = this._consume(TokenTypes.access_mode, "Expected access mode for storage texture type.").toString(); this._consume(TokenTypes.tokens.greater_than, "Expected '>' for sampler type."); return new SamplerType(sampler.toString(), format, access); } return null; } _attribute() { let attributes = []; while (this._match(TokenTypes.tokens.attr)) { const name = this._consume(TokenTypes.attribute_name, "Expected attribute name"); const attr = new Attribute(name.toString(), null); if (this._match(TokenTypes.tokens.paren_left)) { attr.value = this._consume(TokenTypes.literal_or_ident, "Expected attribute value").toString(); if (this._check(TokenTypes.tokens.comma)) { this._advance(); do { const v = this._consume(TokenTypes.literal_or_ident, "Expected attribute value").toString(); if (!(attr.value instanceof Array)) { attr.value = [attr.value]; } attr.value.push(v); } while (this._match(TokenTypes.tokens.comma)); } this._consume(TokenTypes.tokens.paren_right, "Expected ')'"); } attributes.push(attr); } if (attributes.length == 0) { return null; } return attributes; } }; var TypeInfo = class { constructor(name, attributes) { this.name = name; this.attributes = attributes; this.size = 0; } get isArray() { return false; } get isStruct() { return false; } get isTemplate() { return false; } }; var MemberInfo = class { constructor(name, type, attributes) { this.name = name; this.type = type; this.attributes = attributes; this.offset = 0; this.size = 0; } get isArray() { return this.type.isArray; } get isStruct() { return this.type.isStruct; } get isTemplate() { return this.type.isTemplate; } get align() { return this.type.isStruct ? this.type.align : 0; } get members() { return this.type.isStruct ? this.type.members : null; } get format() { return this.type.isArray ? this.type.format : this.type.isTemplate ? this.type.format : null; } get count() { return this.type.isArray ? this.type.count : 0; } get stride() { return this.type.isArray ? this.type.stride : this.size; } }; var StructInfo = class extends TypeInfo { constructor(name, attributes) { super(name, attributes); this.members = []; this.align = 0; this.startLine = -1; this.endLine = -1; this.inUse = false; } get isStruct() { return true; } }; var ArrayInfo = class extends TypeInfo { constructor(name, attributes) { super(name, attributes); this.count = 0; this.stride = 0; } get isArray() { return true; } }; var TemplateInfo = class extends TypeInfo { constructor(name, format, attributes, access) { super(name, attributes); this.format = format; this.access = access; } get isTemplate() { return true; } }; var ResourceType; (function(ResourceType2) { ResourceType2[ResourceType2["Uniform"] = 0] = "Uniform"; ResourceType2[ResourceType2["Storage"] = 1] = "Storage"; ResourceType2[ResourceType2["Texture"] = 2] = "Texture"; ResourceType2[ResourceType2["Sampler"] = 3] = "Sampler"; ResourceType2[ResourceType2["StorageTexture"] = 4] = "StorageTexture"; })(ResourceType || (ResourceType = {})); var VariableInfo = class { constructor(name, type, group, binding, attributes, resourceType, access) { this.name = name; this.type = type; this.group = group; this.binding = binding; this.attributes = attributes; this.resourceType = resourceType; this.access = access; } get isArray() { return this.type.isArray; } get isStruct() { return this.type.isStruct; } get isTemplate() { return this.type.isTemplate; } get size() { return this.type.size; } get align() { return this.type.isStruct ? this.type.align : 0; } get members() { return this.type.isStruct ? this.type.members : null; } get format() { return this.type.isArray ? this.type.format : this.type.isTemplate ? this.type.format : null; } get count() { return this.type.isArray ? this.type.count : 0; } get stride() { return this.type.isArray ? this.type.stride : this.size; } }; var AliasInfo = class { constructor(name, type) { this.name = name; this.type = type; } }; var _TypeSize = class { constructor(align, size) { this.align = align; this.size = size; } }; var InputInfo = class { constructor(name, type, locationType, location) { this.name = name; this.type = type; this.locationType = locationType; this.location = location; this.interpolation = null; } }; var OutputInfo = class { constructor(name, type, locationType, location) { this.name = name; this.type = type; this.locationType = locationType; this.location = location; } }; var FunctionInfo = class { constructor(name, stage = null) { this.stage = null; this.inputs = []; this.outputs = []; this.resources = []; this.startLine = -1; this.endLine = -1; this.inUse = false; this.calls = /* @__PURE__ */ new Set(); this.name = name; this.stage = stage; } }; var EntryFunctions = class { constructor() { this.vertex = []; this.fragment = []; this.compute = []; } }; var OverrideInfo = class { constructor(name, type, attributes, id) { this.name = name; this.type = type; this.attributes = attributes; this.id = id; } }; var _FunctionResources = class { constructor(node) { this.resources = null; this.inUse = false; this.info = null; this.node = node; } }; var WgslReflect = class { constructor(code) { this.uniforms = []; this.storage = []; this.textures = []; this.samplers = []; this.aliases = []; this.overrides = []; this.structs = []; this.entry = new EntryFunctions(); this.functions = []; this._types = /* @__PURE__ */ new Map(); this._functions = /* @__PURE__ */ new Map(); if (code) { this.update(code); } } _isStorageTexture(type) { return type.name == "texture_storage_1d" || type.name == "texture_storage_2d" || type.name == "texture_storage_2d_array" || type.name == "texture_storage_3d"; } update(code) { const parser = new WgslParser(); const ast = parser.parse(code); for (const node of ast) { if (node instanceof Function) { this._functions.set(node.name, new _FunctionResources(node)); } } for (const node of ast) { if (node instanceof Struct) { const info = this._getTypeInfo(node, null); if (info instanceof StructInfo) { this.structs.push(info); } } } for (const node of ast) { if (node instanceof Alias) { this.aliases.push(this._getAliasInfo(node)); continue; } if (node instanceof Override) { const v = node; const id = this._getAttributeNum(v.attributes, "id", 0); const type = v.type != null ? this._getTypeInfo(v.type, v.attributes) : null; this.overrides.push(new OverrideInfo(v.name, type, v.attributes, id)); continue; } if (this._isUniformVar(node)) { const v = node; const g = this._getAttributeNum(v.attributes, "group", 0); const b = this._getAttributeNum(v.attributes, "binding", 0); const type = this._getTypeInfo(v.type, v.attributes); const varInfo = new VariableInfo(v.name, type, g, b, v.attributes, ResourceType.Uniform, v.access); this.uniforms.push(varInfo); continue; } if (this._isStorageVar(node)) { const v = node; const g = this._getAttributeNum(v.attributes, "group", 0); const b = this._getAttributeNum(v.attributes, "binding", 0); const type = this._getTypeInfo(v.type, v.attributes); const isStorageTexture = this._isStorageTexture(type); const varInfo = new VariableInfo(v.name, type, g, b, v.attributes, isStorageTexture ? ResourceType.StorageTexture : ResourceType.Storage, v.access); this.storage.push(varInfo); continue; } if (this._isTextureVar(node)) { const v = node; const g = this._getAttributeNum(v.attributes, "group", 0); const b = this._getAttributeNum(v.attributes, "binding", 0); const type = this._getTypeInfo(v.type, v.attributes); const isStorageTexture = this._isStorageTexture(type); const varInfo = new VariableInfo(v.name, type, g, b, v.attributes, isStorageTexture ? ResourceType.StorageTexture : ResourceType.Texture, v.access); if (isStorageTexture) { this.storage.push(varInfo); } else { this.textures.push(varInfo); } continue; } if (this._isSamplerVar(node)) { const v = node; const g = this._getAttributeNum(v.attributes, "group", 0); const b = this._getAttributeNum(v.attributes, "binding", 0); const type = this._getTypeInfo(v.type, v.attributes); const varInfo = new VariableInfo(v.name, type, g, b, v.attributes, ResourceType.Sampler, v.access); this.samplers.push(varInfo); continue; } if (node instanceof Function) { const vertexStage = this._getAttribute(node, "vertex"); const fragmentStage = this._getAttribute(node, "fragment"); const computeStage = this._getAttribute(node, "compute"); const stage = vertexStage || fragmentStage || computeStage; const fn = new FunctionInfo(node.name, stage === null || stage === void 0 ? void 0 : stage.name); fn.startLine = node.startLine; fn.endLine = node.endLine; this.functions.push(fn); this._functions.get(node.name).info = fn; if (stage) { this._functions.get(node.name).inUse = true; fn.inUse = true; fn.resources = this._findResources(node, !!stage); fn.inputs = this._getInputs(node.args); fn.outputs = this._getOutputs(node.returnType); this.entry[stage.name].push(fn); } continue; } } for (const fn of this._functions.values()) { if (fn.info) { fn.info.inUse = fn.inUse; this._addCalls(fn.node, fn.info.calls); } } for (const u of this.uniforms) { this._markStructsInUse(u.type); } for (const s of this.storage) { this._markStructsInUse(s.type); } } _markStructsInUse(type) { if (type.isStruct) { type.inUse = true; for (const m of type.members) { this._markStructsInUse(m.type); } } else if (type.isArray) { this._markStructsInUse(type.format); } else if (type.isTemplate) { this._markStructsInUse(type.format); } else { const alias = this._getAlias(type.name); if (alias) { this._markStructsInUse(alias); } } } _addCalls(fn, calls) { var _a2; for (const call of fn.calls) { const info = (_a2 = this._functions.get(call.name)) === null || _a2 === void 0 ? void 0 : _a2.info; if (info) { calls.add(info); } } } /// Find a resource by its group and binding. findResource(group, binding) { for (const u of this.uniforms) { if (u.group == group && u.binding == binding) { return u; } } for (const s of this.storage) { if (s.group == group && s.binding == binding) { return s; } } for (const t of this.textures) { if (t.group == group && t.binding == binding) { return t; } } for (const s of this.samplers) { if (s.group == group && s.binding == binding) { return s; } } return null; } _findResource(name) { for (const u of this.uniforms) { if (u.name == name) { return u; } } for (const s of this.storage) { if (s.name == name) { return s; } } for (const t of this.textures) { if (t.name == name) { return t; } } for (const s of this.samplers) { if (s.name == name) { return s; } } return null; } _markStructsFromAST(type) { const info = this._getTypeInfo(type, null); this._markStructsInUse(info); } _findResources(fn, isEntry) { const resources = []; const self = this; const varStack = []; fn.search((node) => { if (node instanceof _BlockStart) { varStack.push({}); } else if (node instanceof _BlockEnd) { varStack.pop(); } else if (node instanceof Var) { const v = node; if (isEntry && v.type !== null) { this._markStructsFromAST(v.type); } if (varStack.length > 0) { varStack[varStack.length - 1][v.name] = v; } } else if (node instanceof CreateExpr) { const c = node; if (isEntry && c.type !== null) { this._markStructsFromAST(c.type); } } else if (node instanceof Let) { const v = node; if (isEntry && v.type !== null) { this._markStructsFromAST(v.type); } if (varStack.length > 0) { varStack[varStack.length - 1][v.name] = v; } } else if (node instanceof VariableExpr) { const v = node; if (varStack.length > 0) { const varInfo2 = varStack[varStack.length - 1][v.name]; if (varInfo2) { return; } } const varInfo = self._findResource(v.name); if (varInfo) { resources.push(varInfo); } } else if (node instanceof CallExpr) { const c = node; const callFn = self._functions.get(c.name); if (callFn) { if (isEntry) { callFn.inUse = true; } fn.calls.add(callFn.node); if (callFn.resources === null) { callFn.resources = self._findResources(callFn.node, isEntry); } resources.push(...callFn.resources); } } else if (node instanceof Call) { const c = node; const callFn = self._functions.get(c.name); if (callFn) { if (isEntry) { callFn.inUse = true; } fn.calls.add(callFn.node); if (callFn.resources === null) { callFn.resources = self._findResources(callFn.node, isEntry); } resources.push(...callFn.resources); } } }); return [...new Map(resources.map((r) => [r.name, r])).values()]; } getBindGroups() { const groups = []; function _makeRoom(group, binding) { if (group >= groups.length) { groups.length = group + 1; } if (groups[group] === void 0) { groups[group] = []; } if (binding >= groups[group].length) { groups[group].length = binding + 1; } } for (const u of this.uniforms) { _makeRoom(u.group, u.binding); const group = groups[u.group]; group[u.binding] = u; } for (const u of this.storage) { _makeRoom(u.group, u.binding); const group = groups[u.group]; group[u.binding] = u; } for (const t of this.textures) { _makeRoom(t.group, t.binding); const group = groups[t.group]; group[t.binding] = t; } for (const t of this.samplers) { _makeRoom(t.group, t.binding); const group = groups[t.group]; group[t.binding] = t; } return groups; } _getOutputs(type, outputs = void 0) { if (outputs === void 0) { outputs = []; } if (type instanceof Struct) { this._getStructOutputs(type, outputs); } else { const output = this._getOutputInfo(type); if (output !== null) { outputs.push(output); } } return outputs; } _getStructOutputs(struct, outputs) { for (const m of struct.members) { if (m.type instanceof Struct) { this._getStructOutputs(m.type, outputs); } else { const location = this._getAttribute(m, "location") || this._getAttribute(m, "builtin"); if (location !== null) { const typeInfo = this._getTypeInfo(m.type, m.type.attributes); const locationValue = this._parseInt(location.value); const info = new OutputInfo(m.name, typeInfo, location.name, locationValue); outputs.push(info); } } } } _getOutputInfo(type) { const location = this._getAttribute(type, "location") || this._getAttribute(type, "builtin"); if (location !== null) { const typeInfo = this._getTypeInfo(type, type.attributes); const locationValue = this._parseInt(location.value); const info = new OutputInfo("", typeInfo, location.name, locationValue); return info; } return null; } _getInputs(args, inputs = void 0) { if (inputs === void 0) { inputs = []; } for (const arg of args) { if (arg.type instanceof Struct) { this._getStructInputs(arg.type, inputs); } else { const input = this._getInputInfo(arg); if (input !== null) { inputs.push(input); } } } return inputs; } _getStructInputs(struct, inputs) { for (const m of struct.members) { if (m.type instanceof Struct) { this._getStructInputs(m.type, inputs); } else { const input = this._getInputInfo(m); if (input !== null) { inputs.push(input); } } } } _getInputInfo(node) { const location = this._getAttribute(node, "location") || this._getAttribute(node, "builtin"); if (location !== null) { const interpolation = this._getAttribute(node, "interpolation"); const type = this._getTypeInfo(node.type, node.attributes); const locationValue = this._parseInt(location.value); const info = new InputInfo(node.name, type, location.name, locationValue); if (interpolation !== null) { info.interpolation = this._parseString(interpolation.value); } return info; } return null; } _parseString(s) { if (s instanceof Array) { s = s[0]; } return s; } _parseInt(s) { if (s instanceof Array) { s = s[0]; } const n = parseInt(s); return isNaN(n) ? s : n; } _getAlias(name) { for (const a of this.aliases) { if (a.name == name) { return a.type; } } return null; } _getAliasInfo(node) { return new AliasInfo(node.name, this._getTypeInfo(node.type, null)); } _getTypeInfo(type, attributes) { if (this._types.has(type)) { return this._types.get(type); } if (type instanceof ArrayType) { const a = type; const t = this._getTypeInfo(a.format, a.attributes); const info2 = new ArrayInfo(a.name, attributes); info2.format = t; info2.count = a.count; this._types.set(type, info2); this._updateTypeInfo(info2); return info2; } if (type instanceof Struct) { const s = type; const info2 = new StructInfo(s.name, attributes); info2.startLine = s.startLine; info2.endLine = s.endLine; for (const m of s.members) { const t = this._getTypeInfo(m.type, m.attributes); info2.members.push(new MemberInfo(m.name, t, m.attributes)); } this._types.set(type, info2); this._updateTypeInfo(info2); return info2; } if (type instanceof SamplerType) { const s = type; const formatIsType = s.format instanceof Type; const format = s.format ? formatIsType ? this._getTypeInfo(s.format, null) : new TypeInfo(s.format, null) : null; const info2 = new TemplateInfo(s.name, format, attributes, s.access); this._types.set(type, info2); this._updateTypeInfo(info2); return info2; } if (type instanceof TemplateType) { const t = type; const format = t.format ? this._getTypeInfo(t.format, null) : null; const info2 = new TemplateInfo(t.name, format, attributes, t.access); this._types.set(type, info2); this._updateTypeInfo(info2); return info2; } const info = new TypeInfo(type.name, attributes); this._types.set(type, info); this._updateTypeInfo(info); return info; } _updateTypeInfo(type) { var _a2, _b; const typeSize = this._getTypeSize(type); type.size = (_a2 = typeSize === null || typeSize === void 0 ? void 0 : typeSize.size) !== null && _a2 !== void 0 ? _a2 : 0; if (type instanceof ArrayInfo) { const formatInfo = this._getTypeSize(type["format"]); type.stride = (_b = formatInfo === null || formatInfo === void 0 ? void 0 : formatInfo.size) !== null && _b !== void 0 ? _b : 0; this._updateTypeInfo(type["format"]); } if (type instanceof StructInfo) { this._updateStructInfo(type); } } _updateStructInfo(struct) { var _a2; let offset = 0; let lastSize = 0; let lastOffset = 0; let structAlign = 0; for (let mi = 0, ml = struct.members.length; mi < ml; ++mi) { const member = struct.members[mi]; const sizeInfo = this._getTypeSize(member); if (!sizeInfo) { continue; } (_a2 = this._getAlias(member.type.name)) !== null && _a2 !== void 0 ? _a2 : member.type; const align = sizeInfo.align; const size = sizeInfo.size; offset = this._roundUp(align, offset + lastSize); lastSize = size; lastOffset = offset; structAlign = Math.max(structAlign, align); member.offset = offset; member.size = size; this._updateTypeInfo(member.type); } struct.size = this._roundUp(structAlign, lastOffset + lastSize); struct.align = structAlign; } _getTypeSize(type) { var _a2; if (type === null || type === void 0) { return null; } const explicitSize = this._getAttributeNum(type.attributes, "size", 0); const explicitAlign = this._getAttributeNum(type.attributes, "align", 0); if (type instanceof MemberInfo) { type = type.type; } if (type instanceof TypeInfo) { const alias = this._getAlias(type.name); if (alias !== null) { type = alias; } } { const info = WgslReflect._typeInfo[type.name]; if (info !== void 0) { const divisor = type["format"] === "f16" ? 2 : 1; return new _TypeSize(Math.max(explicitAlign, info.align / divisor), Math.max(explicitSize, info.size / divisor)); } } { const info = WgslReflect._typeInfo[type.name.substring(0, type.name.length - 1)]; if (info) { const divisor = type.name[type.name.length - 1] === "h" ? 2 : 1; return new _TypeSize(Math.max(explicitAlign, info.align / divisor), Math.max(explicitSize, info.size / divisor)); } } if (type instanceof ArrayInfo) { let arrayType = type; let align = 8; let size = 8; const E = this._getTypeSize(arrayType.format); if (E !== null) { size = E.size; align = E.align; } const N = arrayType.count; const stride = this._getAttributeNum((_a2 = type === null || type === void 0 ? void 0 : type.attributes) !== null && _a2 !== void 0 ? _a2 : null, "stride", this._roundUp(align, size)); size = N * stride; if (explicitSize) { size = explicitSize; } return new _TypeSize(Math.max(explicitAlign, align), Math.max(explicitSize, size)); } if (type instanceof StructInfo) { let align = 0; let size = 0; let offset = 0; let lastSize = 0; let lastOffset = 0; for (const m of type.members) { const mi = this._getTypeSize(m.type); if (mi !== null) { align = Math.max(mi.align, align); offset = this._roundUp(mi.align, offset + lastSize); lastSize = mi.size; lastOffset = offset; } } size = this._roundUp(align, lastOffset + lastSize); return new _TypeSize(Math.max(explicitAlign, align), Math.max(explicitSize, size)); } return null; } _isUniformVar(node) { return node instanceof Var && node.storage == "uniform"; } _isStorageVar(node) { return node instanceof Var && node.storage == "storage"; } _isTextureVar(node) { return node instanceof Var && node.type !== null && WgslReflect._textureTypes.indexOf(node.type.name) != -1; } _isSamplerVar(node) { return node instanceof Var && node.type !== null && WgslReflect._samplerTypes.indexOf(node.type.name) != -1; } _getAttribute(node, name) { const obj = node; if (!obj || !obj["attributes"]) { return null; } const attrs = obj["attributes"]; for (let a of attrs) { if (a.name == name) { return a; } } return null; } _getAttributeNum(attributes, name, defaultValue) { if (attributes === null) { return defaultValue; } for (let a of attributes) { if (a.name == name) { let v = a !== null && a.value !== null ? a.value : defaultValue; if (v instanceof Array) { v = v[0]; } if (typeof v === "number") { return v; } if (typeof v === "string") { return parseInt(v); } return defaultValue; } } return defaultValue; } _roundUp(k, n) { return Math.ceil(n / k) * k; } }; WgslReflect._typeInfo = { f16: { align: 2, size: 2 }, i32: { align: 4, size: 4 }, u32: { align: 4, size: 4 }, f32: { align: 4, size: 4 }, atomic: { align: 4, size: 4 }, vec2: { align: 8, size: 8 }, vec3: { align: 16, size: 12 }, vec4: { align: 16, size: 16 }, mat2x2: { align: 8, size: 16 }, mat3x2: { align: 8, size: 24 }, mat4x2: { align: 8, size: 32 }, mat2x3: { align: 16, size: 32 }, mat3x3: { align: 16, size: 48 }, mat4x3: { align: 16, size: 64 }, mat2x4: { align: 16, size: 32 }, mat3x4: { align: 16, size: 48 }, mat4x4: { align: 16, size: 64 } }; WgslReflect._textureTypes = TokenTypes.any_texture_type.map((t) => { return t.name; }); WgslReflect._samplerTypes = TokenTypes.sampler_type.map((t) => { return t.name; }); // src/lib/wgsl/get-shader-layout-wgsl.ts function getShaderLayoutFromWGSL(source) { const shaderLayout = { attributes: [], bindings: [] }; let parsedWGSL; try { parsedWGSL = parseWGSL(source); } catch (error) { import_core.log.error(error.message)(); return shaderLayout; } for (const uniform of parsedWGSL.uniforms) { const members = []; for (const attribute of uniform.type?.members || []) { members.push({ name: attribute.name, type: getType(attribute.type) }); } shaderLayout.bindings.push({ type: "uniform", name: uniform.name, location: uniform.binding, // @ts-expect-error group: uniform.group, members }); } const vertex = parsedWGSL.entry.vertex[0]; const attributeCount = vertex?.inputs.length || 0; for (let i = 0; i < attributeCount; i++) { const wgslAttribute = vertex.inputs[i]; if (wgslAttribute.locationType === "location") { const type = getType(wgslAttribute.type); shaderLayout.attributes.push({ name: wgslAttribute.name, location: Number(wgslAttribute.location), type }); } } return shaderLayout; } function getType(type) { return type.format ? `${type.name}<${type.format.name}>` : type.name; } function parseWGSL(source) { try { return new WgslReflect(source); } catch (error) { if (error instanceof Error) { throw error; } let message = "WGSL parse error"; if (typeof error === "object" && error?.message) { message += `: ${error.message} `; } if (typeof error === "object" && error?.token) { message += error.token.line || ""; } throw new Error(message, { cause: error }); } } // src/modules/math/random/random.ts var fs = glsl`\ float random(vec3 scale, float seed) { /* use the fragment position for a different seed per-pixel */ return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed); } `; var random = { name: "random", fs }; // src/modules/math/fp32/fp32.ts var fp32shader = glsl`\ #ifdef LUMA_FP32_TAN_PRECISION_WORKAROUND // All these functions are for substituting tan() function from Intel GPU only const float TWO_PI = 6.2831854820251465; const float PI_2 = 1.5707963705062866; const float PI_16 = 0.1963495463132858; const float SIN_TABLE_0 = 0.19509032368659973; const float SIN_TABLE_1 = 0.3826834261417389; const float SIN_TABLE_2 = 0.5555702447891235; const float SIN_TABLE_3 = 0.7071067690849304; const float COS_TABLE_0 = 0.9807852506637573; const float COS_TABLE_1 = 0.9238795042037964; const float COS_TABLE_2 = 0.8314695954322815; const float COS_TABLE_3 = 0.7071067690849304; const float INVERSE_FACTORIAL_3 = 1.666666716337204e-01; // 1/3! const float INVERSE_FACTORIAL_5 = 8.333333767950535e-03; // 1/5! const float INVERSE_FACTORIAL_7 = 1.9841270113829523e-04; // 1/7! const float INVERSE_FACTORIAL_9 = 2.75573188446287533e-06; // 1/9! float sin_taylor_fp32(float a) { float r, s, t, x; if (a == 0.0) { return 0.0; } x = -a * a; s = a; r = a; r = r * x; t = r * INVERSE_FACTORIAL_3; s = s + t; r = r * x; t = r * INVERSE_FACTORIAL_5; s = s + t; r = r * x; t = r * INVERSE_FACTORIAL_7; s = s + t; r = r * x; t = r * INVERSE_FACTORIAL_9; s = s + t; return s; } void sincos_taylor_fp32(float a, out float sin_t, out float cos_t) { if (a == 0.0) { sin_t = 0.0; cos_t = 1.0; } sin_t = sin_taylor_fp32(a); cos_t = sqrt(1.0 - sin_t * sin_t); } float tan_taylor_fp32(float a) { float sin_a; float cos_a; if (a == 0.0) { return 0.0; } // 2pi range reduction float z = floor(a / TWO_PI); float r = a - TWO_PI * z; float t; float q = floor(r / PI_2 + 0.5); int j = int(q); if (j < -2 || j > 2) { return 1.0 / 0.0; } t = r - PI_2 * q; q = floor(t / PI_16 + 0.5); int k = int(q); int abs_k = int(abs(float(k))); if (abs_k > 4) { return 1.0 / 0.0; } else { t = t - PI_16 * q; } float u = 0.0; float v = 0.0; float sin_t, cos_t; float s, c; sincos_taylor_fp32(t, sin_t, cos_t); if (k == 0) { s = sin_t; c = cos_t; } else { if (abs(float(abs_k) - 1.0) < 0.5) { u = COS_TABLE_0; v = SIN_TABLE_0; } else if (abs(float(abs_k) - 2.0) < 0.5) { u = COS_TABLE_1; v = SIN_TABLE_1; } else if (abs(float(abs_k) - 3.0) < 0.5) { u = COS_TABLE_2; v = SIN_TABLE_2; } else if (abs(float(abs_k) - 4.0) < 0.5) { u = COS_TABLE_3; v = SIN_TABLE_3; } if (k > 0) { s = u * sin_t + v * cos_t; c = u * cos_t - v * sin_t; } else { s = u * sin_t - v * cos_t; c = u * cos_t + v * sin_t; } } if (j == 0) { sin_a = s; cos_a = c; } else if (j == 1) { sin_a = c; cos_a = -s; } else if (j == -1) { sin_a = -c; cos_a = s; } else { sin_a = -s; cos_a = -c; } return sin_a / cos_a; } #endif float tan_fp32(float a) { #ifdef LUMA_FP32_TAN_PRECISION_WORKAROUND return tan_taylor_fp32(a); #else return tan(a); #endif } `; var fp32 = { name: "fp32", vs: fp32shader }; // src/modules/engine/picking/picking.ts var DEFAULT_HIGHLIGHT_COLOR = [0, 1, 1, 1]; var vs = glsl`\ uniform pickingUniforms { float isActive; float isAttribute; float isHighlightActive; float useFloatColors; vec3 highlightedObjectColor; vec4 highlightColor; } picking; out vec4 picking_vRGBcolor_Avalid; // Normalize unsigned byte color to 0-1 range vec3 picking_normalizeColor(vec3 color) { return picking.useFloatColors > 0.5 ? color : color / 255.0; } // Normalize unsigned byte color to 0-1 range vec4 picking_normalizeColor(vec4 color) { return picking.useFloatColors > 0.5 ? color : color / 255.0; } bool picking_isColorZero(vec3 color) { return dot(color, vec3(1.0)) < 0.00001; } bool picking_isColorValid(vec3 color) { return dot(color, vec3(1.0)) > 0.00001; } // Check if this vertex is highlighted bool isVertexHighlighted(vec3 vertexColor) { vec3 highlightedObjectColor = picking_normalizeColor(picking.highlightedObjectColor); return bool(picking.isHighlightActive) && picking_isColorZero(abs(vertexColor - highlightedObjectColor)); } // Set the current picking color void picking_setPickingColor(vec3 pickingColor) { pickingColor = picking_normalizeColor(pickingColor); if (bool(picking.isActive)) { // Use alpha as the validity flag. If pickingColor is [0, 0, 0] fragment is non-pickable picking_vRGBcolor_Avalid.a = float(picking_isColorValid(pickingColor)); if (!bool(picking.isAttribute)) { // Stores the picking color so that the fragment shader can render it during picking picking_vRGBcolor_Avalid.rgb = pickingColor; } } else { // Do the comparison with selected item color in vertex shader as it should mean fewer compares picking_vRGBcolor_Avalid.a = float(isVertexHighlighted(pickingColor)); } } void picking_setPickingAttribute(float value) { if (bool(picking.isAttribute)) { picking_vRGBcolor_Avalid.r = value; } } void picking_setPickingAttribute(vec2 value) { if (bool(picking.isAttribute)) { picking_vRGBcolor_Avalid.rg = value; } } void picking_setPickingAttribute(vec3 value) { if (bool(picking.isAttribute)) { picking_vRGBcolor_Avalid.rgb = value; } } `; var fs2 = glsl`\ uniform pickingUniforms { float isActive; float isAttribute; float isHighlightActive; float useFloatColors; vec3 highlightedObjectColor; vec4 highlightColor; } picking; in vec4 picking_vRGBcolor_Avalid; /* * Returns highlight color if this item is selected. */ vec4 picking_filterHighlightColor(vec4 color) { // If we are still picking, we don't highlight if (picking.isActive > 0.5) { return color; } bool selected = bool(picking_vRGBcolor_Avalid.a); if (selected) { // Blend in highlight color based on its alpha value float highLightAlpha = picking.highlightColor.a; float blendedAlpha = highLightAlpha + color.a * (1.0 - highLightAlpha); float highLightRatio = highLightAlpha / blendedAlpha; vec3 blendedRGB = mix(color.rgb, picking.highlightColor.rgb, highLightRatio); return vec4(blendedRGB, blendedAlpha); } else { return color; } } /* * Returns picking color if picking enabled else unmodified argument. */ vec4 picking_filterPickingColor(vec4 color) { if (bool(picking.isActive)) { if (picking_vRGBcolor_Avalid.a == 0.0) { discard; } return picking_vRGBcolor_Avalid; } return color; } /* * Returns picking color if picking is enabled if not * highlight color if this item is selected, otherwise unmodified argument. */ vec4 picking_filterColor(vec4 color) { vec4 highlightColor = picking_filterHighlightColor(color); return picking_filterPickingColor(highlightColor); } `; var picking = { name: "picking", vs, fs: fs2, uniformTypes: { isActive: "f32", isAttribute: "f32", isHighlightActive: "f32", useFloatColors: "f32", highlightedObjectColor: "vec3", highlightColor: "vec4" }, defaultUniforms: { isActive: false, isAttribute: false, isHighlightActive: false, useFloatColors: true, highlightedObjectColor: [0, 0, 0], highlightColor: DEFAULT_HIGHLIGHT_COLOR }, getUniforms }; function getUniforms(opts = {}, prevUniforms) { const uniforms = {}; if (opts.highlightedObjectColor === void 0) { } else if (opts.highlightedObjectColor === null) { uniforms.isHighlightActive = false; } else { uniforms.isHighlightActive = true; const highlightedObjectColor = opts.highlightedObjectColor.slice(0, 3); uniforms.highlightedObjectColor = highlightedObjectColor; } if (opts.highlightColor) { const color = Array.from(opts.highlightColor, (x) => x / 255); if (!Number.isFinite(color[3])) { color[3] = 1; } uniforms.highlightColor = color; } if (opts.isActive !== void 0) { uniforms.isActive = Boolean(opts.isActive); uniforms.isAttribute = Boolean(opts.isAttribute); } if (opts.useFloatColors !== void 0) { uniforms.useFloatColors = Boolean(opts.useFloatColors); } return uniforms; } // src/modules/lighting/lights/lighting-uniforms-glsl.ts var lightingUniforms = glsl`\ precision highp int; // #if (defined(SHADER_TYPE_FRAGMENT) && defined(LIGHTING_FRAGMENT)) || (defined(SHADER_TYPE_VERTEX) && defined(LIGHTING_VERTEX)) struct AmbientLight { vec3 color; }; struct PointLight { vec3 color; vec3 position; vec3 attenuation; // 2nd order x:Constant-y:Linear-z:Exponential }; struct DirectionalLight { vec3 color; vec3 direction; }; uniform lightingUniforms { int enabled; int lightType; int directionalLightCount; int pointLightCount; vec3 ambientColor; vec3 lightColor0; vec3 lightPosition0; vec3 lightDirection0; vec3 lightAttenuation0; vec3 lightColor1; vec3 lightPosition1; vec3 lightDirection1; vec3 lightAttenuation1; vec3 lightColor2; vec3 lightPosition2; vec3 lightDirection2; vec3 lightAttenuation2; } lighting; PointLight lighting_getPointLight(int index) { switch (index) { case 0: return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0); case 1: return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1); case 2: default: return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2); } } DirectionalLight lighting_getDirectionalLight(int index) { switch (index) { case 0: return DirectionalLight(lighting.lightColor0, lighting.lightDirection0); case 1: return DirectionalLight(lighting.lightColor1, lighting.lightDirection1); case 2: default: return DirectionalLight(lighting.lightColor2, lighting.lightDirection2); } } float getPointLightAttenuation(PointLight pointLight, float distance) { return pointLight.attenuation.x + pointLight.attenuation.y * distance + pointLight.attenuation.z * distance * distance; } // #endif `; // src/modules/lighting/lights/lighting-uniforms.ts var import_core2 = __toESM(require_core(), 1); var MAX_LIGHTS = 3; var COLOR_FACTOR = 255; var lighting = { name: "lighting", vs: lightingUniforms, fs: lightingUniforms, getUniforms(props, prevUniforms) { return getUniforms2(props); }, defines: { MAX_LIGHTS }, uniformTypes: { enabled: "i32", lightType: "i32", directionalLightCount: "i32", pointLightCount: "i32", ambientLightColor: "vec3", // TODO define as arrays once we have appropriate uniformTypes lightColor0: "vec3", lightPosition0: "vec3", // TODO - could combine direction and attenuation lightDirection0: "vec3", lightAttenuation0: "vec3", lightColor1: "vec3", lightPosition1: "vec3", lightDirection1: "vec3", lightAttenuation1: "vec3", lightColor2: "vec3", lightPosition2: "vec3", lightDirection2: "vec3", lightAttenuation2: "vec3" }, defaultUniforms: { enabled: 1, lightType: 0 /* POINT */, directionalLightCount: 0, pointLightCount: 0, ambientLightColor: [0.1, 0.1, 0.1], lightColor0: [1, 1, 1], lightPosition0: [1, 1, 2], // TODO - could combine direction and attenuation lightDirection0: [1, 1, 1], lightAttenuation0: [1, 0, 0], lightColor1: [1, 1, 1], lightPosition1: [1, 1, 2], lightDirection1: [1, 1, 1], lightAttenuation1: [1, 0, 0], lightColor2: [1, 1, 1], lightPosition2: [1, 1, 2], lightDirection2: [1, 1, 1], lightAttenuation2: [1, 0, 0] } }; function getUniforms2(props, prevUniforms = {}) { props = props ? { ...props } : props; if (!props) { return { ...lighting.defaultUniforms }; } if (props.lights) { props = { ...props, ...extractLightTypes(props.lights), lights: void 0 }; } const { ambientLight, pointLights, directionalLights } = props || {}; const hasLights = ambientLight || pointLights && pointLights.length > 0 || directionalLights && directionalLights.length > 0; if (!hasLights) { return { ...lighting.defaultUniforms, enabled: 0 }; } const uniforms = { ...lighting.defaultUniforms, ...prevUniforms, ...getLightSourceUniforms({ ambientLight, pointLights, directionalLights }) }; if (props.enabled !== void 0) { uniforms.enabled = props.enabled ? 1 : 0; } return uniforms; } function getLightSourceUniforms({ ambientLight, pointLights = [], directionalLights = [] }) { const lightSourceUniforms = {}; lightSourceUniforms.ambientLightColor = convertColor(ambientLight); let currentLight = 0; for (const pointLight of pointLights) { lightSourceUniforms.lightType = 0 /* POINT */; const i = currentLight; lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight); lightSourceUniforms[`lightPosition${i}`] = pointLight.position; lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0]; currentLight++; } for (const directionalLight of directionalLights) { lightSourceUniforms.lightType = 1 /* DIRECTIONAL */; const i = currentLight; lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight); lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction; currentLight++; } if (currentLight > MAX_LIGHTS) { import_core2.log.warn("MAX_LIGHTS exceeded")(); } lightSourceUniforms.directionalLightCount = directionalLights.length; lightSourceUniforms.pointLightCount = pointLights.length; return lightSourceUniforms; } function extractLightTypes(lights2) { const lightSources = { pointLights: [], directionalLights: [] }; for (const light of lights2 || []) { switch (light.type) { case "ambient": lightSources.ambientLight = light; break; case "directional": lightSources.directionalLights?.push(light); break; case "point": lightSources.pointLights?.push(light); break; default: } } return lightSources; } function convertColor(colorDef = {}) { const { color = [0, 0, 0], intensity = 1 } = colorDef; return color.map((component) => component * intensity / COLOR_FACTOR); } // src/modules/lighting/no-material/dirlight.ts var VS_GLSL = glsl`\ out vec3 dirlight_vNormal; void dirlight_setNormal(vec3 normal) { dirlight_vNormal = normalize(normal); } `; var FS_GLSL = glsl`\ uniform dirlightUniforms { vec3 lightDirection; } dirlight; in vec3 dirlight_vNormal; // Returns color attenuated by angle from light source vec4 dirlight_filterColor(vec4 color) { float d = abs(dot(dirlight_vNormal, normalize(dirlight.lightDirection))); return vec4(color.rgb * d, color.a); } `; var dirlight = { name: "dirlight", dependencies: [], vs: VS_GLSL, fs: FS_GLSL, // fragmentInputs: [ // { // name: 'dirlight_vNormal', // type: 'vec3' // } // ], uniformTypes: { lightDirection: "vec3" }, defaultUniforms: { lightDirection: [1, 1, 2] }, getUniforms: getUniforms3 }; function getUniforms3(opts = dirlight.defaultUniforms) { const uniforms = {}; if (opts.lightDirection) { uniforms.dirlight_uLightDirection = opts.lightDirection; } return uniforms; } // src/modules/lighting/phong-material/phong-shaders-glsl.ts var PHONG_VS = glsl`\ uniform phongMaterialUniforms { uniform float ambient; uniform float diffuse; uniform float shininess; uniform vec3 specularColor; } material; `; var PHONG_FS = glsl`\ uniform phongMaterialUniforms { uniform float ambient; uniform float diffuse; uniform float shininess; uniform vec3 specularColor; } material; vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_direction, vec3 normal_worldspace, vec3 color) { vec3 halfway_direction = normalize(light_direction + view_direction); float lambertian = dot(light_direction, normal_worldspace); float specular = 0.0; if (lambertian > 0.0) { float specular_angle = max(dot(normal_worldspace, halfway_direction), 0.0); specular = pow(specular_angle, material.shininess); } lambertian = max(lambertian, 0.0); return (lambertian * material.diffuse * surfaceColor + specular * material.specularColor) * color; } vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) { vec3 lightColor = surfaceColor; if (lighting.enabled == 0) { return lightColor; } vec3 view_direction = normalize(cameraPosition - position_worldspace); lightColor = material.ambient * surfaceColor * lighting.ambientColor; for (int i = 0; i < lighting.pointLightCount; i++) { PointLight pointLight = lighting_getPointLight(i); vec3 light_position_worldspace = pointLight.position; vec3 light_direction = normalize(light_position_worldspace - position_worldspace); float light_attenuation = getPointLightAttenuation(pointLight, distance(light_position_worldspace, position_worldspace)); lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation); } int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount); for (int i = lighting.pointLightCount; i < totalLights; i++) { DirectionalLight directionalLight = lighting_getDirectionalLight(i); lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color); } return lightColor; } `; // src/modules/lighting/gouraud-material/gouraud-material.ts var gouraudMaterial = { name: "gouraudMaterial", // Note these are switched between phong and gouraud vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"), fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"), defines: { LIGHTING_VERTEX: 1 }, dependencies: [lighting], uniformTypes: { ambient: "f32", diffuse: "f32", shininess: "f32", specularColor: "vec3" }, defaultUniforms: { ambient: 0.35, diffuse: 0.6, shininess: 32, specularColor: [0.15, 0.15, 0.15] }, getUniforms(props) { const uniforms = { ...props }; if (uniforms.specularColor) { uniforms.specularColor = uniforms.specularColor.map((x) => x / 255); } return { ...gouraudMaterial.defaultUniforms, ...uniforms }; } }; // src/modules/lighting/phong-material/phong-material.ts var phongMaterial = { name: "phongMaterial", // Note these are switched between phong and gouraud vs: PHONG_VS, fs: PHONG_FS, defines: { LIGHTING_FRAGMENT: 1 }, dependencies: [lighting], uniformTypes: { ambient: "f32", diffuse: "f32", shininess: "f32", specularColor: "vec3" }, defaultUniforms: { ambient: 0.35, diffuse: 0.6, shininess: 32, specularColor: [0.15, 0.15, 0.15] }, getUniforms(props) { const uniforms = { ...props }; if (uniforms.specularColor) { uniforms.specularColor = uniforms.specularColor.map((x) => x / 255); } return { ...phongMaterial.defaultUniforms, ...uniforms }; } }; // src/modules/lighting/pbr-material/pbr-vertex-glsl.ts var vs2 = glsl`\ uniform projection { mat4 u_MVPMatrix; mat4 u_ModelMatrix; mat4 u_NormalMatrix; // Projection vec3 u_Camera; } varying vec3 pbr_vPosition; varying vec2 pbr_vUV; #ifdef HAS_NORMALS # ifdef HAS_TANGENTS varying mat3 pbr_vTBN; # else varying vec3 pbr_vNormal; # endif #endif void pbr_setPositionNormalTangentUV(vec4 position, vec4 normal, vec4 tangent, vec2 uv) { vec4 pos = u_ModelMatrix * position; pbr_vPosition = vec3(pos.xyz) / pos.w; #ifdef HAS_NORMALS #ifdef HAS_TANGENTS vec3 normalW = normalize(vec3(u_NormalMatrix * vec4(normal.xyz, 0.0))); vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(tangent.xyz, 0.0))); vec3 bitangentW = cross(normalW, tangentW) * tangent.w; pbr_vTBN = mat3(tangentW, bitangentW, normalW); #else // HAS_TANGENTS != 1 pbr_vNormal = normalize(vec3(u_ModelMatrix * vec4(normal.xyz, 0.0))); #endif #endif #ifdef HAS_UV pbr_vUV = uv; #else pbr_vUV = vec2(0.,0.); #endif } `; // src/modules/lighting/pbr-material/pbr-fragment-glsl.ts var fs3 = glsl`\ precision highp float; uniform Projection { // Projection uniform vec3 u_Camera; }; uniform pbrMaterial { // Material is unlit bool unlit; // Base color map bool baseColorMapEnabled; vec4 baseColorFactor; bool normalMapEnabled; float normalScale; // #ifdef HAS_NORMALMAP bool emissiveMapEnabled; vec3 emissiveFactor; // #ifdef HAS_EMISSIVEMAP vec2 metallicRoughnessValues; bool metallicRoughnessMapEnabled; bool occlusionMapEnabled; float occlusionStrength; // #ifdef HAS_OCCLUSIONMAP bool alphaCutoffEnabled; float alphaCutoff; // #ifdef ALPHA_CUTOFF // IBL bool IBLenabled; vec2 scaleIBLAmbient; // #ifdef USE_IBL // debugging flags used for shader output of intermediate PBR variables // #ifdef PBR_DEBUG vec4 scaleDiffBaseMR; vec4 scaleFGDSpec; // #endif } u_pbrMaterial; // Samplers #ifdef HAS_BASECOLORMAP uniform sampler2D u_BaseColorSampler; #endif #ifdef HAS_NORMALMAP uniform sampler2D u_NormalSampler; #endif #ifdef HAS_EMISSIVEMAP uniform sampler2D u_EmissiveSampler; #endif #ifdef HAS_METALROUGHNESSMAP uniform sampler2D u_MetallicRoughnessSampler; #endif #ifdef HAS_OCCLUSIONMAP uniform sampler2D u_OcclusionSampler; #endif #ifdef USE_IBL uniform samplerCube u_DiffuseEnvSampler; uniform samplerCube u_SpecularEnvSampler; uniform sampler2D u_brdfLUT; #endif // Inputs from vertex shader varying vec3 pbr_vPosition; varying vec2 pbr_vUV; #ifdef HAS_NORMALS #ifdef HAS_TANGENTS varying mat3 pbr_vTBN; #else varying vec3 pbr_vNormal; #endif #endif // Encapsulate the various inputs used by the various functions in the shading equation // We store values in this struct to simplify the integration of alternative implementations // of the shading terms, outlined in the Readme.MD Appendix. struct PBRInfo { float NdotL; // cos angle between normal and light direction float NdotV; // cos angle between normal and view direction float NdotH; // cos angle between normal and half vector float LdotH; // cos angle between light direction and half vector float VdotH; // cos angle between view direction and half vector float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) float metalness; // metallic value at the surface vec3 reflectance0; // full reflectance color (normal incidence angle) vec3 reflectance90; // reflectance color at grazing angle float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) vec3 diffuseColor; // color contribution from diffuse lighting vec3 specularColor; // color contribution from specular lighting vec3 n; // normal at surface point vec3 v; // vector from surface point to camera }; const float M_PI = 3.141592653589793; const float c_MinRoughness = 0.04; vec4 SRGBtoLINEAR(vec4 srgbIn) { #ifdef MANUAL_SRGB #ifdef SRGB_FAST_APPROXIMATION vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); #else // SRGB_FAST_APPROXIMATION vec3 bLess = step(vec3(0.04045),srgbIn.xyz); vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); #endif //SRGB_FAST_APPROXIMATION return vec4(linOut,srgbIn.w);; #else //MANUAL_SRGB return srgbIn; #endif //MANUAL_SRGB } // Find the normal for this fragment, pulling either from a predefined normal map // or from the interpolated mesh normal and tangent attributes. vec3 getNormal() { // Retrieve the tangent space matrix #ifndef HAS_TANGENTS vec3 pos_dx = dFdx(pbr_vPosition); vec3 pos_dy = dFdy(pbr_vPosition); vec3 tex_dx = dFdx(vec3(pbr_vUV, 0.0)); vec3 tex_dy = dFdy(vec3(pbr_vUV, 0.0)); vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t); #ifdef HAS_NORMALS vec3 ng = normalize(pbr_vNormal); #else vec3 ng = cross(pos_dx, pos_dy); #endif t = normalize(t - ng * dot(ng, t)); vec3 b = normalize(cross(ng, t)); mat3 tbn = mat3(t, b, ng); #else // HAS_TANGENTS mat3 tbn = pbr_vTBN; #endif #ifdef HAS_NORMALMAP vec3 n = texture2D(u_NormalSampler, pbr_vUV).rgb; n = normalize(tbn * ((2.0 * n - 1.0) * vec3(u_pbrMaterial.normalScale, u_pbrMaterial.normalScale, 1.0))); #else // The tbn matrix is linearly interpolated, so we need to re-normalize vec3 n = normalize(tbn[2].xyz); #endif return n; } // Calculation of the lighting contribution from an optional Image Based Light source. // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. // See our README.md on Environment Maps [3] for additional discussion. #ifdef USE_IBL vec3 getIBLContribution(PBRInfo pbrInfo, vec3 n, vec3 reflection) { float mipCount = 9.0; // resolution of 512x512 float lod = (pbrInfo.perceptualRoughness * mipCount); // retrieve a scale and bias to F0. See [1], Figure 3 vec3 brdf = SRGBtoLINEAR(texture2D(u_brdfLUT, vec2(pbrInfo.NdotV, 1.0 - pbrInfo.perceptualRoughness))).rgb; vec3 diffuseLight = SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb; #ifdef USE_TEX_LOD vec3 specularLight = SRGBtoLINEAR(textureCubeLod(u_SpecularEnvSampler, reflection, lod)).rgb; #else vec3 specularLight = SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb; #endif vec3 diffuse = diffuseLight * pbrInfo.diffuseColor; vec3 specular = specularLight * (pbrInfo.specularColor * brdf.x + brdf.y); // For presentation, this allows us to disable IBL terms diffuse *= u_pbrMaterial.scaleIBLAmbient.x; specular *= u_pbrMaterial.scaleIBLAmbient.y; return diffuse + specular; } #endif // Basic Lambertian diffuse // Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog // See also [1], Equation 1 vec3 diffuse(PBRInfo pbrInfo) { return pbrInfo.diffuseColor / M_PI; } // The following equation models the Fresnel reflectance term of the spec equation (aka F()) // Implementation of fresnel from [4], Equation 15 vec3 specularReflection(PBRInfo pbrInfo) { return pbrInfo.reflectance0 + (pbrInfo.reflectance90 - pbrInfo.reflectance0) * pow(clamp(1.0 - pbrInfo.VdotH, 0.0, 1.0), 5.0); } // This calculates the specular geometric attenuation (aka G()), // where rougher material will reflect less light back to the viewer. // This implementation is based on [1] Equation 4, and we adopt their modifications to // alphaRoughness as input as originally proposed in [2]. float geometricOcclusion(PBRInfo pbrInfo) { float NdotL = pbrInfo.NdotL; float NdotV = pbrInfo.NdotV; float r = pbrInfo.alphaRoughness; float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL))); float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV))); return attenuationL * attenuationV; } // The following equation(s) model the distribution of microfacet normals across // the area being drawn (aka D()) // Implementation from "Average Irregularity Representation of a Roughened Surface // for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz // Follows the distribution function recommended in the SIGGRAPH 2013 course notes // from EPIC Games [1], Equation 3. float microfacetDistribution(PBRInfo pbrInfo) { float roughnessSq = pbrInfo.alphaRoughness * pbrInfo.alphaRoughness; float f = (pbrInfo.NdotH * roughnessSq - pbrInfo.NdotH) * pbrInfo.NdotH + 1.0; return roughnessSq / (M_PI * f * f); } void PBRInfo_setAmbientLight(inout PBRInfo pbrInfo) { pbrInfo.NdotL = 1.0; pbrInfo.NdotH = 0.0; pbrInfo.LdotH = 0.0; pbrInfo.VdotH = 1.0; } void PBRInfo_setDirectionalLight(inout PBRInfo pbrInfo, vec3 lightDirection) { vec3 n = pbrInfo.n; vec3 v = pbrInfo.v; vec3 l = normalize(lightDirection); // Vector from surface point to light vec3 h = normalize(l+v); // Half vector between both l and v pbrInfo.NdotL = clamp(dot(n, l), 0.001, 1.0); pbrInfo.NdotH = clamp(dot(n, h), 0.0, 1.0); pbrInfo.LdotH = clamp(dot(l, h), 0.0, 1.0); pbrInfo.VdotH = clamp(dot(v, h), 0.0, 1.0); } void PBRInfo_setPointLight(inout PBRInfo pbrInfo, PointLight pointLight) { vec3 light_direction = normalize(pointLight.position - pbr_vPosition); PBRInfo_setDirectionalLight(pbrInfo, light_direction); } vec3 calculateFinalColor(PBRInfo pbrInfo, vec3 lightColor) { // Calculate the shading terms for the microfacet specular shading model vec3 F = specularReflection(pbrInfo); float G = geometricOcclusion(pbrInfo); float D = microfacetDistribution(pbrInfo); // Calculation of analytical lighting contribution vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInfo); vec3 specContrib = F * G * D / (4.0 * pbrInfo.NdotL * pbrInfo.NdotV); // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law) return pbrInfo.NdotL * lightColor * (diffuseContrib + specContrib); } vec4 pbr_filterColor(vec4 colorUnused) { // The albedo may be defined from a base texture or a flat color #ifdef HAS_BASECOLORMAP vec4 baseColor = SRGBtoLINEAR(texture2D(u_BaseColorSampler, pbr_vUV)) * u_pbrMaterial.baseColorFactor; #else vec4 baseColor = u_pbrMaterial.baseColorFactor; #endif #ifdef ALPHA_CUTOFF if (baseColor.a < u_pbrMaterial.alphaCutoff) { discard; } #endif vec3 color = vec3(0, 0, 0); if(u_pbrMaterial.unlit){ color.rgb = baseColor.rgb; } else{ // Metallic and Roughness material properties are packed together // In glTF, these factors can be specified by fixed scalar values // or from a metallic-roughness map float perceptualRoughness = u_pbrMaterial.metallicRoughnessValues.y; float metallic = u_pbrMaterial.metallicRoughnessValues.x; #ifdef HAS_METALROUGHNESSMAP // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. // This layout intentionally reserves the 'r' channel for (optional) occlusion map data vec4 mrSample = texture2D(u_MetallicRoughnessSampler, pbr_vUV); perceptualRoughness = mrSample.g * perceptualRoughness; metallic = mrSample.b * metallic; #endif perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); metallic = clamp(metallic, 0.0, 1.0); // Roughness is authored as perceptual roughness; as is convention, // convert to material roughness by squaring the perceptual roughness [2]. float alphaRoughness = perceptualRoughness * perceptualRoughness; vec3 f0 = vec3(0.04); vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0); diffuseColor *= 1.0 - metallic; vec3 specularColor = mix(f0, baseColor.rgb, metallic); // Compute reflectance. float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); // For typical incident reflectance range (between 4% to 100%) set the grazing // reflectance to 100% for typical fresnel effect. // For very low reflectance range on highly diffuse objects (below 4%), // incrementally reduce grazing reflecance to 0%. float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); vec3 specularEnvironmentR0 = specularColor.rgb; vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90; vec3 n = getNormal(); // normal at surface point vec3 v = normalize(u_Camera - pbr_vPosition); // Vector from surface point to camera float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); vec3 reflection = -normalize(reflect(v, n)); PBRInfo pbrInfo = PBRInfo( 0.0, // NdotL NdotV, 0.0, // NdotH 0.0, // LdotH 0.0, // VdotH perceptualRoughness, metallic, specularEnvironmentR0, specularEnvironmentR90, alphaRoughness, diffuseColor, specularColor, n, v ); #ifdef USE_LIGHTS // Apply ambient light PBRInfo_setAmbientLight(pbrInfo); color += calculateFinalColor(pbrInfo, lighting_uAmbientLight.color); // Apply directional light for(int i = 0; i < lighting_uDirectionalLightCount; i++) { if (i < lighting_uDirectionalLightCount) { PBRInfo_setDirectionalLight(pbrInfo, lighting_uDirectionalLight[i].direction); color += calculateFinalColor(pbrInfo, lighting_uDirectionalLight[i].color); } } // Apply point light for(int i = 0; i < lighting_uPointLightCount; i++) { if (i < lighting_uPointLightCount) { PBRInfo_setPointLight(pbrInfo, lighting_uPointLight[i]); float attenuation = getPointLightAttenuation(lighting_uPointLight[i], distance(lighting_uPointLight[i].position, pbr_vPosition)); color += calculateFinalColor(pbrInfo, lighting_uPointLight[i].color / attenuation); } } #endif // Calculate lighting contribution from image based lighting source (IBL) #ifdef USE_IBL if (u_pbrMateral.IBLEnabled) { color += getIBLContribution(pbrInfo, n, reflection); } #endif // Apply optional PBR terms for additional (optional) shading #ifdef HAS_OCCLUSIONMAP if (u_pbrMaterial.occlusionMapEnabled) { float ao = texture2D(u_OcclusionSampler, pbr_vUV).r; color = mix(color, color * ao, u_pbrMaterial.occlusionStrength); } #endif #ifdef HAS_EMISSIVEMAP if (u_pbrMaterial.emmissiveMapEnabled) { vec3 emissive = SRGBtoLINEAR(texture2D(u_EmissiveSampler, pbr_vUV)).rgb * u_pbrMaterial.emissiveFactor; color += emissive; } #endif // This section uses mix to override final color for reference app visualization // of various parameters in the lighting equation. #ifdef PBR_DEBUG // TODO: Figure out how to debug multiple lights // color = mix(color, F, u_ScaleFGDSpec.x); // color = mix(color, vec3(G), u_ScaleFGDSpec.y); // color = mix(color, vec3(D), u_ScaleFGDSpec.z); // color = mix(color, specContrib, u_ScaleFGDSpec.w); // color = mix(color, diffuseContrib, u_ScaleDiffBaseMR.x); color = mix(color, baseColor.rgb, u_pbrMaterial.scaleDiffBaseMR.y); color = mix(color, vec3(metallic), u_pbrMaterial.scaleDiffBaseMR.z); color = mix(color, vec3(perceptualRoughness), u_pbrMaterial.scaleDiffBaseMR.w); #endif } return vec4(pow(color,vec3(1.0/2.2)), baseColor.a); } `; // src/modules/lighting/pbr-material/pbr-material.ts var pbrMaterial = { name: "pbr", vs: vs2, fs: fs3, defines: { LIGHTING_FRAGMENT: 1, HAS_NORMALMAP: 0, HAS_EMISSIVEMAP: 0, HAS_OCCLUSIONMAP: 0, HAS_BASECOLORMAP: 0, HAS_METALROUGHNESSMAP: 0, ALPHA_CUTOFF: 0, USE_IBL: 0, PBR_DEBUG: 0 }, uniformTypes: { // Material is unlit unlit: "i32", // Base color map baseColorMapEnabled: "i32", baseColorFactor: "vec4", normalMapEnabled: "i32", normalScale: "f32", // #ifdef HAS_NORMALMAP emissiveMapEnabled: "i32", emissiveFactor: "vec3", // #ifdef HAS_EMISSIVEMAP metallicRoughnessValues: "vec2", metallicRoughnessMapEnabled: "i32", occlusionMapEnabled: "i32", occlusionStrength: "f32", // #ifdef HAS_OCCLUSIONMAP alphaCutoffEnabled: "i32", alphaCutoff: "f32", // #ifdef ALPHA_CUTOFF // IBL IBLenabled: "i32", scaleIBLAmbient: "vec2", // #ifdef USE_IBL // debugging flags used for shader output of intermediate PBR variables // #ifdef PBR_DEBUG scaleDiffBaseMR: "vec4", scaleFGDSpec: "vec4" }, dependencies: [lighting] }; // src/modules/postprocessing/image-adjust-filters/brightnesscontrast.ts var fs4 = glsl`\ uniform brightnessContrastUniforms { float brightness; float contrast; } brightnessContrast; vec4 brightnessContrast_filterColor(vec4 color) { color.rgb += brightnessContrast.brightness; if (brightnessContrast.contrast > 0.0) { color.rgb = (color.rgb - 0.5) / (1.0 - brightnessContrast.contrast) + 0.5; } else { color.rgb = (color.rgb - 0.5) * (1.0 + brightnessContrast.contrast) + 0.5; } return color; } vec4 brightnessContrast_filterColor(vec4 color, vec2 texSize, vec2 texCoords) { return brightnessContrast_filterColor(color); } `; var brightnessContrast = { name: "brightnessContrast", uniformTypes: { brightness: "f32", contrast: "f32" }, uniformPropTypes: { brightness: { format: "f32", value: 0, min: -1, max: 1 }, contrast: { format: "f32", value: 0, min: -1, max: 1 } }, fs: fs4, passes: [{ filter: true }] }; // src/modules/postprocessing/image-adjust-filters/denoise.ts var fs5 = glsl`\ uniform denoiseUniforms { float strength; } noise; vec4 denoise_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { float adjustedExponent = 3. + 200. * pow(1. - noise.strength, 4.); vec4 center = texture(source, texCoord); vec4 color = vec4(0.0); float total = 0.0; for (float x = -4.0; x <= 4.0; x += 1.0) { for (float y = -4.0; y <= 4.0; y += 1.0) { vec4 offsetColor = texture(source, texCoord + vec2(x, y) / texSize); float weight = 1.0 - abs(dot(offsetColor.rgb - center.rgb, vec3(0.25))); weight = pow(weight, adjustedExponent); color += offsetColor * weight; total += weight; } } return color / total; } `; var denoise = { name: "denoise", uniformTypes: { strength: "f32" }, uniformPropTypes: { strength: { format: "f32", value: 0.5, min: 0, max: 1 } // strength: {..., adjust: (strength: number): number => 0.53 + 200 * Math.pow(1 - strength, 4) // TODO - JS preprocessing }, fs: fs5, passes: [{ sampler: true }, { sampler: true }] }; // src/modules/postprocessing/image-adjust-filters/huesaturation.ts var fs6 = glsl`\ uniform hueSaturationUniforms { float hue; float saturation; } hueSaturation; vec4 hueSaturation_filterColor(vec4 color) { // hue adjustment, wolfram alpha: RotationTransform[angle, {1, 1, 1}][{x, y, z}] float angle = hueSaturation.hue * 3.14159265; float s = sin(angle), c = cos(angle); vec3 weights = (vec3(2.0 * c, -sqrt(3.0) * s - c, sqrt(3.0) * s - c) + 1.0) / 3.0; float len = length(color.rgb); color.rgb = vec3( dot(color.rgb, weights.xyz), dot(color.rgb, weights.zxy), dot(color.rgb, weights.yzx) ); // saturation adjustment float average = (color.r + color.g + color.b) / 3.0; if (hueSaturation.saturation > 0.0) { color.rgb += (average - color.rgb) * (1.0 - 1.0 / (1.001 - hueSaturation.saturation)); } else { color.rgb += (average - color.rgb) * (-hueSaturation.saturation); } return color; } vec4 hueSaturation_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { return hueSaturation_filterColor(color); } `; var hueSaturation = { name: "hueSaturation", uniformTypes: { hue: "f32", saturation: "f32" }, uniformPropTypes: { hue: { value: 0, min: -1, max: 1 }, saturation: { value: 0, min: -1, max: 1 } }, fs: fs6, passes: [{ filter: true }] }; // src/modules/postprocessing/image-adjust-filters/noise.ts var fs7 = glsl`\ uniform noiseUniforms { float amount; } noise; float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } vec4 noise_filterColor(vec4 color, vec2 texCoord) { float diff = (rand(texCoord) - 0.5) * noise.amount; color.r += diff; color.g += diff; color.b += diff; return color; } vec4 noise_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { return noise_filterColor(color, texCoord); } `; var noise = { name: "noise", uniformTypes: { amount: "f32" }, uniformPropTypes: { amount: { value: 0.5, min: 0, max: 1 } }, fs: fs7, passes: [{ filter: true }] }; // src/modules/postprocessing/image-adjust-filters/sepia.ts var fs8 = glsl`\ uniform sepiaUniforms { float amount; } sepia; vec4 sepia_filterColor(vec4 color) { float r = color.r; float g = color.g; float b = color.b; color.r = min(1.0, (r * (1.0 - (0.607 * sepia.amount))) + (g * (0.769 * sepia.amount)) + (b * (0.189 * sepia.amount))); color.g = min(1.0, (r * 0.349 * sepia.amount) + (g * (1.0 - (0.314 * sepia.amount))) + (b * 0.168 * sepia.amount)); color.b = min(1.0, (r * 0.272 * sepia.amount) + (g * 0.534 * sepia.amount) + (b * (1.0 - (0.869 * sepia.amount)))); return color; } vec4 sepia_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { return sepia_filterColor(color); } `; var sepia = { name: "sepia", uniformTypes: { amount: "f32" }, uniformPropTypes: { amount: { value: 0.5, min: 0, max: 1 } }, fs: fs8, passes: [{ filter: true }] }; // src/modules/postprocessing/image-adjust-filters/vibrance.ts var fs9 = glsl`\ uniform vibranceUniforms { float amount; } vibrance; vec4 vibrance_filterColor(vec4 color) { float average = (color.r + color.g + color.b) / 3.0; float mx = max(color.r, max(color.g, color.b)); float amt = (mx - average) * (-vibrance.amount * 3.0); color.rgb = mix(color.rgb, vec3(mx), amt); return color; } vec4 vibrance_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { return vibrance_filterColor(color); } `; var vibrance = { name: "vibrance", uniformPropTypes: { amount: { value: 0, min: -1, max: 1 } }, fs: fs9, passes: [{ filter: true }] }; // src/modules/postprocessing/image-adjust-filters/vignette.ts var fs10 = glsl`\ uniform vignetteUniforms { float radius; float amount; } vignette; vec4 vignette_filterColor(vec4 color, vec2 texCoord) { float dist = distance(texCoord, vec2(0.5, 0.5)); float ratio = smoothstep(0.8, vignette.radius * 0.799, dist * (vignette.amount + vignette.radius)); return color.rgba * ratio + (1.0 - ratio)*vec4(0.0, 0.0, 0.0, 1.0); } vec4 vignette_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { return vignette_filterColor(color, texCoord); } `; var vignette = { name: "vignette", fs: fs10, uniformTypes: { radius: "f32", amount: "f32" }, uniformPropTypes: { radius: { value: 0.5, min: 0, max: 1 }, amount: { value: 0.5, min: 0, max: 1 } }, passes: [{ filter: true }] }; // src/modules/postprocessing/image-blur-filters/tiltshift.ts var fs11 = glsl`\ uniform tiltShiftUniforms { float blurRadius; float gradientRadius; vec2 start; vec2 end; bool invert; } tiltShift; vec2 tiltShift_getDelta(vec2 texSize) { vec2 vector = normalize((tiltShift.end - tiltShift.start) * texSize); return tiltShift.invert ? vec2(-vector.y, vector.x) : vector; } vec4 tiltShift_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec4 color = vec4(0.0); float total = 0.0; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); vec2 normal = normalize(vec2((tiltShift.start.y - tiltShift.end.y) * texSize.y, (tiltShift.end.x - tiltShift.start.x) * texSize.x)); float radius = smoothstep(0.0, 1.0, abs(dot(texCoord * texSize - tiltShift.start * texSize, normal)) / tiltShift.gradientRadius) * tiltShift.blurRadius; for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec4 offsetColor = texture(source, texCoord + tiltShift_getDelta(texSize) / texSize * percent * radius); /* switch to pre-multiplied alpha to correctly blur transparent images */ offsetColor.rgb *= offsetColor.a; color += offsetColor * weight; total += weight; } color = color / total; /* switch back from pre-multiplied alpha */ color.rgb /= color.a + 0.00001; return color; } `; var tiltShift = { name: "tiltShift", uniformTypes: { blurRadius: "f32", gradientRadius: "f32", start: "vec2", end: "vec2", invert: "i32" }, uniformPropTypes: { blurRadius: { value: 15, min: 0, max: 50 }, gradientRadius: { value: 200, min: 0, max: 400 }, start: { value: [0, 0] }, end: { value: [1, 1] }, invert: { value: false, private: true } }, passes: [ { sampler: true, uniforms: { invert: false } }, { sampler: true, uniforms: { invert: true } } ], dependencies: [random], fs: fs11 }; // src/modules/postprocessing/image-blur-filters/triangleblur.ts var fs12 = glsl`\ uniform triangleBlurUniforms { float radius; vec2 delta; } triangleBlur; vec4 triangleBlur_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 adjustedDelta = triangleBlur.delta * triangleBlur.radius / texSize; vec4 color = vec4(0.0); float total = 0.0; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec4 offsetColor = texture(source, texCoord + adjustedDelta * percent); /* switch to pre-multiplied alpha to correctly blur transparent images */ offsetColor.rgb *= offsetColor.a; color += offsetColor * weight; total += weight; } color = color / total; /* switch back from pre-multiplied alpha */ color.rgb /= color.a + 0.00001; return color; } `; var triangleBlur = { name: "triangleBlur", uniformTypes: { radius: "f32", delta: "vec2" }, uniformPropTypes: { radius: { value: 20, min: 0, softMax: 100 }, delta: { value: [1, 0], private: true } }, fs: fs12, dependencies: [random], passes: [ { sampler: true, uniforms: { delta: [1, 0] } }, { sampler: true, uniforms: { delta: [0, 1] } } ] }; // src/modules/postprocessing/image-blur-filters/zoomblur.ts var fs13 = ` uniform zoomBlurUniforms { vec2 center; float strength; } zoomBlur; vec4 zoomBlur_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec4 color = vec4(0.0); float total = 0.0; vec2 toCenter = zoomBlur.center * texSize - texCoord * texSize; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = 0.0; t <= 40.0; t++) { float percent = (t + offset) / 40.0; float weight = 4.0 * (percent - percent * percent); vec4 offsetColor = texture(source, texCoord + toCenter * percent * zoomBlur.strength / texSize); /* switch to pre-multiplied alpha to correctly blur transparent images */ offsetColor.rgb *= offsetColor.a; color += offsetColor * weight; total += weight; } color = color / total; /* switch back from pre-multiplied alpha */ color.rgb /= color.a + 0.00001; return color; } `; var zoomBlur = { name: "zoomBlur", uniformTypes: { center: "vec2", strength: "f32" }, uniformPropTypes: { center: { value: [0.5, 0.5] }, strength: { value: 0.3, min: 0, softMax: 1 } }, fs: fs13, dependencies: [random], passes: [{ sampler: true }] }; // src/modules/postprocessing/image-fun-filters/colorhalftone.ts var fs14 = glsl`\ uniform colorHalftoneUniforms { vec2 center; float angle; float size; } colorHalftone; float pattern(float angle, float scale, vec2 texSize, vec2 texCoord) { float s = sin(angle), c = cos(angle); vec2 tex = texCoord * texSize - colorHalftone.center * texSize; vec2 point = vec2( c * tex.x - s * tex.y, s * tex.x + c * tex.y ) * scale; return (sin(point.x) * sin(point.y)) * 4.0; } vec4 colorHalftone_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { float scale = 3.1514 / colorHalftone.size; vec3 cmy = 1.0 - color.rgb; float k = min(cmy.x, min(cmy.y, cmy.z)); cmy = (cmy - k) / (1.0 - k); cmy = clamp( cmy * 10.0 - 3.0 + vec3( pattern(colorHalftone.angle + 0.26179, scale, texSize, texCoord), pattern(colorHalftone.angle + 1.30899, scale, texSize, texCoord), pattern(colorHalftone.angle, scale, texSize, texCoord) ), 0.0, 1.0 ); k = clamp(k * 10.0 - 5.0 + pattern(colorHalftone.angle + 0.78539, scale, texSize, texCoord), 0.0, 1.0); return vec4(1.0 - cmy - k, color.a); } `; var colorHalftone = { name: "colorHalftone", uniformTypes: { center: "vec2", angle: "f32", size: "f32" }, uniformPropTypes: { center: { value: [0.5, 0.5] }, angle: { value: 1.1, softMin: 0, softMax: Math.PI / 2 }, size: { value: 4, min: 1, softMin: 3, softMax: 20 } }, fs: fs14, passes: [{ filter: true }] }; // src/modules/postprocessing/image-fun-filters/dotscreen.ts var fs15 = glsl`\ uniform dotScreenUniforms { vec2 center; float angle; float size; } dotScreen; float pattern(vec2 texSize, vec2 texCoord) { float scale = 3.1415 / dotScreen.size; float s = sin(dotScreen.angle), c = cos(dotScreen.angle); vec2 tex = texCoord * texSize - dotScreen.center * texSize; vec2 point = vec2( c * tex.x - s * tex.y, s * tex.x + c * tex.y ) * scale; return (sin(point.x) * sin(point.y)) * 4.0; } vec4 dotScreen_filterColor(vec4 color, vec2 texSize, vec2 texCoord) { float average = (color.r + color.g + color.b) / 3.0; return vec4(vec3(average * 10.0 - 5.0 + pattern(texSize, texCoord)), color.a); } `; var dotScreen = { name: "dotScreen", uniformTypes: { center: "vec2", angle: "f32", size: "f32" }, uniformPropTypes: { center: { value: [0.5, 0.5] }, angle: { value: 1.1, softMin: 0, softMax: Math.PI / 2 }, size: { value: 3, min: 1, softMin: 3, softMax: 20 } }, fs: fs15, passes: [{ filter: true }] }; // src/modules/postprocessing/image-fun-filters/edgework.ts var fs16 = glsl`\ uniform edgeWorkUniforms { float radius; vec2 delta; } edgeWork; vec4 edgeWork_sampleColor1(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 relativeDelta = edgeWork.radius * edgeWork.delta / texSize; vec2 color = vec2(0.0); vec2 total = vec2(0.0); /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec3 sampleColor = texture(source, texCoord + relativeDelta * percent).rgb; float average = (sampleColor.r + sampleColor.g + sampleColor.b) / 3.0; color.x += average * weight; total.x += weight; if (abs(t) < 15.0) { weight = weight * 2.0 - 1.0; color.y += average * weight; total.y += weight; } } return vec4(color / total, 0.0, 1.0); } vec4 edgeWork_sampleColor2(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 relativeDelta = edgeWork.radius * edgeWork.delta / texSize; vec2 color = vec2(0.0); vec2 total = vec2(0.0); /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec2 sampleColor = texture(source, texCoord + relativeDelta * percent).xy; color.x += sampleColor.x * weight; total.x += weight; if (abs(t) < 15.0) { weight = weight * 2.0 - 1.0; color.y += sampleColor.y * weight; total.y += weight; } } float c = clamp(10000.0 * (color.y / total.y - color.x / total.x) + 0.5, 0.0, 1.0); return vec4(c, c, c, 1.0); } `; var edgeWork = { name: "edgeWork", uniformPropTypes: { radius: { value: 2, min: 1, softMax: 50 }, delta: { value: [1, 0], private: true } }, fs: fs16, dependencies: [random], passes: [ { // @ts-expect-error sampler: "edgeWork_sampleColor1", uniformPropTypes: { delta: [1, 0] } }, { // @ts-expect-error sampler: "edgeWork_sampleColor2", uniformPropTypes: { delta: [0, 1] } } ] }; // src/modules/postprocessing/image-fun-filters/hexagonalpixelate.ts var fs17 = glsl`\ uniform hexagonalPixelateUniforms { vec2 center; float scale; } hexagonalPixelate; vec4 hexagonalPixelate_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 tex = (texCoord * texSize - hexagonalPixelate.center * texSize) / hexagonalPixelate.scale; tex.y /= 0.866025404; tex.x -= tex.y * 0.5; vec2 a; if (tex.x + tex.y - floor(tex.x) - floor(tex.y) < 1.0) { a = vec2(floor(tex.x), floor(tex.y)); } else a = vec2(ceil(tex.x), ceil(tex.y)); vec2 b = vec2(ceil(tex.x), floor(tex.y)); vec2 c = vec2(floor(tex.x), ceil(tex.y)); vec3 TEX = vec3(tex.x, tex.y, 1.0 - tex.x - tex.y); vec3 A = vec3(a.x, a.y, 1.0 - a.x - a.y); vec3 B = vec3(b.x, b.y, 1.0 - b.x - b.y); vec3 C = vec3(c.x, c.y, 1.0 - c.x - c.y); float alen = length(TEX - A); float blen = length(TEX - B); float clen = length(TEX - C); vec2 choice; if (alen < blen) { if (alen < clen) choice = a; else choice = c; } else { if (blen < clen) choice = b; else choice = c; } choice.x += choice.y * 0.5; choice.y *= 0.866025404; choice *= hexagonalPixelate.scale / texSize; return texture(source, choice + hexagonalPixelate.center); } `; var hexagonalPixelate = { name: "hexagonalPixelate", uniformTypes: { center: "vec2", scale: "f32" }, uniformPropTypes: { center: { value: [0.5, 0.5], hint: "screenspace" }, scale: { value: 10, min: 1, softMin: 5, softMax: 50 } }, fs: fs17, passes: [{ sampler: true }] }; // src/modules/postprocessing/image-fun-filters/ink.ts var fs18 = glsl`\ uniform inkUniforms { float strength; } ink; vec4 ink_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 dx = vec2(1.0 / texSize.x, 0.0); vec2 dy = vec2(0.0, 1.0 / texSize.y); vec4 color = texture(source, texCoord); float bigTotal = 0.0; float smallTotal = 0.0; vec3 bigAverage = vec3(0.0); vec3 smallAverage = vec3(0.0); for (float x = -2.0; x <= 2.0; x += 1.0) { for (float y = -2.0; y <= 2.0; y += 1.0) { vec3 offsetColor = texture(source, texCoord + dx * x + dy * y).rgb; bigAverage += offsetColor; bigTotal += 1.0; if (abs(x) + abs(y) < 2.0) { smallAverage += offsetColor; smallTotal += 1.0; } } } vec3 edge = max(vec3(0.0), bigAverage / bigTotal - smallAverage / smallTotal); float power = ink.strength * ink.strength * ink.strength * ink.strength * ink.strength; return vec4(color.rgb - dot(edge, edge) * power * 100000.0, color.a); } `; var ink = { name: "ink", uniformTypes: { strength: "f32" }, uniformPropTypes: { strength: { value: 0.25, min: 0, softMax: 1 } }, fs: fs18, passes: [{ sampler: true }] }; // src/modules/postprocessing/image-fun-filters/magnify.ts var fs19 = glsl`\ uniform magnifyUniforms { vec2 screenXY; float radiusPixels; float zoom; float borderWidthPixels; vec4 borderColor; } magnify; vec4 magnify_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 pos = vec2(magnify.screenXY.x, 1.0 - magnify.screenXY.y); float dist = distance(texCoord * texSize, pos * texSize); if (dist < magnify.radiusPixels) { return texture(source, (texCoord - pos) / magnify.zoom + pos); } if (dist <= magnify.radiusPixels + magnify.borderWidthPixels) { return magnify.borderColor; } return texture(source, texCoord); } `; var magnify = { name: "magnify", uniformTypes: { screenXY: "vec2", radiusPixels: "f32", zoom: "f32", borderWidthPixels: "f32", borderColor: "vec4" }, uniformPropTypes: { // range 0 to 1 screenXY: { value: [0, 0] }, radiusPixels: 200, zoom: 2, borderWidthPixels: 0, borderColor: { value: [255, 255, 255, 255] } }, fs: fs19, passes: [{ sampler: true }] }; // src/modules/postprocessing/image-warp-filters/warp.ts var fs20 = glsl`\ vec4 warp_sampleColor(sampler2D source, vec2 texSize, vec2 coord) { vec4 color = texture(source, coord / texSize); vec2 clampedCoord = clamp(coord, vec2(0.0), texSize); if (coord != clampedCoord) { /* fade to transparent if we are outside the image */ color.a *= max(0.0, 1.0 - length(coord - clampedCoord)); } return color; } `; var warp = { name: "warp", passes: [], fs: fs20 }; // src/modules/postprocessing/image-warp-filters/bulgepinch.ts var fs21 = glsl`\ uniform bulgePinchUniforms { float radius; float strength; vec2 center; } bulgePinch; vec2 bulgePinch_warp(vec2 coord, vec2 texCenter) { coord -= texCenter; float distance = length(coord); if (distance < bulgePinch.radius) { float percent = distance / bulgePinch.radius; if (bulgePinch.strength > 0.0) { coord *= mix(1.0, smoothstep(0.0, bulgePinch.radius / distance, percent), bulgePinch.strength * 0.75); } else { coord *= mix(1.0, pow(percent, 1.0 + bulgePinch.strength * 0.75) * bulgePinch.radius / distance, 1.0 - percent); } } coord += texCenter; return coord; } vec4 bulgePinch_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 coord = texCoord * texSize; coord = bulgePinch_warp(coord, bulgePinch.center * texSize); return warp_sampleColor(source, texSize, coord); } `; var bulgePinch = { name: "bulgePinch", fs: fs21, uniformTypes: { center: "vec2", radius: "f32", strength: "f32" }, uniformPropTypes: { center: { value: [0.5, 0.5] }, radius: { value: 200, min: 1, softMax: 600 }, strength: { value: 0.5, min: -1, max: 1 } }, dependencies: [warp], passes: [{ sampler: true }] }; // src/modules/postprocessing/image-warp-filters/swirl.ts var fs22 = glsl`\ uniform swirlUniforms { float radius; float angle; vec2 center; } swirl; vec2 swirl_warp(vec2 coord, vec2 texCenter) { coord -= texCenter; float distance = length(coord); if (distance < swirl.radius) { float percent = (swirl.radius - distance) / swirl.radius; float theta = percent * percent * swirl.angle; float s = sin(theta); float c = cos(theta); coord = vec2( coord.x * c - coord.y * s, coord.x * s + coord.y * c ); } coord += texCenter; return coord; } vec4 swirl_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { vec2 coord = texCoord * texSize; coord = swirl_warp(coord, swirl.center * texSize); return warp_sampleColor(source, texSize, coord); } `; var swirl = { name: "swirl", fs: fs22, uniformTypes: { center: "vec2", radius: "f32", angle: "f32" }, uniformPropTypes: { center: { value: [0.5, 0.5] }, radius: { value: 200, min: 1, softMax: 600 }, angle: { value: 3, softMin: -25, softMax: 25 } }, dependencies: [warp], passes: [{ sampler: true }] }; // src/modules/postprocessing/fxaa/fxaa.ts var fs23 = ` #define FXAA_QUALITY_PRESET 29 #if (FXAA_QUALITY_PRESET == 10) #define FXAA_QUALITY_PS 3 #define FXAA_QUALITY_P0 1.5 #define FXAA_QUALITY_P1 3.0 #define FXAA_QUALITY_P2 12.0 #endif #if (FXAA_QUALITY_PRESET == 11) #define FXAA_QUALITY_PS 4 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 3.0 #define FXAA_QUALITY_P3 12.0 #endif #if (FXAA_QUALITY_PRESET == 12) #define FXAA_QUALITY_PS 5 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 4.0 #define FXAA_QUALITY_P4 12.0 #endif #if (FXAA_QUALITY_PRESET == 13) #define FXAA_QUALITY_PS 6 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 4.0 #define FXAA_QUALITY_P5 12.0 #endif #if (FXAA_QUALITY_PRESET == 14) #define FXAA_QUALITY_PS 7 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 4.0 #define FXAA_QUALITY_P6 12.0 #endif #if (FXAA_QUALITY_PRESET == 15) #define FXAA_QUALITY_PS 8 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 2.0 #define FXAA_QUALITY_P6 4.0 #define FXAA_QUALITY_P7 12.0 #endif #if (FXAA_QUALITY_PRESET == 20) #define FXAA_QUALITY_PS 3 #define FXAA_QUALITY_P0 1.5 #define FXAA_QUALITY_P1 2.0 #define FXAA_QUALITY_P2 8.0 #endif #if (FXAA_QUALITY_PRESET == 21) #define FXAA_QUALITY_PS 4 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 8.0 #endif #if (FXAA_QUALITY_PRESET == 22) #define FXAA_QUALITY_PS 5 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 8.0 #endif #if (FXAA_QUALITY_PRESET == 23) #define FXAA_QUALITY_PS 6 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 8.0 #endif #if (FXAA_QUALITY_PRESET == 24) #define FXAA_QUALITY_PS 7 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 3.0 #define FXAA_QUALITY_P6 8.0 #endif #if (FXAA_QUALITY_PRESET == 25) #define FXAA_QUALITY_PS 8 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 2.0 #define FXAA_QUALITY_P6 4.0 #define FXAA_QUALITY_P7 8.0 #endif #if (FXAA_QUALITY_PRESET == 26) #define FXAA_QUALITY_PS 9 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 2.0 #define FXAA_QUALITY_P6 2.0 #define FXAA_QUALITY_P7 4.0 #define FXAA_QUALITY_P8 8.0 #endif #if (FXAA_QUALITY_PRESET == 27) #define FXAA_QUALITY_PS 10 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 2.0 #define FXAA_QUALITY_P6 2.0 #define FXAA_QUALITY_P7 2.0 #define FXAA_QUALITY_P8 4.0 #define FXAA_QUALITY_P9 8.0 #endif #if (FXAA_QUALITY_PRESET == 28) #define FXAA_QUALITY_PS 11 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 2.0 #define FXAA_QUALITY_P6 2.0 #define FXAA_QUALITY_P7 2.0 #define FXAA_QUALITY_P8 2.0 #define FXAA_QUALITY_P9 4.0 #define FXAA_QUALITY_P10 8.0 #endif #if (FXAA_QUALITY_PRESET == 29) #define FXAA_QUALITY_PS 12 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.5 #define FXAA_QUALITY_P2 2.0 #define FXAA_QUALITY_P3 2.0 #define FXAA_QUALITY_P4 2.0 #define FXAA_QUALITY_P5 2.0 #define FXAA_QUALITY_P6 2.0 #define FXAA_QUALITY_P7 2.0 #define FXAA_QUALITY_P8 2.0 #define FXAA_QUALITY_P9 2.0 #define FXAA_QUALITY_P10 4.0 #define FXAA_QUALITY_P11 8.0 #endif #if (FXAA_QUALITY_PRESET == 39) #define FXAA_QUALITY_PS 12 #define FXAA_QUALITY_P0 1.0 #define FXAA_QUALITY_P1 1.0 #define FXAA_QUALITY_P2 1.0 #define FXAA_QUALITY_P3 1.0 #define FXAA_QUALITY_P4 1.0 #define FXAA_QUALITY_P5 1.5 #define FXAA_QUALITY_P6 2.0 #define FXAA_QUALITY_P7 2.0 #define FXAA_QUALITY_P8 2.0 #define FXAA_QUALITY_P9 2.0 #define FXAA_QUALITY_P10 4.0 #define FXAA_QUALITY_P11 8.0 #endif #define FxaaBool bool #define FxaaFloat float #define FxaaFloat2 vec2 #define FxaaFloat3 vec3 #define FxaaFloat4 vec4 #define FxaaHalf float #define FxaaHalf2 vec2 #define FxaaHalf3 vec3 #define FxaaHalf4 vec4 #define FxaaInt2 vec2 #define FxaaTex sampler2D #define FxaaSat(x) clamp(x, 0.0, 1.0) #define FxaaTexTop(t, p) texture(t, p) #define FxaaTexOff(t, p, o, r) texture(t, p + (o * r)) FxaaFloat FxaaLuma_(FxaaFloat4 rgba) { return dot(rgba.rgb, vec3(0.2126, 0.7152, 0.0722)); } FxaaFloat4 FxaaPixelShader_( // // Use noperspective interpolation here (turn off perspective interpolation). // {xy} = center of pixel FxaaFloat2 pos, // // Input color texture. // {rgb_} = color in linear or perceptual color space // if (FXAA_GREEN_AS_LUMA == 0) // {___a} = luma in perceptual color space (not linear) FxaaTex tex, // // Only used on FXAA Quality. // This must be from a constant/uniform. // {x_} = 1.0/screenWidthInPixels // {_y} = 1.0/screenHeightInPixels FxaaFloat2 fxaaQualityRcpFrame, // // Only used on FXAA Quality. // This used to be the FXAA_QUALITY_SUBPIX define. // It is here now to allow easier tuning. // Choose the amount of sub-pixel aliasing removal. // This can effect sharpness. // 1.00 - upper limit (softer) // 0.75 - default amount of filtering // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) // 0.25 - almost off // 0.00 - completely off FxaaFloat fxaaQualitySubpix, // // Only used on FXAA Quality. // This used to be the FXAA_QUALITY_EDGE_THRESHOLD define. // It is here now to allow easier tuning. // The minimum amount of local contrast required to apply algorithm. // 0.333 - too little (faster) // 0.250 - low quality // 0.166 - default // 0.125 - high quality // 0.063 - overkill (slower) FxaaFloat fxaaQualityEdgeThreshold, // // Only used on FXAA Quality. // This used to be the FXAA_QUALITY_EDGE_THRESHOLD_MIN define. // It is here now to allow easier tuning. // Trims the algorithm from processing darks. // 0.0833 - upper limit (default, the start of visible unfiltered edges) // 0.0625 - high quality (faster) // 0.0312 - visible limit (slower) // Special notes when using FXAA_GREEN_AS_LUMA, // Likely want to set this to zero. // As colors that are mostly not-green // will appear very dark in the green channel! // Tune by looking at mostly non-green content, // then start at zero and increase until aliasing is a problem. FxaaFloat fxaaQualityEdgeThresholdMin ) { /*--------------------------------------------------------------------------*/ FxaaFloat2 posM; posM.x = pos.x; posM.y = pos.y; FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); #define lumaM rgbyM.y FxaaFloat lumaS = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaE = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); FxaaFloat lumaN = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaW = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); /*--------------------------------------------------------------------------*/ FxaaFloat maxSM = max(lumaS, lumaM); FxaaFloat minSM = min(lumaS, lumaM); FxaaFloat maxESM = max(lumaE, maxSM); FxaaFloat minESM = min(lumaE, minSM); FxaaFloat maxWN = max(lumaN, lumaW); FxaaFloat minWN = min(lumaN, lumaW); FxaaFloat rangeMax = max(maxWN, maxESM); FxaaFloat rangeMin = min(minWN, minESM); FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; FxaaFloat range = rangeMax - rangeMin; FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); FxaaBool earlyExit = range < rangeMaxClamped; /*--------------------------------------------------------------------------*/ if(earlyExit) return rgbyM; /*--------------------------------------------------------------------------*/ FxaaFloat lumaNW = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSE = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaNE = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); FxaaFloat lumaSW = FxaaLuma_(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); /*--------------------------------------------------------------------------*/ FxaaFloat lumaNS = lumaN + lumaS; FxaaFloat lumaWE = lumaW + lumaE; FxaaFloat subpixRcpRange = 1.0/range; FxaaFloat subpixNSWE = lumaNS + lumaWE; FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; /*--------------------------------------------------------------------------*/ FxaaFloat lumaNESE = lumaNE + lumaSE; FxaaFloat lumaNWNE = lumaNW + lumaNE; FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; /*--------------------------------------------------------------------------*/ FxaaFloat lumaNWSW = lumaNW + lumaSW; FxaaFloat lumaSWSE = lumaSW + lumaSE; FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; /*--------------------------------------------------------------------------*/ FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; FxaaFloat lengthSign = fxaaQualityRcpFrame.x; FxaaBool horzSpan = edgeHorz >= edgeVert; FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; /*--------------------------------------------------------------------------*/ if(!horzSpan) lumaN = lumaW; if(!horzSpan) lumaS = lumaE; if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; /*--------------------------------------------------------------------------*/ FxaaFloat gradientN = lumaN - lumaM; FxaaFloat gradientS = lumaS - lumaM; FxaaFloat lumaNN = lumaN + lumaM; FxaaFloat lumaSS = lumaS + lumaM; FxaaBool pairN = abs(gradientN) >= abs(gradientS); FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); if(pairN) lengthSign = -lengthSign; FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); /*--------------------------------------------------------------------------*/ FxaaFloat2 posB; posB.x = posM.x; posB.y = posM.y; FxaaFloat2 offNP; offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; if(!horzSpan) posB.x += lengthSign * 0.5; if( horzSpan) posB.y += lengthSign * 0.5; /*--------------------------------------------------------------------------*/ FxaaFloat2 posN; posN.x = posB.x - offNP.x * FXAA_QUALITY_P0; posN.y = posB.y - offNP.y * FXAA_QUALITY_P0; FxaaFloat2 posP; posP.x = posB.x + offNP.x * FXAA_QUALITY_P0; posP.y = posB.y + offNP.y * FXAA_QUALITY_P0; FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; FxaaFloat lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN)); FxaaFloat subpixE = subpixC * subpixC; FxaaFloat lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP)); /*--------------------------------------------------------------------------*/ if(!pairN) lumaNN = lumaSS; FxaaFloat gradientScaled = gradient * 1.0/4.0; FxaaFloat lumaMM = lumaM - lumaNN * 0.5; FxaaFloat subpixF = subpixD * subpixE; FxaaBool lumaMLTZero = lumaMM < 0.0; /*--------------------------------------------------------------------------*/ lumaEndN -= lumaNN * 0.5; lumaEndP -= lumaNN * 0.5; FxaaBool doneN = abs(lumaEndN) >= gradientScaled; FxaaBool doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P1; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P1; FxaaBool doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P1; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P1; /*--------------------------------------------------------------------------*/ if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P2; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P2; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P2; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P2; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 3) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P3; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P3; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P3; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P3; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 4) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P4; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P4; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P4; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P4; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 5) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P5; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P5; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P5; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P5; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 6) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P6; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P6; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P6; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P6; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 7) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P7; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P7; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P7; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P7; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 8) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P8; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P8; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P8; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P8; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 9) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P9; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P9; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P9; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P9; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 10) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P10; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P10; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P10; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P10; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 11) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P11; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P11; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P11; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P11; /*--------------------------------------------------------------------------*/ #if (FXAA_QUALITY_PS > 12) if(doneNP) { if(!doneN) lumaEndN = FxaaLuma_(FxaaTexTop(tex, posN.xy)); if(!doneP) lumaEndP = FxaaLuma_(FxaaTexTop(tex, posP.xy)); if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; doneN = abs(lumaEndN) >= gradientScaled; doneP = abs(lumaEndP) >= gradientScaled; if(!doneN) posN.x -= offNP.x * FXAA_QUALITY_P12; if(!doneN) posN.y -= offNP.y * FXAA_QUALITY_P12; doneNP = (!doneN) || (!doneP); if(!doneP) posP.x += offNP.x * FXAA_QUALITY_P12; if(!doneP) posP.y += offNP.y * FXAA_QUALITY_P12; /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } #endif /*--------------------------------------------------------------------------*/ } /*--------------------------------------------------------------------------*/ FxaaFloat dstN = posM.x - posN.x; FxaaFloat dstP = posP.x - posM.x; if(!horzSpan) dstN = posM.y - posN.y; if(!horzSpan) dstP = posP.y - posM.y; /*--------------------------------------------------------------------------*/ FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; FxaaFloat spanLength = (dstP + dstN); FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; FxaaFloat spanLengthRcp = 1.0/spanLength; /*--------------------------------------------------------------------------*/ FxaaBool directionN = dstN < dstP; FxaaFloat dst = min(dstN, dstP); FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; FxaaFloat subpixG = subpixF * subpixF; FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; FxaaFloat subpixH = subpixG * fxaaQualitySubpix; /*--------------------------------------------------------------------------*/ FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; return FxaaTexTop(tex, posM); } vec4 fxaa_sampleColor(sampler2D source, vec2 texSize, vec2 texCoord) { const float fxaa_QualitySubpix = 0.5; const float fxaa_QualityEdgeThreshold = 0.125; const float fxaa_QualityEdgeThresholdMin = 0.0833; return FxaaPixelShader_( texCoord, source, vec2(1.0) / texSize, fxaa_QualitySubpix, fxaa_QualityEdgeThreshold, fxaa_QualityEdgeThresholdMin ); } `; var fxaa = { name: "fxaa", uniformPropTypes: {}, fs: fs23, passes: [{ sampler: true }] }; // src/modules-webgl1/math/fp64/fp64-utils.ts function fp64ify(a, out = [], startIndex = 0) { const hiPart = Math.fround(a); const loPart = a - hiPart; out[startIndex] = hiPart; out[startIndex + 1] = loPart; return out; } function fp64LowPart(a) { return a - Math.fround(a); } function fp64ifyMatrix4(matrix) { const matrixFP64 = new Float32Array(32); for (let i = 0; i < 4; ++i) { for (let j = 0; j < 4; ++j) { const index2 = i * 4 + j; fp64ify(matrix[j * 4 + i], matrixFP64, index2 * 2); } } return matrixFP64; } // src/modules-webgl1/math/fp64/fp64-arithmetic-glsl.ts var fp64arithmeticShader = glsl`\ uniform float ONE; /* About LUMA_FP64_CODE_ELIMINATION_WORKAROUND The purpose of this workaround is to prevent shader compilers from optimizing away necessary arithmetic operations by swapping their sequences or transform the equation to some 'equivalent' form. The method is to multiply an artifical variable, ONE, which will be known to the compiler to be 1 only at runtime. The whole expression is then represented as a polynomial with respective to ONE. In the coefficients of all terms, only one a and one b should appear err = (a + b) * ONE^6 - a * ONE^5 - (a + b) * ONE^4 + a * ONE^3 - b - (a + b) * ONE^2 + a * ONE */ // Divide float number to high and low floats to extend fraction bits vec2 split(float a) { const float SPLIT = 4097.0; float t = a * SPLIT; #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float a_hi = t * ONE - (t - a); float a_lo = a * ONE - a_hi; #else float a_hi = t - (t - a); float a_lo = a - a_hi; #endif return vec2(a_hi, a_lo); } // Divide float number again when high float uses too many fraction bits vec2 split2(vec2 a) { vec2 b = split(a.x); b.y += a.y; return b; } // Special sum operation when a > b vec2 quickTwoSum(float a, float b) { #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float sum = (a + b) * ONE; float err = b - (sum - a) * ONE; #else float sum = a + b; float err = b - (sum - a); #endif return vec2(sum, err); } // General sum operation vec2 twoSum(float a, float b) { float s = (a + b); #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float v = (s * ONE - a) * ONE; float err = (a - (s - v) * ONE) * ONE * ONE * ONE + (b - v); #else float v = s - a; float err = (a - (s - v)) + (b - v); #endif return vec2(s, err); } vec2 twoSub(float a, float b) { float s = (a - b); #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float v = (s * ONE - a) * ONE; float err = (a - (s - v) * ONE) * ONE * ONE * ONE - (b + v); #else float v = s - a; float err = (a - (s - v)) - (b + v); #endif return vec2(s, err); } vec2 twoSqr(float a) { float prod = a * a; vec2 a_fp64 = split(a); #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) float err = ((a_fp64.x * a_fp64.x - prod) * ONE + 2.0 * a_fp64.x * a_fp64.y * ONE * ONE) + a_fp64.y * a_fp64.y * ONE * ONE * ONE; #else float err = ((a_fp64.x * a_fp64.x - prod) + 2.0 * a_fp64.x * a_fp64.y) + a_fp64.y * a_fp64.y; #endif return vec2(prod, err); } vec2 twoProd(float a, float b) { float prod = a * b; vec2 a_fp64 = split(a); vec2 b_fp64 = split(b); float err = ((a_fp64.x * b_fp64.x - prod) + a_fp64.x * b_fp64.y + a_fp64.y * b_fp64.x) + a_fp64.y * b_fp64.y; return vec2(prod, err); } vec2 sum_fp64(vec2 a, vec2 b) { vec2 s, t; s = twoSum(a.x, b.x); t = twoSum(a.y, b.y); s.y += t.x; s = quickTwoSum(s.x, s.y); s.y += t.y; s = quickTwoSum(s.x, s.y); return s; } vec2 sub_fp64(vec2 a, vec2 b) { vec2 s, t; s = twoSub(a.x, b.x); t = twoSub(a.y, b.y); s.y += t.x; s = quickTwoSum(s.x, s.y); s.y += t.y; s = quickTwoSum(s.x, s.y); return s; } vec2 mul_fp64(vec2 a, vec2 b) { vec2 prod = twoProd(a.x, b.x); // y component is for the error prod.y += a.x * b.y; #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) prod = split2(prod); #endif prod = quickTwoSum(prod.x, prod.y); prod.y += a.y * b.x; #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) prod = split2(prod); #endif prod = quickTwoSum(prod.x, prod.y); return prod; } vec2 div_fp64(vec2 a, vec2 b) { float xn = 1.0 / b.x; #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) vec2 yn = mul_fp64(a, vec2(xn, 0)); #else vec2 yn = a * xn; #endif float diff = (sub_fp64(a, mul_fp64(b, yn))).x; vec2 prod = twoProd(xn, diff); return sum_fp64(yn, prod); } vec2 sqrt_fp64(vec2 a) { if (a.x == 0.0 && a.y == 0.0) return vec2(0.0, 0.0); if (a.x < 0.0) return vec2(0.0 / 0.0, 0.0 / 0.0); float x = 1.0 / sqrt(a.x); float yn = a.x * x; #if defined(LUMA_FP64_CODE_ELIMINATION_WORKAROUND) vec2 yn_sqr = twoSqr(yn) * ONE; #else vec2 yn_sqr = twoSqr(yn); #endif float diff = sub_fp64(a, yn_sqr).x; vec2 prod = twoProd(x * 0.5, diff); #if defined(LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND) return sum_fp64(split(yn), prod); #else return sum_fp64(vec2(yn, 0.0), prod); #endif } `; // src/modules-webgl1/math/fp64/fp64-functions-glsl.ts var fp64functionShader = glsl`\ const vec2 E_FP64 = vec2(2.7182817459106445e+00, 8.254840366817007e-08); const vec2 LOG2_FP64 = vec2(0.6931471824645996e+00, -1.9046542121259336e-09); const vec2 PI_FP64 = vec2(3.1415927410125732, -8.742278012618954e-8); const vec2 TWO_PI_FP64 = vec2(6.2831854820251465, -1.7484556025237907e-7); const vec2 PI_2_FP64 = vec2(1.5707963705062866, -4.371139006309477e-8); const vec2 PI_4_FP64 = vec2(0.7853981852531433, -2.1855695031547384e-8); const vec2 PI_16_FP64 = vec2(0.19634954631328583, -5.463923757886846e-9); const vec2 PI_16_2_FP64 = vec2(0.39269909262657166, -1.0927847515773692e-8); const vec2 PI_16_3_FP64 = vec2(0.5890486240386963, -1.4906100798128818e-9); const vec2 PI_180_FP64 = vec2(0.01745329238474369, 1.3519960498364902e-10); const vec2 SIN_TABLE_0_FP64 = vec2(0.19509032368659973, -1.6704714833615242e-9); const vec2 SIN_TABLE_1_FP64 = vec2(0.3826834261417389, 6.22335089017767e-9); const vec2 SIN_TABLE_2_FP64 = vec2(0.5555702447891235, -1.1769521357507529e-8); const vec2 SIN_TABLE_3_FP64 = vec2(0.7071067690849304, 1.2101617041793133e-8); const vec2 COS_TABLE_0_FP64 = vec2(0.9807852506637573, 2.9739473106360492e-8); const vec2 COS_TABLE_1_FP64 = vec2(0.9238795042037964, 2.8307490351764386e-8); const vec2 COS_TABLE_2_FP64 = vec2(0.8314695954322815, 1.6870263741530778e-8); const vec2 COS_TABLE_3_FP64 = vec2(0.7071067690849304, 1.2101617152815436e-8); const vec2 INVERSE_FACTORIAL_3_FP64 = vec2(1.666666716337204e-01, -4.967053879312289e-09); // 1/3! const vec2 INVERSE_FACTORIAL_4_FP64 = vec2(4.16666679084301e-02, -1.2417634698280722e-09); // 1/4! const vec2 INVERSE_FACTORIAL_5_FP64 = vec2(8.333333767950535e-03, -4.34617203337595e-10); // 1/5! const vec2 INVERSE_FACTORIAL_6_FP64 = vec2(1.3888889225199819e-03, -3.3631094437103215e-11); // 1/6! const vec2 INVERSE_FACTORIAL_7_FP64 = vec2(1.9841270113829523e-04, -2.725596874933456e-12); // 1/7! const vec2 INVERSE_FACTORIAL_8_FP64 = vec2(2.4801587642286904e-05, -3.406996025904184e-13); // 1/8! const vec2 INVERSE_FACTORIAL_9_FP64 = vec2(2.75573188446287533e-06, 3.7935713937038186e-14); // 1/9! const vec2 INVERSE_FACTORIAL_10_FP64 = vec2(2.755731998149713e-07, -7.575112367869873e-15); // 1/10! float nint(float d) { if (d == floor(d)) return d; return floor(d + 0.5); } vec2 nint_fp64(vec2 a) { float hi = nint(a.x); float lo; vec2 tmp; if (hi == a.x) { lo = nint(a.y); tmp = quickTwoSum(hi, lo); } else { lo = 0.0; if (abs(hi - a.x) == 0.5 && a.y < 0.0) { hi -= 1.0; } tmp = vec2(hi, lo); } return tmp; } /* k_power controls how much range reduction we would like to have Range reduction uses the following method: assume a = k_power * r + m * log(2), k and m being integers. Set k_power = 4 (we can choose other k to trade accuracy with performance. we only need to calculate exp(r) and using exp(a) = 2^m * exp(r)^k_power; */ vec2 exp_fp64(vec2 a) { // We need to make sure these two numbers match // as bit-wise shift is not available in GLSL 1.0 const int k_power = 4; const float k = 16.0; const float inv_k = 1.0 / k; if (a.x <= -88.0) return vec2(0.0, 0.0); if (a.x >= 88.0) return vec2(1.0 / 0.0, 1.0 / 0.0); if (a.x == 0.0 && a.y == 0.0) return vec2(1.0, 0.0); if (a.x == 1.0 && a.y == 0.0) return E_FP64; float m = floor(a.x / LOG2_FP64.x + 0.5); vec2 r = sub_fp64(a, mul_fp64(LOG2_FP64, vec2(m, 0.0))) * inv_k; vec2 s, t, p; p = mul_fp64(r, r); s = sum_fp64(r, p * 0.5); p = mul_fp64(p, r); t = mul_fp64(p, INVERSE_FACTORIAL_3_FP64); s = sum_fp64(s, t); p = mul_fp64(p, r); t = mul_fp64(p, INVERSE_FACTORIAL_4_FP64); s = sum_fp64(s, t); p = mul_fp64(p, r); t = mul_fp64(p, INVERSE_FACTORIAL_5_FP64); // s = sum_fp64(s, t); // p = mul_fp64(p, r); // t = mul_fp64(p, INVERSE_FACTORIAL_6_FP64); // s = sum_fp64(s, t); // p = mul_fp64(p, r); // t = mul_fp64(p, INVERSE_FACTORIAL_7_FP64); s = sum_fp64(s, t); // At this point, s = exp(r) - 1; but after following 4 recursions, we will get exp(r) ^ 512 - 1. for (int i = 0; i < k_power; i++) { s = sum_fp64(s * 2.0, mul_fp64(s, s)); } #if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) s = sum_fp64(s, vec2(ONE, 0.0)); #else s = sum_fp64(s, vec2(1.0, 0.0)); #endif return s * pow(2.0, m); // return r; } vec2 log_fp64(vec2 a) { if (a.x == 1.0 && a.y == 0.0) return vec2(0.0, 0.0); if (a.x <= 0.0) return vec2(0.0 / 0.0, 0.0 / 0.0); vec2 x = vec2(log(a.x), 0.0); vec2 s; #if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) s = vec2(ONE, 0.0); #else s = vec2(1.0, 0.0); #endif x = sub_fp64(sum_fp64(x, mul_fp64(a, exp_fp64(-x))), s); return x; } vec2 sin_taylor_fp64(vec2 a) { vec2 r, s, t, x; if (a.x == 0.0 && a.y == 0.0) { return vec2(0.0, 0.0); } x = -mul_fp64(a, a); s = a; r = a; r = mul_fp64(r, x); t = mul_fp64(r, INVERSE_FACTORIAL_3_FP64); s = sum_fp64(s, t); r = mul_fp64(r, x); t = mul_fp64(r, INVERSE_FACTORIAL_5_FP64); s = sum_fp64(s, t); /* keep the following commented code in case we need them for extra accuracy from the Taylor expansion*/ // r = mul_fp64(r, x); // t = mul_fp64(r, INVERSE_FACTORIAL_7_FP64); // s = sum_fp64(s, t); // r = mul_fp64(r, x); // t = mul_fp64(r, INVERSE_FACTORIAL_9_FP64); // s = sum_fp64(s, t); return s; } vec2 cos_taylor_fp64(vec2 a) { vec2 r, s, t, x; if (a.x == 0.0 && a.y == 0.0) { return vec2(1.0, 0.0); } x = -mul_fp64(a, a); r = x; s = sum_fp64(vec2(1.0, 0.0), r * 0.5); r = mul_fp64(r, x); t = mul_fp64(r, INVERSE_FACTORIAL_4_FP64); s = sum_fp64(s, t); r = mul_fp64(r, x); t = mul_fp64(r, INVERSE_FACTORIAL_6_FP64); s = sum_fp64(s, t); /* keep the following commented code in case we need them for extra accuracy from the Taylor expansion*/ // r = mul_fp64(r, x); // t = mul_fp64(r, INVERSE_FACTORIAL_8_FP64); // s = sum_fp64(s, t); // r = mul_fp64(r, x); // t = mul_fp64(r, INVERSE_FACTORIAL_10_FP64); // s = sum_fp64(s, t); return s; } void sincos_taylor_fp64(vec2 a, out vec2 sin_t, out vec2 cos_t) { if (a.x == 0.0 && a.y == 0.0) { sin_t = vec2(0.0, 0.0); cos_t = vec2(1.0, 0.0); } sin_t = sin_taylor_fp64(a); cos_t = sqrt_fp64(sub_fp64(vec2(1.0, 0.0), mul_fp64(sin_t, sin_t))); } vec2 sin_fp64(vec2 a) { if (a.x == 0.0 && a.y == 0.0) { return vec2(0.0, 0.0); } // 2pi range reduction vec2 z = nint_fp64(div_fp64(a, TWO_PI_FP64)); vec2 r = sub_fp64(a, mul_fp64(TWO_PI_FP64, z)); vec2 t; float q = floor(r.x / PI_2_FP64.x + 0.5); int j = int(q); if (j < -2 || j > 2) { return vec2(0.0 / 0.0, 0.0 / 0.0); } t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); q = floor(t.x / PI_16_FP64.x + 0.5); int k = int(q); if (k == 0) { if (j == 0) { return sin_taylor_fp64(t); } else if (j == 1) { return cos_taylor_fp64(t); } else if (j == -1) { return -cos_taylor_fp64(t); } else { return -sin_taylor_fp64(t); } } int abs_k = int(abs(float(k))); if (abs_k > 4) { return vec2(0.0 / 0.0, 0.0 / 0.0); } else { t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); } vec2 u = vec2(0.0, 0.0); vec2 v = vec2(0.0, 0.0); #if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) if (abs(float(abs_k) - 1.0) < 0.5) { u = COS_TABLE_0_FP64; v = SIN_TABLE_0_FP64; } else if (abs(float(abs_k) - 2.0) < 0.5) { u = COS_TABLE_1_FP64; v = SIN_TABLE_1_FP64; } else if (abs(float(abs_k) - 3.0) < 0.5) { u = COS_TABLE_2_FP64; v = SIN_TABLE_2_FP64; } else if (abs(float(abs_k) - 4.0) < 0.5) { u = COS_TABLE_3_FP64; v = SIN_TABLE_3_FP64; } #else if (abs_k == 1) { u = COS_TABLE_0_FP64; v = SIN_TABLE_0_FP64; } else if (abs_k == 2) { u = COS_TABLE_1_FP64; v = SIN_TABLE_1_FP64; } else if (abs_k == 3) { u = COS_TABLE_2_FP64; v = SIN_TABLE_2_FP64; } else if (abs_k == 4) { u = COS_TABLE_3_FP64; v = SIN_TABLE_3_FP64; } #endif vec2 sin_t, cos_t; sincos_taylor_fp64(t, sin_t, cos_t); vec2 result = vec2(0.0, 0.0); if (j == 0) { if (k > 0) { result = sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); } else { result = sub_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); } } else if (j == 1) { if (k > 0) { result = sub_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } else { result = sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } } else if (j == -1) { if (k > 0) { result = sub_fp64(mul_fp64(v, sin_t), mul_fp64(u, cos_t)); } else { result = -sum_fp64(mul_fp64(v, sin_t), mul_fp64(u, cos_t)); } } else { if (k > 0) { result = -sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); } else { result = sub_fp64(mul_fp64(v, cos_t), mul_fp64(u, sin_t)); } } return result; } vec2 cos_fp64(vec2 a) { if (a.x == 0.0 && a.y == 0.0) { return vec2(1.0, 0.0); } // 2pi range reduction vec2 z = nint_fp64(div_fp64(a, TWO_PI_FP64)); vec2 r = sub_fp64(a, mul_fp64(TWO_PI_FP64, z)); vec2 t; float q = floor(r.x / PI_2_FP64.x + 0.5); int j = int(q); if (j < -2 || j > 2) { return vec2(0.0 / 0.0, 0.0 / 0.0); } t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); q = floor(t.x / PI_16_FP64.x + 0.5); int k = int(q); if (k == 0) { if (j == 0) { return cos_taylor_fp64(t); } else if (j == 1) { return -sin_taylor_fp64(t); } else if (j == -1) { return sin_taylor_fp64(t); } else { return -cos_taylor_fp64(t); } } int abs_k = int(abs(float(k))); if (abs_k > 4) { return vec2(0.0 / 0.0, 0.0 / 0.0); } else { t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); } vec2 u = vec2(0.0, 0.0); vec2 v = vec2(0.0, 0.0); #if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) if (abs(float(abs_k) - 1.0) < 0.5) { u = COS_TABLE_0_FP64; v = SIN_TABLE_0_FP64; } else if (abs(float(abs_k) - 2.0) < 0.5) { u = COS_TABLE_1_FP64; v = SIN_TABLE_1_FP64; } else if (abs(float(abs_k) - 3.0) < 0.5) { u = COS_TABLE_2_FP64; v = SIN_TABLE_2_FP64; } else if (abs(float(abs_k) - 4.0) < 0.5) { u = COS_TABLE_3_FP64; v = SIN_TABLE_3_FP64; } #else if (abs_k == 1) { u = COS_TABLE_0_FP64; v = SIN_TABLE_0_FP64; } else if (abs_k == 2) { u = COS_TABLE_1_FP64; v = SIN_TABLE_1_FP64; } else if (abs_k == 3) { u = COS_TABLE_2_FP64; v = SIN_TABLE_2_FP64; } else if (abs_k == 4) { u = COS_TABLE_3_FP64; v = SIN_TABLE_3_FP64; } #endif vec2 sin_t, cos_t; sincos_taylor_fp64(t, sin_t, cos_t); vec2 result = vec2(0.0, 0.0); if (j == 0) { if (k > 0) { result = sub_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } else { result = sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } } else if (j == 1) { if (k > 0) { result = -sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); } else { result = sub_fp64(mul_fp64(v, cos_t), mul_fp64(u, sin_t)); } } else if (j == -1) { if (k > 0) { result = sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); } else { result = sub_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); } } else { if (k > 0) { result = sub_fp64(mul_fp64(v, sin_t), mul_fp64(u, cos_t)); } else { result = -sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } } return result; } vec2 tan_fp64(vec2 a) { vec2 sin_a; vec2 cos_a; if (a.x == 0.0 && a.y == 0.0) { return vec2(0.0, 0.0); } // 2pi range reduction vec2 z = nint_fp64(div_fp64(a, TWO_PI_FP64)); vec2 r = sub_fp64(a, mul_fp64(TWO_PI_FP64, z)); vec2 t; float q = floor(r.x / PI_2_FP64.x + 0.5); int j = int(q); if (j < -2 || j > 2) { return vec2(0.0 / 0.0, 0.0 / 0.0); } t = sub_fp64(r, mul_fp64(PI_2_FP64, vec2(q, 0.0))); q = floor(t.x / PI_16_FP64.x + 0.5); int k = int(q); int abs_k = int(abs(float(k))); // We just can't get PI/16 * 3.0 very accurately. // so let's just store it if (abs_k > 4) { return vec2(0.0 / 0.0, 0.0 / 0.0); } else { t = sub_fp64(t, mul_fp64(PI_16_FP64, vec2(q, 0.0))); } vec2 u = vec2(0.0, 0.0); vec2 v = vec2(0.0, 0.0); vec2 sin_t, cos_t; vec2 s, c; sincos_taylor_fp64(t, sin_t, cos_t); if (k == 0) { s = sin_t; c = cos_t; } else { #if defined(NVIDIA_FP64_WORKAROUND) || defined(INTEL_FP64_WORKAROUND) if (abs(float(abs_k) - 1.0) < 0.5) { u = COS_TABLE_0_FP64; v = SIN_TABLE_0_FP64; } else if (abs(float(abs_k) - 2.0) < 0.5) { u = COS_TABLE_1_FP64; v = SIN_TABLE_1_FP64; } else if (abs(float(abs_k) - 3.0) < 0.5) { u = COS_TABLE_2_FP64; v = SIN_TABLE_2_FP64; } else if (abs(float(abs_k) - 4.0) < 0.5) { u = COS_TABLE_3_FP64; v = SIN_TABLE_3_FP64; } #else if (abs_k == 1) { u = COS_TABLE_0_FP64; v = SIN_TABLE_0_FP64; } else if (abs_k == 2) { u = COS_TABLE_1_FP64; v = SIN_TABLE_1_FP64; } else if (abs_k == 3) { u = COS_TABLE_2_FP64; v = SIN_TABLE_2_FP64; } else if (abs_k == 4) { u = COS_TABLE_3_FP64; v = SIN_TABLE_3_FP64; } #endif if (k > 0) { s = sum_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); c = sub_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } else { s = sub_fp64(mul_fp64(u, sin_t), mul_fp64(v, cos_t)); c = sum_fp64(mul_fp64(u, cos_t), mul_fp64(v, sin_t)); } } if (j == 0) { sin_a = s; cos_a = c; } else if (j == 1) { sin_a = c; cos_a = -s; } else if (j == -1) { sin_a = -c; cos_a = s; } else { sin_a = -s; cos_a = -c; } return div_fp64(sin_a, cos_a); } vec2 radians_fp64(vec2 degree) { return mul_fp64(degree, PI_180_FP64); } vec2 mix_fp64(vec2 a, vec2 b, float x) { vec2 range = sub_fp64(b, a); return sum_fp64(a, mul_fp64(range, vec2(x, 0.0))); } // Vector functions // vec2 functions void vec2_sum_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { out_val[0] = sum_fp64(a[0], b[0]); out_val[1] = sum_fp64(a[1], b[1]); } void vec2_sub_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { out_val[0] = sub_fp64(a[0], b[0]); out_val[1] = sub_fp64(a[1], b[1]); } void vec2_mul_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { out_val[0] = mul_fp64(a[0], b[0]); out_val[1] = mul_fp64(a[1], b[1]); } void vec2_div_fp64(vec2 a[2], vec2 b[2], out vec2 out_val[2]) { out_val[0] = div_fp64(a[0], b[0]); out_val[1] = div_fp64(a[1], b[1]); } void vec2_mix_fp64(vec2 x[2], vec2 y[2], float a, out vec2 out_val[2]) { vec2 range[2]; vec2_sub_fp64(y, x, range); vec2 portion[2]; portion[0] = range[0] * a; portion[1] = range[1] * a; vec2_sum_fp64(x, portion, out_val); } vec2 vec2_length_fp64(vec2 x[2]) { return sqrt_fp64(sum_fp64(mul_fp64(x[0], x[0]), mul_fp64(x[1], x[1]))); } void vec2_normalize_fp64(vec2 x[2], out vec2 out_val[2]) { vec2 length = vec2_length_fp64(x); vec2 length_vec2[2]; length_vec2[0] = length; length_vec2[1] = length; vec2_div_fp64(x, length_vec2, out_val); } vec2 vec2_distance_fp64(vec2 x[2], vec2 y[2]) { vec2 diff[2]; vec2_sub_fp64(x, y, diff); return vec2_length_fp64(diff); } vec2 vec2_dot_fp64(vec2 a[2], vec2 b[2]) { vec2 v[2]; v[0] = mul_fp64(a[0], b[0]); v[1] = mul_fp64(a[1], b[1]); return sum_fp64(v[0], v[1]); } // vec3 functions void vec3_sub_fp64(vec2 a[3], vec2 b[3], out vec2 out_val[3]) { for (int i = 0; i < 3; i++) { out_val[i] = sum_fp64(a[i], b[i]); } } void vec3_sum_fp64(vec2 a[3], vec2 b[3], out vec2 out_val[3]) { for (int i = 0; i < 3; i++) { out_val[i] = sum_fp64(a[i], b[i]); } } vec2 vec3_length_fp64(vec2 x[3]) { return sqrt_fp64(sum_fp64(sum_fp64(mul_fp64(x[0], x[0]), mul_fp64(x[1], x[1])), mul_fp64(x[2], x[2]))); } vec2 vec3_distance_fp64(vec2 x[3], vec2 y[3]) { vec2 diff[3]; vec3_sub_fp64(x, y, diff); return vec3_length_fp64(diff); } // vec4 functions void vec4_fp64(vec4 a, out vec2 out_val[4]) { out_val[0].x = a[0]; out_val[0].y = 0.0; out_val[1].x = a[1]; out_val[1].y = 0.0; out_val[2].x = a[2]; out_val[2].y = 0.0; out_val[3].x = a[3]; out_val[3].y = 0.0; } void vec4_scalar_mul_fp64(vec2 a[4], vec2 b, out vec2 out_val[4]) { out_val[0] = mul_fp64(a[0], b); out_val[1] = mul_fp64(a[1], b); out_val[2] = mul_fp64(a[2], b); out_val[3] = mul_fp64(a[3], b); } void vec4_sum_fp64(vec2 a[4], vec2 b[4], out vec2 out_val[4]) { for (int i = 0; i < 4; i++) { out_val[i] = sum_fp64(a[i], b[i]); } } void vec4_dot_fp64(vec2 a[4], vec2 b[4], out vec2 out_val) { vec2 v[4]; v[0] = mul_fp64(a[0], b[0]); v[1] = mul_fp64(a[1], b[1]); v[2] = mul_fp64(a[2], b[2]); v[3] = mul_fp64(a[3], b[3]); out_val = sum_fp64(sum_fp64(v[0], v[1]), sum_fp64(v[2], v[3])); } void mat4_vec4_mul_fp64(vec2 b[16], vec2 a[4], out vec2 out_val[4]) { vec2 tmp[4]; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { tmp[j] = b[j + i * 4]; } vec4_dot_fp64(a, tmp, out_val[i]); } } `; // src/modules-webgl1/math/fp64/fp64.ts var CONST_UNIFORMS = { // Used in LUMA_FP64_CODE_ELIMINATION_WORKAROUND ONE: 1 }; function getUniforms4() { return CONST_UNIFORMS; } var fp64arithmetic = { name: "fp64-arithmetic", vs: fp64arithmeticShader, getUniforms: getUniforms4, fp64ify, fp64LowPart, fp64ifyMatrix4 }; var fp64 = { name: "fp64", vs: fp64functionShader, dependencies: [fp64arithmetic], // Additional Functions fp64ify, fp64LowPart, fp64ifyMatrix4 }; // src/modules-webgl1/geometry/geometry.ts var vs3 = glsl`\ varying vec4 geometry_vPosition; varying vec3 geometry_vNormal; void geometry_setNormal(vec3 normal) { geometry_vNormal = normal; } void geometry_setPosition(vec4 position) { geometry_vPosition = position; } void geometry_setPosition(vec3 position) { geometry_vPosition = vec4(position, 1.); } `; var fs24 = glsl`\ varying vec4 geometry_vPosition; varying vec3 geometry_vNormal; vec4 geometry_getPosition() { return geometry_vPosition; } vec3 geometry_getNormal() { return geometry_vNormal; } `; var geometry = { name: "geometry", vs: vs3, fs: fs24 }; // ../../node_modules/@math.gl/core/dist/lib/common.js var RADIANS_TO_DEGREES = 1 / Math.PI * 180; var DEGREES_TO_RADIANS = 1 / 180 * Math.PI; var DEFAULT_CONFIG = { EPSILON: 1e-12, debug: false, precision: 4, printTypes: false, printDegrees: false, printRowMajor: true, _cartographicRadians: false }; globalThis.mathgl = globalThis.mathgl || { config: { ...DEFAULT_CONFIG } }; var config = globalThis.mathgl.config; function formatValue(value, { precision = config.precision } = {}) { value = round(value); return `${parseFloat(value.toPrecision(precision))}`; } function isArray(value) { return Array.isArray(value) || ArrayBuffer.isView(value) && !(value instanceof DataView); } function equals(a, b, epsilon) { const oldEpsilon = config.EPSILON; if (epsilon) { config.EPSILON = epsilon; } try { if (a === b) { return true; } if (isArray(a) && isArray(b)) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; ++i) { if (!equals(a[i], b[i])) { return false; } } return true; } if (a && a.equals) { return a.equals(b); } if (b && b.equals) { return b.equals(a); } if (typeof a === "number" && typeof b === "number") { return Math.abs(a - b) <= config.EPSILON * Math.max(1, Math.abs(a), Math.abs(b)); } return false; } finally { config.EPSILON = oldEpsilon; } } function round(value) { return Math.round(value / config.EPSILON) * config.EPSILON; } // ../../node_modules/@math.gl/core/dist/classes/base/math-array.js var MathArray = class extends Array { // Common methods /** * Clone the current object * @returns a new copy of this object */ clone() { return new this.constructor().copy(this); } fromArray(array, offset = 0) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = array[i + offset]; } return this.check(); } toArray(targetArray = [], offset = 0) { for (let i = 0; i < this.ELEMENTS; ++i) { targetArray[offset + i] = this[i]; } return targetArray; } toObject(targetObject) { return targetObject; } from(arrayOrObject) { return Array.isArray(arrayOrObject) ? this.copy(arrayOrObject) : ( // @ts-ignore this.fromObject(arrayOrObject) ); } to(arrayOrObject) { if (arrayOrObject === this) { return this; } return isArray(arrayOrObject) ? this.toArray(arrayOrObject) : this.toObject(arrayOrObject); } toTarget(target) { return target ? this.to(target) : this; } /** @deprecated */ toFloat32Array() { return new Float32Array(this); } toString() { return this.formatString(config); } /** Formats string according to options */ formatString(opts) { let string = ""; for (let i = 0; i < this.ELEMENTS; ++i) { string += (i > 0 ? ", " : "") + formatValue(this[i], opts); } return `${opts.printTypes ? this.constructor.name : ""}[${string}]`; } equals(array) { if (!array || this.length !== array.length) { return false; } for (let i = 0; i < this.ELEMENTS; ++i) { if (!equals(this[i], array[i])) { return false; } } return true; } exactEquals(array) { if (!array || this.length !== array.length) { return false; } for (let i = 0; i < this.ELEMENTS; ++i) { if (this[i] !== array[i]) { return false; } } return true; } // Modifiers /** Negates all values in this object */ negate() { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = -this[i]; } return this.check(); } lerp(a, b, t) { if (t === void 0) { return this.lerp(this, a, b); } for (let i = 0; i < this.ELEMENTS; ++i) { const ai = a[i]; const endValue = typeof b === "number" ? b : b[i]; this[i] = ai + t * (endValue - ai); } return this.check(); } /** Minimal */ min(vector) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = Math.min(vector[i], this[i]); } return this.check(); } /** Maximal */ max(vector) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = Math.max(vector[i], this[i]); } return this.check(); } clamp(minVector, maxVector) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = Math.min(Math.max(this[i], minVector[i]), maxVector[i]); } return this.check(); } add(...vectors) { for (const vector of vectors) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] += vector[i]; } } return this.check(); } subtract(...vectors) { for (const vector of vectors) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] -= vector[i]; } } return this.check(); } scale(scale2) { if (typeof scale2 === "number") { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] *= scale2; } } else { for (let i = 0; i < this.ELEMENTS && i < scale2.length; ++i) { this[i] *= scale2[i]; } } return this.check(); } /** * Multiplies all elements by `scale` * Note: `Matrix4.multiplyByScalar` only scales its 3x3 "minor" */ multiplyByScalar(scalar) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] *= scalar; } return this.check(); } // Debug checks /** Throws an error if array length is incorrect or contains illegal values */ check() { if (config.debug && !this.validate()) { throw new Error(`math.gl: ${this.constructor.name} some fields set to invalid numbers'`); } return this; } /** Returns false if the array length is incorrect or contains illegal values */ validate() { let valid = this.length === this.ELEMENTS; for (let i = 0; i < this.ELEMENTS; ++i) { valid = valid && Number.isFinite(this[i]); } return valid; } // three.js compatibility /** @deprecated */ sub(a) { return this.subtract(a); } /** @deprecated */ setScalar(a) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = a; } return this.check(); } /** @deprecated */ addScalar(a) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] += a; } return this.check(); } /** @deprecated */ subScalar(a) { return this.addScalar(-a); } /** @deprecated */ multiplyScalar(scalar) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] *= scalar; } return this.check(); } /** @deprecated */ divideScalar(a) { return this.multiplyByScalar(1 / a); } /** @deprecated */ clampScalar(min, max) { for (let i = 0; i < this.ELEMENTS; ++i) { this[i] = Math.min(Math.max(this[i], min), max); } return this.check(); } /** @deprecated */ get elements() { return this; } }; // ../../node_modules/@math.gl/core/dist/lib/validators.js function validateVector(v, length) { if (v.length !== length) { return false; } for (let i = 0; i < v.length; ++i) { if (!Number.isFinite(v[i])) { return false; } } return true; } function checkNumber(value) { if (!Number.isFinite(value)) { throw new Error(`Invalid number ${JSON.stringify(value)}`); } return value; } function checkVector(v, length, callerName = "") { if (config.debug && !validateVector(v, length)) { throw new Error(`math.gl: ${callerName} some fields set to invalid numbers'`); } return v; } // ../../node_modules/@math.gl/core/dist/gl-matrix/common.js var EPSILON = 1e-6; var ARRAY_TYPE = typeof Float32Array !== "undefined" ? Float32Array : Array; var degree = Math.PI / 180; // ../../node_modules/@math.gl/core/dist/gl-matrix/vec2.js function create() { const out = new ARRAY_TYPE(2); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; } return out; } function transformMat4(out, a, m) { const x = a[0]; const y = a[1]; out[0] = m[0] * x + m[4] * y + m[12]; out[1] = m[1] * x + m[5] * y + m[13]; return out; } var forEach = function() { const vec = create(); return function(a, stride, offset, count, fn, arg) { let i; let l; if (!stride) { stride = 2; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; } return a; }; }(); // ../../node_modules/@math.gl/core/dist/lib/gl-matrix-extras.js function vec2_transformMat4AsVector(out, a, m) { const x = a[0]; const y = a[1]; const w = m[3] * x + m[7] * y || 1; out[0] = (m[0] * x + m[4] * y) / w; out[1] = (m[1] * x + m[5] * y) / w; return out; } function vec3_transformMat4AsVector(out, a, m) { const x = a[0]; const y = a[1]; const z = a[2]; const w = m[3] * x + m[7] * y + m[11] * z || 1; out[0] = (m[0] * x + m[4] * y + m[8] * z) / w; out[1] = (m[1] * x + m[5] * y + m[9] * z) / w; out[2] = (m[2] * x + m[6] * y + m[10] * z) / w; return out; } // ../../node_modules/@math.gl/core/dist/gl-matrix/vec3.js function create2() { const out = new ARRAY_TYPE(3); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; } return out; } function transformMat42(out, a, m) { const x = a[0]; const y = a[1]; const z = a[2]; let w = m[3] * x + m[7] * y + m[11] * z + m[15]; w = w || 1; out[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w; out[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; out[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w; return out; } var forEach2 = function() { const vec = create2(); return function(a, stride, offset, count, fn, arg) { let i; let l; if (!stride) { stride = 3; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; vec[2] = a[i + 2]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; a[i + 2] = vec[2]; } return a; }; }(); // ../../node_modules/@math.gl/core/dist/classes/base/matrix.js var Matrix = class extends MathArray { // fromObject(object) { // const array = object.elements; // return this.fromRowMajor(array); // } // toObject(object) { // const array = object.elements; // this.toRowMajor(array); // return object; // } // TODO better override formatString? toString() { let string = "["; if (config.printRowMajor) { string += "row-major:"; for (let row = 0; row < this.RANK; ++row) { for (let col = 0; col < this.RANK; ++col) { string += ` ${this[col * this.RANK + row]}`; } } } else { string += "column-major:"; for (let i = 0; i < this.ELEMENTS; ++i) { string += ` ${this[i]}`; } } string += "]"; return string; } getElementIndex(row, col) { return col * this.RANK + row; } // By default assumes row major indices getElement(row, col) { return this[col * this.RANK + row]; } // By default assumes row major indices setElement(row, col, value) { this[col * this.RANK + row] = checkNumber(value); return this; } getColumn(columnIndex, result = new Array(this.RANK).fill(-0)) { const firstIndex = columnIndex * this.RANK; for (let i = 0; i < this.RANK; ++i) { result[i] = this[firstIndex + i]; } return result; } setColumn(columnIndex, columnVector) { const firstIndex = columnIndex * this.RANK; for (let i = 0; i < this.RANK; ++i) { this[firstIndex + i] = columnVector[i]; } return this; } }; // ../../node_modules/@math.gl/core/dist/gl-matrix/mat4.js function identity(out) { out[0] = 1; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = 1; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 1; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } function transpose(out, a) { if (out === a) { const a01 = a[1]; const a02 = a[2]; const a03 = a[3]; const a12 = a[6]; const a13 = a[7]; const a23 = a[11]; out[1] = a[4]; out[2] = a[8]; out[3] = a[12]; out[4] = a01; out[6] = a[9]; out[7] = a[13]; out[8] = a02; out[9] = a12; out[11] = a[14]; out[12] = a03; out[13] = a13; out[14] = a23; } else { out[0] = a[0]; out[1] = a[4]; out[2] = a[8]; out[3] = a[12]; out[4] = a[1]; out[5] = a[5]; out[6] = a[9]; out[7] = a[13]; out[8] = a[2]; out[9] = a[6]; out[10] = a[10]; out[11] = a[14]; out[12] = a[3]; out[13] = a[7]; out[14] = a[11]; out[15] = a[15]; } return out; } function invert(out, a) { const a00 = a[0]; const a01 = a[1]; const a02 = a[2]; const a03 = a[3]; const a10 = a[4]; const a11 = a[5]; const a12 = a[6]; const a13 = a[7]; const a20 = a[8]; const a21 = a[9]; const a22 = a[10]; const a23 = a[11]; const a30 = a[12]; const a31 = a[13]; const a32 = a[14]; const a33 = a[15]; const b00 = a00 * a11 - a01 * a10; const b01 = a00 * a12 - a02 * a10; const b02 = a00 * a13 - a03 * a10; const b03 = a01 * a12 - a02 * a11; const b04 = a01 * a13 - a03 * a11; const b05 = a02 * a13 - a03 * a12; const b06 = a20 * a31 - a21 * a30; const b07 = a20 * a32 - a22 * a30; const b08 = a20 * a33 - a23 * a30; const b09 = a21 * a32 - a22 * a31; const b10 = a21 * a33 - a23 * a31; const b11 = a22 * a33 - a23 * a32; let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (!det) { return null; } det = 1 / det; out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; return out; } function determinant(a) { const a00 = a[0]; const a01 = a[1]; const a02 = a[2]; const a03 = a[3]; const a10 = a[4]; const a11 = a[5]; const a12 = a[6]; const a13 = a[7]; const a20 = a[8]; const a21 = a[9]; const a22 = a[10]; const a23 = a[11]; const a30 = a[12]; const a31 = a[13]; const a32 = a[14]; const a33 = a[15]; const b0 = a00 * a11 - a01 * a10; const b1 = a00 * a12 - a02 * a10; const b2 = a01 * a12 - a02 * a11; const b3 = a20 * a31 - a21 * a30; const b4 = a20 * a32 - a22 * a30; const b5 = a21 * a32 - a22 * a31; const b6 = a00 * b5 - a01 * b4 + a02 * b3; const b7 = a10 * b5 - a11 * b4 + a12 * b3; const b8 = a20 * b2 - a21 * b1 + a22 * b0; const b9 = a30 * b2 - a31 * b1 + a32 * b0; return a13 * b6 - a03 * b7 + a33 * b8 - a23 * b9; } function multiply(out, a, b) { const a00 = a[0]; const a01 = a[1]; const a02 = a[2]; const a03 = a[3]; const a10 = a[4]; const a11 = a[5]; const a12 = a[6]; const a13 = a[7]; const a20 = a[8]; const a21 = a[9]; const a22 = a[10]; const a23 = a[11]; const a30 = a[12]; const a31 = a[13]; const a32 = a[14]; const a33 = a[15]; let b0 = b[0]; let b1 = b[1]; let b2 = b[2]; let b3 = b[3]; out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[3] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = b[4]; b1 = b[5]; b2 = b[6]; b3 = b[7]; out[4] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[5] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[6] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[7] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = b[8]; b1 = b[9]; b2 = b[10]; b3 = b[11]; out[8] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[9] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[10] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[11] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; b0 = b[12]; b1 = b[13]; b2 = b[14]; b3 = b[15]; out[12] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; out[13] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; out[14] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; out[15] = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; return out; } function translate(out, a, v) { const x = v[0]; const y = v[1]; const z = v[2]; let a00; let a01; let a02; let a03; let a10; let a11; let a12; let a13; let a20; let a21; let a22; let a23; if (a === out) { out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; } else { a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; out[12] = a00 * x + a10 * y + a20 * z + a[12]; out[13] = a01 * x + a11 * y + a21 * z + a[13]; out[14] = a02 * x + a12 * y + a22 * z + a[14]; out[15] = a03 * x + a13 * y + a23 * z + a[15]; } return out; } function scale(out, a, v) { const x = v[0]; const y = v[1]; const z = v[2]; out[0] = a[0] * x; out[1] = a[1] * x; out[2] = a[2] * x; out[3] = a[3] * x; out[4] = a[4] * y; out[5] = a[5] * y; out[6] = a[6] * y; out[7] = a[7] * y; out[8] = a[8] * z; out[9] = a[9] * z; out[10] = a[10] * z; out[11] = a[11] * z; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; return out; } function rotate(out, a, rad, axis) { let x = axis[0]; let y = axis[1]; let z = axis[2]; let len = Math.sqrt(x * x + y * y + z * z); let c; let s; let t; let a00; let a01; let a02; let a03; let a10; let a11; let a12; let a13; let a20; let a21; let a22; let a23; let b00; let b01; let b02; let b10; let b11; let b12; let b20; let b21; let b22; if (len < EPSILON) { return null; } len = 1 / len; x *= len; y *= len; z *= len; s = Math.sin(rad); c = Math.cos(rad); t = 1 - c; a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; b00 = x * x * t + c; b01 = y * x * t + z * s; b02 = z * x * t - y * s; b10 = x * y * t - z * s; b11 = y * y * t + c; b12 = z * y * t + x * s; b20 = x * z * t + y * s; b21 = y * z * t - x * s; b22 = z * z * t + c; out[0] = a00 * b00 + a10 * b01 + a20 * b02; out[1] = a01 * b00 + a11 * b01 + a21 * b02; out[2] = a02 * b00 + a12 * b01 + a22 * b02; out[3] = a03 * b00 + a13 * b01 + a23 * b02; out[4] = a00 * b10 + a10 * b11 + a20 * b12; out[5] = a01 * b10 + a11 * b11 + a21 * b12; out[6] = a02 * b10 + a12 * b11 + a22 * b12; out[7] = a03 * b10 + a13 * b11 + a23 * b12; out[8] = a00 * b20 + a10 * b21 + a20 * b22; out[9] = a01 * b20 + a11 * b21 + a21 * b22; out[10] = a02 * b20 + a12 * b21 + a22 * b22; out[11] = a03 * b20 + a13 * b21 + a23 * b22; if (a !== out) { out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } return out; } function rotateX(out, a, rad) { const s = Math.sin(rad); const c = Math.cos(rad); const a10 = a[4]; const a11 = a[5]; const a12 = a[6]; const a13 = a[7]; const a20 = a[8]; const a21 = a[9]; const a22 = a[10]; const a23 = a[11]; if (a !== out) { out[0] = a[0]; out[1] = a[1]; out[2] = a[2]; out[3] = a[3]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } out[4] = a10 * c + a20 * s; out[5] = a11 * c + a21 * s; out[6] = a12 * c + a22 * s; out[7] = a13 * c + a23 * s; out[8] = a20 * c - a10 * s; out[9] = a21 * c - a11 * s; out[10] = a22 * c - a12 * s; out[11] = a23 * c - a13 * s; return out; } function rotateY(out, a, rad) { const s = Math.sin(rad); const c = Math.cos(rad); const a00 = a[0]; const a01 = a[1]; const a02 = a[2]; const a03 = a[3]; const a20 = a[8]; const a21 = a[9]; const a22 = a[10]; const a23 = a[11]; if (a !== out) { out[4] = a[4]; out[5] = a[5]; out[6] = a[6]; out[7] = a[7]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } out[0] = a00 * c - a20 * s; out[1] = a01 * c - a21 * s; out[2] = a02 * c - a22 * s; out[3] = a03 * c - a23 * s; out[8] = a00 * s + a20 * c; out[9] = a01 * s + a21 * c; out[10] = a02 * s + a22 * c; out[11] = a03 * s + a23 * c; return out; } function rotateZ(out, a, rad) { const s = Math.sin(rad); const c = Math.cos(rad); const a00 = a[0]; const a01 = a[1]; const a02 = a[2]; const a03 = a[3]; const a10 = a[4]; const a11 = a[5]; const a12 = a[6]; const a13 = a[7]; if (a !== out) { out[8] = a[8]; out[9] = a[9]; out[10] = a[10]; out[11] = a[11]; out[12] = a[12]; out[13] = a[13]; out[14] = a[14]; out[15] = a[15]; } out[0] = a00 * c + a10 * s; out[1] = a01 * c + a11 * s; out[2] = a02 * c + a12 * s; out[3] = a03 * c + a13 * s; out[4] = a10 * c - a00 * s; out[5] = a11 * c - a01 * s; out[6] = a12 * c - a02 * s; out[7] = a13 * c - a03 * s; return out; } function fromQuat(out, q) { const x = q[0]; const y = q[1]; const z = q[2]; const w = q[3]; const x2 = x + x; const y2 = y + y; const z2 = z + z; const xx = x * x2; const yx = y * x2; const yy = y * y2; const zx = z * x2; const zy = z * y2; const zz = z * z2; const wx = w * x2; const wy = w * y2; const wz = w * z2; out[0] = 1 - yy - zz; out[1] = yx + wz; out[2] = zx - wy; out[3] = 0; out[4] = yx - wz; out[5] = 1 - xx - zz; out[6] = zy + wx; out[7] = 0; out[8] = zx + wy; out[9] = zy - wx; out[10] = 1 - xx - yy; out[11] = 0; out[12] = 0; out[13] = 0; out[14] = 0; out[15] = 1; return out; } function frustum(out, left, right, bottom, top, near, far) { const rl = 1 / (right - left); const tb = 1 / (top - bottom); const nf = 1 / (near - far); out[0] = near * 2 * rl; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = near * 2 * tb; out[6] = 0; out[7] = 0; out[8] = (right + left) * rl; out[9] = (top + bottom) * tb; out[10] = (far + near) * nf; out[11] = -1; out[12] = 0; out[13] = 0; out[14] = far * near * 2 * nf; out[15] = 0; return out; } function perspectiveNO(out, fovy, aspect, near, far) { const f = 1 / Math.tan(fovy / 2); out[0] = f / aspect; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = f; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[11] = -1; out[12] = 0; out[13] = 0; out[15] = 0; if (far != null && far !== Infinity) { const nf = 1 / (near - far); out[10] = (far + near) * nf; out[14] = 2 * far * near * nf; } else { out[10] = -1; out[14] = -2 * near; } return out; } var perspective = perspectiveNO; function orthoNO(out, left, right, bottom, top, near, far) { const lr = 1 / (left - right); const bt = 1 / (bottom - top); const nf = 1 / (near - far); out[0] = -2 * lr; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0; out[5] = -2 * bt; out[6] = 0; out[7] = 0; out[8] = 0; out[9] = 0; out[10] = 2 * nf; out[11] = 0; out[12] = (left + right) * lr; out[13] = (top + bottom) * bt; out[14] = (far + near) * nf; out[15] = 1; return out; } var ortho = orthoNO; function lookAt(out, eye, center, up) { let len; let x0; let x1; let x2; let y0; let y1; let y2; let z0; let z1; let z2; const eyex = eye[0]; const eyey = eye[1]; const eyez = eye[2]; const upx = up[0]; const upy = up[1]; const upz = up[2]; const centerx = center[0]; const centery = center[1]; const centerz = center[2]; if (Math.abs(eyex - centerx) < EPSILON && Math.abs(eyey - centery) < EPSILON && Math.abs(eyez - centerz) < EPSILON) { return identity(out); } z0 = eyex - centerx; z1 = eyey - centery; z2 = eyez - centerz; len = 1 / Math.sqrt(z0 * z0 + z1 * z1 + z2 * z2); z0 *= len; z1 *= len; z2 *= len; x0 = upy * z2 - upz * z1; x1 = upz * z0 - upx * z2; x2 = upx * z1 - upy * z0; len = Math.sqrt(x0 * x0 + x1 * x1 + x2 * x2); if (!len) { x0 = 0; x1 = 0; x2 = 0; } else { len = 1 / len; x0 *= len; x1 *= len; x2 *= len; } y0 = z1 * x2 - z2 * x1; y1 = z2 * x0 - z0 * x2; y2 = z0 * x1 - z1 * x0; len = Math.sqrt(y0 * y0 + y1 * y1 + y2 * y2); if (!len) { y0 = 0; y1 = 0; y2 = 0; } else { len = 1 / len; y0 *= len; y1 *= len; y2 *= len; } out[0] = x0; out[1] = y0; out[2] = z0; out[3] = 0; out[4] = x1; out[5] = y1; out[6] = z1; out[7] = 0; out[8] = x2; out[9] = y2; out[10] = z2; out[11] = 0; out[12] = -(x0 * eyex + x1 * eyey + x2 * eyez); out[13] = -(y0 * eyex + y1 * eyey + y2 * eyez); out[14] = -(z0 * eyex + z1 * eyey + z2 * eyez); out[15] = 1; return out; } // ../../node_modules/@math.gl/core/dist/gl-matrix/vec4.js function create3() { const out = new ARRAY_TYPE(4); if (ARRAY_TYPE != Float32Array) { out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0; } return out; } function transformMat43(out, a, m) { const x = a[0]; const y = a[1]; const z = a[2]; const w = a[3]; out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w; out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w; out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w; out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w; return out; } var forEach3 = function() { const vec = create3(); return function(a, stride, offset, count, fn, arg) { let i; let l; if (!stride) { stride = 4; } if (!offset) { offset = 0; } if (count) { l = Math.min(count * stride + offset, a.length); } else { l = a.length; } for (i = offset; i < l; i += stride) { vec[0] = a[i]; vec[1] = a[i + 1]; vec[2] = a[i + 2]; vec[3] = a[i + 3]; fn(vec, vec, arg); a[i] = vec[0]; a[i + 1] = vec[1]; a[i + 2] = vec[2]; a[i + 3] = vec[3]; } return a; }; }(); // ../../node_modules/@math.gl/core/dist/classes/matrix4.js var INDICES; (function(INDICES2) { INDICES2[INDICES2["COL0ROW0"] = 0] = "COL0ROW0"; INDICES2[INDICES2["COL0ROW1"] = 1] = "COL0ROW1"; INDICES2[INDICES2["COL0ROW2"] = 2] = "COL0ROW2"; INDICES2[INDICES2["COL0ROW3"] = 3] = "COL0ROW3"; INDICES2[INDICES2["COL1ROW0"] = 4] = "COL1ROW0"; INDICES2[INDICES2["COL1ROW1"] = 5] = "COL1ROW1"; INDICES2[INDICES2["COL1ROW2"] = 6] = "COL1ROW2"; INDICES2[INDICES2["COL1ROW3"] = 7] = "COL1ROW3"; INDICES2[INDICES2["COL2ROW0"] = 8] = "COL2ROW0"; INDICES2[INDICES2["COL2ROW1"] = 9] = "COL2ROW1"; INDICES2[INDICES2["COL2ROW2"] = 10] = "COL2ROW2"; INDICES2[INDICES2["COL2ROW3"] = 11] = "COL2ROW3"; INDICES2[INDICES2["COL3ROW0"] = 12] = "COL3ROW0"; INDICES2[INDICES2["COL3ROW1"] = 13] = "COL3ROW1"; INDICES2[INDICES2["COL3ROW2"] = 14] = "COL3ROW2"; INDICES2[INDICES2["COL3ROW3"] = 15] = "COL3ROW3"; })(INDICES || (INDICES = {})); var DEFAULT_FOVY = 45 * Math.PI / 180; var DEFAULT_ASPECT = 1; var DEFAULT_NEAR = 0.1; var DEFAULT_FAR = 500; var IDENTITY_MATRIX = Object.freeze([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); var Matrix4 = class extends Matrix { static get IDENTITY() { return getIdentityMatrix(); } static get ZERO() { return getZeroMatrix(); } get ELEMENTS() { return 16; } get RANK() { return 4; } get INDICES() { return INDICES; } constructor(array) { super(-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0); if (arguments.length === 1 && Array.isArray(array)) { this.copy(array); } else { this.identity(); } } copy(array) { this[0] = array[0]; this[1] = array[1]; this[2] = array[2]; this[3] = array[3]; this[4] = array[4]; this[5] = array[5]; this[6] = array[6]; this[7] = array[7]; this[8] = array[8]; this[9] = array[9]; this[10] = array[10]; this[11] = array[11]; this[12] = array[12]; this[13] = array[13]; this[14] = array[14]; this[15] = array[15]; return this.check(); } // eslint-disable-next-line max-params set(m00, m10, m20, m30, m01, m11, m21, m31, m02, m12, m22, m32, m03, m13, m23, m33) { this[0] = m00; this[1] = m10; this[2] = m20; this[3] = m30; this[4] = m01; this[5] = m11; this[6] = m21; this[7] = m31; this[8] = m02; this[9] = m12; this[10] = m22; this[11] = m32; this[12] = m03; this[13] = m13; this[14] = m23; this[15] = m33; return this.check(); } // accepts row major order, stores as column major // eslint-disable-next-line max-params setRowMajor(m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33) { this[0] = m00; this[1] = m10; this[2] = m20; this[3] = m30; this[4] = m01; this[5] = m11; this[6] = m21; this[7] = m31; this[8] = m02; this[9] = m12; this[10] = m22; this[11] = m32; this[12] = m03; this[13] = m13; this[14] = m23; this[15] = m33; return this.check(); } toRowMajor(result) { result[0] = this[0]; result[1] = this[4]; result[2] = this[8]; result[3] = this[12]; result[4] = this[1]; result[5] = this[5]; result[6] = this[9]; result[7] = this[13]; result[8] = this[2]; result[9] = this[6]; result[10] = this[10]; result[11] = this[14]; result[12] = this[3]; result[13] = this[7]; result[14] = this[11]; result[15] = this[15]; return result; } // Constructors /** Set to identity matrix */ identity() { return this.copy(IDENTITY_MATRIX); } /** * * @param object * @returns self */ // eslint-disable-next-line @typescript-eslint/no-unused-vars fromObject(object) { return this.check(); } /** * Calculates a 4x4 matrix from the given quaternion * @param quaternion Quaternion to create matrix from * @returns self */ fromQuaternion(quaternion) { fromQuat(this, quaternion); return this.check(); } /** * Generates a frustum matrix with the given bounds * @param view.left - Left bound of the frustum * @param view.right - Right bound of the frustum * @param view.bottom - Bottom bound of the frustum * @param view.top - Top bound of the frustum * @param view.near - Near bound of the frustum * @param view.far - Far bound of the frustum. Can be set to Infinity. * @returns self */ frustum(view) { const { left, right, bottom, top, near = DEFAULT_NEAR, far = DEFAULT_FAR } = view; if (far === Infinity) { computeInfinitePerspectiveOffCenter(this, left, right, bottom, top, near); } else { frustum(this, left, right, bottom, top, near, far); } return this.check(); } /** * Generates a look-at matrix with the given eye position, focal point, * and up axis * @param view.eye - (vector) Position of the viewer * @param view.center - (vector) Point the viewer is looking at * @param view.up - (vector) Up axis * @returns self */ lookAt(view) { const { eye, center = [0, 0, 0], up = [0, 1, 0] } = view; lookAt(this, eye, center, up); return this.check(); } /** * Generates a orthogonal projection matrix with the given bounds * from "traditional" view space parameters * @param view.left - Left bound of the frustum * @param view.right number Right bound of the frustum * @param view.bottom - Bottom bound of the frustum * @param view.top number Top bound of the frustum * @param view.near - Near bound of the frustum * @param view.far number Far bound of the frustum * @returns self */ ortho(view) { const { left, right, bottom, top, near = DEFAULT_NEAR, far = DEFAULT_FAR } = view; ortho(this, left, right, bottom, top, near, far); return this.check(); } /** * Generates an orthogonal projection matrix with the same parameters * as a perspective matrix (plus focalDistance) * @param view.fovy Vertical field of view in radians * @param view.aspect Aspect ratio. Typically viewport width / viewport height * @param view.focalDistance Distance in the view frustum used for extent calculations * @param view.near Near bound of the frustum * @param view.far Far bound of the frustum * @returns self */ orthographic(view) { const { fovy = DEFAULT_FOVY, aspect = DEFAULT_ASPECT, focalDistance = 1, near = DEFAULT_NEAR, far = DEFAULT_FAR } = view; checkRadians(fovy); const halfY = fovy / 2; const top = focalDistance * Math.tan(halfY); const right = top * aspect; return this.ortho({ left: -right, right, bottom: -top, top, near, far }); } /** * Generates a perspective projection matrix with the given bounds * @param view.fovy Vertical field of view in radians * @param view.aspect Aspect ratio. typically viewport width/height * @param view.near Near bound of the frustum * @param view.far Far bound of the frustum * @returns self */ perspective(view) { const { fovy = 45 * Math.PI / 180, aspect = 1, near = 0.1, far = 500 } = view; checkRadians(fovy); perspective(this, fovy, aspect, near, far); return this.check(); } // Accessors determinant() { return determinant(this); } /** * Extracts the non-uniform scale assuming the matrix is an affine transformation. * The scales are the "lengths" of the column vectors in the upper-left 3x3 matrix. * @param result * @returns self */ getScale(result = [-0, -0, -0]) { result[0] = Math.sqrt(this[0] * this[0] + this[1] * this[1] + this[2] * this[2]); result[1] = Math.sqrt(this[4] * this[4] + this[5] * this[5] + this[6] * this[6]); result[2] = Math.sqrt(this[8] * this[8] + this[9] * this[9] + this[10] * this[10]); return result; } /** * Gets the translation portion, assuming the matrix is a affine transformation matrix. * @param result * @returns self */ getTranslation(result = [-0, -0, -0]) { result[0] = this[12]; result[1] = this[13]; result[2] = this[14]; return result; } /** * Gets upper left 3x3 pure rotation matrix (non-scaling), assume affine transformation matrix * @param result * @param scaleResult * @returns self */ getRotation(result, scaleResult) { result = result || [-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0]; scaleResult = scaleResult || [-0, -0, -0]; const scale2 = this.getScale(scaleResult); const inverseScale0 = 1 / scale2[0]; const inverseScale1 = 1 / scale2[1]; const inverseScale2 = 1 / scale2[2]; result[0] = this[0] * inverseScale0; result[1] = this[1] * inverseScale1; result[2] = this[2] * inverseScale2; result[3] = 0; result[4] = this[4] * inverseScale0; result[5] = this[5] * inverseScale1; result[6] = this[6] * inverseScale2; result[7] = 0; result[8] = this[8] * inverseScale0; result[9] = this[9] * inverseScale1; result[10] = this[10] * inverseScale2; result[11] = 0; result[12] = 0; result[13] = 0; result[14] = 0; result[15] = 1; return result; } /** * * @param result * @param scaleResult * @returns self */ getRotationMatrix3(result, scaleResult) { result = result || [-0, -0, -0, -0, -0, -0, -0, -0, -0]; scaleResult = scaleResult || [-0, -0, -0]; const scale2 = this.getScale(scaleResult); const inverseScale0 = 1 / scale2[0]; const inverseScale1 = 1 / scale2[1]; const inverseScale2 = 1 / scale2[2]; result[0] = this[0] * inverseScale0; result[1] = this[1] * inverseScale1; result[2] = this[2] * inverseScale2; result[3] = this[4] * inverseScale0; result[4] = this[5] * inverseScale1; result[5] = this[6] * inverseScale2; result[6] = this[8] * inverseScale0; result[7] = this[9] * inverseScale1; result[8] = this[10] * inverseScale2; return result; } // Modifiers transpose() { transpose(this, this); return this.check(); } invert() { invert(this, this); return this.check(); } // Operations multiplyLeft(a) { multiply(this, a, this); return this.check(); } multiplyRight(a) { multiply(this, this, a); return this.check(); } // Rotates a matrix by the given angle around the X axis rotateX(radians) { rotateX(this, this, radians); return this.check(); } // Rotates a matrix by the given angle around the Y axis. rotateY(radians) { rotateY(this, this, radians); return this.check(); } /** * Rotates a matrix by the given angle around the Z axis. * @param radians * @returns self */ rotateZ(radians) { rotateZ(this, this, radians); return this.check(); } /** * * @param param0 * @returns self */ rotateXYZ(angleXYZ) { return this.rotateX(angleXYZ[0]).rotateY(angleXYZ[1]).rotateZ(angleXYZ[2]); } /** * * @param radians * @param axis * @returns self */ rotateAxis(radians, axis) { rotate(this, this, radians, axis); return this.check(); } /** * * @param factor * @returns self */ scale(factor) { scale(this, this, Array.isArray(factor) ? factor : [factor, factor, factor]); return this.check(); } /** * * @param vec * @returns self */ translate(vector) { translate(this, this, vector); return this.check(); } // Transforms /** * Transforms any 2, 3 or 4 element vector. 2 and 3 elements are treated as points * @param vector * @param result * @returns self */ transform(vector, result) { if (vector.length === 4) { result = transformMat43(result || [-0, -0, -0, -0], vector, this); checkVector(result, 4); return result; } return this.transformAsPoint(vector, result); } /** * Transforms any 2 or 3 element array as point (w implicitly 1) * @param vector * @param result * @returns self */ transformAsPoint(vector, result) { const { length } = vector; let out; switch (length) { case 2: out = transformMat4(result || [-0, -0], vector, this); break; case 3: out = transformMat42(result || [-0, -0, -0], vector, this); break; default: throw new Error("Illegal vector"); } checkVector(out, vector.length); return out; } /** * Transforms any 2 or 3 element array as vector (w implicitly 0) * @param vector * @param result * @returns self */ transformAsVector(vector, result) { let out; switch (vector.length) { case 2: out = vec2_transformMat4AsVector(result || [-0, -0], vector, this); break; case 3: out = vec3_transformMat4AsVector(result || [-0, -0, -0], vector, this); break; default: throw new Error("Illegal vector"); } checkVector(out, vector.length); return out; } /** @deprecated */ transformPoint(vector, result) { return this.transformAsPoint(vector, result); } /** @deprecated */ transformVector(vector, result) { return this.transformAsPoint(vector, result); } /** @deprecated */ transformDirection(vector, result) { return this.transformAsVector(vector, result); } // three.js math API compatibility makeRotationX(radians) { return this.identity().rotateX(radians); } makeTranslation(x, y, z) { return this.identity().translate([x, y, z]); } }; var ZERO; var IDENTITY; function getZeroMatrix() { if (!ZERO) { ZERO = new Matrix4([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); Object.freeze(ZERO); } return ZERO; } function getIdentityMatrix() { if (!IDENTITY) { IDENTITY = new Matrix4(); Object.freeze(IDENTITY); } return IDENTITY; } function checkRadians(possiblyDegrees) { if (possiblyDegrees > Math.PI * 2) { throw Error("expected radians"); } } function computeInfinitePerspectiveOffCenter(result, left, right, bottom, top, near) { const column0Row0 = 2 * near / (right - left); const column1Row1 = 2 * near / (top - bottom); const column2Row0 = (right + left) / (right - left); const column2Row1 = (top + bottom) / (top - bottom); const column2Row2 = -1; const column2Row3 = -1; const column3Row2 = -2 * near; result[0] = column0Row0; result[1] = 0; result[2] = 0; result[3] = 0; result[4] = 0; result[5] = column1Row1; result[6] = 0; result[7] = 0; result[8] = column2Row0; result[9] = column2Row1; result[10] = column2Row2; result[11] = column2Row3; result[12] = 0; result[13] = 0; result[14] = column3Row2; result[15] = 0; return result; } // src/modules-webgl1/project/project.ts var IDENTITY_MATRIX2 = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; var DEFAULT_MODULE_OPTIONS = { modelMatrix: IDENTITY_MATRIX2, viewMatrix: IDENTITY_MATRIX2, projectionMatrix: IDENTITY_MATRIX2, cameraPositionWorld: [0, 0, 0] }; function getUniforms5(opts = DEFAULT_MODULE_OPTIONS, prevUniforms = {}) { const uniforms = {}; if (opts.modelMatrix !== void 0) { uniforms.modelMatrix = opts.modelMatrix; } if (opts.viewMatrix !== void 0) { uniforms.viewMatrix = opts.viewMatrix; } if (opts.projectionMatrix !== void 0) { uniforms.projectionMatrix = opts.projectionMatrix; } if (opts.cameraPositionWorld !== void 0) { uniforms.cameraPositionWorld = opts.cameraPositionWorld; } if (opts.projectionMatrix !== void 0 || opts.viewMatrix !== void 0) { uniforms.viewProjectionMatrix = new Matrix4(opts.projectionMatrix).multiplyRight( opts.viewMatrix ); } return uniforms; } var common = glsl`\ varying vec4 project_vPositionWorld; varying vec3 project_vNormalWorld; vec4 project_getPosition_World() { return project_vPositionWorld; } vec3 project_getNormal_World() { return project_vNormalWorld; } `; var vs4 = `${common} // Unprefixed uniforms uniform mat4 modelMatrix; uniform mat4 viewMatrix; uniform mat4 projectionMatrix; uniform mat4 viewProjectionMatrix; uniform vec3 cameraPositionWorld; struct World { vec3 position; vec3 normal; }; World world; void project_setPosition(vec4 position) { project_vPositionWorld = position; } void project_setNormal(vec3 normal) { project_vNormalWorld = normal; } void project_setPositionAndNormal_World(vec3 position, vec3 normal) { world.position = position; world.normal = normal; } void project_setPositionAndNormal_Model(vec3 position, vec3 normal) { world.position = (modelMatrix * vec4(position, 1.)).xyz; world.normal = mat3(modelMatrix) * normal; } vec4 project_model_to_clipspace(vec4 position) { return viewProjectionMatrix * modelMatrix * position; } vec4 project_model_to_clipspace(vec3 position) { return viewProjectionMatrix * modelMatrix * vec4(position, 1.); } vec4 project_world_to_clipspace(vec3 position) { return viewProjectionMatrix * vec4(position, 1.); } vec4 project_view_to_clipspace(vec3 position) { return projectionMatrix * vec4(position, 1.); } vec4 project_to_clipspace(vec3 position) { return viewProjectionMatrix * vec4(position, 1.); } `; var fs25 = ` ${common}`; var project = { name: "project", getUniforms: getUniforms5, vs: vs4, fs: fs25 }; // src/modules-webgl1/lighting/lights/lights-glsl.ts var lightingShader = glsl`\ #if (defined(SHADER_TYPE_FRAGMENT) && defined(LIGHTING_FRAGMENT)) || (defined(SHADER_TYPE_VERTEX) && defined(LIGHTING_VERTEX)) struct AmbientLight { vec3 color; }; struct PointLight { vec3 color; vec3 position; // Constant-Linear-Exponential vec3 attenuation; }; struct DirectionalLight { vec3 color; vec3 direction; }; uniform AmbientLight lighting_uAmbientLight; uniform PointLight lighting_uPointLight[MAX_LIGHTS]; uniform DirectionalLight lighting_uDirectionalLight[MAX_LIGHTS]; uniform int lighting_uPointLightCount; uniform int lighting_uDirectionalLightCount; uniform bool lighting_uEnabled; float getPointLightAttenuation(PointLight pointLight, float distance) { return pointLight.attenuation.x + pointLight.attenuation.y * distance + pointLight.attenuation.z * distance * distance; } #endif `; // src/modules-webgl1/lighting/lights/lights.ts var INITIAL_MODULE_OPTIONS = { lightSources: {} }; function convertColor2(colorDef = {}) { const { color = [0, 0, 0], intensity = 1 } = colorDef; return color.map((component) => component * intensity / 255); } function getLightSourceUniforms2({ ambientLight, pointLights = [], directionalLights = [] }) { const lightSourceUniforms = {}; if (ambientLight) { lightSourceUniforms["lighting_uAmbientLight.color"] = convertColor2(ambientLight); } else { lightSourceUniforms["lighting_uAmbientLight.color"] = [0, 0, 0]; } pointLights.forEach((pointLight, index2) => { lightSourceUniforms[`lighting_uPointLight[${index2}].color`] = convertColor2(pointLight); lightSourceUniforms[`lighting_uPointLight[${index2}].position`] = pointLight.position; lightSourceUniforms[`lighting_uPointLight[${index2}].attenuation`] = pointLight.attenuation || [ 1, 0, 0 ]; }); lightSourceUniforms.lighting_uPointLightCount = pointLights.length; directionalLights.forEach((directionalLight, index2) => { lightSourceUniforms[`lighting_uDirectionalLight[${index2}].color`] = convertColor2(directionalLight); lightSourceUniforms[`lighting_uDirectionalLight[${index2}].direction`] = directionalLight.direction; }); lightSourceUniforms.lighting_uDirectionalLightCount = directionalLights.length; return lightSourceUniforms; } function getUniforms6(opts = INITIAL_MODULE_OPTIONS) { if ("lightSources" in opts) { const { ambientLight, pointLights, directionalLights } = opts.lightSources || {}; const hasLights = ambientLight || pointLights && pointLights.length > 0 || directionalLights && directionalLights.length > 0; if (!hasLights) { return { lighting_uEnabled: false }; } return Object.assign( {}, getLightSourceUniforms2({ ambientLight, pointLights, directionalLights }), { lighting_uEnabled: true } ); } if ("lights" in opts) { const lightSources = { pointLights: [], directionalLights: [] }; for (const light of opts.lights || []) { switch (light.type) { case "ambient": lightSources.ambientLight = light; break; case "directional": lightSources.directionalLights?.push(light); break; case "point": lightSources.pointLights?.push(light); break; default: } } return getUniforms6({ lightSources }); } return {}; } var lights = { name: "lights", vs: lightingShader, fs: lightingShader, getUniforms: getUniforms6, defines: { MAX_LIGHTS: 3 } }; // src/modules-webgl1/lighting/dirlight/dirlight.ts var DEFAULT_MODULE_OPTIONS2 = { lightDirection: new Float32Array([1, 1, 2]) }; function getUniforms7(opts = DEFAULT_MODULE_OPTIONS2) { const uniforms = {}; if (opts.lightDirection) { uniforms.dirlight_uLightDirection = opts.lightDirection; } return uniforms; } var fs26 = glsl`\ uniform vec3 dirlight_uLightDirection; /* * Returns color attenuated by angle from light source */ vec4 dirlight_filterColor(vec4 color) { vec3 normal = project_getNormal_World(); float d = abs(dot(normalize(normal), normalize(dirlight_uLightDirection))); return vec4(color.rgb * d, color.a); } `; var dirlight2 = { name: "dirlight", // vs // TODO - reuse normal from geometry module fs: fs26, getUniforms: getUniforms7, dependencies: [project] }; // src/modules-webgl1/lighting/phong-lighting/phong-lighting-glsl.ts var lightingShader2 = glsl`\ uniform float lighting_uAmbient; uniform float lighting_uDiffuse; uniform float lighting_uShininess; uniform vec3 lighting_uSpecularColor; vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_direction, vec3 normal_worldspace, vec3 color) { vec3 halfway_direction = normalize(light_direction + view_direction); float lambertian = dot(light_direction, normal_worldspace); float specular = 0.0; if (lambertian > 0.0) { float specular_angle = max(dot(normal_worldspace, halfway_direction), 0.0); specular = pow(specular_angle, lighting_uShininess); } lambertian = max(lambertian, 0.0); return (lambertian * lighting_uDiffuse * surfaceColor + specular * lighting_uSpecularColor) * color; } vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) { vec3 lightColor = surfaceColor; if (lighting_uEnabled) { vec3 view_direction = normalize(cameraPosition - position_worldspace); lightColor = lighting_uAmbient * surfaceColor * lighting_uAmbientLight.color; for (int i = 0; i < MAX_LIGHTS; i++) { if (i >= lighting_uPointLightCount) { break; } PointLight pointLight = lighting_uPointLight[i]; vec3 light_position_worldspace = pointLight.position; vec3 light_direction = normalize(light_position_worldspace - position_worldspace); lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color); } for (int i = 0; i < MAX_LIGHTS; i++) { if (i >= lighting_uDirectionalLightCount) { break; } DirectionalLight directionalLight = lighting_uDirectionalLight[i]; lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color); } } return lightColor; } vec3 lighting_getSpecularLightColor(vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) { vec3 lightColor = vec3(0, 0, 0); vec3 surfaceColor = vec3(0, 0, 0); if (lighting_uEnabled) { vec3 view_direction = normalize(cameraPosition - position_worldspace); for (int i = 0; i < MAX_LIGHTS; i++) { if (i >= lighting_uPointLightCount) { break; } PointLight pointLight = lighting_uPointLight[i]; vec3 light_position_worldspace = pointLight.position; vec3 light_direction = normalize(light_position_worldspace - position_worldspace); lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color); } for (int i = 0; i < MAX_LIGHTS; i++) { if (i >= lighting_uDirectionalLightCount) { break; } DirectionalLight directionalLight = lighting_uDirectionalLight[i]; lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color); } } return lightColor; } `; // src/modules-webgl1/lighting/phong-lighting/phong-lighting.ts var INITIAL_MODULE_OPTIONS2 = {}; function getMaterialUniforms(material) { const { ambient = 0.35, diffuse = 0.6, shininess = 32, specularColor = [30, 30, 30] } = material; return { lighting_uAmbient: ambient, lighting_uDiffuse: diffuse, lighting_uShininess: shininess, lighting_uSpecularColor: specularColor.map((x) => x / 255) }; } function getUniforms8(opts = INITIAL_MODULE_OPTIONS2) { if (!("material" in opts)) { return {}; } const { material } = opts; if (!material) { return { lighting_uEnabled: false }; } return getMaterialUniforms(material); } var gouraudLighting = { name: "gouraud-lighting", dependencies: [lights], vs: lightingShader2, defines: { LIGHTING_VERTEX: 1 }, getUniforms: getUniforms8 }; var phongLighting = { name: "phong-lighting", dependencies: [lights], fs: lightingShader2, defines: { LIGHTING_FRAGMENT: 1 }, getUniforms: getUniforms8 }; // src/modules-webgl1/lighting/pbr/pbr-vertex-glsl.ts var vs5 = glsl`\ uniform mat4 u_MVPMatrix; uniform mat4 u_ModelMatrix; uniform mat4 u_NormalMatrix; out vec3 pbr_vPosition; out vec2 pbr_vUV; #ifdef HAS_NORMALS # ifdef HAS_TANGENTS out mat3 pbr_vTBN; # else out vec3 pbr_vNormal; # endif #endif void pbr_setPositionNormalTangentUV(vec4 position, vec4 normal, vec4 tangent, vec2 uv) { vec4 pos = u_ModelMatrix * position; pbr_vPosition = vec3(pos.xyz) / pos.w; #ifdef HAS_NORMALS #ifdef HAS_TANGENTS vec3 normalW = normalize(vec3(u_NormalMatrix * vec4(normal.xyz, 0.0))); vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(tangent.xyz, 0.0))); vec3 bitangentW = cross(normalW, tangentW) * tangent.w; pbr_vTBN = mat3(tangentW, bitangentW, normalW); #else // HAS_TANGENTS != 1 pbr_vNormal = normalize(vec3(u_ModelMatrix * vec4(normal.xyz, 0.0))); #endif #endif #ifdef HAS_UV pbr_vUV = uv; #else pbr_vUV = vec2(0.,0.); #endif } `; // src/modules-webgl1/lighting/pbr/pbr-fragment-glsl.ts var fs27 = glsl`\ precision highp float; uniform bool pbr_uUnlit; #ifdef USE_IBL uniform samplerCube u_DiffuseEnvSampler; uniform samplerCube u_SpecularEnvSampler; uniform sampler2D u_brdfLUT; uniform vec2 u_ScaleIBLAmbient; #endif #ifdef HAS_BASECOLORMAP uniform sampler2D u_BaseColorSampler; #endif #ifdef HAS_NORMALMAP uniform sampler2D u_NormalSampler; uniform float u_NormalScale; #endif #ifdef HAS_EMISSIVEMAP uniform sampler2D u_EmissiveSampler; uniform vec3 u_EmissiveFactor; #endif #ifdef HAS_METALROUGHNESSMAP uniform sampler2D u_MetallicRoughnessSampler; #endif #ifdef HAS_OCCLUSIONMAP uniform sampler2D u_OcclusionSampler; uniform float u_OcclusionStrength; #endif #ifdef ALPHA_CUTOFF uniform float u_AlphaCutoff; #endif uniform vec2 u_MetallicRoughnessValues; uniform vec4 u_BaseColorFactor; uniform vec3 u_Camera; // debugging flags used for shader output of intermediate PBR variables #ifdef PBR_DEBUG uniform vec4 u_ScaleDiffBaseMR; uniform vec4 u_ScaleFGDSpec; #endif in vec3 pbr_vPosition; in vec2 pbr_vUV; #ifdef HAS_NORMALS #ifdef HAS_TANGENTS in mat3 pbr_vTBN; #else in vec3 pbr_vNormal; #endif #endif // Encapsulate the various inputs used by the various functions in the shading equation // We store values in this struct to simplify the integration of alternative implementations // of the shading terms, outlined in the Readme.MD Appendix. struct PBRInfo { float NdotL; // cos angle between normal and light direction float NdotV; // cos angle between normal and view direction float NdotH; // cos angle between normal and half vector float LdotH; // cos angle between light direction and half vector float VdotH; // cos angle between view direction and half vector float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) float metalness; // metallic value at the surface vec3 reflectance0; // full reflectance color (normal incidence angle) vec3 reflectance90; // reflectance color at grazing angle float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) vec3 diffuseColor; // color contribution from diffuse lighting vec3 specularColor; // color contribution from specular lighting vec3 n; // normal at surface point vec3 v; // vector from surface point to camera }; const float M_PI = 3.141592653589793; const float c_MinRoughness = 0.04; vec4 SRGBtoLINEAR(vec4 srgbIn) { #ifdef MANUAL_SRGB #ifdef SRGB_FAST_APPROXIMATION vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); #else //SRGB_FAST_APPROXIMATION vec3 bLess = step(vec3(0.04045),srgbIn.xyz); vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); #endif //SRGB_FAST_APPROXIMATION return vec4(linOut,srgbIn.w);; #else //MANUAL_SRGB return srgbIn; #endif //MANUAL_SRGB } // Find the normal for this fragment, pulling either from a predefined normal map // or from the interpolated mesh normal and tangent attributes. vec3 getNormal() { // Retrieve the tangent space matrix #ifndef HAS_TANGENTS vec3 pos_dx = dFdx(pbr_vPosition); vec3 pos_dy = dFdy(pbr_vPosition); vec3 tex_dx = dFdx(vec3(pbr_vUV, 0.0)); vec3 tex_dy = dFdy(vec3(pbr_vUV, 0.0)); vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t); #ifdef HAS_NORMALS vec3 ng = normalize(pbr_vNormal); #else vec3 ng = cross(pos_dx, pos_dy); #endif t = normalize(t - ng * dot(ng, t)); vec3 b = normalize(cross(ng, t)); mat3 tbn = mat3(t, b, ng); #else // HAS_TANGENTS mat3 tbn = pbr_vTBN; #endif #ifdef HAS_NORMALMAP vec3 n = texture(u_NormalSampler, pbr_vUV).rgb; n = normalize(tbn * ((2.0 * n - 1.0) * vec3(u_NormalScale, u_NormalScale, 1.0))); #else // The tbn matrix is linearly interpolated, so we need to re-normalize vec3 n = normalize(tbn[2].xyz); #endif return n; } // Calculation of the lighting contribution from an optional Image Based Light source. // Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. // See our README.md on Environment Maps [3] for additional discussion. #ifdef USE_IBL vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection) { float mipCount = 9.0; // resolution of 512x512 float lod = (pbrInputs.perceptualRoughness * mipCount); // retrieve a scale and bias to F0. See [1], Figure 3 vec3 brdf = SRGBtoLINEAR(texture(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb; vec3 diffuseLight = SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb; #ifdef USE_TEX_LOD vec3 specularLight = SRGBtoLINEAR(textureCubeLod(u_SpecularEnvSampler, reflection, lod)).rgb; #else vec3 specularLight = SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb; #endif vec3 diffuse = diffuseLight * pbrInputs.diffuseColor; vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); // For presentation, this allows us to disable IBL terms diffuse *= u_ScaleIBLAmbient.x; specular *= u_ScaleIBLAmbient.y; return diffuse + specular; } #endif // Basic Lambertian diffuse // Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog // See also [1], Equation 1 vec3 diffuse(PBRInfo pbrInputs) { return pbrInputs.diffuseColor / M_PI; } // The following equation models the Fresnel reflectance term of the spec equation (aka F()) // Implementation of fresnel from [4], Equation 15 vec3 specularReflection(PBRInfo pbrInputs) { return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); } // This calculates the specular geometric attenuation (aka G()), // where rougher material will reflect less light back to the viewer. // This implementation is based on [1] Equation 4, and we adopt their modifications to // alphaRoughness as input as originally proposed in [2]. float geometricOcclusion(PBRInfo pbrInputs) { float NdotL = pbrInputs.NdotL; float NdotV = pbrInputs.NdotV; float r = pbrInputs.alphaRoughness; float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL))); float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV))); return attenuationL * attenuationV; } // The following equation(s) model the distribution of microfacet normals across // the area being drawn (aka D()) // Implementation from "Average Irregularity Representation of a Roughened Surface // for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz // Follows the distribution function recommended in the SIGGRAPH 2013 course notes // from EPIC Games [1], Equation 3. float microfacetDistribution(PBRInfo pbrInputs) { float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0; return roughnessSq / (M_PI * f * f); } void PBRInfo_setAmbientLight(inout PBRInfo pbrInputs) { pbrInputs.NdotL = 1.0; pbrInputs.NdotH = 0.0; pbrInputs.LdotH = 0.0; pbrInputs.VdotH = 1.0; } void PBRInfo_setDirectionalLight(inout PBRInfo pbrInputs, vec3 lightDirection) { vec3 n = pbrInputs.n; vec3 v = pbrInputs.v; vec3 l = normalize(lightDirection); // Vector from surface point to light vec3 h = normalize(l+v); // Half vector between both l and v pbrInputs.NdotL = clamp(dot(n, l), 0.001, 1.0); pbrInputs.NdotH = clamp(dot(n, h), 0.0, 1.0); pbrInputs.LdotH = clamp(dot(l, h), 0.0, 1.0); pbrInputs.VdotH = clamp(dot(v, h), 0.0, 1.0); } void PBRInfo_setPointLight(inout PBRInfo pbrInputs, PointLight pointLight) { vec3 light_direction = normalize(pointLight.position - pbr_vPosition); PBRInfo_setDirectionalLight(pbrInputs, light_direction); } vec3 calculateFinalColor(PBRInfo pbrInputs, vec3 lightColor) { // Calculate the shading terms for the microfacet specular shading model vec3 F = specularReflection(pbrInputs); float G = geometricOcclusion(pbrInputs); float D = microfacetDistribution(pbrInputs); // Calculation of analytical lighting contribution vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs); vec3 specContrib = F * G * D / (4.0 * pbrInputs.NdotL * pbrInputs.NdotV); // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law) return pbrInputs.NdotL * lightColor * (diffuseContrib + specContrib); } vec4 pbr_filterColor(vec4 colorUnused) { // The albedo may be defined from a base texture or a flat color #ifdef HAS_BASECOLORMAP vec4 baseColor = SRGBtoLINEAR(texture(u_BaseColorSampler, pbr_vUV)) * u_BaseColorFactor; #else vec4 baseColor = u_BaseColorFactor; #endif #ifdef ALPHA_CUTOFF if (baseColor.a < u_AlphaCutoff) { discard; } #endif vec3 color = vec3(0, 0, 0); if(pbr_uUnlit){ color.rgb = baseColor.rgb; } else{ // Metallic and Roughness material properties are packed together // In glTF, these factors can be specified by fixed scalar values // or from a metallic-roughness map float perceptualRoughness = u_MetallicRoughnessValues.y; float metallic = u_MetallicRoughnessValues.x; #ifdef HAS_METALROUGHNESSMAP // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. // This layout intentionally reserves the 'r' channel for (optional) occlusion map data vec4 mrSample = texture(u_MetallicRoughnessSampler, pbr_vUV); perceptualRoughness = mrSample.g * perceptualRoughness; metallic = mrSample.b * metallic; #endif perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); metallic = clamp(metallic, 0.0, 1.0); // Roughness is authored as perceptual roughness; as is convention, // convert to material roughness by squaring the perceptual roughness [2]. float alphaRoughness = perceptualRoughness * perceptualRoughness; vec3 f0 = vec3(0.04); vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0); diffuseColor *= 1.0 - metallic; vec3 specularColor = mix(f0, baseColor.rgb, metallic); // Compute reflectance. float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); // For typical incident reflectance range (between 4% to 100%) set the grazing // reflectance to 100% for typical fresnel effect. // For very low reflectance range on highly diffuse objects (below 4%), // incrementally reduce grazing reflecance to 0%. float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); vec3 specularEnvironmentR0 = specularColor.rgb; vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90; vec3 n = getNormal(); // normal at surface point vec3 v = normalize(u_Camera - pbr_vPosition); // Vector from surface point to camera float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); vec3 reflection = -normalize(reflect(v, n)); PBRInfo pbrInputs = PBRInfo( 0.0, // NdotL NdotV, 0.0, // NdotH 0.0, // LdotH 0.0, // VdotH perceptualRoughness, metallic, specularEnvironmentR0, specularEnvironmentR90, alphaRoughness, diffuseColor, specularColor, n, v ); #ifdef USE_LIGHTS // Apply ambient light PBRInfo_setAmbientLight(pbrInputs); color += calculateFinalColor(pbrInputs, lighting_uAmbientLight.color); // Apply directional light for(int i = 0; i < lighting_uDirectionalLightCount; i++) { if (i < lighting_uDirectionalLightCount) { PBRInfo_setDirectionalLight(pbrInputs, lighting_uDirectionalLight[i].direction); color += calculateFinalColor(pbrInputs, lighting_uDirectionalLight[i].color); } } // Apply point light for(int i = 0; i < lighting_uPointLightCount; i++) { if (i < lighting_uPointLightCount) { PBRInfo_setPointLight(pbrInputs, lighting_uPointLight[i]); float attenuation = getPointLightAttenuation(lighting_uPointLight[i], distance(lighting_uPointLight[i].position, pbr_vPosition)); color += calculateFinalColor(pbrInputs, lighting_uPointLight[i].color / attenuation); } } #endif // Calculate lighting contribution from image based lighting source (IBL) #ifdef USE_IBL color += getIBLContribution(pbrInputs, n, reflection); #endif // Apply optional PBR terms for additional (optional) shading #ifdef HAS_OCCLUSIONMAP float ao = texture(u_OcclusionSampler, pbr_vUV).r; color = mix(color, color * ao, u_OcclusionStrength); #endif #ifdef HAS_EMISSIVEMAP vec3 emissive = SRGBtoLINEAR(texture(u_EmissiveSampler, pbr_vUV)).rgb * u_EmissiveFactor; color += emissive; #endif // This section uses mix to override final color for reference app visualization // of various parameters in the lighting equation. #ifdef PBR_DEBUG // TODO: Figure out how to debug multiple lights // color = mix(color, F, u_ScaleFGDSpec.x); // color = mix(color, vec3(G), u_ScaleFGDSpec.y); // color = mix(color, vec3(D), u_ScaleFGDSpec.z); // color = mix(color, specContrib, u_ScaleFGDSpec.w); // color = mix(color, diffuseContrib, u_ScaleDiffBaseMR.x); color = mix(color, baseColor.rgb, u_ScaleDiffBaseMR.y); color = mix(color, vec3(metallic), u_ScaleDiffBaseMR.z); color = mix(color, vec3(perceptualRoughness), u_ScaleDiffBaseMR.w); #endif } return vec4(pow(color,vec3(1.0/2.2)), baseColor.a); } `; // src/modules-webgl1/lighting/pbr/pbr.ts var pbr = { name: "pbr", vs: vs5, fs: fs27, defines: { LIGHTING_FRAGMENT: 1 }, dependencies: [lights] }; return __toCommonJS(bundle_exports); })(); /** * ORIGINAL LICENCE * @license * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of NVIDIA CORPORATION nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ return __exports__; });