import * as THREE from 'three/webgpu';
import type { VirtualMesh, VirtualMeshInstances, VirtualMeshOptions } from './VirtualMesh.js';
import { OcclusionCulling } from './OcclusionCulling.js';
import type { VirtualMeshData } from '../preprocess/buildVirtualMesh.js';
import { GeometryPool } from './GeometryPool.js';
import { type VirtualGeometryImport, type VirtualGeometryImportOptions } from '../import/fromObject3D.js';
export { VG_DEBUG_MODES } from '../constants.js';
export interface VirtualGeometryStats {
    drawnMeshlets: number;
    drawnTriangles: number;
    /** Triangles if every instance were drawn at full detail (no LOD, no culling). */
    fullDetailTriangles: number;
    instances: number;
    /** Instances that survived instance culling (frustum, sub-pixel). */
    visibleInstances: number;
    /** Triangles the LOD cut asked for. Above drawnTriangles means some mesh ran out of index buffer. */
    requestedTriangles: number;
    /** True when a mesh dropped meshlets for lack of capacity (visible as flicker). */
    overflow: boolean;
    /** Fullest draw buffer of any mesh, as a fraction of its capacity (above 1: overflow). */
    capacityUse: number;
    /** Instances hidden from the camera by occlusion culling. */
    occludedInstances: number;
    /** Triangles drawn into shadow maps (the coarser shadow cut). */
    shadowTriangles: number;
    /**
     * Factor the GPU capacity controller applies to the error threshold of the camera cut (largest over pools): 1
     * while the draw buffers have room, more while they would overflow at the requested threshold.
     */
    thresholdScale: number;
}
export interface VirtualGeometryOptions {
    /** Max triangles drawn per frame per pool (camera). Default 10M (120 MB of indices, under the 128 MB binding limit). */
    maxTriangles?: number;
    /** Max triangles drawn into shadow maps per frame per pool. Default 4M. */
    maxShadowTriangles?: number;
    /** Max meshlets drawn per frame per pool. Default 1M. */
    maxMeshlets?: number;
    /**
     * Largest storage-buffer binding a shared pool may use, in bytes. A pool that would exceed it is closed and a new
     * one opened. Default 120 MB (WebGPU guarantees 128 MB). A single mesh larger than this gets a pool of its own,
     * which needs a device created with higher limits: see `virtualGeometryLimits()`.
     */
    maxPoolBytes?: number;
}
export declare class VirtualGeometry {
    readonly viewMatrix: THREE.UniformNode<"mat4", THREE.Matrix4>;
    readonly projectionMatrix: THREE.UniformNode<"mat4", THREE.Matrix4>;
    /** `projection[1][1] * viewportHeight / 2`: converts view-space error to pixels. */
    readonly projScale: THREE.UniformNode<"float", number>;
    readonly near: THREE.UniformNode<"float", number>;
    /** Max projected error, in pixels, a meshlet may have to be drawn (the finest end of the LOD blend band). */
    readonly errorThreshold: THREE.UniformNode<"float", number>;
    /**
     * Lever: width of the LOD blend band, as a factor on the error threshold (default 2). Instead of switching each
     * cluster to its parent at one distance (a pop), every pixel shows the cut for its own threshold between t and
     * t * lodBlend, from a per-pixel pattern, so detail moves from one level to the next a few pixels at a time.
     *
     * Automatic: at an effective threshold of about a pixel (the default) a level switch moves the surface by at most
     * a pixel and does not show, so levels switch directly and blending costs nothing. The band opens as the effective
     * threshold rises (when you set a higher one, or the draw buffers force a coarser one), and is this wide from
     * 2.5 px up. Blending costs while it is open: clusters in the band are drawn by two levels, with a discard. 1 turns
     * it off.
     */
    lodBlend: number;
    /**
     * Lever: change the blend pattern every frame (default false). Turn it on together with temporal anti-aliasing
     * (e.g. three's TRAA), which averages the pattern into a smooth crossfade; without TAA a moving pattern shimmers.
     */
    lodBlendTemporal: boolean;
    /** `max(1, lodBlend)` (set every frame). */
    readonly lodBlendMax: THREE.UniformNode<"float", number>;
    /** `maxErrorThreshold / errorThreshold`: the most the GPU capacity controller may scale the threshold by. */
    readonly lodScaleLimit: THREE.UniformNode<"float", number>;
    /**
     * Lever: target number of drawn triangles per frame. When > 0, the error threshold is adjusted (gliding, see
     * adaptThreshold) to keep the drawn triangle count near this budget. Off by default: a fixed threshold gives
     * the steadiest image, because LODs then only switch with distance, never all at once.
     */
    triangleBudget: number;
    /** Bounds for the automatically adjusted error threshold, in pixels. */
    thresholdRange: [number, number];
    /**
     * The highest error threshold, in pixels, that the engine may switch to on its own to keep draw buffers from
     * overflowing. Default 64. Lower it to keep detail no matter what: if the scene still asks for more triangles than
     * the buffers hold, some clusters are then dropped (raise `maxTriangles` instead if that happens).
     *
     * The capacity controller runs on the GPU, per pool: when a cut would not fit its draw buffers it is selected again
     * at a coarser threshold in the same frame, and between frames the threshold glides back by a percent per frame
     * while there is room. `errorThreshold` keeps the value you set; `lastStats.thresholdScale` shows the factor.
     */
    maxErrorThreshold: number;
    /** Latest GPU readback, updated every `statsInterval` frames (every other frame while a draw buffer is nearly full). */
    lastStats: VirtualGeometryStats | null;
    statsInterval: number;
    /** Frames updated so far. */
    frameCount: number;
    /** Consecutive stats readbacks with the drawn triangles outside the budget's deadband. */
    private outOfBand;
    /** Threshold the controllers glide toward, a few percent per frame (null: none), and the value last written. */
    private thresholdTarget;
    private thresholdWritten;
    /** Threshold when the pending stats readback was issued: the cut it describes was made with about this value. */
    private statsThreshold;
    private statsPending;
    readonly frustumCulling: THREE.UniformNode<"uint", number>;
    readonly debugMode: THREE.UniformNode<"uint", number>;
    /**
     * Instances whose bounding sphere projects to a smaller radius than this (pixels) are skipped. 0 disables.
     * The sphere is never smaller than the object, so at 0.7 nothing wider than ~1.4 px on screen is dropped.
     */
    readonly minPixelRadius: THREE.UniformNode<"float", number>;
    readonly frustumPlanes: THREE.UniformNode<"vec4", THREE.Vector4>[];
    /**
     * Direction * length that light travels (e.g. sun direction * 300). When non-zero, frustum culling
     * keeps meshlets whose shadow, swept along this vector, can reach the view frustum, so shadow
     * casters just outside the view still cast shadows. Zero disables it.
     */
    readonly shadowSweep: THREE.UniformNode<"vec3", THREE.Vector3>;
    /**
     * Set `shadowSweep` automatically from the shadow cameras of the lights that render the meshes (direction
     * of an orthographic shadow camera, length = its larger extent). Turn off to set `shadowSweep` yourself.
     */
    autoShadowSweep: boolean;
    /**
     * Shadow maps use a coarser cut: the error threshold times this. Shadows are soft and seen from the camera
     * at a distance, so this saves most of the shadow-pass triangles with no visible change. 1 = camera detail.
     */
    readonly shadowErrorScale: THREE.UniformNode<"float", number>;
    private readonly detectedSweep;
    private detectedSweepFrame;
    /**
     * Occlusion culling (on by default): geometry hidden behind solid meshes is not drawn. Levers:
     * `occlusion.enabled`, `occlusion.resolutionScale`, and per mesh `occluder` / `occlusionCulling`.
     */
    readonly occlusion: OcclusionCulling;
    /** Skip LOD selection/culling and keep drawing the last selection (for inspecting the cut). */
    freeze: boolean;
    /**
     * Run `update()` automatically before every render of a scene that contains this context's meshes, with the
     * camera that render uses (default true). Shadow-map renders reuse the cut of the camera being rendered.
     * Calling `update()` yourself still works: a render right after it with the same camera does not repeat it.
     */
    autoUpdate: boolean;
    /**
     * Measure the error threshold in CSS pixels (default) instead of device pixels. On a 2x display,
     * device pixels would request ~4x the triangles for detail nobody can see.
     */
    errorInCssPixels: boolean;
    readonly meshes: VirtualMesh[];
    /** Shared buffers and compute passes; meshes go into the first pool with room (see GeometryPool). */
    readonly pools: GeometryPool[];
    private readonly capacity;
    private readonly maxPoolBytes;
    constructor(options?: VirtualGeometryOptions);
    private readonly frustum;
    private readonly projScreen;
    private readonly size;
    /** Compute nodes dispatched this frame (reused to avoid per-frame allocation). */
    private readonly computeList;
    private readonly dispatchedPools;
    /** Frames where `update` ran no compute pass because nothing the cut depends on changed. */
    skippedFrames: number;
    /** Inputs of the cut as of the last frame that changed them (NaN until the first frame). */
    private readonly cutInputs;
    private readonly cutInputsScratch;
    /** Scenes whose onBeforeRender runs the auto update (installed the first time one of our meshes renders in them). */
    private readonly sceneHooks;
    /** `renderer.info.render.calls` and camera of the last automatic and the last manual update. */
    private autoCall;
    private autoCamera;
    private manualCall;
    private manualCamera;
    private updatingAuto;
    /**
     * Converts every static mesh under `object` (e.g. `gltf.scene`) to VirtualGeometry geometry, in place: meshes sharing
     * geometry and material become one VirtualMesh with many instances, materials are converted to node materials
     * with their textures, and the originals stop rendering. Skinned/morphing meshes, points and lines are left
     * alone. Returns the created meshes and a `dispose()` that restores the originals. See `VirtualGeometryImportOptions`.
     */
    add(object: THREE.Object3D, options?: VirtualGeometryImportOptions): Promise<VirtualGeometryImport>;
    /** Create a renderable mesh from a built DAG. Add the result to any three.js scene. */
    createMesh(data: VirtualMeshData, material: THREE.NodeMaterial, instances: VirtualMeshInstances, options?: VirtualMeshOptions): VirtualMesh;
    /**
     * Lever: compile a pool's compute pipelines in the background before its first dispatch (default), so adding
     * meshes never freezes the page; they appear once ready. False: compile on first use (a hitch).
     */
    asyncCompile: boolean;
    private compilePool;
    /** Builds pools whose buffers grew and points their meshes at the new buffers. */
    private preparePools;
    private readonly checkedPools;
    /**
     * Disables pools whose buffers the device cannot bind (a mesh with a pool of its own, on a device created with
     * default limits): their compute passes are skipped and their meshes hidden, with one message saying how to fix it.
     */
    private checkDeviceLimits;
    /**
     * Optional, for loading screens: compiles every pipeline the scene needs (compute and render), so the first
     * frames neither freeze nor show meshes popping in. Call after adding the meshes, before the first render.
     */
    compileAsync(renderer: THREE.WebGPURenderer, scene: THREE.Object3D, camera: THREE.Camera): Promise<void>;
    /** Called by meshes from onBeforeShadow: remembers the light direction for `autoShadowSweep`. */
    noteShadowCamera(shadowCamera: THREE.Camera): void;
    /** Puts the mesh's geometry and instances into the first pool with room (a new pool when none has). */
    register(mesh: VirtualMesh, instances: VirtualMeshInstances): void;
    /**
     * Auto update, from a VirtualMesh's onBeforeRender. The first time, this also hooks the scene's onBeforeRender,
     * which runs before the render pass starts (so shadow maps rendered inside the pass already see the new cut);
     * later frames update from there. A compute submitted from inside a pass still runs before that pass on the
     * GPU, because the pass is submitted when it ends.
     */
    private autoUpdateFor;
    unregister(mesh: VirtualMesh): void;
    /** Dispose every mesh created by this context. */
    dispose(): void;
    /**
     * Runs LOD selection + culling on the GPU for `camera`. With `autoUpdate` (default) the renderer calls this
     * for you before each render; call it yourself before `renderer.render` when `autoUpdate` is false.
     * When the camera, the settings and every mesh's instances are unchanged, the compute passes are
     * skipped and the previous cut (index buffer + draw args) is drawn again.
     */
    update(renderer: THREE.WebGPURenderer, camera: THREE.PerspectiveCamera): void;
    /**
     * True when anything the GPU cut reads from the context differs from the last frame that was computed
     * (and records the new values). Exact comparison on purpose: an unmoved camera recomputes the same
     * matrices bit for bit, while any real change, including a budget-controller step, differs.
     */
    private cutInputsChanged;
    /**
     * Budget controller (only with `triangleBudget`): picks the threshold to glide toward (see stepThreshold) from a
     * stats readback. Drawn triangles grow roughly with 1/threshold^2. Capacity is not its job: the GPU controller keeps
     * every cut inside its draw buffers (see `maxErrorThreshold`).
     */
    private adaptThreshold;
    private glideTo;
    /** Every frame: moves the threshold a few percent toward the controllers' target. */
    private stepThreshold;
    /** GPU -> CPU readback of the last culling result. Async; never call every frame. One small readback per pool. */
    readStats(renderer: THREE.WebGPURenderer): Promise<VirtualGeometryStats>;
}
