/** * Binds the part slots `world/buildings` generates to real models, so a procedural building renders * as an instanced asset kit instead of untextured blocks. Pure serializable data — core resolves * bindings, the renderer loads them. */ import { type MaterialAsset, type MaterialAssignment } from "../material/materialAsset.js"; import type { BuildingKitSlot, BuildingPartKind, BuildingSurfaceMaterial, BuildingVariantCounts, Vec3 } from "./buildings.js"; /** * How a kit model's native bounds are mapped onto the slot box `BuildingPartPlacement.scale` * describes. * * - `stretch` — non-uniform scale to exactly fill the slot on all three axes. Right for tiling * panels (walls, shutters) whose art is meant to fill its bay. * - `contain` — uniform scale so the model fits entirely inside the slot. Right for props that * must not distort (AC units, chimneys, roof clutter). * - `cover` — uniform scale so the model covers the slot on its widest axis; may overhang. * - `native` — no fit scaling; the model's authored size is used as-is. */ export type BuildingKitFit = "stretch" | "contain" | "cover" | "native"; /** * One bindable variant for a building part slot. `model` is an opaque reference the host resolves * (an asset id, a pack-relative path, or a URL) — core never loads anything. * * Adjustments apply in slot-local space after `fit`: `x` runs along the facade, `y` is up, `z` * points outward from the wall. */ export interface BuildingKitPart { model: string; fit?: BuildingKitFit; offset?: Vec3; /** Euler XYZ in radians, applied on top of the slot's facade rotation. */ rotation?: Vec3; /** Multiplier applied after `fit`. */ scale?: Vec3; tint?: string; /** Tiled PBR maps applied over the model's own materials; `color` tints, `repeat` sets the tiling. */ material?: BuildingSurfaceMaterial; /** Editor-authored mesh/slot selectors applied after the broad tint and surface. */ materialAssignments?: readonly MaterialAssignment[]; /** * Quarter-turn the model when its long horizontal axis disagrees with the slot's, so a panel * authored running along Z still tiles a bay that runs along X. Default true — modular kits do not * agree on a facing convention, and a stretched panel that is 90° off is always wrong. Set false * when a model's authored orientation is deliberate. */ autoOrient?: boolean; } /** * Binds a building's generated part slots to models. A kit is plain serializable data — scene * documents can carry one — and is deliberately unopinionated about art direction: the engine ships * the seam, games ship the bindings. */ export interface BuildingKit { id: string; /** The caller's existing editor-document material assets; only assigned IDs are loaded. */ materialAssets?: readonly MaterialAsset[]; /** Variants per part kind; `BuildingKitSlot.variant` indexes this list, wrapping. */ parts: Readonly>>; /** Overrides keyed by `BuildingKitSlot.key` (`${facade}.${kind}`); beats `parts` for that facade. */ slots?: Readonly>; /** Kinds this kit deliberately drops — no mesh, and no box fallback either. */ omit?: readonly BuildingPartKind[]; /** Fit for parts that do not set their own. Defaults to `stretch`. */ fit?: BuildingKitFit; } /** A variant as authored: a bare model reference, or the full {@link BuildingKitPart} when it needs fit or transform tuning. */ export type BuildingKitPartInput = string | BuildingKitPart; /** The authoring shape {@link defineBuildingKit} normalizes into a {@link BuildingKit}. */ export interface BuildingKitInput { id?: string; materialAssets?: readonly MaterialAsset[]; parts: Readonly>>; slots?: Readonly>; omit?: readonly BuildingPartKind[]; fit?: BuildingKitFit; } /** * What a renderer should do with one generated part. * * `model` carries a resolved variant with its fit already defaulted; `box` means fall back to the * engine's untextured block; `omit` means draw nothing. */ export type BuildingKitBinding = { readonly type: "model"; readonly part: BuildingKitPart; readonly fit: BuildingKitFit; } | { readonly type: "box"; } | { readonly type: "omit"; }; /** * Validates and normalizes a kit description, accepting bare model strings as single-field variants. * Throws on empty variants or invalid editor material references/selectors before loading models. * * @capability building-kit bind generated facade part slots to caller models and editor-authored named materials */ export declare function defineBuildingKit(input: BuildingKitInput): BuildingKit; /** * Resolves one generated part against a kit. Pass the placement's `kind` and its `kit` slot; the * slot's `variant` wraps around the bound list, so a kit may carry any number of variants * independent of the generator's `variants` counts. * * Returns `box` when the kind is unbound — the renderer keeps drawing today's block, so a partial * kit is a valid kit. * * @capability building-kit resolve one generated part to its bound model, a block fallback, or nothing */ export declare function resolveBuildingKitPart(kit: BuildingKit | undefined, kind: BuildingPartKind, slot?: BuildingKitSlot): BuildingKitBinding; /** * The variant counts a kit actually supplies, shaped for `BuildingConfigInput.variants`. Feed this * into `generateBuilding` so seeded picks spread across exactly the models the kit binds instead of * indexing past them and wrapping unevenly. * * @capability building-kit bind generated facade part slots to real models instead of untextured blocks */ export declare function buildingKitVariantCounts(kit: BuildingKit): Partial;