@threepipe/plugin-path-tracing
Version:
Path tracing plugin interfaces for Threepipe
1,481 lines • 343 kB
JavaScript
/**
* @license
* @threepipe/plugin-path-tracing v0.1.2
* Copyright 2022-2025 repalash <palash@shaders.app>
* Apache-2.0 License
* See ./dependencies.txt for any bundled third-party dependencies and licenses.
*/
import { BufferAttribute, Vector3, Plane, Line3, Vector2, Triangle, Sphere, Matrix4, Box3, BackSide, DoubleSide, REVISION, FrontSide, UnsignedIntType, DataTexture, NearestFilter, IntType, FloatType, UnsignedByteType, UnsignedShortType, ByteType, ShortType, RGBAFormat, RGBAIntegerFormat, RGIntegerFormat, RedIntegerFormat, RGFormat, RedFormat, BufferGeometry, Matrix3, Vector4, MeshBasicMaterial, Mesh, ShaderMaterial, NoBlending, WebGLRenderTarget, FullScreenQuad, PerspectiveCamera, DataUtils, HalfFloatType, LinearFilter, RepeatWrapping, ClampToEdgeWrapping, Source, Quaternion, DataArrayTexture, WebGLArrayRenderTarget, Color, NoToneMapping, NormalBlending, EquirectangularReflectionMapping, Spherical, LinearMipMapLinearFilter, Clock, Scene, AdditiveBlending, uiNumber, uiVector, uiToggle, serialize, uiSlider, uiButton, uiFolderContainer, Euler, onChange, uiConfig, AViewerPluginSync, ProgressivePlugin, NoColorSpace } from "threepipe";
const CENTER = 0;
const AVERAGE = 1;
const SAH = 2;
const CONTAINED = 2;
const TRIANGLE_INTERSECT_COST = 1.25;
const TRAVERSAL_COST = 1;
const BYTES_PER_NODE = 6 * 4 + 4 + 4;
const IS_LEAFNODE_FLAG = 65535;
const FLOAT32_EPSILON = Math.pow(2, -24);
const SKIP_GENERATION = Symbol("SKIP_GENERATION");
function getVertexCount(geo) {
return geo.index ? geo.index.count : geo.attributes.position.count;
}
function getTriCount(geo) {
return getVertexCount(geo) / 3;
}
function getIndexArray(vertexCount, BufferConstructor = ArrayBuffer) {
if (vertexCount > 65535) {
return new Uint32Array(new BufferConstructor(4 * vertexCount));
} else {
return new Uint16Array(new BufferConstructor(2 * vertexCount));
}
}
function ensureIndex(geo, options) {
if (!geo.index) {
const vertexCount = geo.attributes.position.count;
const BufferConstructor = options.useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
const index = getIndexArray(vertexCount, BufferConstructor);
geo.setIndex(new BufferAttribute(index, 1));
for (let i = 0; i < vertexCount; i++) {
index[i] = i;
}
}
}
function getFullGeometryRange(geo, range) {
const triCount = getTriCount(geo);
const drawRange = range ? range : geo.drawRange;
const start = drawRange.start / 3;
const end = (drawRange.start + drawRange.count) / 3;
const offset = Math.max(0, start);
const count = Math.min(triCount, end) - offset;
return [{
offset: Math.floor(offset),
count: Math.floor(count)
}];
}
function getRootIndexRanges(geo, range) {
if (!geo.groups || !geo.groups.length) {
return getFullGeometryRange(geo, range);
}
const ranges = [];
const rangeBoundaries = /* @__PURE__ */ new Set();
const drawRange = range ? range : geo.drawRange;
const drawRangeStart = drawRange.start / 3;
const drawRangeEnd = (drawRange.start + drawRange.count) / 3;
for (const group of geo.groups) {
const groupStart = group.start / 3;
const groupEnd = (group.start + group.count) / 3;
rangeBoundaries.add(Math.max(drawRangeStart, groupStart));
rangeBoundaries.add(Math.min(drawRangeEnd, groupEnd));
}
const sortedBoundaries = Array.from(rangeBoundaries.values()).sort((a, b) => a - b);
for (let i = 0; i < sortedBoundaries.length - 1; i++) {
const start = sortedBoundaries[i];
const end = sortedBoundaries[i + 1];
ranges.push({
offset: Math.floor(start),
count: Math.floor(end - start)
});
}
return ranges;
}
function hasGroupGaps(geometry, range) {
const vertexCount = getTriCount(geometry);
const groups = getRootIndexRanges(geometry, range).sort((a, b) => a.offset - b.offset);
const finalGroup = groups[groups.length - 1];
finalGroup.count = Math.min(vertexCount - finalGroup.offset, finalGroup.count);
let total = 0;
groups.forEach(({ count }) => total += count);
return vertexCount !== total;
}
function getBounds(triangleBounds, offset, count, target2, centroidTarget) {
let minx = Infinity;
let miny = Infinity;
let minz = Infinity;
let maxx = -Infinity;
let maxy = -Infinity;
let maxz = -Infinity;
let cminx = Infinity;
let cminy = Infinity;
let cminz = Infinity;
let cmaxx = -Infinity;
let cmaxy = -Infinity;
let cmaxz = -Infinity;
for (let i = offset * 6, end = (offset + count) * 6; i < end; i += 6) {
const cx = triangleBounds[i + 0];
const hx = triangleBounds[i + 1];
const lx = cx - hx;
const rx = cx + hx;
if (lx < minx) minx = lx;
if (rx > maxx) maxx = rx;
if (cx < cminx) cminx = cx;
if (cx > cmaxx) cmaxx = cx;
const cy = triangleBounds[i + 2];
const hy = triangleBounds[i + 3];
const ly = cy - hy;
const ry = cy + hy;
if (ly < miny) miny = ly;
if (ry > maxy) maxy = ry;
if (cy < cminy) cminy = cy;
if (cy > cmaxy) cmaxy = cy;
const cz = triangleBounds[i + 4];
const hz = triangleBounds[i + 5];
const lz = cz - hz;
const rz = cz + hz;
if (lz < minz) minz = lz;
if (rz > maxz) maxz = rz;
if (cz < cminz) cminz = cz;
if (cz > cmaxz) cmaxz = cz;
}
target2[0] = minx;
target2[1] = miny;
target2[2] = minz;
target2[3] = maxx;
target2[4] = maxy;
target2[5] = maxz;
centroidTarget[0] = cminx;
centroidTarget[1] = cminy;
centroidTarget[2] = cminz;
centroidTarget[3] = cmaxx;
centroidTarget[4] = cmaxy;
centroidTarget[5] = cmaxz;
}
function computeTriangleBounds(geo, target2 = null, offset = null, count = null) {
const posAttr = geo.attributes.position;
const index = geo.index ? geo.index.array : null;
const triCount = getTriCount(geo);
const normalized = posAttr.normalized;
let triangleBounds;
if (target2 === null) {
triangleBounds = new Float32Array(triCount * 6);
offset = 0;
count = triCount;
} else {
triangleBounds = target2;
offset = offset || 0;
count = count || triCount;
}
const posArr = posAttr.array;
const bufferOffset = posAttr.offset || 0;
let stride = 3;
if (posAttr.isInterleavedBufferAttribute) {
stride = posAttr.data.stride;
}
const getters = ["getX", "getY", "getZ"];
for (let tri = offset; tri < offset + count; tri++) {
const tri3 = tri * 3;
const tri6 = tri * 6;
let ai = tri3 + 0;
let bi = tri3 + 1;
let ci = tri3 + 2;
if (index) {
ai = index[ai];
bi = index[bi];
ci = index[ci];
}
if (!normalized) {
ai = ai * stride + bufferOffset;
bi = bi * stride + bufferOffset;
ci = ci * stride + bufferOffset;
}
for (let el = 0; el < 3; el++) {
let a, b, c;
if (normalized) {
a = posAttr[getters[el]](ai);
b = posAttr[getters[el]](bi);
c = posAttr[getters[el]](ci);
} else {
a = posArr[ai + el];
b = posArr[bi + el];
c = posArr[ci + el];
}
let min = a;
if (b < min) min = b;
if (c < min) min = c;
let max = a;
if (b > max) max = b;
if (c > max) max = c;
const halfExtents = (max - min) / 2;
const el2 = el * 2;
triangleBounds[tri6 + el2 + 0] = min + halfExtents;
triangleBounds[tri6 + el2 + 1] = halfExtents + (Math.abs(min) + halfExtents) * FLOAT32_EPSILON;
}
}
return triangleBounds;
}
function arrayToBox(nodeIndex32, array, target2) {
target2.min.x = array[nodeIndex32];
target2.min.y = array[nodeIndex32 + 1];
target2.min.z = array[nodeIndex32 + 2];
target2.max.x = array[nodeIndex32 + 3];
target2.max.y = array[nodeIndex32 + 4];
target2.max.z = array[nodeIndex32 + 5];
return target2;
}
function getLongestEdgeIndex(bounds) {
let splitDimIdx = -1;
let splitDist = -Infinity;
for (let i = 0; i < 3; i++) {
const dist = bounds[i + 3] - bounds[i];
if (dist > splitDist) {
splitDist = dist;
splitDimIdx = i;
}
}
return splitDimIdx;
}
function copyBounds(source, target2) {
target2.set(source);
}
function unionBounds(a, b, target2) {
let aVal, bVal;
for (let d = 0; d < 3; d++) {
const d3 = d + 3;
aVal = a[d];
bVal = b[d];
target2[d] = aVal < bVal ? aVal : bVal;
aVal = a[d3];
bVal = b[d3];
target2[d3] = aVal > bVal ? aVal : bVal;
}
}
function expandByTriangleBounds(startIndex, triangleBounds, bounds) {
for (let d = 0; d < 3; d++) {
const tCenter = triangleBounds[startIndex + 2 * d];
const tHalf = triangleBounds[startIndex + 2 * d + 1];
const tMin = tCenter - tHalf;
const tMax = tCenter + tHalf;
if (tMin < bounds[d]) {
bounds[d] = tMin;
}
if (tMax > bounds[d + 3]) {
bounds[d + 3] = tMax;
}
}
}
function computeSurfaceArea(bounds) {
const d0 = bounds[3] - bounds[0];
const d1 = bounds[4] - bounds[1];
const d2 = bounds[5] - bounds[2];
return 2 * (d0 * d1 + d1 * d2 + d2 * d0);
}
const BIN_COUNT = 32;
const binsSort = (a, b) => a.candidate - b.candidate;
const sahBins = new Array(BIN_COUNT).fill().map(() => {
return {
count: 0,
bounds: new Float32Array(6),
rightCacheBounds: new Float32Array(6),
leftCacheBounds: new Float32Array(6),
candidate: 0
};
});
const leftBounds = new Float32Array(6);
function getOptimalSplit(nodeBoundingData, centroidBoundingData, triangleBounds, offset, count, strategy) {
let axis = -1;
let pos = 0;
if (strategy === CENTER) {
axis = getLongestEdgeIndex(centroidBoundingData);
if (axis !== -1) {
pos = (centroidBoundingData[axis] + centroidBoundingData[axis + 3]) / 2;
}
} else if (strategy === AVERAGE) {
axis = getLongestEdgeIndex(nodeBoundingData);
if (axis !== -1) {
pos = getAverage(triangleBounds, offset, count, axis);
}
} else if (strategy === SAH) {
const rootSurfaceArea = computeSurfaceArea(nodeBoundingData);
let bestCost = TRIANGLE_INTERSECT_COST * count;
const cStart = offset * 6;
const cEnd = (offset + count) * 6;
for (let a = 0; a < 3; a++) {
const axisLeft = centroidBoundingData[a];
const axisRight = centroidBoundingData[a + 3];
const axisLength = axisRight - axisLeft;
const binWidth = axisLength / BIN_COUNT;
if (count < BIN_COUNT / 4) {
const truncatedBins = [...sahBins];
truncatedBins.length = count;
let b = 0;
for (let c = cStart; c < cEnd; c += 6, b++) {
const bin = truncatedBins[b];
bin.candidate = triangleBounds[c + 2 * a];
bin.count = 0;
const {
bounds,
leftCacheBounds,
rightCacheBounds
} = bin;
for (let d = 0; d < 3; d++) {
rightCacheBounds[d] = Infinity;
rightCacheBounds[d + 3] = -Infinity;
leftCacheBounds[d] = Infinity;
leftCacheBounds[d + 3] = -Infinity;
bounds[d] = Infinity;
bounds[d + 3] = -Infinity;
}
expandByTriangleBounds(c, triangleBounds, bounds);
}
truncatedBins.sort(binsSort);
let splitCount = count;
for (let bi = 0; bi < splitCount; bi++) {
const bin = truncatedBins[bi];
while (bi + 1 < splitCount && truncatedBins[bi + 1].candidate === bin.candidate) {
truncatedBins.splice(bi + 1, 1);
splitCount--;
}
}
for (let c = cStart; c < cEnd; c += 6) {
const center = triangleBounds[c + 2 * a];
for (let bi = 0; bi < splitCount; bi++) {
const bin = truncatedBins[bi];
if (center >= bin.candidate) {
expandByTriangleBounds(c, triangleBounds, bin.rightCacheBounds);
} else {
expandByTriangleBounds(c, triangleBounds, bin.leftCacheBounds);
bin.count++;
}
}
}
for (let bi = 0; bi < splitCount; bi++) {
const bin = truncatedBins[bi];
const leftCount = bin.count;
const rightCount = count - bin.count;
const leftBounds2 = bin.leftCacheBounds;
const rightBounds = bin.rightCacheBounds;
let leftProb = 0;
if (leftCount !== 0) {
leftProb = computeSurfaceArea(leftBounds2) / rootSurfaceArea;
}
let rightProb = 0;
if (rightCount !== 0) {
rightProb = computeSurfaceArea(rightBounds) / rootSurfaceArea;
}
const cost = TRAVERSAL_COST + TRIANGLE_INTERSECT_COST * (leftProb * leftCount + rightProb * rightCount);
if (cost < bestCost) {
axis = a;
bestCost = cost;
pos = bin.candidate;
}
}
} else {
for (let i = 0; i < BIN_COUNT; i++) {
const bin = sahBins[i];
bin.count = 0;
bin.candidate = axisLeft + binWidth + i * binWidth;
const bounds = bin.bounds;
for (let d = 0; d < 3; d++) {
bounds[d] = Infinity;
bounds[d + 3] = -Infinity;
}
}
for (let c = cStart; c < cEnd; c += 6) {
const triCenter = triangleBounds[c + 2 * a];
const relativeCenter = triCenter - axisLeft;
let binIndex = ~~(relativeCenter / binWidth);
if (binIndex >= BIN_COUNT) binIndex = BIN_COUNT - 1;
const bin = sahBins[binIndex];
bin.count++;
expandByTriangleBounds(c, triangleBounds, bin.bounds);
}
const lastBin = sahBins[BIN_COUNT - 1];
copyBounds(lastBin.bounds, lastBin.rightCacheBounds);
for (let i = BIN_COUNT - 2; i >= 0; i--) {
const bin = sahBins[i];
const nextBin = sahBins[i + 1];
unionBounds(bin.bounds, nextBin.rightCacheBounds, bin.rightCacheBounds);
}
let leftCount = 0;
for (let i = 0; i < BIN_COUNT - 1; i++) {
const bin = sahBins[i];
const binCount = bin.count;
const bounds = bin.bounds;
const nextBin = sahBins[i + 1];
const rightBounds = nextBin.rightCacheBounds;
if (binCount !== 0) {
if (leftCount === 0) {
copyBounds(bounds, leftBounds);
} else {
unionBounds(bounds, leftBounds, leftBounds);
}
}
leftCount += binCount;
let leftProb = 0;
let rightProb = 0;
if (leftCount !== 0) {
leftProb = computeSurfaceArea(leftBounds) / rootSurfaceArea;
}
const rightCount = count - leftCount;
if (rightCount !== 0) {
rightProb = computeSurfaceArea(rightBounds) / rootSurfaceArea;
}
const cost = TRAVERSAL_COST + TRIANGLE_INTERSECT_COST * (leftProb * leftCount + rightProb * rightCount);
if (cost < bestCost) {
axis = a;
bestCost = cost;
pos = bin.candidate;
}
}
}
}
} else {
console.warn(`MeshBVH: Invalid build strategy value ${strategy} used.`);
}
return { axis, pos };
}
function getAverage(triangleBounds, offset, count, axis) {
let avg = 0;
for (let i = offset, end = offset + count; i < end; i++) {
avg += triangleBounds[i * 6 + axis * 2];
}
return avg / count;
}
class MeshBVHNode {
constructor() {
this.boundingData = new Float32Array(6);
}
}
function partition(indirectBuffer, index, triangleBounds, offset, count, split) {
let left = offset;
let right = offset + count - 1;
const pos = split.pos;
const axisOffset = split.axis * 2;
while (true) {
while (left <= right && triangleBounds[left * 6 + axisOffset] < pos) {
left++;
}
while (left <= right && triangleBounds[right * 6 + axisOffset] >= pos) {
right--;
}
if (left < right) {
for (let i = 0; i < 3; i++) {
let t0 = index[left * 3 + i];
index[left * 3 + i] = index[right * 3 + i];
index[right * 3 + i] = t0;
}
for (let i = 0; i < 6; i++) {
let tb = triangleBounds[left * 6 + i];
triangleBounds[left * 6 + i] = triangleBounds[right * 6 + i];
triangleBounds[right * 6 + i] = tb;
}
left++;
right--;
} else {
return left;
}
}
}
function partition_indirect(indirectBuffer, index, triangleBounds, offset, count, split) {
let left = offset;
let right = offset + count - 1;
const pos = split.pos;
const axisOffset = split.axis * 2;
while (true) {
while (left <= right && triangleBounds[left * 6 + axisOffset] < pos) {
left++;
}
while (left <= right && triangleBounds[right * 6 + axisOffset] >= pos) {
right--;
}
if (left < right) {
let t = indirectBuffer[left];
indirectBuffer[left] = indirectBuffer[right];
indirectBuffer[right] = t;
for (let i = 0; i < 6; i++) {
let tb = triangleBounds[left * 6 + i];
triangleBounds[left * 6 + i] = triangleBounds[right * 6 + i];
triangleBounds[right * 6 + i] = tb;
}
left++;
right--;
} else {
return left;
}
}
}
function IS_LEAF(n16, uint16Array2) {
return uint16Array2[n16 + 15] === 65535;
}
function OFFSET(n32, uint32Array2) {
return uint32Array2[n32 + 6];
}
function COUNT(n16, uint16Array2) {
return uint16Array2[n16 + 14];
}
function LEFT_NODE(n32) {
return n32 + 8;
}
function RIGHT_NODE(n32, uint32Array2) {
return uint32Array2[n32 + 6];
}
function SPLIT_AXIS(n32, uint32Array2) {
return uint32Array2[n32 + 7];
}
function BOUNDING_DATA_INDEX(n32) {
return n32;
}
let float32Array, uint32Array, uint16Array, uint8Array;
const MAX_POINTER = Math.pow(2, 32);
function countNodes(node) {
if ("count" in node) {
return 1;
} else {
return 1 + countNodes(node.left) + countNodes(node.right);
}
}
function populateBuffer(byteOffset, node, buffer) {
float32Array = new Float32Array(buffer);
uint32Array = new Uint32Array(buffer);
uint16Array = new Uint16Array(buffer);
uint8Array = new Uint8Array(buffer);
return _populateBuffer(byteOffset, node);
}
function _populateBuffer(byteOffset, node) {
const stride4Offset = byteOffset / 4;
const stride2Offset = byteOffset / 2;
const isLeaf = "count" in node;
const boundingData = node.boundingData;
for (let i = 0; i < 6; i++) {
float32Array[stride4Offset + i] = boundingData[i];
}
if (isLeaf) {
if (node.buffer) {
const buffer = node.buffer;
uint8Array.set(new Uint8Array(buffer), byteOffset);
for (let offset = byteOffset, l = byteOffset + buffer.byteLength; offset < l; offset += BYTES_PER_NODE) {
const offset2 = offset / 2;
if (!IS_LEAF(offset2, uint16Array)) {
uint32Array[offset / 4 + 6] += stride4Offset;
}
}
return byteOffset + buffer.byteLength;
} else {
const offset = node.offset;
const count = node.count;
uint32Array[stride4Offset + 6] = offset;
uint16Array[stride2Offset + 14] = count;
uint16Array[stride2Offset + 15] = IS_LEAFNODE_FLAG;
return byteOffset + BYTES_PER_NODE;
}
} else {
const left = node.left;
const right = node.right;
const splitAxis = node.splitAxis;
let nextUnusedPointer;
nextUnusedPointer = _populateBuffer(byteOffset + BYTES_PER_NODE, left);
if (nextUnusedPointer / 4 > MAX_POINTER) {
throw new Error("MeshBVH: Cannot store child pointer greater than 32 bits.");
}
uint32Array[stride4Offset + 6] = nextUnusedPointer / 4;
nextUnusedPointer = _populateBuffer(nextUnusedPointer, right);
uint32Array[stride4Offset + 7] = splitAxis;
return nextUnusedPointer;
}
}
function generateIndirectBuffer(geometry, useSharedArrayBuffer) {
const triCount = (geometry.index ? geometry.index.count : geometry.attributes.position.count) / 3;
const useUint32 = triCount > 2 ** 16;
const byteCount = useUint32 ? 4 : 2;
const buffer = useSharedArrayBuffer ? new SharedArrayBuffer(triCount * byteCount) : new ArrayBuffer(triCount * byteCount);
const indirectBuffer = useUint32 ? new Uint32Array(buffer) : new Uint16Array(buffer);
for (let i = 0, l = indirectBuffer.length; i < l; i++) {
indirectBuffer[i] = i;
}
return indirectBuffer;
}
function buildTree(bvh, triangleBounds, offset, count, options) {
const {
maxDepth,
verbose,
maxLeafTris,
strategy,
onProgress,
indirect
} = options;
const indirectBuffer = bvh._indirectBuffer;
const geometry = bvh.geometry;
const indexArray = geometry.index ? geometry.index.array : null;
const partionFunc = indirect ? partition_indirect : partition;
const totalTriangles = getTriCount(geometry);
const cacheCentroidBoundingData = new Float32Array(6);
let reachedMaxDepth = false;
const root = new MeshBVHNode();
getBounds(triangleBounds, offset, count, root.boundingData, cacheCentroidBoundingData);
splitNode(root, offset, count, cacheCentroidBoundingData);
return root;
function triggerProgress(trianglesProcessed) {
if (onProgress) {
onProgress(trianglesProcessed / totalTriangles);
}
}
function splitNode(node, offset2, count2, centroidBoundingData = null, depth = 0) {
if (!reachedMaxDepth && depth >= maxDepth) {
reachedMaxDepth = true;
if (verbose) {
console.warn(`MeshBVH: Max depth of ${maxDepth} reached when generating BVH. Consider increasing maxDepth.`);
console.warn(geometry);
}
}
if (count2 <= maxLeafTris || depth >= maxDepth) {
triggerProgress(offset2 + count2);
node.offset = offset2;
node.count = count2;
return node;
}
const split = getOptimalSplit(node.boundingData, centroidBoundingData, triangleBounds, offset2, count2, strategy);
if (split.axis === -1) {
triggerProgress(offset2 + count2);
node.offset = offset2;
node.count = count2;
return node;
}
const splitOffset = partionFunc(indirectBuffer, indexArray, triangleBounds, offset2, count2, split);
if (splitOffset === offset2 || splitOffset === offset2 + count2) {
triggerProgress(offset2 + count2);
node.offset = offset2;
node.count = count2;
} else {
node.splitAxis = split.axis;
const left = new MeshBVHNode();
const lstart = offset2;
const lcount = splitOffset - offset2;
node.left = left;
getBounds(triangleBounds, lstart, lcount, left.boundingData, cacheCentroidBoundingData);
splitNode(left, lstart, lcount, cacheCentroidBoundingData, depth + 1);
const right = new MeshBVHNode();
const rstart = splitOffset;
const rcount = count2 - lcount;
node.right = right;
getBounds(triangleBounds, rstart, rcount, right.boundingData, cacheCentroidBoundingData);
splitNode(right, rstart, rcount, cacheCentroidBoundingData, depth + 1);
}
return node;
}
}
function buildPackedTree(bvh, options) {
const geometry = bvh.geometry;
if (options.indirect) {
bvh._indirectBuffer = generateIndirectBuffer(geometry, options.useSharedArrayBuffer);
if (hasGroupGaps(geometry, options.range) && !options.verbose) {
console.warn(
'MeshBVH: Provided geometry contains groups or a range that do not fully span the vertex contents while using the "indirect" option. BVH may incorrectly report intersections on unrendered portions of the geometry.'
);
}
}
if (!bvh._indirectBuffer) {
ensureIndex(geometry, options);
}
const BufferConstructor = options.useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
const triangleBounds = computeTriangleBounds(geometry);
const geometryRanges = options.indirect ? getFullGeometryRange(geometry, options.range) : getRootIndexRanges(geometry, options.range);
bvh._roots = geometryRanges.map((range) => {
const root = buildTree(bvh, triangleBounds, range.offset, range.count, options);
const nodeCount = countNodes(root);
const buffer = new BufferConstructor(BYTES_PER_NODE * nodeCount);
populateBuffer(0, root, buffer);
return buffer;
});
}
class SeparatingAxisBounds {
constructor() {
this.min = Infinity;
this.max = -Infinity;
}
setFromPointsField(points, field) {
let min = Infinity;
let max = -Infinity;
for (let i = 0, l = points.length; i < l; i++) {
const p = points[i];
const val = p[field];
min = val < min ? val : min;
max = val > max ? val : max;
}
this.min = min;
this.max = max;
}
setFromPoints(axis, points) {
let min = Infinity;
let max = -Infinity;
for (let i = 0, l = points.length; i < l; i++) {
const p = points[i];
const val = axis.dot(p);
min = val < min ? val : min;
max = val > max ? val : max;
}
this.min = min;
this.max = max;
}
isSeparated(other) {
return this.min > other.max || other.min > this.max;
}
}
SeparatingAxisBounds.prototype.setFromBox = function() {
const p = new Vector3();
return function setFromBox(axis, box) {
const boxMin = box.min;
const boxMax = box.max;
let min = Infinity;
let max = -Infinity;
for (let x = 0; x <= 1; x++) {
for (let y = 0; y <= 1; y++) {
for (let z = 0; z <= 1; z++) {
p.x = boxMin.x * x + boxMax.x * (1 - x);
p.y = boxMin.y * y + boxMax.y * (1 - y);
p.z = boxMin.z * z + boxMax.z * (1 - z);
const val = axis.dot(p);
min = Math.min(val, min);
max = Math.max(val, max);
}
}
}
this.min = min;
this.max = max;
};
}();
const closestPointLineToLine = function() {
const dir1 = new Vector3();
const dir2 = new Vector3();
const v02 = new Vector3();
return function closestPointLineToLine2(l1, l2, result) {
const v0 = l1.start;
const v10 = dir1;
const v2 = l2.start;
const v32 = dir2;
v02.subVectors(v0, v2);
dir1.subVectors(l1.end, l1.start);
dir2.subVectors(l2.end, l2.start);
const d0232 = v02.dot(v32);
const d3210 = v32.dot(v10);
const d3232 = v32.dot(v32);
const d0210 = v02.dot(v10);
const d1010 = v10.dot(v10);
const denom = d1010 * d3232 - d3210 * d3210;
let d, d2;
if (denom !== 0) {
d = (d0232 * d3210 - d0210 * d3232) / denom;
} else {
d = 0;
}
d2 = (d0232 + d * d3210) / d3232;
result.x = d;
result.y = d2;
};
}();
const closestPointsSegmentToSegment = function() {
const paramResult = new Vector2();
const temp12 = new Vector3();
const temp22 = new Vector3();
return function closestPointsSegmentToSegment2(l1, l2, target1, target2) {
closestPointLineToLine(l1, l2, paramResult);
let d = paramResult.x;
let d2 = paramResult.y;
if (d >= 0 && d <= 1 && d2 >= 0 && d2 <= 1) {
l1.at(d, target1);
l2.at(d2, target2);
return;
} else if (d >= 0 && d <= 1) {
if (d2 < 0) {
l2.at(0, target2);
} else {
l2.at(1, target2);
}
l1.closestPointToPoint(target2, true, target1);
return;
} else if (d2 >= 0 && d2 <= 1) {
if (d < 0) {
l1.at(0, target1);
} else {
l1.at(1, target1);
}
l2.closestPointToPoint(target1, true, target2);
return;
} else {
let p;
if (d < 0) {
p = l1.start;
} else {
p = l1.end;
}
let p2;
if (d2 < 0) {
p2 = l2.start;
} else {
p2 = l2.end;
}
const closestPoint = temp12;
const closestPoint2 = temp22;
l1.closestPointToPoint(p2, true, temp12);
l2.closestPointToPoint(p, true, temp22);
if (closestPoint.distanceToSquared(p2) <= closestPoint2.distanceToSquared(p)) {
target1.copy(closestPoint);
target2.copy(p2);
return;
} else {
target1.copy(p);
target2.copy(closestPoint2);
return;
}
}
};
}();
const sphereIntersectTriangle = function() {
const closestPointTemp = new Vector3();
const projectedPointTemp = new Vector3();
const planeTemp = new Plane();
const lineTemp = new Line3();
return function sphereIntersectTriangle2(sphere, triangle3) {
const { radius, center } = sphere;
const { a, b, c } = triangle3;
lineTemp.start = a;
lineTemp.end = b;
const closestPoint1 = lineTemp.closestPointToPoint(center, true, closestPointTemp);
if (closestPoint1.distanceTo(center) <= radius) return true;
lineTemp.start = a;
lineTemp.end = c;
const closestPoint2 = lineTemp.closestPointToPoint(center, true, closestPointTemp);
if (closestPoint2.distanceTo(center) <= radius) return true;
lineTemp.start = b;
lineTemp.end = c;
const closestPoint3 = lineTemp.closestPointToPoint(center, true, closestPointTemp);
if (closestPoint3.distanceTo(center) <= radius) return true;
const plane = triangle3.getPlane(planeTemp);
const dp = Math.abs(plane.distanceToPoint(center));
if (dp <= radius) {
const pp = plane.projectPoint(center, projectedPointTemp);
const cp = triangle3.containsPoint(pp);
if (cp) return true;
}
return false;
};
}();
const ZERO_EPSILON = 1e-15;
function isNearZero(value) {
return Math.abs(value) < ZERO_EPSILON;
}
class ExtendedTriangle extends Triangle {
constructor(...args) {
super(...args);
this.isExtendedTriangle = true;
this.satAxes = new Array(4).fill().map(() => new Vector3());
this.satBounds = new Array(4).fill().map(() => new SeparatingAxisBounds());
this.points = [this.a, this.b, this.c];
this.sphere = new Sphere();
this.plane = new Plane();
this.needsUpdate = true;
}
intersectsSphere(sphere) {
return sphereIntersectTriangle(sphere, this);
}
update() {
const a = this.a;
const b = this.b;
const c = this.c;
const points = this.points;
const satAxes = this.satAxes;
const satBounds = this.satBounds;
const axis0 = satAxes[0];
const sab0 = satBounds[0];
this.getNormal(axis0);
sab0.setFromPoints(axis0, points);
const axis1 = satAxes[1];
const sab1 = satBounds[1];
axis1.subVectors(a, b);
sab1.setFromPoints(axis1, points);
const axis2 = satAxes[2];
const sab2 = satBounds[2];
axis2.subVectors(b, c);
sab2.setFromPoints(axis2, points);
const axis3 = satAxes[3];
const sab3 = satBounds[3];
axis3.subVectors(c, a);
sab3.setFromPoints(axis3, points);
this.sphere.setFromPoints(this.points);
this.plane.setFromNormalAndCoplanarPoint(axis0, a);
this.needsUpdate = false;
}
}
ExtendedTriangle.prototype.closestPointToSegment = function() {
const point1 = new Vector3();
const point2 = new Vector3();
const edge = new Line3();
return function distanceToSegment(segment, target1 = null, target2 = null) {
const { start, end } = segment;
const points = this.points;
let distSq;
let closestDistanceSq = Infinity;
for (let i = 0; i < 3; i++) {
const nexti = (i + 1) % 3;
edge.start.copy(points[i]);
edge.end.copy(points[nexti]);
closestPointsSegmentToSegment(edge, segment, point1, point2);
distSq = point1.distanceToSquared(point2);
if (distSq < closestDistanceSq) {
closestDistanceSq = distSq;
if (target1) target1.copy(point1);
if (target2) target2.copy(point2);
}
}
this.closestPointToPoint(start, point1);
distSq = start.distanceToSquared(point1);
if (distSq < closestDistanceSq) {
closestDistanceSq = distSq;
if (target1) target1.copy(point1);
if (target2) target2.copy(start);
}
this.closestPointToPoint(end, point1);
distSq = end.distanceToSquared(point1);
if (distSq < closestDistanceSq) {
closestDistanceSq = distSq;
if (target1) target1.copy(point1);
if (target2) target2.copy(end);
}
return Math.sqrt(closestDistanceSq);
};
}();
ExtendedTriangle.prototype.intersectsTriangle = function() {
const saTri2 = new ExtendedTriangle();
const arr1 = new Array(3);
const arr2 = new Array(3);
const cachedSatBounds = new SeparatingAxisBounds();
const cachedSatBounds2 = new SeparatingAxisBounds();
const cachedAxis = new Vector3();
const dir = new Vector3();
const dir1 = new Vector3();
const dir2 = new Vector3();
const tempDir = new Vector3();
const edge = new Line3();
const edge1 = new Line3();
const edge2 = new Line3();
const tempPoint = new Vector3();
function triIntersectPlane(tri, plane, targetEdge) {
const points = tri.points;
let count = 0;
let startPointIntersection = -1;
for (let i = 0; i < 3; i++) {
const { start, end } = edge;
start.copy(points[i]);
end.copy(points[(i + 1) % 3]);
edge.delta(dir);
const startIntersects = isNearZero(plane.distanceToPoint(start));
if (isNearZero(plane.normal.dot(dir)) && startIntersects) {
targetEdge.copy(edge);
count = 2;
break;
}
const doesIntersect = plane.intersectLine(edge, tempPoint);
if (!doesIntersect && startIntersects) {
tempPoint.copy(start);
}
if ((doesIntersect || startIntersects) && !isNearZero(tempPoint.distanceTo(end))) {
if (count <= 1) {
const point = count === 1 ? targetEdge.start : targetEdge.end;
point.copy(tempPoint);
if (startIntersects) {
startPointIntersection = count;
}
} else if (count >= 2) {
const point = startPointIntersection === 1 ? targetEdge.start : targetEdge.end;
point.copy(tempPoint);
count = 2;
break;
}
count++;
if (count === 2 && startPointIntersection === -1) {
break;
}
}
}
return count;
}
return function intersectsTriangle(other, target2 = null, suppressLog = false) {
if (this.needsUpdate) {
this.update();
}
if (!other.isExtendedTriangle) {
saTri2.copy(other);
saTri2.update();
other = saTri2;
} else if (other.needsUpdate) {
other.update();
}
const plane1 = this.plane;
const plane2 = other.plane;
if (Math.abs(plane1.normal.dot(plane2.normal)) > 1 - 1e-10) {
const satBounds1 = this.satBounds;
const satAxes1 = this.satAxes;
arr2[0] = other.a;
arr2[1] = other.b;
arr2[2] = other.c;
for (let i = 0; i < 4; i++) {
const sb = satBounds1[i];
const sa = satAxes1[i];
cachedSatBounds.setFromPoints(sa, arr2);
if (sb.isSeparated(cachedSatBounds)) return false;
}
const satBounds2 = other.satBounds;
const satAxes2 = other.satAxes;
arr1[0] = this.a;
arr1[1] = this.b;
arr1[2] = this.c;
for (let i = 0; i < 4; i++) {
const sb = satBounds2[i];
const sa = satAxes2[i];
cachedSatBounds.setFromPoints(sa, arr1);
if (sb.isSeparated(cachedSatBounds)) return false;
}
for (let i = 0; i < 4; i++) {
const sa1 = satAxes1[i];
for (let i2 = 0; i2 < 4; i2++) {
const sa2 = satAxes2[i2];
cachedAxis.crossVectors(sa1, sa2);
cachedSatBounds.setFromPoints(cachedAxis, arr1);
cachedSatBounds2.setFromPoints(cachedAxis, arr2);
if (cachedSatBounds.isSeparated(cachedSatBounds2)) return false;
}
}
if (target2) {
if (!suppressLog) {
console.warn("ExtendedTriangle.intersectsTriangle: Triangles are coplanar which does not support an output edge. Setting edge to 0, 0, 0.");
}
target2.start.set(0, 0, 0);
target2.end.set(0, 0, 0);
}
return true;
} else {
const count1 = triIntersectPlane(this, plane2, edge1);
if (count1 === 1 && other.containsPoint(edge1.end)) {
if (target2) {
target2.start.copy(edge1.end);
target2.end.copy(edge1.end);
}
return true;
} else if (count1 !== 2) {
return false;
}
const count2 = triIntersectPlane(other, plane1, edge2);
if (count2 === 1 && this.containsPoint(edge2.end)) {
if (target2) {
target2.start.copy(edge2.end);
target2.end.copy(edge2.end);
}
return true;
} else if (count2 !== 2) {
return false;
}
edge1.delta(dir1);
edge2.delta(dir2);
if (dir1.dot(dir2) < 0) {
let tmp = edge2.start;
edge2.start = edge2.end;
edge2.end = tmp;
}
const s1 = edge1.start.dot(dir1);
const e1 = edge1.end.dot(dir1);
const s2 = edge2.start.dot(dir1);
const e2 = edge2.end.dot(dir1);
const separated1 = e1 < s2;
const separated2 = s1 < e2;
if (s1 !== e2 && s2 !== e1 && separated1 === separated2) {
return false;
}
if (target2) {
tempDir.subVectors(edge1.start, edge2.start);
if (tempDir.dot(dir1) > 0) {
target2.start.copy(edge1.start);
} else {
target2.start.copy(edge2.start);
}
tempDir.subVectors(edge1.end, edge2.end);
if (tempDir.dot(dir1) < 0) {
target2.end.copy(edge1.end);
} else {
target2.end.copy(edge2.end);
}
}
return true;
}
};
}();
ExtendedTriangle.prototype.distanceToPoint = function() {
const target2 = new Vector3();
return function distanceToPoint(point) {
this.closestPointToPoint(point, target2);
return point.distanceTo(target2);
};
}();
ExtendedTriangle.prototype.distanceToTriangle = function() {
const point = new Vector3();
const point2 = new Vector3();
const cornerFields = ["a", "b", "c"];
const line1 = new Line3();
const line2 = new Line3();
return function distanceToTriangle(other, target1 = null, target2 = null) {
const lineTarget = target1 || target2 ? line1 : null;
if (this.intersectsTriangle(other, lineTarget)) {
if (target1 || target2) {
if (target1) lineTarget.getCenter(target1);
if (target2) lineTarget.getCenter(target2);
}
return 0;
}
let closestDistanceSq = Infinity;
for (let i = 0; i < 3; i++) {
let dist;
const field = cornerFields[i];
const otherVec = other[field];
this.closestPointToPoint(otherVec, point);
dist = otherVec.distanceToSquared(point);
if (dist < closestDistanceSq) {
closestDistanceSq = dist;
if (target1) target1.copy(point);
if (target2) target2.copy(otherVec);
}
const thisVec = this[field];
other.closestPointToPoint(thisVec, point);
dist = thisVec.distanceToSquared(point);
if (dist < closestDistanceSq) {
closestDistanceSq = dist;
if (target1) target1.copy(thisVec);
if (target2) target2.copy(point);
}
}
for (let i = 0; i < 3; i++) {
const f11 = cornerFields[i];
const f12 = cornerFields[(i + 1) % 3];
line1.set(this[f11], this[f12]);
for (let i2 = 0; i2 < 3; i2++) {
const f21 = cornerFields[i2];
const f22 = cornerFields[(i2 + 1) % 3];
line2.set(other[f21], other[f22]);
closestPointsSegmentToSegment(line1, line2, point, point2);
const dist = point.distanceToSquared(point2);
if (dist < closestDistanceSq) {
closestDistanceSq = dist;
if (target1) target1.copy(point);
if (target2) target2.copy(point2);
}
}
}
return Math.sqrt(closestDistanceSq);
};
}();
class OrientedBox {
constructor(min, max, matrix) {
this.isOrientedBox = true;
this.min = new Vector3();
this.max = new Vector3();
this.matrix = new Matrix4();
this.invMatrix = new Matrix4();
this.points = new Array(8).fill().map(() => new Vector3());
this.satAxes = new Array(3).fill().map(() => new Vector3());
this.satBounds = new Array(3).fill().map(() => new SeparatingAxisBounds());
this.alignedSatBounds = new Array(3).fill().map(() => new SeparatingAxisBounds());
this.needsUpdate = false;
if (min) this.min.copy(min);
if (max) this.max.copy(max);
if (matrix) this.matrix.copy(matrix);
}
set(min, max, matrix) {
this.min.copy(min);
this.max.copy(max);
this.matrix.copy(matrix);
this.needsUpdate = true;
}
copy(other) {
this.min.copy(other.min);
this.max.copy(other.max);
this.matrix.copy(other.matrix);
this.needsUpdate = true;
}
}
OrientedBox.prototype.update = /* @__PURE__ */ function() {
return function update() {
const matrix = this.matrix;
const min = this.min;
const max = this.max;
const points = this.points;
for (let x = 0; x <= 1; x++) {
for (let y = 0; y <= 1; y++) {
for (let z = 0; z <= 1; z++) {
const i = (1 << 0) * x | (1 << 1) * y | (1 << 2) * z;
const v2 = points[i];
v2.x = x ? max.x : min.x;
v2.y = y ? max.y : min.y;
v2.z = z ? max.z : min.z;
v2.applyMatrix4(matrix);
}
}
}
const satBounds = this.satBounds;
const satAxes = this.satAxes;
const minVec = points[0];
for (let i = 0; i < 3; i++) {
const axis = satAxes[i];
const sb = satBounds[i];
const index = 1 << i;
const pi = points[index];
axis.subVectors(minVec, pi);
sb.setFromPoints(axis, points);
}
const alignedSatBounds = this.alignedSatBounds;
alignedSatBounds[0].setFromPointsField(points, "x");
alignedSatBounds[1].setFromPointsField(points, "y");
alignedSatBounds[2].setFromPointsField(points, "z");
this.invMatrix.copy(this.matrix).invert();
this.needsUpdate = false;
};
}();
OrientedBox.prototype.intersectsBox = function() {
const aabbBounds = new SeparatingAxisBounds();
return function intersectsBox(box) {
if (this.needsUpdate) {
this.update();
}
const min = box.min;
const max = box.max;
const satBounds = this.satBounds;
const satAxes = this.satAxes;
const alignedSatBounds = this.alignedSatBounds;
aabbBounds.min = min.x;
aabbBounds.max = max.x;
if (alignedSatBounds[0].isSeparated(aabbBounds)) return false;
aabbBounds.min = min.y;
aabbBounds.max = max.y;
if (alignedSatBounds[1].isSeparated(aabbBounds)) return false;
aabbBounds.min = min.z;
aabbBounds.max = max.z;
if (alignedSatBounds[2].isSeparated(aabbBounds)) return false;
for (let i = 0; i < 3; i++) {
const axis = satAxes[i];
const sb = satBounds[i];
aabbBounds.setFromBox(axis, box);
if (sb.isSeparated(aabbBounds)) return false;
}
return true;
};
}();
OrientedBox.prototype.intersectsTriangle = function() {
const saTri = new ExtendedTriangle();
const pointsArr = new Array(3);
const cachedSatBounds = new SeparatingAxisBounds();
const cachedSatBounds2 = new SeparatingAxisBounds();
const cachedAxis = new Vector3();
return function intersectsTriangle(triangle3) {
if (this.needsUpdate) {
this.update();
}
if (!triangle3.isExtendedTriangle) {
saTri.copy(triangle3);
saTri.update();
triangle3 = saTri;
} else if (triangle3.needsUpdate) {
triangle3.update();
}
const satBounds = this.satBounds;
const satAxes = this.satAxes;
pointsArr[0] = triangle3.a;
pointsArr[1] = triangle3.b;
pointsArr[2] = triangle3.c;
for (let i = 0; i < 3; i++) {
const sb = satBounds[i];
const sa = satAxes[i];
cachedSatBounds.setFromPoints(sa, pointsArr);
if (sb.isSeparated(cachedSatBounds)) return false;
}
const triSatBounds = triangle3.satBounds;
const triSatAxes = triangle3.satAxes;
const points = this.points;
for (let i = 0; i < 3; i++) {
const sb = triSatBounds[i];
const sa = triSatAxes[i];
cachedSatBounds.setFromPoints(sa, points);
if (sb.isSeparated(cachedSatBounds)) return false;
}
for (let i = 0; i < 3; i++) {
const sa1 = satAxes[i];
for (let i2 = 0; i2 < 4; i2++) {
const sa2 = triSatAxes[i2];
cachedAxis.crossVectors(sa1, sa2);
cachedSatBounds.setFromPoints(cachedAxis, pointsArr);
cachedSatBounds2.setFromPoints(cachedAxis, points);
if (cachedSatBounds.isSeparated(cachedSatBounds2)) return false;
}
}
return true;
};
}();
OrientedBox.prototype.closestPointToPoint = /* @__PURE__ */ function() {
return function closestPointToPoint2(point, target1) {
if (this.needsUpdate) {
this.update();
}
target1.copy(point).applyMatrix4(this.invMatrix).clamp(this.min, this.max).applyMatrix4(this.matrix);
return target1;
};
}();
OrientedBox.prototype.distanceToPoint = function() {
const target2 = new Vector3();
return function distanceToPoint(point) {
this.closestPointToPoint(point, target2);
return point.distanceTo(target2);
};
}();
OrientedBox.prototype.distanceToBox = function() {
const xyzFields = ["x", "y", "z"];
const segments1 = new Array(12).fill().map(() => new Line3());
const segments2 = new Array(12).fill().map(() => new Line3());
const point1 = new Vector3();
const point2 = new Vector3();
return function distanceToBox(box, threshold = 0, target1 = null, target2 = null) {
if (this.needsUpdate) {
this.update();
}
if (this.intersectsBox(box)) {
if (target1 || target2) {
box.getCenter(point2);
this.closestPointToPoint(point2, point1);
box.closestPointToPoint(point1, point2);
if (target1) target1.copy(point1);
if (target2) target2.copy(point2);
}
return 0;
}
const threshold2 = threshold * threshold;
const min = box.min;
const max = box.max;
const points = this.points;
let closestDistanceSq = Infinity;
for (let i = 0; i < 8; i++) {
const p = points[i];
point2.copy(p).clamp(min, max);
const dist = p.distanceToSquared(point2);
if (dist < closestDistanceSq) {
closestDistanceSq = dist;
if (target1) target1.copy(p);
if (target2) target2.copy(point2);
if (dist < threshold2) return Math.sqrt(dist);
}
}
let count = 0;
for (let i = 0; i < 3; i++) {
for (let i1 = 0; i1 <= 1; i1++) {
for (let i2 = 0; i2 <= 1; i2++) {
const nextIndex = (i + 1) % 3;
const nextIndex2 = (i + 2) % 3;
const index = i1 << nextIndex | i2 << nextIndex2;
const index2 = 1 << i | i1 << nextIndex | i2 << nextIndex2;
const p1 = points[index];
const p2 = points[index2];
const line1 = segments1[count];
line1.set(p1, p2);
const f1 = xyzFields[i];
const f2 = xyzFields[nextIndex];
const f3 = xyzFields[nextIndex2];
const line2 = segments2[count];
const start = line2.start;
const end = line2.end;
start[f1] = min[f1];
start[f2] = i1 ? min[f2] : max[f2];
start[f3] = i2 ? min[f3] : max[f2];
end[f1] = max[f1];
end[f2] = i1 ? min[f2] : max[f2];
end[f3] = i2 ? min[f3] : max[f2];
count++;
}
}
}
for (let x = 0; x <= 1; x++) {
for (let y = 0; y <= 1; y++) {
for (let z = 0; z <= 1; z++) {
point2.x = x ? max.x : min.x;
point2.y = y ? max.y : min.y;
point2.z = z ? max.z : min.z;
this.closestPointToPoint(point2, point1);
const dist = point2.distanceToSquared(point1);
if (dist < closestDistanceSq) {
closestDistanceSq = dist;
if (target1) target1.copy(point1);
if (target2) target2.copy(point2);
if (dist < threshold2) return Math.sqrt(dist);
}
}
}
}
for (let i = 0; i < 12; i++) {
const l1 = segments1[i];
for (let i2 = 0; i2 < 12; i2++) {
const l2 = segments2[i2];
closestPointsSegmentToSegment(l1, l2, point1, point2);
const dist = point1.distanceToSquared(point2);
if (dist < closestDistanceSq) {
closestDistanceSq = dist;
if (target1) target1.copy(point1);
if (target2) target2.copy(point2);
if (dist < threshold2) return Math.sqrt(dist);
}
}
}
return Math.sqrt(closestDistanceSq);
};
}();
class PrimitivePool {
constructor(getNewPrimitive) {
this._getNewPrimitive = getNewPrimitive;
this._primitives = [];
}
getPrimitive() {
const primitives = this._primitives;
if (primitives.length === 0) {
return this._getNewPrimitive();
} else {
return primitives.pop();
}
}
releasePrimitive(primitive) {
this._primitives.push(primitive);
}
}
class ExtendedTrianglePoolBase extends PrimitivePool {
constructor() {
super(() => new ExtendedTriangle());
}
}
const ExtendedTrianglePool = /* @__PURE__ */ new ExtendedTrianglePoolBase();
class _BufferStack {
constructor() {
this.float32Array = null;
this.uint16Array = null;
this.uint32Array = null;
const stack = [];
let prevBuffer = null;
this.setBuffer = (buffer) => {
if (prevBuffer) {
stack.push(prevBuffer);
}
prevBuffer = buffer;
this.float32Array = new Float32Array(buffer);
this.uint16Array = new Uint16Array(buffer);
this.uint32Array = new Uint32Array(buffer);
};
this.clearBuffer = () => {
prevBuffer = null;
this.float32Array = null;
this.uint16Array = null;
this.uint32Array = null;
if (stack.length !== 0) {
this.setBuffer(stack.pop());
}
};
}
}
const BufferStack = new _BufferStack();
let _box1, _box2;
const boxStack = [];
const boxPool = /* @__PURE__ */ new PrimitivePool(() => new Box3());
function shapecast(bvh, root, intersectsBounds, intersectsRange, boundsTraverseOrder, byteOffset) {
_box1 = boxPool.getPrimitive();
_box2 = boxPool.getPrimitive();
boxStack.push(_box1, _box2);
BufferStack.setBuffer(bvh._roots[root]);
const result = shapecastTraverse(0, bvh.geometry, intersectsBounds, intersectsRange, boundsTraverseOrder, byteOffset);
BufferStack.cle