import type { Graph } from '../graph/Graph'; import type { PropertyDef } from '../properties/PropertySchema'; import type { AssetType } from './AssetTypes'; import type { PropertyBag } from './Properties'; import type { Scope } from './Scope'; import type { SceneObject } from './SceneObject'; export interface AssetBase { id: string; type: AssetType; name: string; props: PropertyBag; } export interface SpriteAsset extends AssetBase { type: 'sprite'; } export interface CameraAsset extends AssetBase { type: 'camera'; } export interface TextAsset extends AssetBase { type: 'text'; } export interface SceneAsset extends AssetBase { type: 'scene'; objects: SceneObject[]; } /** `variables` is this asset's own property schema; `props` holds their default values. */ export interface BehaviorAsset extends AssetBase { type: 'behavior'; variables: PropertyDef[]; graph: Graph; } /** Placed as a child of the object it belongs to; its position is the offset from that body. */ export interface ColliderAsset extends AssetBase { type: 'collider'; } /** * `objects` is a template, not content: a placed container shows it without copying it, so an * edit here reaches every instance (see `model/instance.ts`). Positions are local to the * container's origin. */ export interface ContainerAsset extends AssetBase { type: 'container'; objects: SceneObject[]; } /** `id` shares the generator with asset ids and is unique across the document (see `model/texture.ts`). */ export interface TextureFrame { id: string; name: string; x: number; y: number; w: number; h: number; } /** * `file` names the image in `data/textures/` and is content-addressed (`{sha256}.{ext}`), so it * can be cached forever; empty means the upload failed or has not happened yet. `width`/`height` * are the size as uploaded, so frames can be sliced and validated before the image loads. */ export interface TextureAsset extends AssetBase { type: 'texture'; file: string; width: number; height: number; frames: TextureFrame[]; } /** * `frames` holds what a sprite's `texture` holds – a texture id or a frame id – in playing order. * A picture may appear more than once, which is how one frame is held longer. */ export interface AnimationAsset extends AssetBase { type: 'animation'; frames: string[]; } /** * The graph holds one `State` node per state and an edge per transition; a behavior reads the * machine's current state through `State branch` and moves it through `Set state` or `State transition`. * A `global` machine has one current state for the whole run instead of one per behavior instance. */ export interface StateMachineAsset extends AssetBase { type: 'statemachine'; graph: Graph; scope?: Scope; } /** * The graph is GLSL in nodes: `Property` nodes declare the sprite rows, the `Output` node ends * the fragment (see `core/shader/compile.ts`). A sprite wears it through its `shader` property. */ export interface ShaderAsset extends AssetBase { type: 'shader'; graph: Graph; } /** * `file` names the typeface in `data/fonts/` the way a texture names its image: content-addressed * `{sha256}.{ext}`, empty when the upload failed. A text object wears it through its `font` * property; the family the browser knows it under is derived from the file name, see `model/font`. */ export interface FontAsset extends AssetBase { type: 'font'; file: string; } /** * The graph is the motion of one particle: every output of its `Output` node is a function of the * particle's age and its own random number (see `core/particles/compile.ts`). A placed object is * the source that emits them; how many and for how long are its properties. */ export interface ParticlesAsset extends AssetBase { type: 'particles'; graph: Graph; } export type Asset = | SpriteAsset | CameraAsset | TextAsset | SceneAsset | BehaviorAsset | ColliderAsset | ContainerAsset | TextureAsset | AnimationAsset | StateMachineAsset | ShaderAsset | FontAsset | ParticlesAsset; /** The assets edited as a node graph – what the graph canvas and the graph commands work on. */ export type GraphAsset = BehaviorAsset | StateMachineAsset | ShaderAsset | ParticlesAsset; const GRAPH_TYPES: ReadonlySet = new Set(['behavior', 'statemachine', 'shader', 'particles']); export function isGraphAsset(asset: Asset): asset is GraphAsset { return GRAPH_TYPES.has(asset.type); } export function isSceneAsset(asset: Asset): asset is SceneAsset { return asset.type === 'scene'; }