import type { Graph, GraphNode } from '../graph/Graph'; import { colorOf, type PropertyBag, textOf } from '../model/Properties'; import { declaredRows, numberRow, PROPERTY_DEFAULT_PARAM, PROPERTY_NAME_PARAM } from './declaredRows'; import { type FlatDef, flatDef, isSpriteProp, isValidFieldName } from './PropertySchema'; export { PROPERTY_NAME_PARAM } from './declaredRows'; /** The sprite property naming the shader asset it wears; `''` is no shader and no render pass. */ export const SHADER_KEY = 'shader'; /** * The node of a shader graph that declares a parameter: its `name` is a sprite property and a * GLSL uniform, `default`/`min`/`max` are the row the inspector shows. */ export const SHADER_PROPERTY = 'ShaderProperty'; /** The same for a colour (#298): a colour row on the sprite and a vec3 uniform. */ export const SHADER_COLOR_PROPERTY = 'ShaderColorProperty'; export const DEFAULT_PROPERTY_COLOR = 0xffffff; /** A row a shader graph adds to its wearers. */ export type ShaderParamDef = FlatDef<'number'> | FlatDef<'color'>; export function shaderOf(bag: PropertyBag): string { return textOf(bag[SHADER_KEY]); } export function isPropertyNode(node: GraphNode): boolean { return node.type === SHADER_PROPERTY || node.type === SHADER_COLOR_PROPERTY; } export function propertyName(node: GraphNode): string { return textOf(node.params[PROPERTY_NAME_PARAM] ?? ''); } /** * A property's name is a bag key and a uniform, so it has to be an identifier – and not one of the * rows the sprite brings itself: both rows would share the key and the uniform would read the * sprite's width instead of the declared default (#63). The compiler names which of the two it is. */ function isValidPropertyName(name: string): boolean { return isValidFieldName(name) && !isSpriteProp(name); } /** The properties a shader graph declares: one row on the sprite and one uniform each. */ export function shaderParamDefs(graph: Graph): ShaderParamDef[] { return declaredRows(graph, { isDeclaration: isPropertyNode, nameOf: propertyName, isValidName: isValidPropertyName, rowOf: (name, node) => node.type === SHADER_COLOR_PROPERTY ? flatDef(name, 'color', { value: propertyColor(node.params) }) : numberRow(name, node.params), }); } function propertyColor(params: PropertyBag): number { return colorOf(params[PROPERTY_DEFAULT_PARAM] ?? DEFAULT_PROPERTY_COLOR, DEFAULT_PROPERTY_COLOR); } /** What a property placed from the palette is called: `value`, then `value2`, `value3` … */ export function nextPropertyName(graph: Graph, base = 'value'): string { const taken = new Set(graph.nodes.filter(isPropertyNode).map(propertyName)); if (!taken.has(base)) return base; for (let n = 2; ; n++) if (!taken.has(`${base}${n}`)) return `${base}${n}`; }