@three.ez/instanced-mesh
Version:
Enhanced InstancedMesh with frustum culling, fast raycasting (using BVH), sorting, visibility management and more.
1 lines • 208 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../src/core/InstancedEntity.ts","../../src/core/InstancedMeshBVH.ts","../../src/core/utils/GLInstancedBufferAttribute.ts","../../src/core/utils/SquareDataTexture.ts","../../src/core/InstancedMesh2.ts","../../src/core/feature/Capacity.ts","../../src/utils/SortingUtils.ts","../../src/core/utils/InstancedRenderList.ts","../../src/core/feature/FrustumCulling.ts","../../src/core/feature/Instances.ts","../../src/core/feature/LOD.ts","../../src/core/feature/Morph.ts","../../src/core/feature/Raycasting.ts","../../src/core/feature/Skeleton.ts","../../src/core/feature/Uniforms.ts","../../src/shaders/chunks/instanced_pars_vertex.glsl","../../src/shaders/chunks/instanced_color_pars_vertex.glsl","../../src/shaders/chunks/instanced_vertex.glsl","../../src/shaders/chunks/instanced_color_vertex.glsl","../../src/shaders/chunks/instanced_skinning_pars_vertex.glsl","../../src/shaders/ShaderChunk.ts","../../src/utils/CreateFrom.ts"],"sourcesContent":["import { Color, ColorRepresentation, Euler, Matrix4, Mesh, Object3D, Quaternion, Vector3 } from 'three';\r\nimport { InstancedMesh2 } from './InstancedMesh2.js';\r\nimport { UniformValue, UniformValueObj } from './utils/SquareDataTexture.js';\r\n\r\n// TODO add other object3D methods\r\n// TODO implement parent\r\n\r\n/**\r\n * Represents an instance in an `InstancedMesh2`.\r\n * This class stores transformation data (position, rotation, scale) and provides methods to manipulate them.\r\n */\r\nexport class InstancedEntity {\r\n /**\r\n * Indicates if this is an `InstancedEntity`.\r\n */\r\n public readonly isInstanceEntity = true;\r\n /**\r\n * The unique identifier for this instance (relative to the `InstancedMesh2` it references).\r\n */\r\n public readonly id: number;\r\n /**\r\n * `InstancedMesh2` to which this instance refers.\r\n */\r\n public readonly owner: InstancedMesh2;\r\n /**\r\n * The local position.\r\n */\r\n public position = new Vector3();\r\n /**\r\n * The local scale.\r\n */\r\n public scale = new Vector3(1, 1, 1);\r\n /**\r\n * The local rotation as `Quaternion`.\r\n */\r\n public quaternion = new Quaternion();\r\n /**\r\n * The local rotation as `Euler`.\r\n * This works only if `allowsEuler` is set to `true` in the `InstancedMesh2` constructor parameters.\r\n */\r\n public rotation: Euler;\r\n\r\n /**\r\n * The visibility state set and got from `owner.availabilityArray`.\r\n */\r\n public get visible(): boolean { return this.owner.getVisibilityAt(this.id); }\r\n public set visible(value: boolean) { this.owner.setVisibilityAt(this.id, value); }\r\n\r\n /**\r\n * The availability set and got from `owner.availabilityArray`.\r\n */\r\n public get active(): boolean { return this.owner.getActiveAt(this.id); }\r\n public set active(value: boolean) { this.owner.setActiveAt(this.id, value); }\r\n\r\n /**\r\n * Color set and got from `owner.colorsTexture`.\r\n */\r\n public get color(): Color { return this.owner.getColorAt(this.id); }\r\n public set color(value: ColorRepresentation) { this.owner.setColorAt(this.id, value); }\r\n\r\n /**\r\n * Opacity set and got from `owner.colorsTexture`.\r\n */\r\n public get opacity(): number { return this.owner.getOpacityAt(this.id); }\r\n public set opacity(value: number) { this.owner.setOpacityAt(this.id, value); }\r\n\r\n /**\r\n * Morph target influences set and got from `owner.morphTexture`.\r\n */\r\n public get morph(): Mesh { return this.owner.getMorphAt(this.id); }\r\n public set morph(value: Mesh) { this.owner.setMorphAt(this.id, value); }\r\n\r\n /**\r\n * The local transform matrix got from `owner.matricesTexture`.\r\n */\r\n public get matrix(): Matrix4 { return this.owner.getMatrixAt(this.id); }\r\n\r\n /**\r\n * The world transform matrix got by multiplying the matrix got from `owner.matricesTexture` and `this.owner.matrixWorld`.\r\n */\r\n public get matrixWorld(): Matrix4 { return this.matrix.premultiply(this.owner.matrixWorld); }\r\n\r\n /**\r\n * This object is instantiated automatically by setting `createEntities` to `true` in the `InstancedMesh2` constructor parameters.\r\n * Dont instantiate this manually.\r\n * @param owner The `InstancedMesh2` that owns this instance.\r\n * @param id The unique identifier for this instance within the `InstancedMesh2`.\r\n * @param useEuler Whether to use Euler rotations in addition to quaternion rotations.\r\n */\r\n constructor(owner: InstancedMesh2, id: number, useEuler: boolean) {\r\n this.id = id;\r\n this.owner = owner;\r\n\r\n if (useEuler) {\r\n const quaternion = this.quaternion;\r\n const rotation = this.rotation = new Euler();\r\n\r\n rotation._onChange(() => quaternion.setFromEuler(rotation, false));\r\n quaternion._onChange(() => rotation.setFromQuaternion(quaternion, undefined, false));\r\n }\r\n }\r\n\r\n /**\r\n * @internal\r\n */\r\n public setMatrixIdentity(): void {\r\n const owner = this.owner;\r\n const te = owner.matricesTexture._data;\r\n const id = this.id;\r\n const offset = id * 16;\r\n\r\n te[offset + 0] = 1;\r\n te[offset + 1] = 0;\r\n te[offset + 2] = 0;\r\n te[offset + 3] = 0;\r\n\r\n te[offset + 4] = 0;\r\n te[offset + 5] = 1;\r\n te[offset + 6] = 0;\r\n te[offset + 7] = 0;\r\n\r\n te[offset + 8] = 0;\r\n te[offset + 9] = 0;\r\n te[offset + 10] = 1;\r\n te[offset + 11] = 0;\r\n\r\n te[offset + 12] = 0;\r\n te[offset + 13] = 0;\r\n te[offset + 14] = 0;\r\n te[offset + 15] = 1;\r\n\r\n owner.matricesTexture.enqueueUpdate(id);\r\n }\r\n\r\n /**\r\n * Updates the transformation matrix with its current position, quaternion, and scale.\r\n * The updated matrix is stored in the `owner.matricesTexture`.\r\n */\r\n public updateMatrix(): void {\r\n const owner = this.owner;\r\n const position = this.position;\r\n const quaternion = this.quaternion as any;\r\n const scale = this.scale;\r\n const te = owner.matricesTexture._data;\r\n const id = this.id;\r\n const offset = id * 16;\r\n\r\n const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;\r\n const x2 = x + x, y2 = y + y, z2 = z + z;\r\n const xx = x * x2, xy = x * y2, xz = x * z2;\r\n const yy = y * y2, yz = y * z2, zz = z * z2;\r\n const wx = w * x2, wy = w * y2, wz = w * z2;\r\n\r\n const sx = scale.x, sy = scale.y, sz = scale.z;\r\n\r\n te[offset + 0] = (1 - (yy + zz)) * sx;\r\n te[offset + 1] = (xy + wz) * sx;\r\n te[offset + 2] = (xz - wy) * sx;\r\n te[offset + 3] = 0;\r\n\r\n te[offset + 4] = (xy - wz) * sy;\r\n te[offset + 5] = (1 - (xx + zz)) * sy;\r\n te[offset + 6] = (yz + wx) * sy;\r\n te[offset + 7] = 0;\r\n\r\n te[offset + 8] = (xz + wy) * sz;\r\n te[offset + 9] = (yz - wx) * sz;\r\n te[offset + 10] = (1 - (xx + yy)) * sz;\r\n te[offset + 11] = 0;\r\n\r\n te[offset + 12] = position.x;\r\n te[offset + 13] = position.y;\r\n te[offset + 14] = position.z;\r\n te[offset + 15] = 1;\r\n\r\n owner.matricesTexture.enqueueUpdate(id);\r\n\r\n if (owner.bvh && owner.autoUpdateBVH) {\r\n owner.bvh.move(id);\r\n }\r\n }\r\n\r\n /**\r\n * Updates only the position component of the transformation matrix.\r\n * This is useful if only position changes, avoiding recalculating the full matrix.\r\n * The updated matrix is stored in the `owner.matricesTexture`.\r\n */\r\n public updateMatrixPosition(): void {\r\n const owner = this.owner;\r\n const position = this.position;\r\n const te = owner.matricesTexture._data;\r\n const id = this.id;\r\n const offset = id * 16;\r\n\r\n te[offset + 12] = position.x;\r\n te[offset + 13] = position.y;\r\n te[offset + 14] = position.z;\r\n\r\n owner.matricesTexture.enqueueUpdate(id);\r\n\r\n if (owner.bvh && owner.autoUpdateBVH) {\r\n owner.bvh.move(id);\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves the uniform value associated with the given name.\r\n * @param name The name of the uniform to retrieve.\r\n * @param target Optional target object where the uniform value will be written.\r\n * @returns The retrieved uniform value.\r\n */\r\n public getUniform(name: string, target?: UniformValueObj): UniformValue {\r\n return this.owner.getUniformAt(this.id, name, target);\r\n }\r\n\r\n /**\r\n * Updates the bones of the skeleton to the instance.\r\n * @param updateBonesMatrices Whether to update the matrices of the bones. Default is `true`.\r\n * @param excludeBonesSet An optional set of bone names to exclude from updates, skipping their local matrix updates.\r\n */\r\n public updateBones(updateBonesMatrices = true, excludeBonesSet?: Set<string>): void {\r\n this.owner.setBonesAt(this.id, updateBonesMatrices, excludeBonesSet);\r\n }\r\n\r\n /**\r\n * Sets the uniform value for the given name\r\n * @param name The name of the uniform to set.\r\n * @param value The new value for the uniform.\r\n */\r\n public setUniform(name: string, value: UniformValue): void {\r\n this.owner.setUniformAt(this.id, name, value);\r\n }\r\n\r\n /**\r\n * Copies the transformation properties (`position`, `scale`, `quaternion`) of this instance to the specified `Object3D`.\r\n * @param target The `Object3D` where the transformation properties will be copied.\r\n */\r\n public copyTo(target: Object3D): void {\r\n target.position.copy(this.position);\r\n target.scale.copy(this.scale);\r\n target.quaternion.copy(this.quaternion);\r\n if (this.rotation) target.rotation.copy(this.rotation); // TODO check if this is necessary.. it's probably already synched\r\n }\r\n\r\n /**\r\n * Applies the matrix transform to the object and updates the object's position, rotation and scale.\r\n * @param m The matrix to apply.\r\n * @returns The instance of the object.\r\n */\r\n public applyMatrix4(m: Matrix4): this {\r\n this.matrix.premultiply(m).decompose(this.position, this.quaternion, this.scale);\r\n return this;\r\n }\r\n\r\n /**\r\n * Applies the rotation represented by the quaternion to the object.\r\n * @param q The quaternion representing the rotation to apply.\r\n * @returns The instance of the object.\r\n */\r\n public applyQuaternion(q: Quaternion): this {\r\n this.quaternion.premultiply(q);\r\n return this;\r\n }\r\n\r\n /**\r\n * Rotate an object along an axis in object space. The axis is assumed to be normalized.\r\n * @param axis A normalized vector in object space.\r\n * @param angle The angle in radians.\r\n * @returns The instance of the object.\r\n */\r\n public rotateOnAxis(axis: Vector3, angle: number): this {\r\n _quat.setFromAxisAngle(axis, angle);\r\n this.quaternion.multiply(_quat);\r\n return this;\r\n }\r\n\r\n /**\r\n * Rotate an object along an axis in world space. The axis is assumed to be normalized. Method Assumes no rotated parent.\r\n * @param axis A normalized vector in world space.\r\n * @param angle The angle in radians.\r\n * @returns The instance of the object.\r\n */\r\n public rotateOnWorldAxis(axis: Vector3, angle: number): this {\r\n _quat.setFromAxisAngle(axis, angle);\r\n this.quaternion.premultiply(_quat);\r\n return this;\r\n }\r\n\r\n /**\r\n * Rotates the object around x axis in local space.\r\n * @param angle The angle to rotate in radians.\r\n * @returns The instance of the object.\r\n */\r\n public rotateX(angle: number): this {\r\n return this.rotateOnAxis(_xAxis, angle);\r\n }\r\n\r\n /**\r\n * Rotates the object around y axis in local space.\r\n * @param angle The angle to rotate in radians.\r\n * @returns The instance of the object.\r\n */\r\n public rotateY(angle: number): this {\r\n return this.rotateOnAxis(_yAxis, angle);\r\n }\r\n\r\n /**\r\n * Rotates the object around z axis in local space.\r\n * @param angle The angle to rotate in radians.\r\n * @returns The instance of the object.\r\n */\r\n public rotateZ(angle: number): this {\r\n return this.rotateOnAxis(_zAxis, angle);\r\n }\r\n\r\n /**\r\n * Translate an object by distance along an axis in object space. The axis is assumed to be normalized.\r\n * @param axis A normalized vector in object space.\r\n * @param distance The distance to translate.\r\n * @returns The instance of the object.\r\n */\r\n public translateOnAxis(axis: Vector3, distance: number): this {\r\n _vec3.copy(axis).applyQuaternion(this.quaternion);\r\n this.position.add(_vec3.multiplyScalar(distance));\r\n return this;\r\n }\r\n\r\n /**\r\n * Translates object along x axis in object space by distance units.\r\n * @param distance The distance to translate.\r\n * @returns The instance of the object.\r\n */\r\n public translateX(distance: number): this {\r\n return this.translateOnAxis(_xAxis, distance);\r\n }\r\n\r\n /**\r\n * Translates object along y axis in object space by distance units.\r\n * @param distance The distance to translate.\r\n * @returns The instance of the object.\r\n */\r\n public translateY(distance: number): this {\r\n return this.translateOnAxis(_yAxis, distance);\r\n }\r\n\r\n /**\r\n * Translates object along z axis in object space by distance units.\r\n * @param distance The distance to translate.\r\n * @returns The instance of the object.\r\n */\r\n public translateZ(distance: number): this {\r\n return this.translateOnAxis(_zAxis, distance);\r\n }\r\n\r\n /**\r\n * Removes this entity from its owner instance.\r\n * @returns The instance of the object.\r\n */\r\n public remove(): this {\r\n this.owner.removeInstances(this.id);\r\n return this;\r\n }\r\n}\r\n\r\nconst _quat = new Quaternion();\r\nconst _vec3 = new Vector3();\r\nconst _xAxis = new Vector3(1, 0, 0);\r\nconst _yAxis = new Vector3(0, 1, 0);\r\nconst _zAxis = new Vector3(0, 0, 1);\r\n","import { box3ToArray, BVH, BVHNode, HybridBuilder, onFrustumIntersectionCallback, onFrustumIntersectionLODCallback, onIntersectionCallback, onIntersectionRayCallback, vec3ToArray, WebGLCoordinateSystem } from 'bvh.js';\r\nimport { Box3, Matrix4, Raycaster, Sphere, Vector3 } from 'three';\r\nimport { LODLevel } from './feature/LOD.js';\r\nimport { InstancedMesh2 } from './InstancedMesh2.js';\r\n\r\n// TODO getBoxFromSphere updated if change geometry (and create accessor)\r\n// TODO accurateCulling in bvh.js?\r\n// TODO use params in constructor\r\n// TODO: intersectBox optional intersectCallback\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 * Uses the geometry bounding sphere to compute instance bounding boxes.\r\n * Otherwise it's calculated by applying the object's matrix to all 8 bounding box points.\r\n * This is faster but less precise. Useful for moving objects.\r\n * Only works if the geometry's bounding sphere is centered at the origin.\r\n * @default false\r\n */\r\n getBBoxFromBSphere?: boolean;\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\ninterface SphereTarget {\r\n centerX: number;\r\n centerY: number;\r\n centerZ: number;\r\n maxScale: number;\r\n}\r\n\r\n/**\r\n * Class to manage BVH (Bounding Volume Hierarchy) for `InstancedMesh2`.\r\n * Provides methods for managing bounding volumes, frustum culling, raycasting, and bounding box computation.\r\n */\r\nexport class InstancedMeshBVH {\r\n /**\r\n * The target `InstancedMesh2` object that the BVH is managing.\r\n */\r\n public target: InstancedMesh2;\r\n /**\r\n * The geometry bounding box of the target.\r\n */\r\n public geoBoundingBox: Box3;\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 LODsMap = new Map<LODLevel[], Float32Array>();\r\n protected _margin: number;\r\n protected _origin: Float32Array;\r\n protected _dir: Float32Array;\r\n protected _boxArray: Float32Array;\r\n protected _cameraPos: Float32Array;\r\n protected _getBoxFromSphere: boolean;\r\n protected _geoBoundingSphere: Sphere = null;\r\n protected _sphereTarget: SphereTarget = null;\r\n\r\n /**\r\n * @param target The target `InstancedMesh2`.\r\n * @param margin The margin applied for bounding box calculations (default is 0).\r\n * @param getBBoxFromBSphere Flag to determine if instance bounding boxes should be computed from the geometry bounding sphere. Faster but less precise (default is false).\r\n * @param accurateCulling Flag to enable accurate frustum culling without considering margin (default is true).\r\n */\r\n constructor(target: InstancedMesh2, margin = 0, getBBoxFromBSphere = false, accurateCulling = true) {\r\n this.target = target;\r\n this.accurateCulling = accurateCulling;\r\n this._margin = margin;\r\n\r\n const geometry = target._geometry;\r\n\r\n if (!geometry.boundingBox) geometry.computeBoundingBox();\r\n this.geoBoundingBox = geometry.boundingBox;\r\n\r\n if (getBBoxFromBSphere) {\r\n if (!geometry.boundingSphere) geometry.computeBoundingSphere();\r\n\r\n const center = geometry.boundingSphere.center;\r\n if (center.x === 0 && center.y === 0 && center.z === 0) {\r\n this._geoBoundingSphere = geometry.boundingSphere;\r\n this._sphereTarget = { centerX: 0, centerY: 0, centerZ: 0, maxScale: 0 };\r\n } else {\r\n console.warn('\"getBoxFromSphere\" is ignored because geometry is not centered.');\r\n getBBoxFromBSphere = false;\r\n }\r\n }\r\n\r\n this.bvh = new BVH(new HybridBuilder(), WebGLCoordinateSystem);\r\n this._origin = new Float32Array(3);\r\n this._dir = new Float32Array(3);\r\n this._cameraPos = new Float32Array(3);\r\n this._getBoxFromSphere = getBBoxFromBSphere;\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._instancesCount;\r\n const instancesArrayCount = this.target._instancesArrayCount;\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 (!this.target.getActiveAt(i)) 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 frustum culling with Level of Detail (LOD) consideration.\r\n * @param projScreenMatrix The projection screen matrix for frustum culling.\r\n * @param cameraPosition The camera's position used for LOD calculations.\r\n * @param levels An array of LOD levels.\r\n * @param onFrustumIntersection Callback function invoked when an instance intersects the frustum.\r\n */\r\n public frustumCullingLOD(projScreenMatrix: Matrix4, cameraPosition: Vector3, levels: LODLevel[], onFrustumIntersection: onFrustumIntersectionLODCallback<{}, number>): void {\r\n if (!this.LODsMap.has(levels)) {\r\n this.LODsMap.set(levels, new Float32Array(levels.length));\r\n }\r\n\r\n const levelsArray = this.LODsMap.get(levels);\r\n for (let i = 0; i < levels.length; i++) {\r\n levelsArray[i] = levels[i].distance;\r\n }\r\n\r\n const camera = this._cameraPos;\r\n camera[0] = cameraPosition.x;\r\n camera[1] = cameraPosition.y;\r\n camera[2] = cameraPosition.z;\r\n\r\n if (this._margin > 0 && this.accurateCulling) {\r\n this.bvh.frustumCullingLOD(projScreenMatrix.elements, camera, levelsArray, (node, level, frustum, mask) => {\r\n if (frustum.isIntersectedMargin(node.box, mask, this._margin)) {\r\n onFrustumIntersection(node, level);\r\n }\r\n });\r\n } else {\r\n this.bvh.frustumCullingLOD(projScreenMatrix.elements, camera, levelsArray, 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 if (!this._boxArray) this._boxArray = new Float32Array(6);\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 if (this._getBoxFromSphere) {\r\n const matrixArray = this.target.matricesTexture._data as Float32Array;\r\n const { centerX, centerY, centerZ, maxScale } = this.getSphereFromMatrix_centeredGeometry(id, matrixArray, this._sphereTarget);\r\n const radius = this._geoBoundingSphere.radius * maxScale;\r\n array[0] = centerX - radius;\r\n array[1] = centerX + radius;\r\n array[2] = centerY - radius;\r\n array[3] = centerY + radius;\r\n array[4] = centerZ - radius;\r\n array[5] = centerZ + radius;\r\n } else {\r\n _box3.copy(this.geoBoundingBox).applyMatrix4(this.target.getMatrixAt(id));\r\n box3ToArray(_box3, array);\r\n }\r\n\r\n return array;\r\n }\r\n\r\n protected getSphereFromMatrix_centeredGeometry(id: number, array: Float32Array, target: SphereTarget): SphereTarget {\r\n const offset = id * 16;\r\n\r\n const m0 = array[offset + 0];\r\n const m1 = array[offset + 1];\r\n const m2 = array[offset + 2];\r\n const m4 = array[offset + 4];\r\n const m5 = array[offset + 5];\r\n const m6 = array[offset + 6];\r\n const m8 = array[offset + 8];\r\n const m9 = array[offset + 9];\r\n const m10 = array[offset + 10];\r\n\r\n const scaleXSq = m0 * m0 + m1 * m1 + m2 * m2;\r\n const scaleYSq = m4 * m4 + m5 * m5 + m6 * m6;\r\n const scaleZSq = m8 * m8 + m9 * m9 + m10 * m10;\r\n\r\n target.maxScale = Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq));\r\n\r\n target.centerX = array[offset + 12];\r\n target.centerY = array[offset + 13];\r\n target.centerZ = array[offset + 14];\r\n\r\n return target;\r\n }\r\n}\r\n\r\nconst _box3 = new Box3();\r\n","import { GLBufferAttribute, TypedArray, WebGLRenderer } from 'three';\r\n\r\n/**\r\n * A class that extends `GLBufferAttribute` to handle instanced buffer attributes.\r\n * This class was specifically created to allow updating instanced buffer attributes during the `onBeforeRender` callback,\r\n * providing an efficient way to modify the buffer data dynamically before rendering.\r\n */\r\nexport class GLInstancedBufferAttribute extends GLBufferAttribute {\r\n /**\r\n * Indicates if this is an `isGLInstancedBufferAttribute`.\r\n */\r\n public isGLInstancedBufferAttribute = true;\r\n /**\r\n * The number of meshes that share the same attribute data.\r\n */\r\n public meshPerAttribute: number;\r\n /**\r\n * The data array that holds the attribute values.\r\n */\r\n public array: TypedArray;\r\n protected _cacheArray: TypedArray;\r\n /** @internal */ _needsUpdate = false;\r\n\r\n // HACK TO MAKE IT WORK WITHOUT UPDATE CORE\r\n /** @internal */ isInstancedBufferAttribute = true;\r\n\r\n /**\r\n * @param gl The WebGL2RenderingContext used to create the buffer.\r\n * @param type The type of data in the attribute.\r\n * @param itemSize The number of elements per attribute.\r\n * @param elementSize The size of individual elements in the array.\r\n * @param array The data array that holds the attribute values.\r\n * @param meshPerAttribute The number of meshes that share the same attribute data.\r\n */\r\n constructor(gl: WebGL2RenderingContext, type: GLenum, itemSize: number, elementSize: 1 | 2 | 4, array: TypedArray, meshPerAttribute = 1) {\r\n const buffer = gl.createBuffer();\r\n super(buffer, type, itemSize, elementSize, array.length / itemSize);\r\n\r\n this.meshPerAttribute = meshPerAttribute;\r\n this.array = array;\r\n this._cacheArray = array;\r\n\r\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\r\n gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW);\r\n }\r\n\r\n /**\r\n * Updates the buffer data.\r\n * This method is designed to be called during the `onBeforeRender` callback.\r\n * It ensures that the attribute data is updated just before the rendering process begins.\r\n * @param renderer The WebGLRenderer used to render the scene.\r\n * @param count The number of elements to update in the buffer.\r\n */\r\n public update(renderer: WebGLRenderer, count: number): void {\r\n if (!this._needsUpdate || count === 0) return;\r\n\r\n const gl = renderer.getContext();\r\n gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);\r\n\r\n if (this.array === this._cacheArray) {\r\n gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.array, 0, count);\r\n } else {\r\n gl.bufferData(gl.ARRAY_BUFFER, this.array, gl.DYNAMIC_DRAW);\r\n this._cacheArray = this.array;\r\n }\r\n\r\n this._needsUpdate = false;\r\n }\r\n\r\n /** @internal */\r\n public clone(): this {\r\n // This method is intentionally empty but necessary to avoid exceptions when cloning geometry.\r\n return this;\r\n }\r\n}\r\n","import { Color, ColorManagement, DataTexture, FloatType, IntType, Matrix3, Matrix4, NoColorSpace, PixelFormat, RedFormat, RedIntegerFormat, RGBAFormat, RGBAIntegerFormat, RGFormat, RGIntegerFormat, TextureDataType, TypedArray, UnsignedIntType, Vector2, Vector3, Vector4, WebGLRenderer, WebGLUtils } from 'three';\r\n\r\n/**\r\n * Represents the number of elements per pixel.\r\n */\r\nexport type ChannelSize = 1 | 2 | 3 | 4;\r\n/**\r\n * A constructor signature for creating TypedArray.\r\n */\r\nexport type TypedArrayConstructor = new (count: number) => TypedArray;\r\n/**\r\n * Represents the texture information including its data, size, format, and data type.\r\n */\r\nexport type TextureInfo = { array: TypedArray; size: number; format: PixelFormat; type: TextureDataType };\r\n/**\r\n * Represents information for updating rows in the texture, including the row index and number of rows.\r\n */\r\nexport type UpdateRowInfo = { row: number; count: number };\r\n/**\r\n * Defines the possible types of uniforms that can be used in shaders.\r\n */\r\nexport type UniformType = 'float' | 'vec2' | 'vec3' | 'vec4' | 'mat3' | 'mat4';\r\n/**\r\n * Represents a value that can be used as a uniform.\r\n */\r\nexport type UniformValueObj = Vector2 | Vector3 | Vector4 | Matrix3 | Matrix4 | Color;\r\n/**\r\n * Defines a uniform value as either a number or a compatible Three.js object.\r\n */\r\nexport type UniformValue = number | UniformValueObj;\r\n/**\r\n * Represents the schema for a uniform, defining its offset, size, and type.\r\n */\r\nexport type UniformMapType = { offset: number; size: number; type: UniformType };\r\n/**\r\n * Represents a map of uniform names to their schema definitions.\r\n */\r\nexport type UniformMap = Map<string, UniformMapType>;\r\n\r\n/**\r\n * Calculates the square texture size based on the capacity and pixels per instance.\r\n * This ensures the texture is large enough to store all instances in a square layout.\r\n * @param capacity The maximum number of instances allowed in the texture.\r\n * @param pixelsPerInstance The number of pixels required for each instance.\r\n * @returns The size of the square texture needed to store all the instances.\r\n */\r\nexport function getSquareTextureSize(capacity: number, pixelsPerInstance: number): number {\r\n return Math.max(pixelsPerInstance, Math.ceil(Math.sqrt(capacity / pixelsPerInstance)) * pixelsPerInstance);\r\n}\r\n\r\n/**\r\n * Generates texture information (size, format, type) for a square texture based on the provided parameters.\r\n * @param arrayType The constructor for the TypedArray.\r\n * @param channels The number of channels in the texture.\r\n * @param pixelsPerInstance The number of pixels required for each instance.\r\n * @param capacity The maximum number of instances allowed in the texture.\r\n * @returns An object containing the texture's array, size, format, and data type.\r\n */\r\nexport function getSquareTextureInfo(arrayType: TypedArrayConstructor, channels: ChannelSize, pixelsPerInstance: number, capacity: number): TextureInfo {\r\n if (channels === 3) {\r\n console.warn('\"channels\" cannot be 3. Set to 4. More info: https://github.com/mrdoob/three.js/pull/23228');\r\n channels = 4;\r\n }\r\n\r\n const size = getSquareTextureSize(capacity, pixelsPerInstance);\r\n const array = new arrayType(size * size * channels);\r\n const isFloat = arrayType.name.includes('Float');\r\n const isUnsignedInt = arrayType.name.includes('Uint');\r\n const type: TextureDataType = isFloat ? FloatType : (isUnsignedInt ? UnsignedIntType : IntType);\r\n let format: PixelFormat;\r\n\r\n switch (channels) {\r\n case 1:\r\n format = isFloat ? RedFormat : RedIntegerFormat;\r\n break;\r\n case 2:\r\n format = isFloat ? RGFormat : RGIntegerFormat;\r\n break;\r\n case 4:\r\n format = isFloat ? RGBAFormat : RGBAIntegerFormat;\r\n break;\r\n }\r\n\r\n return { array, size, type, format };\r\n}\r\n\r\n/**\r\n * A class that extends `DataTexture` to manage a square texture optimized for instances rendering.\r\n * It supports dynamic resizing, partial update based on rows, and allows setting/getting uniforms per instance.\r\n */\r\nexport class SquareDataTexture extends DataTexture {\r\n /**\r\n * Whether to enable partial texture updates by row. If `false`, the entire texture will be updated.\r\n * @default true.\r\n */\r\n public partialUpdate = true;\r\n /**\r\n * The maximum number of update calls per frame.\r\n * @default Infinity\r\n */\r\n public maxUpdateCalls = Infinity;\r\n /** @internal */ _data: TypedArray; // TODO make it public or remove it?\r\n protected _channels: ChannelSize;\r\n protected _pixelsPerInstance: number;\r\n protected _stride: number;\r\n protected _rowToUpdate: boolean[];\r\n protected _uniformMap: UniformMap;\r\n protected _fetchUniformsInFragmentShader: boolean;\r\n protected _utils: WebGLUtils = null; // TODO add it to renderer instead of creating for each texture\r\n protected _needsUpdate: boolean = false;\r\n protected _lastWidth: number = null;\r\n\r\n /**\r\n * @param arrayType The constructor for the TypedArray.\r\n * @param channels The number of channels in the texture.\r\n * @param pixelsPerInstance The number of pixels required for each instance.\r\n * @param capacity The total number of instances.\r\n * @param uniformMap Optional map for handling uniform values.\r\n * @param fetchInFragmentShader Optional flag that determines if uniform values should be fetched in the fragment shader instead of the vertex shader.\r\n */\r\n constructor(arrayType: TypedArrayConstructor, channels: ChannelSize, pixelsPerInstance: number, capacity: number, uniformMap?: UniformMap, fetchInFragmentShader?: boolean) {\r\n if (channels === 3) channels = 4;\r\n const { array, format, size, type } = getSquareTextureInfo(arrayType, channels, pixelsPerInstance, capacity);\r\n super(array, size, size, format, type);\r\n this._data = array;\r\n this._channels = channels;\r\n this._pixelsPerInstance = pixelsPerInstance;\r\n this._stride = pixelsPerInstance * channels;\r\n this._rowToUpdate = new Array(size);\r\n this._uniformMap = uniformMap;\r\n this._fetchUniformsInFragmentShader = fetchInFragmentShader;\r\n this.needsUpdate = true; // necessary to init texture\r\n }\r\n\r\n /**\r\n * Resizes the texture to accommodate a new number of instances.\r\n * @param count The new total number of instances.\r\n */\r\n public resize(count: number): void {\r\n const size = getSquareTextureSize(count, this._pixelsPerInstance);\r\n if (size === this.image.width) return;\r\n\r\n const currentData = this._data;\r\n const channels = this._channels;\r\n this._rowToUpdate.length = size;\r\n const arrayType = (currentData as any).constructor;\r\n\r\n const data = new arrayType(size * size * channels);\r\n const minLength = Math.min(currentData.length, data.length);\r\n data.set(new arrayType(currentData.buffer, 0, minLength));\r\n\r\n this.dispose();\r\n this.image = { data, height: size, width: size };\r\n this._data = data;\r\n }\r\n\r\n /**\r\n * Marks a row of the texture for update during the next render cycle.\r\n * This helps in optimizing texture updates by only modifying the rows that have changed.\r\n * @param index The index of the instance to update.\r\n */\r\n public enqueueUpdate(index: number): void {\r\n this._needsUpdate = true;\r\n if (!this.partialUpdate) return;\r\n\r\n const elementsPerRow = this.image.width / this._pixelsPerInstance;\r\n const rowIndex = Math.floor(index / elementsPerRow);\r\n this._rowToUpdate[rowIndex] = true;\r\n }\r\n\r\n /**\r\n * Updates the texture data based on the rows that need updating.\r\n * This method is optimized to only update the rows that have changed, improving performance.\r\n * @param renderer The WebGLRenderer used for rendering.\r\n */\r\n public update(renderer: WebGLRenderer): void {\r\n const textureProperties: any = renderer.properties.get(this);\r\n const versionChanged = this.version > 0 && textureProperties.__version !== this.version;\r\n const sizeChanged = this._lastWidth !== null && this._lastWidth !== this.image.width;\r\n if (!this._needsUpdate || !textureProperties.__webglTexture || versionChanged || sizeChanged) {\r\n this._lastWidth = this.image.width;\r\n this._needsUpdate = false;\r\n return;\r\n }\r\n\r\n this._needsUpdate = false;\r\n\r\n if (!this.partialUpdate) {\r\n this.needsUpdate = true; // three.js will update the whole texture\r\n return;\r\n }\r\n\r\n const rowsInfo = this.getUpdateRowsInfo();\r\n if (rowsInfo.length === 0) return;\r\n\r\n if (rowsInfo.length > this.maxUpdateCalls) {\r\n this.needsUpdate = true; // three.js will update the whole texture\r\n } else {\r\n this.updateRows(textureProperties, renderer, rowsInfo);\r\n }\r\n\r\n this._rowToUpdate.fill(false);\r\n }\r\n\r\n // TODO reuse same objects to prevent memory leak\r\n protected getUpdateRowsInfo(): UpdateRowInfo[] {\r\n const rowsToUpdate = this._rowToUpdate;\r\n const result: UpdateRowInfo[] = [];\r\n\r\n for (let i = 0, l = rowsToUpdate.length; i < l; i++) {\r\n if (rowsToUpdate[i]) {\r\n const row = i;\r\n for (; i < l; i++) {\r\n if (!rowsToUpdate[i]) break;\r\n }\r\n result.push({ row, count: i - row });\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n protected updateRows(textureProperties: any, renderer: WebGLRenderer, info: UpdateRowInfo[]): void {\r\n const state = renderer.state;\r\n const gl = renderer.getContext() as WebGL2RenderingContext;\r\n // @ts-expect-error Expected 2 arguments, but got 3.\r\n this._utils ??= new WebGLUtils(gl, renderer.extensions, renderer.capabilities); // third argument is necessary for older three versions\r\n const glFormat = this._utils.convert(this.format);\r\n const glType = this._utils.convert(this.type);\r\n const { data, width } = this.image;\r\n const channels = this._channels;\r\n\r\n state.bindTexture(gl.TEXTURE_2D, textureProperties.__webglTexture);\r\n\r\n const workingPrimaries = ColorManagement.getPrimaries(ColorManagement.workingColorSpace);\r\n const texturePrimaries = this.colorSpace === NoColorSpace ? null : ColorManagement.getPrimaries(this.colorSpace);\r\n const unpackConversion = this.colorSpace === NoColorSpace || workingPrimaries === texturePrimaries ? gl.NONE : gl.BROWSER_DEFAULT_WEBGL;\r\n\r\n gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.flipY);\r\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this.premultiplyAlpha);\r\n gl.pixelStorei(gl.UNPACK_ALIGNMENT, this.unpackAlignment);\r\n gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, unpackConversion);\r\n\r\n for (const { count, row } of info) {\r\n gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, row, width, count, glFormat, glType, data, row * width * channels);\r\n }\r\n\r\n if (this.onUpdate) this.onUpdate(this);\r\n }\r\n\r\n /**\r\n * Sets a uniform value at the specified instance ID in the texture.\r\n * @param id The instance ID to set the uniform for.\r\n * @param name The name of the uniform.\r\n * @param value The value to set for the uniform.\r\n */\r\n public setUniformAt(id: number, name: string, value: UniformValue): void {\r\n const { offset, size } = this._uniformMap.get(name);\r\n const stride = this._stride;\r\n\r\n if (size === 1) {\r\n this._data[id * stride + offset] = value as number;\r\n } else {\r\n (value as UniformValueObj).toArray(this._data, id * stride + offset);\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves a uniform value at the specified instance ID from the texture.\r\n * @param id The instance ID to retrieve the uniform from.\r\n * @param name The name of the uniform.\r\n * @param target Optional target object to store the uniform value.\r\n * @returns The uniform value for the specified instance.\r\n */\r\n public getUniformAt(id: number, name: string, target?: UniformValueObj): UniformValue {\r\n const { offset, size } = this._uniformMap.get(name);\r\n const stride = this._stride;\r\n\r\n if (size === 1) {\r\n return this._data[id * stride + offset];\r\n }\r\n\r\n return target.fromArray(this._data, id * stride + offset);\r\n }\r\n\r\n /**\r\n * Generates the GLSL code for accessing the uniform data stored in the texture.\r\n * @param textureName The name of the texture in the GLSL shader.\r\n * @param indexName The name of the index in the GLSL shader.\r\n * @param indexType The type of the index in the GLSL shader.\r\n * @returns An object containing the GLSL code for the vertex and fragment shaders.\r\n */\r\n public getUniformsGLSL(textureName: string, indexName: string, indexType: string): { vertex: string; fragment: string } {\r\n const vertex = this.getUniformsVertexGLSL(textureName, indexName, indexType);\r\n const fragment = this.getUniformsFragmentGLSL(textureName, indexName, indexType);\r\n return { vertex, fragment };\r\n }\r\n\r\n protected getUniformsVertexGLSL(textureName: string, indexName: string, indexType: string): string {\r\n if (this._fetchUniformsInFragmentShader) {\r\n return `\r\n flat varying ${indexType} ez_v${indexName}; \r\n void main() {\r\n ez_v${indexName} = ${indexName};`;\r\n }\r\n\r\n const texelsFetch = this.texelsFetchGLSL(textureName, indexName);\r\n const getFromTexels = this.getFromTexelsGLSL();\r\n const { assignVarying, declareVarying } = this.getVarying();\r\n\r\n return `\r\n uniform highp sampler2D ${textureName}; \r\n ${declareVarying}\r\n void main() {\r\n ${texelsFetch}\r\n ${getFromTexels}\r\n ${assignVarying}`;\r\n }\r\n\r\n protected getUniformsFragmentGLSL(textureName: string, indexName: string, indexType: string): string {\r\n if (!this._fetchUniformsInFragmentShader) {\r\n const { declareVarying, getVarying } = this.getVarying();\r\n\r\n return `\r\n ${declareVarying}\r\n void main() {\r\n ${getVarying}`;\r\n }\r\n\r\n const texelsFetch = this.texelsFetchGLSL(textureName, `ez_v${indexName}`);\r\n const getFromTexels = this.getFromTexelsGLSL();\r\n\r\n return `\r\n uniform highp sampler2D ${textureName}; \r\n flat varying ${indexType} ez_v${indexName};\r\n void main() {\r\n ${texelsFetch}\r\n ${getFromTexels}`;\r\n }\r\n\r\n protected texelsFetchGLSL(textureName: string, indexName: string): string {\r\n const pixelsPerInstance = this._pixelsPerInstance;\r\n\r\n let texelsFetch = `\r\n int size = textureSize(${textureName}, 0).x;\r\n int j = int(${indexName}) * ${pixelsPerInstance};\r\n int x = j % size;\r\n int y = j / size;\r\n `;\r\n\r\n for (let i = 0; i < pixelsPerInstance; i++) {\r\n texelsFetch += `vec4 ez_texel${i} = texelFetch(${textureName}, ivec2(x + ${i}, y), 0);\\n`;\r\n }\r\n\r\n return texelsFetch;\r\n }\r\n\r\n protected getFromTexelsGLSL(): string {\r\n const uniforms = this._uniformMap;\r\n let getFromTexels = '';\r\n\r\n for (const [name, { type, offset, size }] of uniforms) {\r\n const tId = Math.floor(offset / this._channels);\r\n\r\n if (type === 'mat3') {\r\n getFromTexels += `mat3 ${name} = mat3(ez_texel${tId}.rgb, vec3(ez_texel${tId}.a, ez_texel${tId + 1}.rg), vec3(ez_texel${tId + 1}.ba, ez_texel${tId + 2}.r));\\n`;\r\n } else if (type === 'mat4') {\r\n getFromTexels += `mat4 ${name} = mat4(ez_texel${tId}, ez_texel${tId + 1}, ez_texel${tId + 2}, ez_texel${tId + 3});\\n`;\r\n } else {\r\n const components = this.getUniformComponents(offset, size);\r\n getFromTexels += `${type} ${name} = ez_texel${tId}.${components};\\n`;\r\n }\r\n }\r\n\r\n return getFromTexels;\r\n }\r\n\r\n protected getVarying(): { declareVarying: string; assignVarying: string; getVarying: string } {\r\n const uniforms = this._uniformMap;\r\n let declareVarying = '';\r\n let assignVarying = '';\r\n let getVarying = '';\r\n\r\n for (const [name, { type }] of uniforms) {\r\n declareVarying += `flat varying ${type} ez_v${name};\\n`;\r\n assignVarying += `ez_v${name} = ${name};\\n`;\r\n getVarying += `${type} ${name} = ez_v${name};\\n`;\r\n }\r\n\r\n return { declareVarying, assignVarying, getVarying };\r\n }\r\n\r\n protected getUniformComponents(offset: number, size: number): string {\r\n const startIndex = offset % this._channels;\r\n let components = '';\r\n\r\n for (let i = 0; i < size; i++) {\r\n components += componentsArray[startIndex + i];\r\n }\r\n\r\n return components;\r\n }\r\n\r\n public override copy(source: SquareDataTexture): this {\r\n super.copy(source);\r\n\r\n this.partialUpdate = source.partialUpdate;\r\n this.maxUpdateCalls = source.maxUpdateCalls;\r\n this._channels = source._channels;\r\n this._pixelsPerInstance = source._pixelsPerInstance;\r\n this._stride = source._stride;\r\n this._rowToUpdate = source._rowToUpdate;\r\n this._uniformMap = source._uniformMap;\r\n this._fetchUniformsInFragmentShader = source._fetchUniformsInFragmentShader;\r\n\r\n return this;\r\n }\r\n}\r\n\r\nconst componentsArray = ['r', 'g', 'b', 'a'];\r\n","import { AttachedBindMode, BindMode, Box3, BufferAttribute, BufferGeometry, Camera, Color, ColorManagement, ColorRepresentation, DataTexture, DetachedBindMode, InstancedBufferAttribute, Material, Matrix4, Mesh, MeshDistanceMaterial, Object3D, Object3DEventMap, Scene, Skeleton, Sphere, TypedArray, Vector3, WebGLProgramParametersWithUniforms, WebGLRenderer } from 'three';\r\nimport { CustomSortCallback, OnFrustumEnterCallback } from './feature/FrustumCulling.js';\r\nimport { Entity } from './feature/Instances.js';\r\nimport { LODInfo } from './feature/LOD.js';\r\nimport { InstancedEntity } from './InstancedEntity.js';\r\nimport { BVHParams, InstancedMeshBVH } from './InstancedMeshBVH.js';\r\nimport { GLInstancedBufferAttribute } from './utils/GLInstancedBufferAttribute.js';\r\nimport { SquareDataTexture } from './utils/SquareDataTexture.js';\r\n\r\n// TODO: Add check to not update partial texture if needsuupdate already true\r\n// TODO: if bvh present, can override?\r\n// TODO: Use BVH only for raycasting\r\n// TODO LOD: instancedMeshLOD rendering first nearest levels, look out to transparent\r\n// TODO LOD: shared customDepthMaterial and customDistanceMaterial?\r\n// TODO LOD: BVH and handle raycastOnlyFrustum?;\r\n// TODO: check if check first and last material is right.. we should use the first valid index instead\r\n// TODO: use visible = false instead count = 0 for unused LOD?\r\n\r\n/**\r\n * Parameters for configuring an `InstancedMesh2` instance.\r\n */\r\nexport interface InstancedMesh2Params {\r\n /**\r\n * Determines the maximum number of instances that buffers can hold.\r\n * The buffers will be expanded automatically if necessary.\r\n * @default 1000\r\n */\r\n capacity?: number;\r\n /**\r\n * Determines whether to create an array of `InstancedEntity` to easily manipulate instances at the cost of more memory.\r\n * @default false\r\n */\r\n createEntities?: boolean;\r\n /**\r\n * Determines whether `InstancedEntity` can use the `rotation` property.\r\n * If `true` `quaternion` and `rotation` will be synchronized, affecting performance.\r\n * @default false\r\n */\r\n allowsEuler?: boolean;\r\n /**\r\n * WebGL renderer instance.\r\n * If not provided, buffers will be initialized during the first render, resulting in no instances being rendered initially.\r\n * @default null\r\n */\r\n renderer?: WebGLRenderer;\r\n}\r\n\r\ninterface RenderInfo {\r\n frame: number;\r\n camera: Camera | null;\r\n shadowCamera: Camera | null;\r\n}\r\n\r\n/**\r\n * Alternative `InstancedMesh` class to support additional features like frustum culling, fast raycasting, LOD and more.\r\n * @template TData Type for additional instance data.\r\n * @template TGeometry Type extending `BufferGeometry`.\r\n * @