/** * Renderer types for IFC-Lite */ export interface Vec3 { x: number; y: number; z: number; } export interface Mat4 { m: Float32Array; } export interface Camera { position: Vec3; target: Vec3; up: Vec3; fov: number; aspect: number; near: number; far: number; } export interface Material { baseColor: [number, number, number, number]; metallic: number; roughness: number; transparency?: number; } export interface Mesh { expressId: number; modelIndex?: number; vertexBuffer: GPUBuffer; indexBuffer: GPUBuffer; indexCount: number; transform: Mat4; color: [number, number, number, number]; material?: Material; uniformBuffer?: GPUBuffer; bindGroup?: GPUBindGroup; bounds?: { min: [number, number, number]; max: [number, number, number]; }; /** True when this mesh was lazily hydrated for picking / selection highlight * (createMeshFromData) rather than added as authored geometry via addMesh(). * Such meshes duplicate geometry already drawn by a batch, so they are freed * when their entity leaves the selection (see Scene.disposeHydratedMeshesExcept) * to stop unbounded accumulation + transparent double-draw on deselect. */ hydrated?: boolean; } /** * Batched mesh - groups multiple meshes with same color into single draw call * Reduces draw calls from N meshes to ~100-500 batches * * When a single color group's geometry would exceed the GPU's maxBufferSize * limit, the data is automatically split across multiple buckets at * accumulation time. Each bucket gets a unique colorKey (base key or * "base#N"), so the rest of the pipeline stays unchanged. */ export interface BatchedMesh { id: number; colorKey: string; vertexBuffer: GPUBuffer; indexBuffer: GPUBuffer; indexCount: number; color: [number, number, number, number]; expressIds: number[]; bindGroup?: GPUBindGroup; uniformBuffer?: GPUBuffer; bounds?: { min: [number, number, number]; max: [number, number, number]; }; /** Per-batch local-frame origin: stored vertex positions are RELATIVE to it, * so this batch must be drawn with model = translate(origin) to land in world * space (world = origin + position). Keeps f32 vertex coords element-small at * building/georef scale (no fan collapse). [0,0,0] = absolute (legacy). */ origin?: [number, number, number]; /** LOD1 (issue #1682 phase 5): simplified index range over the SAME vertex * buffer, drawn instead of the full indices when the batch projects below * the configured screen size. Absent = no LOD (draw full detail always). */ lod1IndexBuffer?: GPUBuffer; lod1IndexCount?: number; /** 12-byte lattice-quantized vertex buffer (issue #1682 phase 6): when * present, `vertexBuffer` holds uint16x4+u32 records and the batch must * draw through the quantized pipeline variants with these dequantization * params in the uniform. Absent = 28-byte f32 layout (the default). */ quantized?: { min: [number, number, number]; step: number; }; /** GPU residency (issue #1682 phase 3a): `false` = evicted metadata shell — * bounds/expressIds/counts remain valid for culling, picking-fallback and * bookkeeping, but the GPU buffers are destroyed and MUST NOT be bound. * The draw loop skips such batches and requests a rebuild via * `Scene.requestBatchResidency`. `undefined`/`true` = resident. */ gpuResident?: boolean; } export type SectionPlaneAxis = 'down' | 'front' | 'side'; export type SectionCapHatchId = 'solid' | 'diagonal' | 'crossHatch' | 'horizontal' | 'vertical' | 'concrete' | 'brick' | 'insulation'; export interface SectionCapStyleOptions { /** Fill colour behind the hatch. RGBA 0-1. */ fillColor?: [number, number, number, number]; /** Hatch stroke colour. RGBA 0-1. */ strokeColor?: [number, number, number, number]; /** Hatch pattern id. */ pattern?: SectionCapHatchId; /** Spacing between hatch lines, in screen pixels. */ spacingPx?: number; /** Primary angle in radians. */ angleRad?: number; /** Line width in pixels. */ widthPx?: number; /** Secondary angle (cross-hatch). */ secondaryAngleRad?: number; } export interface SectionPlane { axis: SectionPlaneAxis; position: number; enabled: boolean; flipped?: boolean; min?: number; max?: number; /** If true (default), render filled cap surfaces with a screen-space hatch. */ showCap?: boolean; /** * If true (default), draw polygon outlines on the cut surfaces. Users * can turn surfaces and outlines on/off independently from the UI. */ showOutlines?: boolean; /** Override the default cap appearance. */ capStyle?: SectionCapStyleOptions; /** * Optional world-space plane normal (unit vector). When provided * together with `distance`, the shader clip uses them verbatim and * ignores `axis`, `position`, `min`, `max`, and any `buildingRotation`. * Used for face-pick / arbitrary slice planes (issue #243). */ normal?: [number, number, number]; /** Plane offset in world units: `dot(pointOnPlane, normal)`. */ distance?: number; } /** * Axis-aligned clip box (section / crop box): geometry is kept only where it * lies INSIDE all six box planes; fragments outside the box are discarded by the * shader (real geometry cut, unlike bounding-box element isolation). Coordinates * are in the same world space the shader sees as `input.worldPos` — i.e. the * viewer space the camera/section planes use. Independent of `sectionPlane`; both * can be active at once. */ export interface ClipBox { /** Box min corner [x, y, z] in world space. */ min: [number, number, number]; /** Box max corner [x, y, z] in world space. */ max: [number, number, number]; /** When false (or omitted), the box has no effect. */ enabled: boolean; } export type ContactShadingQuality = 'off' | 'low' | 'high'; export type SeparationLinesQuality = 'off' | 'low' | 'high'; export interface VisualEnhancementOptions { enabled?: boolean; edgeContrast?: { enabled?: boolean; intensity?: number; }; contactShading?: { quality?: ContactShadingQuality; intensity?: number; radius?: number; }; separationLines?: { enabled?: boolean; quality?: SeparationLinesQuality; intensity?: number; radius?: number; }; } export interface RenderOptions { clearColor?: [number, number, number, number]; /** * Global lighting environment (sun direction/colour, hemisphere ambient, * exposure, procedural sky). Omitted/empty reproduces the legacy hardcoded * look exactly. See {@link import('./environment.js').LightingEnvironment}. */ environment?: import('./environment.js').LightingEnvironment; enableDepthTest?: boolean; enableFrustumCulling?: boolean; spatialIndex?: import('@ifc-lite/spatial').SpatialIndex; /** * Entities to hide. Change detection is by CONTENT, not reference: the * renderer snapshots the set and compares element-wise each frame, so * mutating the same Set in place and passing a fresh Set per frame are both * correct — and a fresh Set with unchanged content does not invalidate the * per-batch visibility caches. An empty set is equivalent to omitting the * option. The per-frame compare costs O(set size) membership checks. */ hiddenIds?: Set; /** * Only show these entities (`null`/absent = no isolation). Unlike * {@link hiddenIds}, an EMPTY set is meaningful: it isolates nothing, hiding * everything. Same content-based change-detection contract as `hiddenIds`. */ isolatedIds?: Set | null; selectedId?: number | null; selectedIds?: Set; /** * Render the active colour overrides almost full-bright so they POP like a * highlight rather than reading as normal lit materials. Used while a clash is * focused so the amber/cyan pair stands out. (#1277/#1339) */ emphasizeOverrides?: boolean; /** * Per-frame alpha overrides — primary use case is X-Ray mode. * * Map. Non-selected meshes/batches whose expressId * appears in this map render at the override alpha through the transparent * pipeline. Selected meshes (`selectedId` / `selectedIds`) are exempt at * every site, so highlights always paint with their own alpha. * * Mixed batches (some entries overridden, some not) take the minimum * override alpha across non-selected ids; selected meshes in the batch * are then redrawn on top by the highlight pass. * * The renderer snapshots this map at frame start, so callers may freely * mutate or recycle their copy after `render()` returns. * * Note: alphas `>= 0.99` are treated as opaque (the cutoff for switching to * the transparent pipeline). Entries at or above that threshold are no-ops * — keep them out of the map to avoid unnecessary work. */ transparencyOverrides?: Map | null; /** * X-Ray *context* mode: every non-selected mesh whose `expressId` is NOT in * this set renders at {@link ghostAlpha}, so a focused subset (e.g. a clash * pair) stays solid while the rest of the model fades to translucent context. * * `null`/absent disables ghosting. Selected meshes (`selectedId`/`selectedIds`) * are always exempt, and explicit {@link transparencyOverrides} entries win * over the ghost alpha. Same id space as `isolatedIds` (federated global id). * * Mixed colour batches resolve to the minimum alpha among their non-selected * entities (same as {@link transparencyOverrides}), so an excepted id that * shares a batch with ghosted ids fades with the batch unless it is also in * `selectedIds` — the selection highlight pass then repaints it opaque. To * guarantee a focused entity stays fully solid, include it in `selectedIds` * (the clash viewer co-selects the focused pair for exactly this reason). */ ghostExceptIds?: Set | null; /** Alpha (0..1) for ghosted meshes under {@link ghostExceptIds}. Default 0.12. */ ghostAlpha?: number; buildingRotation?: number; selectedModelIndex?: number; sectionPlane?: SectionPlane; clipBox?: ClipBox; terrainClipY?: number; visualEnhancement?: VisualEnhancementOptions; isStreaming?: boolean; isInteracting?: boolean; interactionFrameIntervalMs?: number; /** * Contribution culling (issue #1682): skip colour batches whose world AABB * projects below `pixelRadius` device pixels (raised to * `interactingPixelRadius` while the camera moves). Applies to the batched * draw path only — instanced templates, textured meshes and the no-batches * fallback are never contribution-culled. Absent or `pixelRadius <= 0` * disables it (the default), so snapshot/export renders that omit the * option stay exhaustive. */ contributionCull?: import('./contribution-cull.js').ContributionCullOptions; /** * One-shot capture renders (IDS/clash/BCF snapshots): synchronously * rebuild every GPU-evicted batch before drawing, so isolation options * that reveal batches aged out under the residency budget still capture a * complete image. Has a frame-time cost proportional to the evicted set — * do NOT pass it on the interactive render loop (evicted batches restore * asynchronously there). */ restoreEvictedForCapture?: boolean; /** * LOD selection (issue #1682 phase 5): batches whose world AABB projects * below `screenPx` device pixels draw their simplified LOD1 index range * (when one was built — see Scene.setLodBuildsEnabled) instead of full * detail. Absent or `screenPx <= 0` = always full detail. */ lod?: { screenPx: number; }; } /** * Options for GPU picking/selection * Filters out hidden/invisible elements so users can select what's visible */ export interface PickOptions { isStreaming?: boolean; hiddenIds?: Set; isolatedIds?: Set | null; } /** * Resolved clip state the GPU picker mirrors from the most recent render so that * section/crop-clipped geometry is unpickable, not just invisible. The renderer * stashes this each `render()` and feeds it to the picker; consumers don't build * it. Point clouds are clipped by the section plane only (matching the point * render); the crop box clips triangle meshes only, on render and on pick. */ export interface PickClipState { sectionPlane?: { normal: [number, number, number]; distance: number; flipped: boolean; } | null; clipBox?: ClipBox | null; } /** * Result from GPU picking * For multi-model support, includes both expressId and modelIndex */ export interface PickResult { expressId: number; modelIndex?: number; /** * World-space XYZ of the picked surface point. Optional because the * pick path can skip depth readback for callers that only need the * entityId (e.g. selection state). Recovered by sampling the pick * pass's depth texture at the click position and unprojecting. */ worldXYZ?: { x: number; y: number; z: number; }; } //# sourceMappingURL=types.d.ts.map