import { ClipRect, RenderStats, SpriteGroupOptions } from "./types.js"; import { BatchQueryView } from "./batchQuery.js"; import { Sprite2D } from "../sprites/Sprite2D.js"; import { WorldProvider } from "../ecs/world.js"; import { Object3D, Vector3 } from "three"; import { ClippingGroup } from "three/webgpu"; import { World } from "koota"; //#region src/pipeline/SpriteGroup.d.ts declare class SpriteGroup extends ClippingGroup implements WorldProvider { readonly isSpriteGroup = true; /** * ECS world for this renderer. * Lazily created on first access. */ private _world; /** * Entity holding the BatchRegistry singleton trait. */ private _registryEntity; /** Bound source getter registered with the devtools sink. */ private _batchSource; /** * Last observed `registry.scheduleRuns` value at the time one of * this SpriteGroup's own entry points (`update` / * `updateMatrixWorld`) invoked `schedule.run`. When the registry's * counter has already advanced past this value — e.g. Flatland * bumped it by running the schedule directly — the entry point * treats the run as already satisfied for this frame and skips. * Reset by host frame bookkeeping (Flatland does this by * incrementing `scheduleRuns` when it runs the schedule itself). */ private _lastRunSeen; /** * Maximum sprites per batch (explicit `maxBatchSize` opt-in). When the * user doesn't pass one, batches size themselves off the tier ladder. */ private _maxBatchSize; /** * Tier ladder for batch sizing — non-null unless the user pinned an * explicit `maxBatchSize`, in which case every batch uses that size. */ private _tierLadder; private _clipRect; private readonly _localClipPlanes; private readonly _clipMatrixWorld; private _clipPlanesDirty; /** Local-space clip rectangle, exposed as a property for R3F props. */ get clipRect(): ClipRect | null; set clipRect(value: ClipRect | null); /** * Maximum sprites per batch. Reads back whichever sizing mode is * active; setting it pins every future batch in this group to that * fixed size (tier ladder off) — the escape hatch for hand-tuned * scenes where the ladder's warmup tiers cost more than they save * (e.g. a scene that's always going to hold tens of thousands of * sprites). Property setter (not just a constructor option) so R3F's * JSX prop path (``) works — only * affects batches created after the set; existing live batches keep * their size. */ get maxBatchSize(): number; set maxBatchSize(value: number); /** * Whether frustum culling is enabled. */ frustumCulling: boolean; /** * Whether auto-sorting is enabled. */ autoSort: boolean; /** * Whether to automatically invalidate transforms every frame. * Enable for games where sprites move frequently. * Disable for static UIs and call invalidateTransforms() manually. */ autoInvalidateTransforms: boolean; /** * Sprite count for stats. */ private _spriteCount; /** * Per-instance ECS system functions. Each holds its own scratch state * (Koota change-tracking subscriptions, scratch arrays, Sets) so two * SpriteGroups don't share buffers or interfere with each other's * change-tracking. */ private readonly _batchAssignSystem; private readonly _batchReassignSystem; private readonly _batchRemoveSystem; private readonly _batchSortSystem; private readonly _sceneGraphSyncSystem; /** * Per-SpriteGroup state consumed by the schedule closures (built once * in `get world()`). These are the SAME references the BatchRegistry * spawn points at, so the factory systems and the registry never * diverge. */ private _effectTraits; private _pendingDestroy; private _parentAdd; private _parentRemove; /** * Source sprites retained below ordinary Object3D parents. Three.js only * dispatches `added` / `removed` on the directly-mutated node, so a whole * subtree can enter or leave this group without any descendant sprite * receiving an event. Reconcile the authored tree before each schedule run * to keep ownership exact in those cases. */ private readonly _hierarchySprites; private readonly _hierarchySeen; constructor(options?: SpriteGroupOptions); /** Build parent-local clipping planes from the public rectangle property. */ private _updateLocalClipPlanes; /** Project local clipping planes into world space for Three.js clipping. */ private _syncWorldClipPlanes; /** CPU counterpart to the renderer's clip planes, used by picking. @internal */ _containsWorldPoint(point: Vector3): boolean; /** * The ECS world managed by this renderer. * Sprites added to this renderer are enrolled in this world. */ get world(): World; /** * Add a sprite to the renderer. */ add(...objects: Object3D[]): this; add(sprite: Sprite2D): this; /** Enroll a sprite while leaving it under its authored Object3D parent. @internal */ _enrollHierarchySprite(sprite: Sprite2D): void; /** Release a retained source descendant from this group's world. @internal */ _releaseHierarchySprite(sprite: Sprite2D): void; /** Release a direct (non-hierarchy) enrollment so another world can adopt it. @internal */ _releaseDirectEnrollment(sprite: Sprite2D): void; /** Resolve and release the SpriteGroup that owns a sprite's previous ECS world. */ private _releasePreviousWorldEnrollment; /** True when this is the first SpriteGroup above the source sprite. */ private _ownsHierarchySprite; /** Collect authored descendants without crossing a nested SpriteGroup boundary. */ private _collectHierarchySprites; /** * Repair descendant ownership after subtree attach/detach/reparent mutations. * The steady-state direct-sprite path visits only SpriteBatch children. */ private _reconcileHierarchySprites; /** * Re-resolve a bootstrap default or bootstrap effect-variant material * to this group's world-scoped store for the sprite's texture. * Explicit user materials pass through untouched (their dispose hook * still installs via _trackMaterial → ensureMaterialDisposeHook). */ private _resolveDefaultMaterial; /** * Add multiple sprites to the renderer. */ addSprites(...sprites: Sprite2D[]): this; /** * Remove a sprite from the renderer. */ remove(...objects: Object3D[]): this; remove(sprite: Sprite2D): this; /** * Remove multiple sprites from the renderer. */ removeSprites(...sprites: Sprite2D[]): this; /** * Mark a sprite as needing sort recalculation. * Call when sprite's layer or zIndex changes. * Note: With pure ECS batching, Changed() queries detect this automatically. * This method is kept for explicit invalidation if needed. */ invalidate(_sprite: Sprite2D): void; /** * Mark all sprites as needing update. * Note: With pure ECS batching, this is largely a no-op since systems detect changes. */ invalidateAll(): void; /** * Mark transforms as needing update. * Note: With autoInvalidateTransforms=true (default), this happens every frame. */ invalidateTransforms(): void; /** * Three.js render hook — runs ECS systems and syncs buffers. * * Called automatically by Three.js during `renderer.render(scene, camera)` * before drawing children. This is the main integration point — no manual * `update()` call is needed. * * Per-frame flow is the `SystemSchedule` built in `get world()`: * deferredDestroy → material-version/effect-traits → batchAssign → * batchReassign → conditionalTransformSync → batchSort → sceneGraphSync * → batchRemove → late-assign → flushDirtyRanges, with lighting systems * prepended by `Flatland.setLighting`. Color / UV / flip / effect writes * are NOT in the schedule — they happen at the setter site via the * sprite's cached `_batchMesh`/`_batchSlot`; the BucketedDirtyTracker on * each instance attribute coalesces uploads. */ private _inSystems; updateMatrixWorld(force?: boolean): void; /** * Explicitly run all ECS systems for a new frame. * @deprecated Use Three.js `renderer.render()` instead — systems run * automatically in `updateMatrixWorld()`. Kept for backwards compatibility. */ update(): void; /** * Force-run the ECS schedule for this frame if it hasn't already run. * The non-deprecated internal used by callers that need the schedule * to have executed before they proceed this frame (e.g. the * auto-orchestration scene sweep) — `update()` is the deprecated * public alias of this same logic. * @internal */ _runScheduleNow(): void; /** * Track a material for schema version detection. */ private _trackMaterial; /** * Check for material schema version changes (tier upgrades from effect registration). * When detected, evicts sprites from old batches (wrong buffer layout) and * re-triggers IsRenderable so batchAssignSystem creates new batches with * the correct effect buffer tier. */ private _checkMaterialVersions; /** * Force-rebuild batches for a material by evicting sprite entities from * old batches and re-triggering IsRenderable for batchAssignSystem. * Called when a material's effect tier changes (e.g., new effect registered * that requires larger GPU buffers). */ private _rebuildBatchesForMaterial; /** * Rebuild the effect traits map from tracked materials. */ private _rebuildEffectTraits; /** * Get render statistics. * * Note: `drawCalls` is NOT computed here — it must come from * `renderer.info.render.calls` after the actual Three.js render pass. * See Flatland.stats for the real value, or capture the delta yourself: * ```ts * const before = renderer.info.render.calls * renderer.render(scene, camera) * const drawCalls = renderer.info.render.calls - before * ``` */ get stats(): RenderStats; /** * Read-only view of this group's batches keyed by run key, with the * classification query facade (`group.batches.where(IsLitBatch)`). */ get batches(): BatchQueryView; /** * Get the number of sprites. */ get spriteCount(): number; /** * Get the number of batches. */ get batchCount(): number; /** * Check if the renderer has any sprites. */ get isEmpty(): boolean; /** * Clear all sprites. */ clear(): this; /** * Get the BatchRegistry data from the world singleton. */ private _getRegistry; /** * Clone for devtools/serialization compatibility. * SpriteGroup manages an ECS world that cannot be meaningfully cloned. * Returns a Group containing cloned child meshes (the SpriteBatch instances). */ clone(recursive?: boolean): this; /** * Dispose of all resources. */ dispose(): void; } //#endregion export { SpriteGroup }; //# sourceMappingURL=SpriteGroup.d.ts.map