"use strict"; var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const THREE = require("three"); const ID_ATTR_NAME = "_batch_id_"; const _identityMatrix = /* @__PURE__ */ new THREE.Matrix4(); const _zeroScaleMatrix = /* @__PURE__ */ (() => new THREE.Matrix4().set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))(); const batchingParsVertex = ( /* glsl */ ` #ifdef BATCHING attribute float ${ID_ATTR_NAME}; uniform highp sampler2D batchingTexture; mat4 getBatchingMatrix( const in float i ) { int size = textureSize( batchingTexture, 0 ).x; int j = int( i ) * 4; int x = j % size; int y = j / size; vec4 v1 = texelFetch( batchingTexture, ivec2( x, y ), 0 ); vec4 v2 = texelFetch( batchingTexture, ivec2( x + 1, y ), 0 ); vec4 v3 = texelFetch( batchingTexture, ivec2( x + 2, y ), 0 ); vec4 v4 = texelFetch( batchingTexture, ivec2( x + 3, y ), 0 ); return mat4( v1, v2, v3, v4 ); } #endif ` ); const batchingbaseVertex = ( /* glsl */ ` #ifdef BATCHING mat4 batchingMatrix = getBatchingMatrix( ${ID_ATTR_NAME} ); #endif ` ); const batchingnormalVertex = ( /* glsl */ ` #ifdef BATCHING objectNormal = vec4( batchingMatrix * vec4( objectNormal, 0.0 ) ).xyz; #ifdef USE_TANGENT objectTangent = vec4( batchingMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif #endif ` ); const batchingVertex = ( /* glsl */ ` #ifdef BATCHING transformed = ( batchingMatrix * vec4( transformed, 1.0 ) ).xyz; #endif ` ); function copyAttributeData(src, target, targetOffset = 0) { const itemSize = target.itemSize; if (src.isInterleavedBufferAttribute || src.array.constructor !== target.array.constructor) { const vertexCount = src.count; for (let i = 0; i < vertexCount; i++) { for (let c = 0; c < itemSize; c++) { target.setComponent(i + targetOffset, c, src.getComponent(i, c)); } } } else { target.array.set(src.array, targetOffset * itemSize); } target.needsUpdate = true; } class BatchedMesh extends THREE.Mesh { constructor(maxGeometryCount, maxVertexCount, maxIndexCount = maxVertexCount * 2, material) { super(new THREE.BufferGeometry(), material); __publicField(this, "_vertexStarts"); __publicField(this, "_vertexCounts"); __publicField(this, "_indexStarts"); __publicField(this, "_indexCounts"); __publicField(this, "_reservedRanges"); __publicField(this, "_visible"); __publicField(this, "_active"); __publicField(this, "_maxGeometryCount"); __publicField(this, "_maxVertexCount"); __publicField(this, "_maxIndexCount"); __publicField(this, "_geometryInitialized"); __publicField(this, "_geometryCount"); __publicField(this, "_matrices"); __publicField(this, "_matricesTexture"); __publicField(this, "_customUniforms"); this._vertexStarts = []; this._vertexCounts = []; this._indexStarts = []; this._indexCounts = []; this._reservedRanges = []; this._visible = []; this._active = []; this._maxGeometryCount = maxGeometryCount; this._maxVertexCount = maxVertexCount; this._maxIndexCount = maxIndexCount; this._geometryInitialized = false; this._geometryCount = 0; this._matrices = []; this._matricesTexture = null; this.frustumCulled = false; this._customUniforms = { batchingTexture: { value: null } }; this._initMatricesTexture(); this._initShader(); this.onBeforeRender = function() { if (this.material.defines) { this.material.defines.BATCHING = true; } }; this.onAfterRender = function() { if (this.material.defines) { this.material.defines.BATCHING = false; } }; } _initMatricesTexture() { let size = Math.sqrt(this._maxGeometryCount * 4); size = THREE.MathUtils.ceilPowerOfTwo(size); size = Math.max(size, 4); const matricesArray = new Float32Array(size * size * 4); const matricesTexture = new THREE.DataTexture(matricesArray, size, size, THREE.RGBAFormat, THREE.FloatType); this._matricesTexture = matricesTexture; this._customUniforms.batchingTexture.value = this._matricesTexture; } _initShader() { const material = this.material; const currentOnBeforeCompile = material.onBeforeCompile; const customUniforms = this._customUniforms; material.onBeforeCompile = function onBeforeCompile(parameters, renderer) { parameters.vertexShader = parameters.vertexShader.replace("#include ", "#include \n" + batchingParsVertex).replace("#include ", "#include \n" + batchingbaseVertex).replace("#include ", "#include \n" + batchingnormalVertex).replace("#include ", "#include \n" + batchingVertex); for (const uniformName in customUniforms) { parameters.uniforms[uniformName] = customUniforms[uniformName]; } currentOnBeforeCompile.call(this, parameters, renderer); }; material.defines = material.defines || {}; material.defines.BATCHING = false; } _initializeGeometry(reference) { const geometry = this.geometry; const maxVertexCount = this._maxVertexCount; const maxGeometryCount = this._maxGeometryCount; const maxIndexCount = this._maxIndexCount; if (this._geometryInitialized === false) { for (const attributeName in reference.attributes) { const srcAttribute = reference.getAttribute(attributeName); const { array, itemSize, normalized } = srcAttribute; const dstArray = new array.constructor(maxVertexCount * itemSize); const dstAttribute = new srcAttribute.constructor(dstArray, itemSize, normalized); dstAttribute.setUsage(srcAttribute.usage); geometry.setAttribute(attributeName, dstAttribute); } if (reference.getIndex() !== null) { const indexArray = maxVertexCount > 65536 ? new Uint32Array(maxIndexCount) : new Uint16Array(maxIndexCount); geometry.setIndex(new THREE.BufferAttribute(indexArray, 1)); } const idArray = maxGeometryCount > 65536 ? new Uint32Array(maxVertexCount) : new Uint16Array(maxVertexCount); geometry.setAttribute(ID_ATTR_NAME, new THREE.BufferAttribute(idArray, 1)); this._geometryInitialized = true; } } // Make sure the geometry is compatible with the existing combined geometry atributes _validateGeometry(geometry) { if (geometry.getAttribute(ID_ATTR_NAME)) { throw new Error(`BatchedMesh: Geometry cannot use attribute "${ID_ATTR_NAME}"`); } const batchGeometry = this.geometry; if (Boolean(geometry.getIndex()) !== Boolean(batchGeometry.getIndex())) { throw new Error('BatchedMesh: All geometries must consistently have "index".'); } for (const attributeName in batchGeometry.attributes) { if (attributeName === ID_ATTR_NAME) { continue; } if (!geometry.hasAttribute(attributeName)) { throw new Error( `BatchedMesh: Added geometry missing "${attributeName}". All geometries must have consistent attributes.` ); } const srcAttribute = geometry.getAttribute(attributeName); const dstAttribute = batchGeometry.getAttribute(attributeName); if (srcAttribute.itemSize !== dstAttribute.itemSize || srcAttribute.normalized !== dstAttribute.normalized) { throw new Error("BatchedMesh: All attributes must have a consistent itemSize and normalized value."); } } } getGeometryCount() { return this._geometryCount; } getVertexCount() { const reservedRanges = this._reservedRanges; if (reservedRanges.length === 0) { return 0; } else { const finalRange = reservedRanges[reservedRanges.length - 1]; return finalRange.vertexStart + finalRange.vertexCount; } } getIndexCount() { const reservedRanges = this._reservedRanges; const geometry = this.geometry; if (geometry.getIndex() === null || reservedRanges.length === 0) { return 0; } else { const finalRange = reservedRanges[reservedRanges.length - 1]; return finalRange.indexStart + finalRange.indexCount; } } addGeometry(geometry, vertexCount = -1, indexCount = -1) { this._initializeGeometry(geometry); this._validateGeometry(geometry); if (this._geometryCount >= this._maxGeometryCount) { throw new Error("BatchedMesh: Maximum geometry count reached."); } const range = { vertexStart: -1, vertexCount: -1, indexStart: -1, indexCount: -1 }; let lastRange = null; const reservedRanges = this._reservedRanges; if (this._geometryCount !== 0) { lastRange = reservedRanges[reservedRanges.length - 1]; } if (vertexCount === -1) { range.vertexCount = geometry.getAttribute("position").count; } else { range.vertexCount = vertexCount; } if (lastRange === null) { range.vertexStart = 0; } else { range.vertexStart = lastRange.vertexStart + lastRange.vertexCount; } if (geometry.getIndex() !== null) { if (indexCount === -1) { range.indexCount = geometry.getIndex().count; } else { range.indexCount = indexCount; } if (lastRange === null) { range.indexStart = 0; } else { range.indexStart = lastRange.indexStart + lastRange.indexCount; } } if (range.indexStart !== -1 && range.indexStart + range.indexCount > this._maxIndexCount || range.vertexStart + range.vertexCount > this._maxVertexCount) { throw new Error("BatchedMesh: Reserved space request exceeds the maximum buffer size."); } const indexCounts = this._indexCounts; const indexStarts = this._indexStarts; const vertexCounts = this._vertexCounts; const vertexStarts = this._vertexStarts; const visible = this._visible; const active = this._active; const matricesTexture = this._matricesTexture; const matrices = this._matrices; const matricesArray = this._matricesTexture.image.data; visible.push(true); active.push(true); const geometryId = this._geometryCount; this._geometryCount++; matrices.push(new THREE.Matrix4()); _identityMatrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; reservedRanges.push(range); vertexStarts.push(range.vertexStart); vertexCounts.push(range.vertexCount); if (geometry.getIndex() !== null) { indexStarts.push(range.indexCount); indexCounts.push(range.indexCount); } const idAttribute = this.geometry.getAttribute(ID_ATTR_NAME); for (let i = 0; i < range.vertexCount; i++) { idAttribute.setX(range.vertexStart + i, geometryId); } idAttribute.needsUpdate = true; this.setGeometryAt(geometryId, geometry); return geometryId; } /** * @deprecated use `addGeometry` instead. */ applyGeometry(geometry) { return this.addGeometry(geometry); } setGeometryAt(id, geometry) { if (id >= this._geometryCount) { throw new Error("BatchedMesh: Maximum geometry count reached."); } this._validateGeometry(geometry); const range = this._reservedRanges[id]; if (geometry.getIndex() !== null && geometry.getIndex().count > range.indexCount || geometry.attributes.position.count > range.vertexCount) { throw new Error("BatchedMesh: Reserved space not large enough for provided geometry."); } const batchGeometry = this.geometry; const srcPositionAttribute = geometry.getAttribute("position"); const hasIndex = batchGeometry.getIndex() !== null; const dstIndex = batchGeometry.getIndex(); const srcIndex = geometry.getIndex(); const vertexStart = range.vertexStart; const vertexCount = range.vertexCount; for (const attributeName in batchGeometry.attributes) { if (attributeName === ID_ATTR_NAME) { continue; } const srcAttribute = geometry.getAttribute(attributeName); const dstAttribute = batchGeometry.getAttribute(attributeName); copyAttributeData(srcAttribute, dstAttribute, vertexStart); const itemSize = srcAttribute.itemSize; for (let i = srcAttribute.count, l = vertexCount; i < l; i++) { const index = vertexStart + i; for (let c = 0; c < itemSize; c++) { dstAttribute.setComponent(index, c, 0); } } dstAttribute.needsUpdate = true; } this._vertexCounts[id] = srcPositionAttribute.count; if (hasIndex) { const indexStart = range.indexStart; for (let i = 0; i < srcIndex.count; i++) { dstIndex.setX(indexStart + i, vertexStart + srcIndex.getX(i)); } for (let i = srcIndex.count, l = range.indexCount; i < l; i++) { dstIndex.setX(indexStart + i, vertexStart); } dstIndex.needsUpdate = true; this._indexCounts[id] = srcIndex.count; } return id; } deleteGeometry(geometryId) { const active = this._active; const matricesTexture = this._matricesTexture; const matricesArray = matricesTexture.image.data; if (geometryId >= active.length || active[geometryId] === false) { return this; } active[geometryId] = false; _zeroScaleMatrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; return this; } optimize() { throw new Error("BatchedMesh: Optimize function not implemented."); } setMatrixAt(geometryId, matrix) { const visible = this._visible; const active = this._active; const matricesTexture = this._matricesTexture; const matrices = this._matrices; const matricesArray = matricesTexture.image.data; if (geometryId >= matrices.length || active[geometryId] === false) { return this; } if (visible[geometryId] === true) { matrix.toArray(matricesArray, geometryId * 16); matricesTexture.needsUpdate = true; } matrices[geometryId].copy(matrix); return this; } getMatrixAt(geometryId, matrix) { const matrices = this._matrices; const active = this._active; if (geometryId >= matrices.length || active[geometryId] === false) { return matrix; } return matrix.copy(matrices[geometryId]); } setVisibleAt(geometryId, value) { const visible = this._visible; const active = this._active; const matricesTexture = this._matricesTexture; const matrices = this._matrices; const matricesArray = matricesTexture.image.data; if (geometryId >= visible.length || active[geometryId] === false || visible[geometryId] === value) { return this; } if (value === true) { matrices[geometryId].toArray(matricesArray, geometryId * 16); } else { _zeroScaleMatrix.toArray(matricesArray, geometryId * 16); } matricesTexture.needsUpdate = true; visible[geometryId] = value; return this; } getVisibleAt(geometryId) { const visible = this._visible; const active = this._active; if (geometryId >= visible.length || active[geometryId] === false) { return false; } return visible[geometryId]; } raycast() { console.warn("BatchedMesh: Raycast function not implemented."); } copy() { throw new Error("BatchedMesh: Copy function not implemented."); } toJSON() { throw new Error("BatchedMesh: toJSON function not implemented."); } dispose() { this.geometry.dispose(); this._matricesTexture.dispose(); this._matricesTexture = null; return this; } } exports.BatchedMesh = BatchedMesh; //# sourceMappingURL=BatchedMesh.cjs.map