import type { GraphLookup } from '../graph/lookup'; import { INSTRUMENT_PARAMETERS, SOUND_FX_INPUT, terminalNode } from '../graph/nested'; import { startStateOf, statesOf, transitionsOf } from '../graph/stateMachine'; import { pictureSize } from '../properties/aspect'; import { resolveAssetProps } from '../properties/resolve'; import { clipOf } from './animation'; import { isGraphAsset, isVariableOwner } from './Asset'; import { bagWithoutItemTypes } from './Properties'; import { fieldTypings, typeFields, typingOf } from './structTypes'; import { findAsset, findAssetOf, type Project } from './Project'; import { resolvePicture } from './texture'; /** The project's shader functions by id, as a call and the compiler see them (#303). */ export function shaderFunctionsOf(project: Project): GraphLookup['shaderFunction'] { return (assetId) => { const asset = findAssetOf(project, assetId, 'shaderfunction'); return asset ? { name: asset.name, graph: asset.graph } : null; }; } /** * What a graph may see of the project around it. A behavior's `defaults` are its variable values; * every call starts from a copy of them, except for the variables it keeps (#229). */ export function graphLookup(project: Project): GraphLookup { return { behavior: (assetId) => { const asset = findAssetOf(project, assetId, 'behavior'); if (!asset) return null; return { name: asset.name, graph: asset.graph, defaults: bagWithoutItemTypes(resolveAssetProps(project, asset)), kept: asset.keptVariables ?? [], }; }, shaderFunction: shaderFunctionsOf(project), soundFx: (assetId) => { const asset = findAssetOf(project, assetId, 'soundfx') ?? findAssetOf(project, assetId, 'instrument'); if (!asset) return null; const declaring = asset.type === 'soundfx' ? SOUND_FX_INPUT : INSTRUMENT_PARAMETERS; return { name: asset.name, params: terminalNode(asset.graph, declaring)?.ports ?? [] }; }, machine: (assetId) => { const asset = findAssetOf(project, assetId, 'statemachine'); if (!asset) return null; return { name: asset.name, states: statesOf(asset.graph), transitions: transitionsOf(asset.graph), start: startStateOf(asset.graph, asset.start), }; }, curve: (assetId) => findAssetOf(project, assetId, 'curve')?.points ?? null, structType: (assetId) => { const asset = findAssetOf(project, assetId, 'struct'); if (!asset) return null; const fields = typeFields(asset); return { name: asset.name, fields: fields.map((f) => f.key), inner: fieldTypings(fields) ?? {} }; }, textureSize: (pictureId) => { const ref = resolvePicture(project, pictureId); return ref ? pictureSize(ref) : null; }, animation: (assetId) => { const asset = findAssetOf(project, assetId, 'animation'); return asset ? clipOf(asset) : null; }, collisionLayers: () => project.collisionLayers, renderLayers: () => project.renderLayers, ownerOf: (graph) => project.assets.find((a) => isGraphAsset(a) && a.graph === graph)?.id ?? '', variableTyping: (assetId, key) => { const owner = findAsset(project, assetId); const def = owner && isVariableOwner(owner) ? owner.variables.find((v) => v.key === key) : undefined; return def && typingOf(def); }, }; }