UNPKG

@three.ez/batched-mesh-extensions

Version:
1 lines 54.9 kB
{"version":3,"file":"webgpu.cjs","sources":["../../src/core/BatchedMeshBVH.ts","../../src/core/MultiDrawRenderList.ts","../../src/core/feature/ComputeBVH.ts","../../src/utils/SortingUtils.ts","../../src/core/feature/FrustumCulling.ts","../../src/core/feature/GetPositionAt.ts","../../src/core/feature/LOD.ts","../../src/core/feature/Raycasting.ts","../../src/patch/ExtendBatchedMeshPrototype.common.ts","../../src/patch/ExtendBatchedMeshPrototype.webgpu.ts","../../src/patch/PatchBatchedMeshMaterial.ts","../../src/utils/CountUtils.ts"],"sourcesContent":["import { box3ToArray, BVH, BVHNode, HybridBuilder, onFrustumIntersectionCallback, onIntersectionCallback, onIntersectionRayCallback, vec3ToArray, WebGLCoordinateSystem, WebGPUCoordinateSystem } from 'bvh.js';\r\nimport { BatchedMesh, Box3, CoordinateSystem, Matrix4, Raycaster } from 'three';\r\n\r\n// TODO implement getBBoxFromBSphere (add property to geometryInfo)\r\n// TODO implement frustumCullingLOD?\r\n\r\n/**\r\n * Class to manage BVH (Bounding Volume Hierarchy) for `BatchedMesh`.\r\n * Provides methods for managing bounding volumes, frustum culling, raycasting, and bounding box computation.\r\n */\r\nexport class BatchedMeshBVH {\r\n /**\r\n * The target `BatchedMesh` object that the BVH is managing.\r\n */\r\n public target: BatchedMesh;\r\n /**\r\n * The BVH instance used to organize bounding volumes.\r\n */\r\n public bvh: BVH<{}, number>;\r\n /**\r\n * A map that stores the BVH nodes for each instance.\r\n */\r\n public nodesMap = new Map<number, BVHNode<{}, number>>();\r\n /**\r\n * Enables accurate frustum culling by checking intersections without applying margin to the bounding box.\r\n */\r\n public accurateCulling: boolean;\r\n protected _margin: number;\r\n protected _origin = new Float32Array(3);\r\n protected _dir = new Float32Array(3);\r\n protected _cameraPos = new Float32Array(3);\r\n protected _boxArray = new Float32Array(6);\r\n\r\n /**\r\n * @param target The target `BatchedMesh`.\r\n * @param margin The margin applied for bounding box calculations (default is 0).\r\n * @param accurateCulling Flag to enable accurate frustum culling without considering margin (default is true).\r\n */\r\n constructor(target: BatchedMesh, coordinateSystem: CoordinateSystem, margin = 0, accurateCulling = true) {\r\n this.target = target;\r\n this.accurateCulling = accurateCulling;\r\n this._margin = margin;\r\n\r\n this.bvh = new BVH(new HybridBuilder(), coordinateSystem === 2000 ? WebGLCoordinateSystem : WebGPUCoordinateSystem); // TODO fix in BVH.js\r\n }\r\n\r\n /**\r\n * Builds the BVH from the target mesh's instances using a top-down construction method.\r\n * This approach is more efficient and accurate compared to incremental methods, which add one instance at a time.\r\n */\r\n public create(): void {\r\n const count = this.target.instanceCount;\r\n const instancesArrayCount = this.target._instanceInfo.length; // TODO this may change.. don't like it too much\r\n const instancesInfo = this.target._instanceInfo;\r\n const boxes: Float32Array[] = new Array(count); // test if single array and recreation inside node creation is faster due to memory location\r\n const objects = new Uint32Array(count);\r\n let index = 0;\r\n\r\n this.clear();\r\n\r\n for (let i = 0; i < instancesArrayCount; i++) {\r\n if (!instancesInfo[i].active) continue;\r\n boxes[index] = this.getBox(i, new Float32Array(6));\r\n objects[index] = i;\r\n index++;\r\n }\r\n\r\n this.bvh.createFromArray(objects as unknown as number[], boxes, (node) => {\r\n this.nodesMap.set(node.object, node);\r\n }, this._margin);\r\n }\r\n\r\n /**\r\n * Inserts an instance into the BVH.\r\n * @param id The id of the instance to insert.\r\n */\r\n public insert(id: number): void {\r\n const node = this.bvh.insert(id, this.getBox(id, new Float32Array(6)), this._margin);\r\n this.nodesMap.set(id, node);\r\n }\r\n\r\n /**\r\n * Inserts a range of instances into the BVH.\r\n * @param ids An array of ids to insert.\r\n */\r\n public insertRange(ids: number[]): void {\r\n const count = ids.length;\r\n const boxes: Float32Array[] = new Array(count);\r\n\r\n for (let i = 0; i < count; i++) {\r\n boxes[i] = this.getBox(ids[i], new Float32Array(6));\r\n }\r\n\r\n this.bvh.insertRange(ids, boxes, this._margin, (node) => {\r\n this.nodesMap.set(node.object, node);\r\n });\r\n }\r\n\r\n /**\r\n * Moves an instance within the BVH.\r\n * @param id The id of the instance to move.\r\n */\r\n public move(id: number): void {\r\n const node = this.nodesMap.get(id);\r\n if (!node) return;\r\n this.getBox(id, node.box as Float32Array); // this also updates box\r\n this.bvh.move(node, this._margin);\r\n }\r\n\r\n /**\r\n * Deletes an instance from the BVH.\r\n * @param id The id of the instance to delete.\r\n */\r\n public delete(id: number): void {\r\n const node = this.nodesMap.get(id);\r\n if (!node) return;\r\n this.bvh.delete(node);\r\n this.nodesMap.delete(id);\r\n }\r\n\r\n /**\r\n * Clears the BVH.\r\n */\r\n public clear(): void {\r\n this.bvh.clear();\r\n this.nodesMap.clear();\r\n }\r\n\r\n /**\r\n * Performs frustum culling to determine which instances are visible based on the provided projection matrix.\r\n * @param projScreenMatrix The projection screen matrix for frustum culling.\r\n * @param onFrustumIntersection Callback function invoked when an instance intersects the frustum.\r\n */\r\n public frustumCulling(projScreenMatrix: Matrix4, onFrustumIntersection: onFrustumIntersectionCallback<{}, number>): void {\r\n if (this._margin > 0 && this.accurateCulling) {\r\n this.bvh.frustumCulling(projScreenMatrix.elements, (node, frustum, mask) => {\r\n if (frustum.isIntersectedMargin(node.box, mask, this._margin)) {\r\n onFrustumIntersection(node);\r\n }\r\n });\r\n } else {\r\n this.bvh.frustumCulling(projScreenMatrix.elements, onFrustumIntersection);\r\n }\r\n }\r\n\r\n /**\r\n * Performs raycasting to check if a ray intersects any instances.\r\n * @param raycaster The raycaster used for raycasting.\r\n * @param onIntersection Callback function invoked when a ray intersects an instance.\r\n */\r\n public raycast(raycaster: Raycaster, onIntersection: onIntersectionRayCallback<number>): void {\r\n const ray = raycaster.ray;\r\n const origin = this._origin;\r\n const dir = this._dir;\r\n\r\n vec3ToArray(ray.origin, origin);\r\n vec3ToArray(ray.direction, dir);\r\n\r\n // TODO should we add margin check? maybe is not worth it\r\n this.bvh.rayIntersections(dir, origin, onIntersection, raycaster.near, raycaster.far);\r\n }\r\n\r\n /**\r\n * Checks if a given box intersects with any instance bounding box.\r\n * @param target The target bounding box.\r\n * @param onIntersection Callback function invoked when an intersection occurs.\r\n * @returns `True` if there is an intersection, otherwise `false`.\r\n */\r\n public intersectBox(target: Box3, onIntersection: onIntersectionCallback<number>): boolean {\r\n const array = this._boxArray;\r\n box3ToArray(target, array);\r\n return this.bvh.intersectsBox(array, onIntersection);\r\n }\r\n\r\n protected getBox(id: number, array: Float32Array): Float32Array {\r\n const target = this.target;\r\n const geometryId = target._instanceInfo[id].geometryIndex;\r\n target.getBoundingBoxAt(geometryId, _box3).applyMatrix4(target.getMatrixAt(id, _matrix));\r\n box3ToArray(_box3, array);\r\n return array;\r\n }\r\n}\r\n\r\nconst _box3 = new Box3();\r\nconst _matrix = new Matrix4();\r\n","export type MultiDrawRenderItem = { start: number; count: number; z: number; zSort?: number; index?: number };\r\n\r\n/**\r\n * A class that creates and manages a list of render items, used to determine the rendering order based on depth.\r\n * @internal\r\n */\r\nexport class MultiDrawRenderList {\r\n public array: MultiDrawRenderItem[] = [];\r\n protected pool: MultiDrawRenderItem[] = [];\r\n\r\n public push(instanceId: number, depth: number, start: number, count: number): void {\r\n const pool = this.pool;\r\n const list = this.array;\r\n const index = list.length;\r\n\r\n if (index >= pool.length) {\r\n pool.push({ start: null, count: null, z: null, zSort: null, index: null });\r\n }\r\n\r\n const item = pool[index];\r\n item.index = instanceId;\r\n item.start = start;\r\n item.count = count;\r\n item.z = depth;\r\n\r\n list.push(item);\r\n }\r\n\r\n public reset(): void {\r\n this.array.length = 0;\r\n }\r\n}\r\n","import { BatchedMesh, CoordinateSystem } from 'three';\r\nimport { BatchedMeshBVH } from '../BatchedMeshBVH.js';\r\n\r\n/**\r\n * Parameters for configuring the BVH (Bounding Volume Hierarchy).\r\n */\r\nexport interface BVHParams {\r\n /**\r\n * Margin applied to accommodate animated or moving objects.\r\n * Improves BVH update performance but slows down frustum culling and raycasting.\r\n * For static objects, set to 0 to optimize culling and raycasting efficiency.\r\n * @default 0\r\n */\r\n margin?: number;\r\n /**\r\n * Enables accurate frustum culling by checking intersections without applying margin to the bounding box.\r\n * @default true\r\n */\r\n accurateCulling?: boolean;\r\n}\r\n\r\ndeclare module 'three' {\r\n interface BatchedMesh {\r\n /**\r\n * BVH structure for optimized culling and intersection testing.\r\n * It's possible to create the BVH using the `computeBVH` method. Once created it will be updated automatically.\r\n */\r\n bvh?: BatchedMeshBVH;\r\n /**\r\n * Creates and computes the BVH (Bounding Volume Hierarchy) for the instances.\r\n * It's recommended to create it when all the instance matrices have been assigned.\r\n * Once created it will be updated automatically.\r\n * @param coordinateSystem The coordinate system (webgl or webgpu) in which the BVH is computed.\r\n * @param config Optional configuration parameters object. See `BVHParams` for details.\r\n */\r\n computeBVH(coordinateSystem: CoordinateSystem, config?: BVHParams): void;\r\n }\r\n}\r\n\r\nexport function computeBVH(this: BatchedMesh, coordinateSystem: CoordinateSystem, config: BVHParams = {}): void {\r\n this.bvh = new BatchedMeshBVH(this, coordinateSystem, config.margin, config.accurateCulling);\r\n this.bvh.create();\r\n}\r\n","import { BatchedMesh } from 'three';\r\nimport { radixSort, RadixSortOptions } from 'three/addons/utils/SortUtils.js';\r\nimport { MultiDrawRenderItem } from '../core/MultiDrawRenderList.js';\r\n\r\ntype radixSortCallback = (list: MultiDrawRenderItem[]) => void;\r\n\r\n/**\r\n * Creates a radix sort function specifically for sorting `BatchedMesh` instances.\r\n * The sorting is based on the `depth` property of each `MultiDrawRenderItem`.\r\n * This function dynamically adjusts for transparent materials by reversing the sort order if necessary.\r\n * @param target The `BatchedMesh` instance that contains the instances to be sorted.\r\n * @returns A radix sort function.\r\n * @reference https://github.com/mrdoob/three.js/blob/master/examples/webgl_mesh_batch.html#L291\r\n */\r\nexport function createRadixSort(target: BatchedMesh): radixSortCallback {\r\n const options: RadixSortOptions<MultiDrawRenderItem> = {\r\n get: (el) => el.zSort,\r\n aux: new Array(target.maxInstanceCount),\r\n reversed: null\r\n };\r\n\r\n return function sortFunction(list: MultiDrawRenderItem[]): void {\r\n options.reversed = target.material.transparent;\r\n\r\n if (target.maxInstanceCount > options.aux.length) {\r\n options.aux.length = target.maxInstanceCount;\r\n }\r\n\r\n let minZ = Infinity;\r\n let maxZ = -Infinity;\r\n\r\n for (const { z } of list) {\r\n if (z > maxZ) maxZ = z;\r\n if (z < minZ) minZ = z;\r\n }\r\n\r\n const depthDelta = maxZ - minZ;\r\n const factor = (2 ** 32 - 1) / depthDelta;\r\n\r\n for (const item of list) {\r\n item.zSort = (item.z - minZ) * factor;\r\n }\r\n\r\n radixSort(list, options);\r\n };\r\n}\r\n\r\n/** @internal */\r\nexport function sortOpaque(a: MultiDrawRenderItem, b: MultiDrawRenderItem): number {\r\n return a.z - b.z;\r\n}\r\n\r\n/** @internal */\r\nexport function sortTransparent(a: MultiDrawRenderItem, b: MultiDrawRenderItem): number {\r\n return b.z - a.z;\r\n}\r\n","import { BVHNode } from 'bvh.js';\r\nimport { BatchedMesh, BufferGeometry, Camera, Frustum, Material, Matrix4, Scene, Sphere, Vector3, WebGLRenderer } from 'three';\r\nimport { MultiDrawRenderItem, MultiDrawRenderList } from '../MultiDrawRenderList.js';\r\nimport { sortOpaque, sortTransparent } from '../../utils/SortingUtils.js';\r\n\r\n// TODO: fix LOD if no sorting and no culling\r\n\r\n/**\r\n * A custom sorting callback for render items.\r\n */\r\nexport type CustomSortCallback = (list: MultiDrawRenderItem[]) => void;\r\n\r\n/**\r\n * Callback invoked when an instance is within the frustum.\r\n * @param index The index of the instance.\r\n * @param camera The camera used for rendering.\r\n * @param cameraLOD The camera used for LOD calculations (provided only if LODs are initialized).\r\n * @param LODIndex The LOD level of the instance (provided only if LODs are initialized and `sortObjects` is false).\r\n * @returns True if the instance should be rendered, false otherwise.\r\n */\r\nexport type OnFrustumEnterCallback = (index: number, camera: Camera, cameraLOD?: Camera, LODIndex?: number) => boolean;\r\n\r\ndeclare module 'three' {\r\n interface BatchedMesh {\r\n /**\r\n * Callback function called if an instance is inside the frustum.\r\n */\r\n onFrustumEnter?: OnFrustumEnterCallback;\r\n\r\n /**\r\n * Performs frustum culling and sorting.\r\n * @param camera The main camera used for rendering.\r\n * @param cameraLOD The camera used for LOD calculations (provided only if LODs are initialized).\r\n */\r\n frustumCulling(camera: Camera, cameraLOD?: Camera): void;\r\n /**\r\n * Updates the index array for indirect rendering.\r\n */\r\n updateIndexArray(): void;\r\n /**\r\n * Updates the render list based on the current visibility and sorting settings.\r\n */\r\n updateRenderList(): void;\r\n /**\r\n * Performs BVH frustum culling.\r\n * @param camera The main camera used for rendering.\r\n * @param cameraLOD The camera used for LOD calculations (provided only if LODs are initialized).\r\n */\r\n BVHCulling(camera: Camera, cameraLOD: Camera): void;\r\n /**\r\n * Performs linear frustum culling.\r\n * @param camera The main camera used for rendering.\r\n * @param cameraLOD The camera used for LOD calculations (provided only if LODs are initialized).\r\n */\r\n linearCulling(camera: Camera, cameraLOD: Camera): void;\r\n }\r\n}\r\n\r\nconst _frustum = new Frustum();\r\nconst _renderList = new MultiDrawRenderList();\r\nconst _projScreenMatrix = new Matrix4();\r\nconst _invMatrixWorld = new Matrix4();\r\nconst _forward = new Vector3();\r\nconst _cameraPos = new Vector3();\r\nconst _cameraLODPos = new Vector3();\r\nconst _position = new Vector3();\r\nconst _sphere = new Sphere();\r\n\r\nexport function onBeforeRender(this: BatchedMesh, renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: any): void {\r\n // TODO check if nothing changed\r\n this.frustumCulling(camera);\r\n}\r\n\r\nexport function frustumCulling(this: BatchedMesh, camera: Camera, cameraLOD = camera): void {\r\n if (!this._visibilityChanged && !this.perObjectFrustumCulled && !this.sortObjects) {\r\n return;\r\n }\r\n\r\n this._indirectTexture.needsUpdate = true;\r\n this._visibilityChanged = false;\r\n\r\n const sortObjects = this.sortObjects;\r\n const perObjectFrustumCulled = this.perObjectFrustumCulled;\r\n\r\n if (!perObjectFrustumCulled && !sortObjects) {\r\n this.updateIndexArray();\r\n return;\r\n }\r\n\r\n _invMatrixWorld.copy(this.matrixWorld).invert();\r\n _cameraPos.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(_invMatrixWorld);\r\n _cameraLODPos.setFromMatrixPosition(cameraLOD.matrixWorld).applyMatrix4(_invMatrixWorld);\r\n _forward.set(0, 0, -1).transformDirection(camera.matrixWorld).transformDirection(_invMatrixWorld);\r\n\r\n if (!perObjectFrustumCulled) {\r\n this.updateRenderList();\r\n } else {\r\n _projScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse).multiply(this.matrixWorld);\r\n\r\n if (this.bvh) this.BVHCulling(camera, cameraLOD);\r\n else this.linearCulling(camera, cameraLOD);\r\n }\r\n\r\n if (sortObjects) {\r\n const index = this.geometry.getIndex();\r\n const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;\r\n const multiDrawStarts = this._multiDrawStarts;\r\n const multiDrawCounts = this._multiDrawCounts;\r\n const indirectArray = this._indirectTexture.image.data as unknown as number[];\r\n const customSort = this.customSort as unknown as CustomSortCallback;\r\n\r\n if (customSort === null) {\r\n _renderList.array.sort(!this.material.transparent ? sortOpaque : sortTransparent);\r\n } else {\r\n customSort(_renderList.array); // TODO fix and remove second useless parameter... make a PR on main repo\r\n }\r\n\r\n const list = _renderList.array;\r\n const count = list.length;\r\n for (let i = 0; i < count; i++) {\r\n const item = list[i];\r\n multiDrawStarts[i] = item.start * bytesPerElement; // TODO multiply bytesPerElement in the renderList?\r\n multiDrawCounts[i] = item.count;\r\n indirectArray[i] = item.index;\r\n }\r\n\r\n _renderList.reset();\r\n }\r\n}\r\n\r\nexport function updateIndexArray(this: BatchedMesh): void {\r\n if (!this._visibilityChanged) return;\r\n\r\n const index = this.geometry.getIndex();\r\n const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;\r\n const instanceInfo = this._instanceInfo;\r\n const geometryInfoList = this._geometryInfo;\r\n const multiDrawStarts = this._multiDrawStarts;\r\n const multiDrawCounts = this._multiDrawCounts;\r\n const indirectArray = this._indirectTexture.image.data as unknown as number[];\r\n let count = 0;\r\n\r\n for (let i = 0, l = instanceInfo.length; i < l; i++) {\r\n const instance = instanceInfo[i];\r\n if (instance.visible && instance.active) {\r\n const geometryId = instance.geometryIndex;\r\n const geometryInfo = geometryInfoList[geometryId];\r\n\r\n multiDrawStarts[count] = geometryInfo.start * bytesPerElement;\r\n multiDrawCounts[count] = geometryInfo.count;\r\n indirectArray[count] = i;\r\n count++;\r\n }\r\n }\r\n\r\n this._multiDrawCount = count;\r\n}\r\n\r\nexport function updateRenderList(this: BatchedMesh): void {\r\n const instanceInfo = this._instanceInfo;\r\n const geometryInfoList = this._geometryInfo;\r\n\r\n for (let i = 0, l = instanceInfo.length; i < l; i++) {\r\n const instance = instanceInfo[i];\r\n if (instance.visible && instance.active) {\r\n const geometryId = instance.geometryIndex;\r\n const geometryInfo = geometryInfoList[geometryId];\r\n const depth = this.getPositionAt(i).sub(_cameraPos).dot(_forward); // getPosition instead of _sphere.center\r\n _renderList.push(i, depth, geometryInfo.start, geometryInfo.count);\r\n }\r\n }\r\n\r\n this._multiDrawCount = _renderList.array.length;\r\n}\r\n\r\nexport function BVHCulling(this: BatchedMesh, camera: Camera, cameraLOD: Camera): void {\r\n const index = this.geometry.getIndex();\r\n const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;\r\n const instanceInfo = this._instanceInfo;\r\n const geometryInfoList = this._geometryInfo;\r\n const sortObjects = this.sortObjects;\r\n const multiDrawStarts = this._multiDrawStarts;\r\n const multiDrawCounts = this._multiDrawCounts;\r\n const indirectArray = this._indirectTexture.image.data as unknown as number[];\r\n const onFrustumEnter = this.onFrustumEnter;\r\n let instancesCount = 0;\r\n\r\n this.bvh.frustumCulling(_projScreenMatrix, (node: BVHNode<{}, number>) => {\r\n const index = node.object;\r\n const instance = instanceInfo[index];\r\n\r\n // we don't check if active because we remove inactive instances from BVH\r\n if (!instance.visible) return;\r\n\r\n const geometryId = instance.geometryIndex;\r\n const geometryInfo = geometryInfoList[geometryId];\r\n const LOD = geometryInfo.LOD;\r\n let start: number;\r\n let count: number;\r\n\r\n if (LOD) {\r\n const distance = this.getPositionAt(index).distanceToSquared(_cameraLODPos);\r\n const LODIndex = this.getLODIndex(LOD, distance);\r\n if (onFrustumEnter && !onFrustumEnter(index, camera, cameraLOD, LODIndex)) return;\r\n start = LOD[LODIndex].start;\r\n count = LOD[LODIndex].count;\r\n } else {\r\n if (onFrustumEnter && !onFrustumEnter(index, camera)) return;\r\n start = geometryInfo.start;\r\n count = geometryInfo.count;\r\n }\r\n\r\n // TODO don't reuse getPositionAt for sort\r\n // TODO LOD optimized if bvh and sort?\r\n\r\n if (sortObjects) {\r\n const depth = this.getPositionAt(index).sub(_cameraPos).dot(_forward);\r\n _renderList.push(index, depth, start, count);\r\n } else {\r\n multiDrawStarts[instancesCount] = start * bytesPerElement;\r\n multiDrawCounts[instancesCount] = count;\r\n indirectArray[instancesCount] = index;\r\n instancesCount++;\r\n }\r\n });\r\n\r\n this._multiDrawCount = sortObjects ? _renderList.array.length : instancesCount;\r\n}\r\n\r\nexport function linearCulling(this: BatchedMesh, camera: Camera, cameraLOD: Camera): void {\r\n const index = this.geometry.getIndex();\r\n const bytesPerElement = index === null ? 1 : index.array.BYTES_PER_ELEMENT;\r\n const instanceInfo = this._instanceInfo;\r\n const geometryInfoList = this._geometryInfo;\r\n const sortObjects = this.sortObjects;\r\n const multiDrawStarts = this._multiDrawStarts;\r\n const multiDrawCounts = this._multiDrawCounts;\r\n const indirectArray = this._indirectTexture.image.data as unknown as number[];\r\n const onFrustumEnter = this.onFrustumEnter;\r\n let instancesCount = 0;\r\n\r\n _frustum.setFromProjectionMatrix(_projScreenMatrix);\r\n\r\n for (let i = 0, l = instanceInfo.length; i < l; i++) {\r\n const instance = instanceInfo[i];\r\n if (!instance.visible || !instance.active) continue;\r\n\r\n const geometryId = instance.geometryIndex;\r\n const geometryInfo = geometryInfoList[geometryId];\r\n const LOD = geometryInfo.LOD;\r\n let start: number;\r\n let count: number;\r\n\r\n const bSphere = geometryInfo.boundingSphere;\r\n const radius = bSphere.radius;\r\n const center = bSphere.center;\r\n const geometryCentered = center.x === 0 && center.y === 0 && center.z === 0; // TODO add to geometryInfo?\r\n\r\n if (geometryCentered) {\r\n const maxScale = this.getPositionAndMaxScaleOnAxisAt(i, _sphere.center);\r\n _sphere.radius = radius * maxScale;\r\n } else {\r\n this.applyMatrixAtToSphere(i, _sphere, center, radius);\r\n }\r\n\r\n if (!_frustum.intersectsSphere(_sphere)) continue;\r\n\r\n if (LOD) {\r\n const distance = _sphere.center.distanceToSquared(_cameraLODPos);\r\n const LODIndex = this.getLODIndex(LOD, distance);\r\n if (onFrustumEnter && !onFrustumEnter(i, camera, cameraLOD, LODIndex)) continue;\r\n start = LOD[LODIndex].start;\r\n count = LOD[LODIndex].count;\r\n } else {\r\n if (onFrustumEnter && !onFrustumEnter(i, camera)) continue;\r\n start = geometryInfo.start;\r\n count = geometryInfo.count;\r\n }\r\n\r\n // TODO LOD optimized if sort?\r\n\r\n if (sortObjects) {\r\n const depth = _position.subVectors(_sphere.center, _cameraPos).dot(_forward);\r\n _renderList.push(i, depth, start, count);\r\n } else {\r\n multiDrawStarts[instancesCount] = start * bytesPerElement;\r\n multiDrawCounts[instancesCount] = count;\r\n indirectArray[instancesCount] = i;\r\n instancesCount++;\r\n }\r\n }\r\n\r\n this._multiDrawCount = sortObjects ? _renderList.array.length : instancesCount;\r\n}\r\n","import { BatchedMesh, Sphere, Vector3 } from 'three';\r\n\r\ndeclare module 'three' {\r\n interface BatchedMesh {\r\n /**\r\n * Retrieves the position of a specific instance.\r\n * @param index The index of the instance.\r\n * @param target Optional `Vector3` to store the result.\r\n * @returns The position of the instance as a `Vector3`.\r\n */\r\n getPositionAt(index: number, target?: Vector3): Vector3;\r\n /**\r\n * Retrieves the position and maximum scale on any axis of a specific instance.\r\n * @param index The index of the instance.\r\n * @param position Optional `Vector3` to store the position result.\r\n * @returns The maximum scale on any axis.\r\n */\r\n getPositionAndMaxScaleOnAxisAt(index: number, position: Vector3): number;\r\n /**\r\n * Applies the transformation matrix of a specific instance to a sphere.\r\n * @param index The index of the instance.\r\n * @param sphere The sphere to transform.\r\n * @param center TODO\r\n * @param radius TODO\r\n */\r\n applyMatrixAtToSphere(index: number, sphere: Sphere, center: Vector3, radius: number): void;\r\n }\r\n}\r\n\r\nconst _position = new Vector3();\r\n\r\nexport function getPositionAt(this: BatchedMesh, index: number, target = _position): Vector3 {\r\n const offset = index * 16;\r\n const array = this._matricesTexture.image.data as unknown as number[];\r\n\r\n target.x = array[offset + 12];\r\n target.y = array[offset + 13];\r\n target.z = array[offset + 14];\r\n\r\n return target;\r\n}\r\n\r\nexport function getPositionAndMaxScaleOnAxisAt(this: BatchedMesh, index: number, position: Vector3): number {\r\n const offset = index * 16;\r\n const array = this._matricesTexture.image.data as unknown as number[];\r\n\r\n const te0 = array[offset + 0];\r\n const te1 = array[offset + 1];\r\n const te2 = array[offset + 2];\r\n const scaleXSq = te0 * te0 + te1 * te1 + te2 * te2;\r\n\r\n const te4 = array[offset + 4];\r\n const te5 = array[offset + 5];\r\n const te6 = array[offset + 6];\r\n const scaleYSq = te4 * te4 + te5 * te5 + te6 * te6;\r\n\r\n const te8 = array[offset + 8];\r\n const te9 = array[offset + 9];\r\n const te10 = array[offset + 10];\r\n const scaleZSq = te8 * te8 + te9 * te9 + te10 * te10;\r\n\r\n position.x = array[offset + 12];\r\n position.y = array[offset + 13];\r\n position.z = array[offset + 14];\r\n\r\n return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq));\r\n}\r\n\r\nexport function applyMatrixAtToSphere(this: BatchedMesh, index: number, sphere: Sphere, center: Vector3, radius: number): void {\r\n const offset = index * 16;\r\n const array = this._matricesTexture.image.data as unknown as number[];\r\n\r\n const te0 = array[offset + 0];\r\n const te1 = array[offset + 1];\r\n const te2 = array[offset + 2];\r\n const te3 = array[offset + 3];\r\n const te4 = array[offset + 4];\r\n const te5 = array[offset + 5];\r\n const te6 = array[offset + 6];\r\n const te7 = array[offset + 7];\r\n const te8 = array[offset + 8];\r\n const te9 = array[offset + 9];\r\n const te10 = array[offset + 10];\r\n const te11 = array[offset + 11];\r\n const te12 = array[offset + 12];\r\n const te13 = array[offset + 13];\r\n const te14 = array[offset + 14];\r\n const te15 = array[offset + 15];\r\n\r\n const position = sphere.center;\r\n const x = center.x;\r\n const y = center.y;\r\n const z = center.z;\r\n const w = 1 / (te3 * x + te7 * y + te11 * z + te15);\r\n\r\n position.x = (te0 * x + te4 * y + te8 * z + te12) * w;\r\n position.y = (te1 * x + te5 * y + te9 * z + te13) * w;\r\n position.z = (te2 * x + te6 * y + te10 * z + te14) * w;\r\n\r\n const scaleXSq = te0 * te0 + te1 * te1 + te2 * te2;\r\n const scaleYSq = te4 * te4 + te5 * te5 + te6 * te6;\r\n const scaleZSq = te8 * te8 + te9 * te9 + te10 * te10;\r\n\r\n sphere.radius = radius * Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq));\r\n}\r\n","import { BatchedMesh, BufferGeometry, TypedArray } from 'three';\r\n\r\n// TODO: add optional distance and first load function like InstancedMesh2\r\n\r\nexport type LODInfo = { start: number; count: number; distance: number; hysteresis: number };\r\n\r\ndeclare module 'three' {\r\n interface BatchedMesh {\r\n /**\r\n * Adds a Level of Detail (LOD) geometry to the BatchedMesh.\r\n * @param geometryId The ID of the geometry to which the LOD is being added.\r\n * @param geometryOrIndex The BufferGeometry to be added as LOD or the index array.\r\n * @param distance The distance at which this LOD should be used.\r\n * @param hysteresis Optional hysteresis value for LOD transition.\r\n */\r\n addGeometryLOD(geometryId: number, geometryOrIndex: BufferGeometry | TypedArray, distance: number, hysteresis?: number): void;\r\n /**\r\n * Retrieves the LOD index for a given distance.\r\n * @param LOD The array of LOD information.\r\n * @param distance The distance to check against the LODs.\r\n * @returns The index of the appropriate LOD\r\n */\r\n getLODIndex(LOD: LODInfo[], distance: number): number;\r\n }\r\n}\r\n\r\nexport function addGeometryLOD(this: BatchedMesh, geometryId: number, geoOrIndex: BufferGeometry | TypedArray, distance: number, hysteresis = 0): void {\r\n const geometryInfo = this._geometryInfo[geometryId];\r\n const srcIndexArray = (geoOrIndex as BufferGeometry).isBufferGeometry ? (geoOrIndex as BufferGeometry).index.array : geoOrIndex as TypedArray;\r\n distance = distance ** 2;\r\n\r\n geometryInfo.LOD ??= [{ start: geometryInfo.start, count: geometryInfo.count, distance: 0, hysteresis: 0 }];\r\n\r\n const LOD = geometryInfo.LOD;\r\n const lastLOD = LOD[LOD.length - 1];\r\n const start = lastLOD.start + lastLOD.count;\r\n const count = srcIndexArray.length;\r\n\r\n if ((start - geometryInfo.start) + count > geometryInfo.reservedIndexCount) {\r\n throw new Error('BatchedMesh LOD: Reserved space request exceeds the maximum buffer size.');\r\n }\r\n\r\n LOD.push({ start, count, distance, hysteresis });\r\n\r\n const dstIndex = this.geometry.getIndex();\r\n const dstIndexArray = dstIndex.array;\r\n const vertexStart = geometryInfo.vertexStart;\r\n\r\n for (let i = 0; i < count; i++) {\r\n dstIndexArray[start + i] = srcIndexArray[i] + vertexStart;\r\n }\r\n\r\n dstIndex.needsUpdate = true;\r\n}\r\n\r\nexport function getLODIndex(LODs: LODInfo[], distance: number): number {\r\n for (let i = LODs.length - 1; i > 0; i--) {\r\n const level = LODs[i];\r\n const levelDistance = level.distance - (level.distance * level.hysteresis);\r\n if (distance >= levelDistance) return i;\r\n }\r\n\r\n return 0;\r\n}\r\n","import { BatchedMesh, Box3, Intersection, Matrix4, Mesh, Ray, Raycaster, Sphere, Vector3 } from 'three';\r\nimport { } from 'three-mesh-bvh'; // include only types\r\n\r\ndeclare module 'three' {\r\n interface BatchedMesh {\r\n checkInstanceIntersection(raycaster: Raycaster, objectIndex: number, result: Intersection[]): void;\r\n }\r\n}\r\n\r\nconst _intersections: Intersection[] = [];\r\nconst _mesh = new Mesh();\r\nconst _ray = new Ray();\r\nconst _direction = new Vector3();\r\nconst _worldScale = new Vector3();\r\nconst _invMatrixWorld = new Matrix4();\r\n\r\nexport function raycast(this: BatchedMesh, raycaster: Raycaster, result: Intersection[]): void {\r\n if (!this.material || this.instanceCount === 0) return;\r\n\r\n _mesh.geometry = this.geometry;\r\n _mesh.material = this.material;\r\n\r\n _mesh.geometry.boundingBox ??= new Box3();\r\n _mesh.geometry.boundingSphere ??= new Sphere();\r\n\r\n const originalRay = raycaster.ray;\r\n const originalNear = raycaster.near;\r\n const originalFar = raycaster.far;\r\n\r\n _invMatrixWorld.copy(this.matrixWorld).invert();\r\n\r\n _worldScale.setFromMatrixScale(this.matrixWorld);\r\n _direction.copy(raycaster.ray.direction).multiply(_worldScale);\r\n const scaleFactor = _direction.length();\r\n\r\n raycaster.ray = _ray.copy(raycaster.ray).applyMatrix4(_invMatrixWorld);\r\n raycaster.near /= scaleFactor;\r\n raycaster.far /= scaleFactor;\r\n\r\n if (this.bvh) {\r\n this.bvh.raycast(raycaster, (instanceId) => this.checkInstanceIntersection(raycaster, instanceId, result));\r\n } else {\r\n if (this.boundingSphere === null) this.computeBoundingSphere();\r\n\r\n if (raycaster.ray.intersectsSphere(this.boundingSphere)) {\r\n for (let i = 0, l = this._instanceInfo.length; i < l; i++) {\r\n this.checkInstanceIntersection(raycaster, i, result);\r\n }\r\n }\r\n }\r\n\r\n raycaster.ray = originalRay;\r\n raycaster.near = originalNear;\r\n raycaster.far = originalFar;\r\n}\r\n\r\nexport function checkInstanceIntersection(this: BatchedMesh, raycaster: Raycaster, instanceId: number, result: Intersection[]): void {\r\n const info = this._instanceInfo[instanceId];\r\n if (!info.active || !info.visible) return;\r\n\r\n const geometryId = info.geometryIndex;\r\n const geometryInfo = this._geometryInfo[geometryId];\r\n\r\n this.getMatrixAt(instanceId, _mesh.matrixWorld);\r\n\r\n _mesh.geometry.boundsTree = this.boundsTrees ? this.boundsTrees[geometryId] : undefined; // three-mesh-bvh compatibility\r\n\r\n if (!_mesh.geometry.boundsTree) {\r\n this.getBoundingBoxAt(geometryId, _mesh.geometry.boundingBox);\r\n this.getBoundingSphereAt(geometryId, _mesh.geometry.boundingSphere);\r\n _mesh.geometry.setDrawRange(geometryInfo.start, geometryInfo.count);\r\n }\r\n\r\n _mesh.raycast(raycaster, _intersections);\r\n\r\n for (const intersect of _intersections) {\r\n intersect.batchId = instanceId;\r\n intersect.object = this;\r\n result.push(intersect);\r\n }\r\n\r\n _intersections.length = 0;\r\n}\r\n","import { BatchedMesh } from 'three';\r\nimport { computeBVH } from '../core/feature/ComputeBVH.js';\r\nimport { BVHCulling, frustumCulling, linearCulling, onBeforeRender, updateIndexArray, updateRenderList } from '../core/feature/FrustumCulling.js';\r\nimport { applyMatrixAtToSphere, getPositionAndMaxScaleOnAxisAt, getPositionAt } from '../core/feature/GetPositionAt.js';\r\nimport { addGeometryLOD, getLODIndex } from '../core/feature/LOD.js';\r\nimport { checkInstanceIntersection, raycast } from '../core/feature/Raycasting.js';\r\n\r\n/**\r\n * @internal\r\n * Enhances the BatchedMesh prototype with additional methods.\r\n */\r\nexport function extendBatchedMeshPrototypeCommon(): void {\r\n BatchedMesh.prototype.computeBVH = computeBVH;\r\n\r\n BatchedMesh.prototype.onBeforeRender = onBeforeRender;\r\n BatchedMesh.prototype.frustumCulling = frustumCulling;\r\n BatchedMesh.prototype.updateIndexArray = updateIndexArray;\r\n BatchedMesh.prototype.updateRenderList = updateRenderList;\r\n BatchedMesh.prototype.BVHCulling = BVHCulling;\r\n BatchedMesh.prototype.linearCulling = linearCulling;\r\n\r\n BatchedMesh.prototype.getPositionAt = getPositionAt;\r\n BatchedMesh.prototype.getPositionAndMaxScaleOnAxisAt = getPositionAndMaxScaleOnAxisAt;\r\n BatchedMesh.prototype.applyMatrixAtToSphere = applyMatrixAtToSphere;\r\n\r\n BatchedMesh.prototype.addGeometryLOD = addGeometryLOD;\r\n BatchedMesh.prototype.getLODIndex = getLODIndex;\r\n\r\n BatchedMesh.prototype.raycast = raycast;\r\n BatchedMesh.prototype.checkInstanceIntersection = checkInstanceIntersection;\r\n}\r\n","import { extendBatchedMeshPrototypeCommon } from './ExtendBatchedMeshPrototype.common.js';\r\n\r\n/**\r\n * Enhances the BatchedMesh prototype with additional methods.\r\n */\r\nexport function extendBatchedMeshPrototype(): void {\r\n extendBatchedMeshPrototypeCommon();\r\n}\r\n","import { BatchedMesh, WebGLProgramParametersWithUniforms, WebGLRenderer } from 'three';\r\n\r\nexport function patchBatchedMeshMaterial(batchedMesh: BatchedMesh): void {\r\n const material = batchedMesh.material;\r\n const onBeforeCompileBase = material.onBeforeCompile.bind(material);\r\n\r\n // TODO edit also the material compile cache key?\r\n\r\n material.onBeforeCompile = (shader: WebGLProgramParametersWithUniforms, renderer: WebGLRenderer) => {\r\n if (batchedMesh.uniformsTexture) {\r\n shader.uniforms.uniformsTexture = { value: batchedMesh.uniformsTexture };\r\n const { vertex, fragment } = batchedMesh.uniformsTexture.getUniformsGLSL('uniformsTexture', 'batchIndex', 'float');\r\n shader.vertexShader = shader.vertexShader.replace('void main() {', vertex);\r\n shader.fragmentShader = shader.fragmentShader.replace('void main() {', fragment);\r\n\r\n shader.vertexShader = shader.vertexShader.replace('void main() {', 'void main() { float batchIndex = getIndirectIndex( gl_DrawID );');\r\n }\r\n\r\n onBeforeCompileBase(shader, renderer);\r\n };\r\n}\r\n","import { BufferGeometry } from 'three';\r\n\r\nexport type VertexIndexCount = { vertexCount: number; indexCount: number };\r\nexport type VertexIndexLODCount = { vertexCount: number; indexCount: number; LODIndexCount: number[] };\r\n\r\nexport function getBatchedMeshCount(geometries: BufferGeometry[]): VertexIndexCount {\r\n let vertexCount = 0;\r\n let indexCount = 0;\r\n\r\n for (const geometry of geometries) {\r\n vertexCount += geometry.attributes.position.count;\r\n indexCount += geometry.index.count;\r\n }\r\n\r\n return { vertexCount, indexCount };\r\n}\r\n\r\nexport function getBatchedMeshLODCount(geometryLOD: BufferGeometry[][]): VertexIndexLODCount {\r\n const LODIndexCount: number[] = [];\r\n let vertexCount = 0;\r\n let indexCount = 0;\r\n\r\n for (const geometries of geometryLOD) {\r\n let sum = 0;\r\n\r\n for (const geometry of geometries) {\r\n const count = geometry.index.count;\r\n indexCount += count;\r\n sum += count;\r\n vertexCount += geometry.attributes.position.count;\r\n }\r\n\r\n LODIndexCount.push(sum);\r\n }\r\n\r\n return { vertexCount, indexCount, LODIndexCount };\r\n}\r\n"],"names":["BatchedMeshBVH","target","coordinateSystem","margin","accurateCulling","BVH","HybridBuilder","WebGLCoordinateSystem","WebGPUCoordinateSystem","count","instancesArrayCount","instancesInfo","boxes","objects","index","i","node","id","ids","projScreenMatrix","onFrustumIntersection","frustum","mask","raycaster","onIntersection","ray","origin","dir","vec3ToArray","array","box3ToArray","geometryId","_box3","_matrix","Box3","Matrix4","MultiDrawRenderList","instanceId","depth","start","pool","list","item","computeBVH","config","createRadixSort","options","el","minZ","maxZ","z","depthDelta","factor","radixSort","sortOpaque","a","b","sortTransparent","_frustum","Frustum","_renderList","_projScreenMatrix","_invMatrixWorld","_forward","Vector3","_cameraPos","_cameraLODPos","_position","_sphere","Sphere","onBeforeRender","renderer","scene","camera","geometry","material","group","frustumCulling","cameraLOD","sortObjects","perObjectFrustumCulled","bytesPerElement","multiDrawStarts","multiDrawCounts","indirectArray","customSort","updateIndexArray","instanceInfo","geometryInfoList","l","instance","geometryInfo","updateRenderList","BVHCulling","onFrustumEnter","instancesCount","LOD","distance","LODIndex","linearCulling","bSphere","radius","center","maxScale","getPositionAt","offset","getPositionAndMaxScaleOnAxisAt","position","te0","te1","te2","scaleXSq","te4","te5","te6","scaleYSq","te8","te9","te10","scaleZSq","applyMatrixAtToSphere","sphere","te3","te7","te11","te12","te13","te14","te15","x","y","w","addGeometryLOD","geoOrIndex","hysteresis","srcIndexArray","lastLOD","dstIndex","dstIndexArray","vertexStart","getLODIndex","LODs","level","levelDistance","_intersections","_mesh","Mesh","_ray","Ray","_direction","_worldScale","raycast","result","_a","_b","originalRay","originalNear","originalFar","scaleFactor","checkInstanceIntersection","info","intersect","extendBatchedMeshPrototypeCommon","BatchedMesh","extendBatchedMeshPrototype","patchBatchedMeshMaterial","batchedMesh","onBeforeCompileBase","shader","vertex","fragment","getBatchedMeshCount","geometries","vertexCount","indexCount","getBatchedMeshLODCount","geometryLOD","LODIndexCount","sum"],"mappings":"2KAUO,MAAMA,CAAe,CA4B1B,YAAYC,EAAqBC,EAAoCC,EAAS,EAAGC,EAAkB,GAAM,CAhBlG,KAAA,aAAe,IAMZ,KAAA,QAAU,IAAI,aAAa,CAAC,EAC5B,KAAA,KAAO,IAAI,aAAa,CAAC,EACzB,KAAA,WAAa,IAAI,aAAa,CAAC,EAC/B,KAAA,UAAY,IAAI,aAAa,CAAC,EAQtC,KAAK,OAASH,EACd,KAAK,gBAAkBG,EACvB,KAAK,QAAUD,EAEV,KAAA,IAAM,IAAIE,EAAAA,IAAI,IAAIC,EAAAA,cAAiBJ,IAAqB,IAAOK,EAAA,sBAAwBC,wBAAsB,CAAA,CAO7G,QAAe,CACd,MAAAC,EAAQ,KAAK,OAAO,cACpBC,EAAsB,KAAK,OAAO,cAAc,OAChDC,EAAgB,KAAK,OAAO,cAC5BC,EAAwB,IAAI,MAAMH,CAAK,EACvCI,EAAU,IAAI,YAAYJ,CAAK,EACrC,IAAIK,EAAQ,EAEZ,KAAK,MAAM,EAEX,QAASC,EAAI,EAAGA,EAAIL,EAAqBK,IAClCJ,EAAcI,CAAC,EAAE,SAChBH,EAAAE,CAAK,EAAI,KAAK,OAAOC,EAAG,IAAI,aAAa,CAAC,CAAC,EACjDF,EAAQC,CAAK,EAAIC,EACjBD,KAGF,KAAK,IAAI,gBAAgBD,EAAgCD,EAAQI,GAAS,CACxE,KAAK,SAAS,IAAIA,EAAK,OAAQA,CAAI,CAAA,EAClC,KAAK,OAAO,CAAA,CAOV,OAAOC,EAAkB,CAC9B,MAAMD,EAAO,KAAK,IAAI,OAAOC,EAAI,KAAK,OAAOA,EAAI,IAAI,aAAa,CAAC,CAAC,EAAG,KAAK,OAAO,EAC9E,KAAA,SAAS,IAAIA,EAAID,CAAI,CAAA,CAOrB,YAAYE,EAAqB,CACtC,MAAMT,EAAQS,EAAI,OACZN,EAAwB,IAAI,MAAMH,CAAK,EAE7C,QAASM,EAAI,EAAGA,EAAIN,EAAOM,IACnBH,EAAAG,CAAC,EAAI,KAAK,OAAOG,EAAIH,CAAC,EAAG,IAAI,aAAa,CAAC,CAAC,EAGpD,KAAK,IAAI,YAAYG,EAAKN,EAAO,KAAK,QAAUI,GAAS,CACvD,KAAK,SAAS,IAAIA,EAAK,OAAQA,CAAI,CAAA,CACpC,CAAA,CAOI,KAAKC,EAAkB,CAC5B,MAAMD,EAAO,KAAK,SAAS,IAAIC,CAAE,EAC5BD,IACA,KAAA,OAAOC,EAAID,EAAK,GAAmB,EACxC,KAAK,IAAI,KAAKA,EAAM,KAAK,OAAO,EAAA,CAO3B,OAAOC,EAAkB,CAC9B,MAAMD,EAAO,KAAK,SAAS,IAAIC,CAAE,EAC5BD,IACA,KAAA,IAAI,OAAOA,CAAI,EACf,KAAA,SAAS,OAAOC,CAAE,EAAA,CAMlB,OAAc,CACnB,KAAK,IAAI,MAAM,EACf,KAAK,SAAS,MAAM,CAAA,CAQf,eAAeE,EAA2BC,EAAwE,CACnH,KAAK,QAAU,GAAK,KAAK,gBAC3B,KAAK,IAAI,eAAeD,EAAiB,SAAU,CAACH,EAAMK,EAASC,IAAS,CACtED,EAAQ,oBAAoBL,EAAK,IAAKM,EAAM,KAAK,OAAO,GAC1DF,EAAsBJ,CAAI,CAC5B,CACD,EAED,KAAK,IAAI,eAAeG,EAAiB,SAAUC,CAAqB,CAC1E,CAQK,QAAQG,EAAsBC,EAAyD,CAC5F,MAAMC,EAAMF,EAAU,IAChBG,EAAS,KAAK,QACdC,EAAM,KAAK,KAELC,cAAAH,EAAI,OAAQC,CAAM,EAClBE,cAAAH,EAAI,UAAWE,CAAG,EAGzB,KAAA,IAAI,iBAAiBA,EAAKD,EAAQF,EAAgBD,EAAU,KAAMA,EAAU,GAAG,CAAA,CAS/E,aAAatB,EAAcuB,EAAyD,CACzF,MAAMK,EAAQ,KAAK,UACnBC,OAAAA,EAAA,YAAY7B,EAAQ4B,CAAK,EAClB,KAAK,IAAI,cAAcA,EAAOL,CAAc,CAAA,CAG3C,OAAOP,EAAYY,EAAmC,CAC9D,MAAM5B,EAAS,KAAK,OACd8B,EAAa9B,EAAO,cAAcgB,CAAE,EAAE,cACrC,OAAAhB,EAAA,iBAAiB8B,EAAYC,CAAK,EAAE,aAAa/B,EAAO,YAAYgB,EAAIgB,EAAO,CAAC,EACvFH,EAAA,YAAYE,EAAOH,CAAK,EACjBA,CAAA,CAEX,CAEA,MAAMG,EAAQ,IAAIE,EAAAA,KACZD,GAAU,IAAIE,EAAAA,QClLb,MAAMC,CAAoB,CAA1B,aAAA,CACL,KAAO,MAA+B,CAAC,EACvC,KAAU,KAA8B,CAAC,CAAA,CAElC,KAAKC,EAAoBC,EAAeC,EAAe9B,EAAqB,CACjF,MAAM+B,EAAO,KAAK,KACZC,EAAO,KAAK,MACZ3B,EAAQ2B,EAAK,OAEf3B,GAAS0B,EAAK,QAChBA,EAAK,KAAK,CAAE,MAAO,KAAM,MAAO,KAAM,EAAG,KAAM,MAAO,KAAM,MAAO,IAAA,CAAM,EAGrE,MAAAE,EAAOF,EAAK1B,CAAK,EACvB4B,EAAK,MAAQL,EACbK,EAAK,MAAQH,EACbG,EAAK,MAAQjC,EACbiC,EAAK,EAAIJ,EAETG,EAAK,KAAKC,CAAI,CAAA,CAGT,OAAc,CACnB,KAAK,MAAM,OAAS,CAAA,CAExB,CCQO,SAASC,EAA8BzC,EAAoC0C,EAAoB,GAAU,CACzG,KAAA,IAAM,IAAI5C,EAAe,KAAME,EAAkB0C,EAAO,OAAQA,EAAO,eAAe,EAC3F,KAAK,IAAI,OAAO,CAClB,CC5BO,SAASC,GAAgB5C,EAAwC,CACtE,MAAM6C,EAAiD,CACrD,IAAMC,GAAOA,EAAG,MAChB,IAAK,IAAI,MAAM9C,EAAO,gBAAgB,EACtC,SAAU,IACZ,EAEO,OAAA,SAAsBwC,EAAmC,CACtDK,EAAA,SAAW7C,EAAO,SAAS,YAE/BA,EAAO,iBAAmB6C,EAAQ,IAAI,SAChCA,EAAA,IAAI,OAAS7C,EAAO,kBAG9B,IAAI+C,EAAO,IACPC,EAAO,KAEA,SAAA,CAAE,EAAAC,CAAE,IAAKT,EACdS,EAAID,IAAaA,EAAAC,GACjBA,EAAIF,IAAaA,EAAAE,GAGvB,MAAMC,EAAaF,EAAOD,EACpBI,GAAU,GAAK,GAAK,GAAKD,EAE/B,UAAWT,KAAQD,EACZC,EAAA,OAASA,EAAK,EAAIM,GAAQI,EAGjCC,GAAA,UAAUZ,EAAMK,CAAO,CACzB,CACF,CAGgB,SAAAQ,EAAWC,EAAwBC,EAAgC,CAC1E,OAAAD,EAAE,EAAIC,EAAE,CACjB,CAGgB,SAAAC,EAAgBF,EAAwBC,EAAgC,CAC/E,OAAAA,EAAE,EAAID,EAAE,CACjB,CCGA,MAAMG,EAAW,IAAIC,EAAAA,QACfC,EAAc,IAAIxB,EAClByB,EAAoB,IAAI1B,EAAAA,QACxB2B,EAAkB,IAAI3B,EAAAA,QACtB4B,EAAW,IAAIC,EAAAA,QACfC,EAAa,IAAID,EAAAA,QACjBE,EAAgB,IAAIF,EAAAA,QACpBG,GAAY,IAAIH,EAAAA,QAChBI,EAAU,IAAIC,EAAAA,OAEb,SAASC,EAAkCC,EAAyBC,EAAcC,EAAgBC,EAA0BC,EAAoBC,EAAkB,CAEvK,KAAK,eAAeH,CAAM,CAC5B,CAEgB,SAAAI,EAAkCJ,EAAgBK,EAAYL,EAAc,CACtF,GAAA,CAAC,KAAK,oBAAsB,CAAC,KAAK,wBAA0B,CAAC,KAAK,YACpE,OAGF,KAAK,iBAAiB,YAAc,GACpC,KAAK,mBAAqB,GAE1B,MAAMM,EAAc,KAAK,YACnBC,EAAyB,KAAK,uBAEhC,GAAA,CAACA,GAA0B,CAACD,EAAa,CAC3C,KAAK,iBAAiB,EACtB,MAAA,CAiBF,GAdAjB,EAAgB,KAAK,KAAK,WAAW,EAAE,OAAO,EAC9CG,EAAW,sBAAsBQ,EAAO,WAAW,EAAE,aAAaX,CAAe,EACjFI,EAAc,sBAAsBY,EAAU,WAAW,EAAE,aAAahB,CAAe,EAC9EC,EAAA,IAAI,EAAG,EAAG,EAAE,EAAE,mBAAmBU,EAAO,WAAW,EAAE,mBAAmBX,CAAe,EAE3FkB,GAGenB,EAAA,iBAAiBY,EAAO,iBAAkBA,EAAO,kBAAkB,EAAE,SAAS,KAAK,WAAW,EAE5G,KAAK,IAAU,KAAA,WAAWA,EAAQK,CAAS,EAC1C,KAAK,cAAcL,EAAQK,CAAS,GALzC,KAAK,iBAAiB,EAQpBC,EAAa,CACT,MAAAjE,EAAQ,KAAK,SAAS,SAAS,EAC/BmE,EAAkBnE,IAAU,KAAO,EAAIA,EAAM,MAAM,kBACnDoE,EAAkB,KAAK,iBACvBC,EAAkB,KAAK,iBACvBC,EAAgB,KAAK,iBAAiB,MAAM,KAC5CC,EAAa,KAAK,WAEpBA,IAAe,KACjBzB,EAAY,MAAM,KAAM,KAAK,SAAS,YAA2BH,EAAbH,CAA4B,EAEhF+B,EAAWzB,EAAY,KAAK,EAG9B,MAAMnB,EAAOmB,EAAY,MACnBnD,EAAQgC,EAAK,OACnB,QAAS1B,EAAI,EAAGA,EAAIN,EAAOM,IAAK,CACxB,MAAA2B,EAAOD,EAAK1B,CAAC,EACHmE,EAAAnE,CAAC,EAAI2B,EAAK,MAAQuC,EAClBE,EAAApE,CAAC,EAAI2B,EAAK,MACZ0C,EAAArE,CAAC,EAAI2B,EAAK,KAAA,CAG1BkB,EAAY,MAAM,CAAA,CAEtB,CAEO,SAAS0B,GAA0C,CACpD,GAAA,CAAC,KAAK,mBAAoB,OAExB,MAAAxE,EAAQ,KAAK,SAAS,SAAS,EAC/BmE,EAAkBnE,IAAU,KAAO,EAAIA,EAAM,MAAM,kBACnDyE,EAAe,KAAK,cACpBC,EAAmB,KAAK,cACxBN,EAAkB,KAAK,iBACvBC,EAAkB,KAAK,iBACvBC,EAAgB,KAAK,iBAAiB,MAAM,KAClD,IAAI3E,EAAQ,EAEZ,QAASM,EAAI,EAAG0E,EAAIF,EAAa,OAAQxE,EAAI0E,EAAG1E,IAAK,CAC7C,MAAA2E,EAAWH,EAAaxE,CAAC,EAC3B,GAAA2E,EAAS,SAAWA,EAAS,OAAQ,CACvC,MAAM3D,EAAa2D,EAAS,cACtBC,EAAeH,EAAiBzD,CAAU,EAEhCmD,EAAAzE,CAAK,EAAIkF,EAAa,MAAQV,EAC9BE,EAAA1E,CAAK,EAAIkF,EAAa,MACtCP,EAAc3E,CAAK,EAAIM,EACvBN,GAAA,CACF,CAGF,KAAK,gBAAkBA,CACzB,CAEO,SAASmF,GAA0C,CACxD,MAAML,EAAe,KAAK,cACpBC,EAAmB,KAAK,cAE9B,QAASzE,EAAI,EAAG0E,EAAIF,EAAa,OAAQxE,EAAI0E,EAAG1E,IAAK,CAC7C,MAAA2E,EAAWH,EAAaxE,CAAC,EAC3B,GAAA2E,EAAS,SAAWA,EAAS,OAAQ,CACvC,MAAM3D,EAAa2D,EAAS,cACtBC,EAAeH,EAAiBzD,CAAU,EAC1CO,EAAQ,KAAK,cAAcvB,CAAC,EAAE,IAAIkD,CAAU,EAAE,IAAIF,CAAQ,EAChEH,EAAY,KAAK7C,EAAGuB,EAAOqD,EAAa,MAAOA,EAAa,KAAK,CAAA,CACnE,CAGG,KAAA,gBAAkB/B,EAAY,MAAM,MAC3C,CAEgB,SAAAiC,EAA8BpB,EAAgBK,EAAyB,CAC/E,MAAAhE,EAAQ,KAAK,SAAS,SAAS,EAC/BmE,EAAkBnE,IAAU,KAAO,EAAIA,EAAM,MAAM,kBACnDyE,EAAe,KAAK,cACpBC,EAAmB,KAAK,cACxBT,EAAc,KAAK,YACnBG,EAAkB,KAAK,iBACvBC,EAAkB,KAAK,iBACvBC,EAAgB,KAAK,iBAAiB,MAAM,KAC5CU,EAAiB,KAAK,eAC5B,IAAIC,EAAiB,EAErB,KAAK,IAAI,eAAelC,EAAoB7C,GAA8B,CACxE,MAAMF,EAAQE,EAAK,OACb0E,EAAWH,EAAazE,CAAK,EAG/B,GAAA,CAAC4E,EAAS,QAAS,OAEvB,MAAM3D,EAAa2D,EAAS,cACtBC,EAAeH,EAAiBzD,CAAU,EAC1CiE,EAAML,EAAa,IACrB,IAAApD,EACA9B,EAEJ,GAAIuF,EAAK,CACP,MAAMC,EAAW,KAAK,cAAcnF,CAAK,EAAE,kBAAkBoD,CAAa,EACpEgC,EAAW,KAAK,YAAYF,EAAKC,CAAQ,EAC/C,GAAIH,GAAkB,CAACA,EAAehF,EAAO2D,EAAQK,EAAWoB,CAAQ,EAAG,OACnE3D,EAAAyD,EAAIE,CAAQ,EAAE,MACdzF,EAAAuF,EAAIE,CAAQ,EAAE,KAAA,KACjB,CACL,GAAIJ,GAAkB,CAACA,EAAehF,EAAO2D,CAAM,EAAG,OACtDlC,EAAQoD,EAAa,MACrBlF,EAAQkF,EAAa,KAAA,CAMvB,GAAIZ,EAAa,CACT,MAAAzC,EAAQ,KAAK,cAAcxB,CAAK,EAAE,IAAImD,CAAU,EAAE,IAAIF,CAAQ,EACpEH,EAAY,KAAK9C,EAAOwB,EAAOC,EAAO9B,CAAK,CAAA,MAE3ByE,EAAAa,CAAc,EAAIxD,EAAQ0C,EAC1CE,EAAgBY,CAAc,EAAItF,EAClC2E,EAAcW,CAAc,EAAIjF,EAChCiF,GACF,CACD,EAED,KAAK,gBAAkBhB,EAAcnB,EAAY,MAAM,OAASmC,CAClE,CAEgB,SAAAI,EAAiC1B,EAAgBK,EAAyB,CAClF,MAAAhE,EAAQ,KAAK,SAAS,SAAS,EAC/BmE,EAAkBnE,IAAU,KAAO,EAAIA,EAAM,MAAM,kBACnDyE,EAAe,KAAK,cACpBC,EAAmB,KAAK,cACxBT,EAAc,KAAK,YACnBG,EAAkB,KAAK,iBACvBC,EAAkB,KAAK,iBACvBC,EAAgB,KAAK,iBAAiB,MAAM,KAC5CU,EAAiB,KAAK,eAC5B,IAAIC,EAAiB,EAErBrC,EAAS,wBAAwBG,CAAiB,EAElD,QAAS9C,EAAI,EAAG0E,EAAIF,EAAa,OAAQxE,EAAI0E,EAAG1E,IAAK,CAC7C,MAAA2E,EAAWH,EAAaxE,CAAC,EAC/B,GAAI,CAAC2E,EAAS,SAAW,CAACA,EAAS,OAAQ,SAE3C,MAAM3D,EAAa2D,EAAS,cACtBC,EAAeH,EAAiBzD,CAAU,EAC1CiE,EAAML,EAAa,IACrB,IAAApD,EACA9B,EAEJ,MAAM2F,EAAUT,EAAa,eACvBU,EAASD,EAAQ,OACjBE,EAASF,EAAQ,OAGvB,GAFyBE,EAAO,IAAM,GAAKA,EAAO,IAAM,GAAKA,EAAO,IAAM,EAEpD,CACpB,MAAMC,EAAW,KAAK,+BAA+BxF,EAAGqD,EAAQ,MAAM,EACtEA,EAAQ,OAASiC,EAASE,CAAA,MAE1B,KAAK,sBAAsBxF,EAAGqD,EAASkC,EAAQD,CAAM,EAGvD,GAAK3C,EAAS,iBAAiBU,CAAO,EAEtC,IAAI4B,EAAK,CACP,MAAMC,EAAW7B,EAAQ,OAAO,kBAAkBF,CAAa,EACzDgC,EAAW,KAAK,YAAYF,EAAKC,CAAQ,EAC/C,GAAIH,GAAkB,CAACA,EAAe/E,EAAG0D,EAAQK,EAAWoB,CAAQ,EAAG,SAC/D3D,EAAAyD,EAAIE,CAAQ,EAAE,MACdzF,EAAAuF,EAAIE,CAAQ,EAAE,KAAA,KACjB,CACL,GAAIJ,GAAkB,CAACA,EAAe/E,EAAG0D,CAAM,EAAG,SAClDlC,EAAQoD,EAAa,MACrBlF,EAAQkF,EAAa,KAAA,CAKvB,GAAIZ,EAAa,CACT,MAAAzC,EAAQ6B,GAAU,WAAWC,EAAQ,OAAQH,CAAU,EAAE,IAAIF,CAAQ,EAC3EH,EAAY,KAAK7C,EAAGuB,EAAOC,EAAO9B,CAAK,CAAA,MAEvByE,EAAAa,CAAc,EAAIxD,EAAQ0C,EAC1CE,EAAgBY,CAAc,EAAItF,EAClC2E,EAAcW,CAAc,EAAIhF,EAChCgF,IACF,CAGF,KAAK,gBAAkBhB,EAAcnB,EAAY,MAAM,OAASmC,CAClE,CCxQA,MAAM5B,GAAY,IAAIH,EAAAA,QAEN,SAAAwC,GAAiC1F,EAAeb,EAASkE,GAAoB,CAC3F,MAAMsC,EAAS3F,EAAQ,GACjBe,EAAQ,KAAK,iBAAiB,MAAM,KAEnC,OAAA5B,EAAA,EAAI4B,EAAM4E,EAAS,EAAE,EACrBxG,EAAA,EAAI4B,EAAM4E,EAAS,EAAE,EACrBxG,EAAA,EAAI4B,EAAM4E,EAAS,EAAE,EAErBxG,CACT,CAEgB,SAAAyG,GAAkD5F,EAAe6F,EAA2B,CAC1G,MAAMF,EAAS3F,EAAQ,GACjBe,EAAQ,KAAK,iBAAiB,MAAM,KAEpC+E,EAAM/E,EAAM4E,EAAS,CAAC,EACtBI,EAAMhF,EAAM4E,EAAS,CAAC,EACtBK,EAAMjF,EAAM4E,EAAS,CAAC,EACtBM,EAAWH,EAAMA,EAAMC,EAAMA,EAAMC,EAAMA,EAEzCE,EAAMnF,EAAM4E,EAAS,CAAC,EACtBQ,EAAMpF,EAAM4E,EAAS,CAAC,EACtBS,EAAMrF,EAAM4E,EAAS,CAAC,EACtBU,EAAWH,EAAMA,EAAMC,EAAMA,EAAMC,EAAMA,EAEzCE,EAAMvF,EAAM4E,EAAS,CAAC,EACtBY,EAAMxF,EAAM4E,EAAS,CAAC,EACtBa,EAAOzF,EAAM4E,EAAS,EAAE,EACxBc,EAAWH,EAAMA,EAAMC,EAAMA,EAAMC,EAAOA,EAEvC,OAAAX,EAAA,EAAI9E,EAAM4E,EAAS,EAAE,EACrBE,EAAA,EAAI9E,EAAM4E,EAAS,EAAE,EACrBE,EAAA,EAAI9E,EAAM4E,EAAS,EAAE,EAEvB,KAAK,KAAK,KAAK,IAAIM,EAAUI,EAAUI,CAAQ,CAAC,CACzD,CAEO,SAASC,GAAyC1G,EAAe2G,EA