UNPKG

@thi.ng/shader-ast-glsl

Version:

Customizable GLSL codegen for @thi.ng/shader-ast

144 lines (143 loc) 5.03 kB
import { isBoolean } from "@thi.ng/checks/is-boolean"; import { isNumber } from "@thi.ng/checks/is-number"; import { unsupported } from "@thi.ng/errors/unsupported"; import { F, V2, V4 } from "@thi.ng/shader-ast/api/types"; import { isMat, isVec } from "@thi.ng/shader-ast/ast/checks"; import { itemType } from "@thi.ng/shader-ast/ast/item"; import { sym } from "@thi.ng/shader-ast/ast/sym"; import { defTarget } from "@thi.ng/shader-ast/target"; import { GLSLVersion } from "./api.js"; const RE_SEMI = /[};]$/; const targetGLSL = (opts) => { const _opts = { type: "fs", version: GLSLVersion.GLES_300, versionPragma: true, prelude: "", ...opts }; const isVS = _opts.type === "vs"; const ff = _opts.prec !== void 0 ? (x) => x === (x | 0) ? x + ".0" : x.toFixed(_opts.prec) : (x) => x === (x | 0) ? x + ".0" : String(x); const $type = (t) => t; const $list = (body, sep = ", ") => body.map(emit).join(sep); const $fn = (t) => `${t.id}(${$list(t.args)})`; const $decl = (sym2, arg = false) => { const { id, type, opts: opts2, init } = sym2; const res = []; if (opts2.type) { let type2; if (_opts.version < GLSLVersion.GLES_300) { if (isVS) { type2 = { in: "attribute", out: "varying", uni: "uniform" }[opts2.type]; } else { type2 = { in: "varying", out: null, uni: "uniform" }[opts2.type]; !type2 && unsupported( "GLSL 100 doesn't support fragment shader output variables" ); } } else { opts2.loc != null && res.push(`layout(location=${opts2.loc}) `); opts2.smooth != null && res.push(opts2.smooth + " "); type2 = opts2.type === "uni" ? "uniform" : opts2.type; } res.push(type2 + " "); } else { opts2.const && res.push("const "); arg && opts2.q && res.push(opts2.q + " "); } opts2.prec && res.push(opts2.prec + " "); res.push($type(itemType(type)), " ", id); opts2.num && res.push(`[${opts2.num}]`); init && res.push(" = ", emit(init)); return res.join(""); }; const emitIndex = (t) => `${emit(t.val)}[${emit(t.id)}]`; const emit = defTarget({ arg: (t) => $decl(t, true), array_init: (t) => _opts.version >= GLSLVersion.GLES_300 ? `${t.type}(${$list(t.init)})` : unsupported( `array initializers not available in GLSL ${_opts.version}` ), assign: (t) => emit(t.l) + " = " + emit(t.r), ctrl: (t) => t.id, call: $fn, call_i: (t) => t.id === "texture" && _opts.version < GLSLVersion.GLES_300 ? `${t.id}${t.args[0].type.substring(7)}(${$list( t.args )})` : $fn(t), decl: (t) => $decl(t.id), fn: (t) => `${$type(t.type)} ${t.id}(${$list(t.args)}) ${emit(t.scope)}`, for: (t) => `for(${t.init ? emit(t.init) : ""}; ${emit(t.test)}; ${t.iter ? emit(t.iter) : ""}) ${emit(t.scope)}`, idx: emitIndex, idxm: emitIndex, if: (t) => { const res = `if (${emit(t.test)}) ` + emit(t.t); return t.f ? res + " else " + emit(t.f) : res; }, lit: (t) => { const v = t.val; switch (t.type) { case "bool": return isBoolean(v) ? String(v) : `bool(${emit(v)})`; case "float": return isNumber(v) ? ff(v) : `float(${emit(v)})`; case "int": return isNumber(v) ? String(v) : `int(${emit(v)})`; case "uint": return isNumber(v) ? `${v}u` : `uint(${emit(v)})`; default: { if (isVec(t) || isMat(t)) { return `${t.type}(${$list(v)})`; } return unsupported(`unknown type: ${t.type}`); } } }, op1: (t) => t.post ? `(${emit(t.val)}${t.op})` : `(${t.op}${emit(t.val)})`, op2: (t) => `(${emit(t.l)} ${t.op} ${emit(t.r)})`, ret: (t) => "return" + (t.val ? " " + emit(t.val) : ""), scope: (t) => { let res = t.body.map(emit).reduce( (acc, x) => (acc.push(RE_SEMI.test(x) ? x : x + ";"), acc), [] ).join("\n"); res += t.body.length && !RE_SEMI.test(res) ? ";" : ""; if (!t.global) { return `{ ${res} }`; } if (_opts.prelude) { res = _opts.prelude + "\n" + res; } if (_opts.versionPragma) { res = `#version ${_opts.version} ` + res; } return res; }, swizzle: (t) => `${emit(t.val)}.${t.id}`, sym: (t) => t.id, ternary: (t) => `(${emit(t.test)} ? ${emit(t.t)} : ${emit(t.f)})`, while: (t) => `while (${emit(t.test)}) ${emit(t.scope)}` }); Object.assign(emit, { gl_FragColor: sym(V4, "gl_FragColor"), gl_FragCoord: sym(V4, "gl_FragCoord", { const: true }), gl_FragData: sym("vec4[]", "gl_FragData", { num: 1 }), gl_FrontFacing: sym("bool", "gl_FrontFacing", { const: true }), gl_PointCoord: sym(V2, "gl_PointCoord", { const: true }), gl_PointSize: sym(F, "gl_PointSize"), gl_Position: sym(V4, "gl_Position") }); return emit; }; export { targetGLSL };