Initial project import
This commit is contained in:
21
node_modules/three-mesh-bvh/LICENSE
generated
vendored
Normal file
21
node_modules/three-mesh-bvh/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Garrett Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1176
node_modules/three-mesh-bvh/README.md
generated
vendored
Normal file
1176
node_modules/three-mesh-bvh/README.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8373
node_modules/three-mesh-bvh/build/index.module.js
generated
vendored
Normal file
8373
node_modules/three-mesh-bvh/build/index.module.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/three-mesh-bvh/build/index.module.js.map
generated
vendored
Normal file
1
node_modules/three-mesh-bvh/build/index.module.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
8408
node_modules/three-mesh-bvh/build/index.umd.cjs
generated
vendored
Normal file
8408
node_modules/three-mesh-bvh/build/index.umd.cjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/three-mesh-bvh/build/index.umd.cjs.map
generated
vendored
Normal file
1
node_modules/three-mesh-bvh/build/index.umd.cjs.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
83
node_modules/three-mesh-bvh/package.json
generated
vendored
Normal file
83
node_modules/three-mesh-bvh/package.json
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "three-mesh-bvh",
|
||||
"version": "0.8.3",
|
||||
"description": "A BVH implementation to speed up raycasting against three.js meshes.",
|
||||
"module": "src/index.js",
|
||||
"main": "build/index.umd.cjs",
|
||||
"type": "module",
|
||||
"types": "src/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"start": "concurrently \"rollup -w -c rollup-templating.config.js\" \"vite --config ./vite.config.js\"",
|
||||
"build": "rollup -c rollup-templating.config.js && rollup -c",
|
||||
"build-silent": "rollup -c rollup-templating.config.js --silent && rollup -c --silent",
|
||||
"build-examples": "npm run build && vite build --config ./vite.config.js && cp ./example/coi-serviceworker.js ./example/bundle/",
|
||||
"test": "npm run build-silent && cd test && jest",
|
||||
"lint": "eslint \"./src/**/*.{js,ts}\" \"./test/**/*.{js,ts}\" \"./example/*.js\" && tsc --noEmit",
|
||||
"benchmark": "npm run build-silent && node benchmark/run-benchmark.js",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"files": [
|
||||
"src/*",
|
||||
"build/*"
|
||||
],
|
||||
"keywords": [
|
||||
"graphics",
|
||||
"raycast",
|
||||
"tree",
|
||||
"bounds",
|
||||
"threejs",
|
||||
"three-js",
|
||||
"bounds-hierarchy",
|
||||
"performance",
|
||||
"raytracing",
|
||||
"pathtracing",
|
||||
"geometry",
|
||||
"mesh",
|
||||
"distance",
|
||||
"intersection",
|
||||
"acceleration",
|
||||
"bvh",
|
||||
"webvr",
|
||||
"webxr"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/gkjohnson/three-mesh-bvh.git"
|
||||
},
|
||||
"author": "Garrett Johnson <garrett.kjohnson@gmail.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/gkjohnson/three-mesh-bvh/issues"
|
||||
},
|
||||
"homepage": "https://github.com/gkjohnson/three-mesh-bvh#readme",
|
||||
"peerDependencies": {
|
||||
"three": ">= 0.159.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/preset-env": "^7.15.4",
|
||||
"@types/eslint": "^7.28.1",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/three": "^0.166.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
||||
"@typescript-eslint/parser": "^7.14.1",
|
||||
"babel-jest": "^27.2.4",
|
||||
"concurrently": "^8.2.1",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-mdcs": "^5.0.0",
|
||||
"eslint-plugin-jest": "^28.6.0",
|
||||
"glob": "^10.3.3",
|
||||
"jest": "^27.2.4",
|
||||
"preprocess": "^3.2.0",
|
||||
"rollup": "^3.28.1",
|
||||
"script-loader": "^0.7.2",
|
||||
"simple-git": "^3.19.1",
|
||||
"simplex-noise": "^2.4.0",
|
||||
"static-server": "^2.2.1",
|
||||
"stats.js": "^0.17.0",
|
||||
"three": "^0.170.0",
|
||||
"typescript": "^5.1.3",
|
||||
"vite": "^5.2.13"
|
||||
}
|
||||
}
|
||||
27
node_modules/three-mesh-bvh/src/core/Constants.js
generated
vendored
Normal file
27
node_modules/three-mesh-bvh/src/core/Constants.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Split strategy constants
|
||||
export const CENTER = 0;
|
||||
export const AVERAGE = 1;
|
||||
export const SAH = 2;
|
||||
|
||||
// Traversal constants
|
||||
export const NOT_INTERSECTED = 0;
|
||||
export const INTERSECTED = 1;
|
||||
export const CONTAINED = 2;
|
||||
|
||||
// SAH cost constants
|
||||
// TODO: hone these costs more. The relative difference between them should be the
|
||||
// difference in measured time to perform a triangle intersection vs traversing
|
||||
// bounds.
|
||||
export const TRIANGLE_INTERSECT_COST = 1.25;
|
||||
export const TRAVERSAL_COST = 1;
|
||||
|
||||
|
||||
// Build constants
|
||||
export const BYTES_PER_NODE = 6 * 4 + 4 + 4;
|
||||
export const IS_LEAFNODE_FLAG = 0xFFFF;
|
||||
|
||||
// EPSILON for computing floating point error during build
|
||||
// https://en.wikipedia.org/wiki/Machine_epsilon#Values_for_standard_hardware_floating_point_arithmetics
|
||||
export const FLOAT32_EPSILON = Math.pow( 2, - 24 );
|
||||
|
||||
export const SKIP_GENERATION = Symbol( 'SKIP_GENERATION' );
|
||||
548
node_modules/three-mesh-bvh/src/core/MeshBVH.js
generated
vendored
Normal file
548
node_modules/three-mesh-bvh/src/core/MeshBVH.js
generated
vendored
Normal file
@@ -0,0 +1,548 @@
|
||||
import { BufferAttribute, Box3, FrontSide } from 'three';
|
||||
import { CENTER, BYTES_PER_NODE, IS_LEAFNODE_FLAG, SKIP_GENERATION } from './Constants.js';
|
||||
import { buildPackedTree } from './build/buildTree.js';
|
||||
import { OrientedBox } from '../math/OrientedBox.js';
|
||||
import { arrayToBox } from '../utils/ArrayBoxUtilities.js';
|
||||
import { ExtendedTrianglePool } from '../utils/ExtendedTrianglePool.js';
|
||||
import { shapecast } from './cast/shapecast.js';
|
||||
import { closestPointToPoint } from './cast/closestPointToPoint.js';
|
||||
|
||||
import { iterateOverTriangles } from './utils/iterationUtils.generated.js';
|
||||
import { refit } from './cast/refit.generated.js';
|
||||
import { raycast } from './cast/raycast.generated.js';
|
||||
import { raycastFirst } from './cast/raycastFirst.generated.js';
|
||||
import { intersectsGeometry } from './cast/intersectsGeometry.generated.js';
|
||||
import { closestPointToGeometry } from './cast/closestPointToGeometry.generated.js';
|
||||
|
||||
import { iterateOverTriangles_indirect } from './utils/iterationUtils_indirect.generated.js';
|
||||
import { refit_indirect } from './cast/refit_indirect.generated.js';
|
||||
import { raycast_indirect } from './cast/raycast_indirect.generated.js';
|
||||
import { raycastFirst_indirect } from './cast/raycastFirst_indirect.generated.js';
|
||||
import { intersectsGeometry_indirect } from './cast/intersectsGeometry_indirect.generated.js';
|
||||
import { closestPointToGeometry_indirect } from './cast/closestPointToGeometry_indirect.generated.js';
|
||||
import { isSharedArrayBufferSupported } from '../utils/BufferUtils.js';
|
||||
import { setTriangle } from '../utils/TriangleUtilities.js';
|
||||
import { bvhcast } from './cast/bvhcast.js';
|
||||
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const tempBox = /* @__PURE__ */ new Box3();
|
||||
export const DEFAULT_OPTIONS = {
|
||||
strategy: CENTER,
|
||||
maxDepth: 40,
|
||||
maxLeafTris: 10,
|
||||
useSharedArrayBuffer: false,
|
||||
setBoundingBox: true,
|
||||
onProgress: null,
|
||||
indirect: false,
|
||||
verbose: true,
|
||||
range: null
|
||||
};
|
||||
|
||||
export class MeshBVH {
|
||||
|
||||
static serialize( bvh, options = {} ) {
|
||||
|
||||
options = {
|
||||
cloneBuffers: true,
|
||||
...options,
|
||||
};
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const rootData = bvh._roots;
|
||||
const indirectBuffer = bvh._indirectBuffer;
|
||||
const indexAttribute = geometry.getIndex();
|
||||
let result;
|
||||
if ( options.cloneBuffers ) {
|
||||
|
||||
result = {
|
||||
roots: rootData.map( root => root.slice() ),
|
||||
index: indexAttribute ? indexAttribute.array.slice() : null,
|
||||
indirectBuffer: indirectBuffer ? indirectBuffer.slice() : null,
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
result = {
|
||||
roots: rootData,
|
||||
index: indexAttribute ? indexAttribute.array : null,
|
||||
indirectBuffer: indirectBuffer,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static deserialize( data, geometry, options = {} ) {
|
||||
|
||||
options = {
|
||||
setIndex: true,
|
||||
indirect: Boolean( data.indirectBuffer ),
|
||||
...options,
|
||||
};
|
||||
|
||||
const { index, roots, indirectBuffer } = data;
|
||||
const bvh = new MeshBVH( geometry, { ...options, [ SKIP_GENERATION ]: true } );
|
||||
bvh._roots = roots;
|
||||
bvh._indirectBuffer = indirectBuffer || null;
|
||||
|
||||
if ( options.setIndex ) {
|
||||
|
||||
const indexAttribute = geometry.getIndex();
|
||||
if ( indexAttribute === null ) {
|
||||
|
||||
const newIndex = new BufferAttribute( data.index, 1, false );
|
||||
geometry.setIndex( newIndex );
|
||||
|
||||
} else if ( indexAttribute.array !== index ) {
|
||||
|
||||
indexAttribute.array.set( index );
|
||||
indexAttribute.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return bvh;
|
||||
|
||||
}
|
||||
|
||||
get indirect() {
|
||||
|
||||
return ! ! this._indirectBuffer;
|
||||
|
||||
}
|
||||
|
||||
constructor( geometry, options = {} ) {
|
||||
|
||||
if ( ! geometry.isBufferGeometry ) {
|
||||
|
||||
throw new Error( 'MeshBVH: Only BufferGeometries are supported.' );
|
||||
|
||||
} else if ( geometry.index && geometry.index.isInterleavedBufferAttribute ) {
|
||||
|
||||
throw new Error( 'MeshBVH: InterleavedBufferAttribute is not supported for the index attribute.' );
|
||||
|
||||
}
|
||||
|
||||
// default options
|
||||
options = Object.assign( {
|
||||
|
||||
...DEFAULT_OPTIONS,
|
||||
|
||||
// undocumented options
|
||||
|
||||
// Whether to skip generating the tree. Used for deserialization.
|
||||
[ SKIP_GENERATION ]: false,
|
||||
|
||||
}, options );
|
||||
|
||||
if ( options.useSharedArrayBuffer && ! isSharedArrayBufferSupported() ) {
|
||||
|
||||
throw new Error( 'MeshBVH: SharedArrayBuffer is not available.' );
|
||||
|
||||
}
|
||||
|
||||
// retain references to the geometry so we can use them it without having to
|
||||
// take a geometry reference in every function.
|
||||
this.geometry = geometry;
|
||||
this._roots = null;
|
||||
this._indirectBuffer = null;
|
||||
if ( ! options[ SKIP_GENERATION ] ) {
|
||||
|
||||
buildPackedTree( this, options );
|
||||
|
||||
if ( ! geometry.boundingBox && options.setBoundingBox ) {
|
||||
|
||||
geometry.boundingBox = this.getBoundingBox( new Box3() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.resolveTriangleIndex = options.indirect ? i => this._indirectBuffer[ i ] : i => i;
|
||||
|
||||
}
|
||||
|
||||
refit( nodeIndices = null ) {
|
||||
|
||||
const refitFunc = this.indirect ? refit_indirect : refit;
|
||||
return refitFunc( this, nodeIndices );
|
||||
|
||||
}
|
||||
|
||||
traverse( callback, rootIndex = 0 ) {
|
||||
|
||||
const buffer = this._roots[ rootIndex ];
|
||||
const uint32Array = new Uint32Array( buffer );
|
||||
const uint16Array = new Uint16Array( buffer );
|
||||
_traverse( 0 );
|
||||
|
||||
function _traverse( node32Index, depth = 0 ) {
|
||||
|
||||
const node16Index = node32Index * 2;
|
||||
const isLeaf = uint16Array[ node16Index + 15 ] === IS_LEAFNODE_FLAG;
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = uint32Array[ node32Index + 6 ];
|
||||
const count = uint16Array[ node16Index + 14 ];
|
||||
callback( depth, isLeaf, new Float32Array( buffer, node32Index * 4, 6 ), offset, count );
|
||||
|
||||
} else {
|
||||
|
||||
// TODO: use node functions here
|
||||
const left = node32Index + BYTES_PER_NODE / 4;
|
||||
const right = uint32Array[ node32Index + 6 ];
|
||||
const splitAxis = uint32Array[ node32Index + 7 ];
|
||||
const stopTraversal = callback( depth, isLeaf, new Float32Array( buffer, node32Index * 4, 6 ), splitAxis );
|
||||
|
||||
if ( ! stopTraversal ) {
|
||||
|
||||
_traverse( left, depth + 1 );
|
||||
_traverse( right, depth + 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Core Cast Functions */
|
||||
raycast( ray, materialOrSide = FrontSide, near = 0, far = Infinity ) {
|
||||
|
||||
const roots = this._roots;
|
||||
const geometry = this.geometry;
|
||||
const intersects = [];
|
||||
const isMaterial = materialOrSide.isMaterial;
|
||||
const isArrayMaterial = Array.isArray( materialOrSide );
|
||||
|
||||
const groups = geometry.groups;
|
||||
const side = isMaterial ? materialOrSide.side : materialOrSide;
|
||||
const raycastFunc = this.indirect ? raycast_indirect : raycast;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
const materialSide = isArrayMaterial ? materialOrSide[ groups[ i ].materialIndex ].side : side;
|
||||
const startCount = intersects.length;
|
||||
|
||||
raycastFunc( this, i, materialSide, ray, intersects, near, far );
|
||||
|
||||
if ( isArrayMaterial ) {
|
||||
|
||||
const materialIndex = groups[ i ].materialIndex;
|
||||
for ( let j = startCount, jl = intersects.length; j < jl; j ++ ) {
|
||||
|
||||
intersects[ j ].face.materialIndex = materialIndex;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return intersects;
|
||||
|
||||
}
|
||||
|
||||
raycastFirst( ray, materialOrSide = FrontSide, near = 0, far = Infinity ) {
|
||||
|
||||
const roots = this._roots;
|
||||
const geometry = this.geometry;
|
||||
const isMaterial = materialOrSide.isMaterial;
|
||||
const isArrayMaterial = Array.isArray( materialOrSide );
|
||||
|
||||
let closestResult = null;
|
||||
|
||||
const groups = geometry.groups;
|
||||
const side = isMaterial ? materialOrSide.side : materialOrSide;
|
||||
const raycastFirstFunc = this.indirect ? raycastFirst_indirect : raycastFirst;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
const materialSide = isArrayMaterial ? materialOrSide[ groups[ i ].materialIndex ].side : side;
|
||||
const result = raycastFirstFunc( this, i, materialSide, ray, near, far );
|
||||
if ( result != null && ( closestResult == null || result.distance < closestResult.distance ) ) {
|
||||
|
||||
closestResult = result;
|
||||
if ( isArrayMaterial ) {
|
||||
|
||||
result.face.materialIndex = groups[ i ].materialIndex;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return closestResult;
|
||||
|
||||
}
|
||||
|
||||
intersectsGeometry( otherGeometry, geomToMesh ) {
|
||||
|
||||
let result = false;
|
||||
const roots = this._roots;
|
||||
const intersectsGeometryFunc = this.indirect ? intersectsGeometry_indirect : intersectsGeometry;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
result = intersectsGeometryFunc( this, i, otherGeometry, geomToMesh );
|
||||
|
||||
if ( result ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
shapecast( callbacks ) {
|
||||
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const iterateFunc = this.indirect ? iterateOverTriangles_indirect : iterateOverTriangles;
|
||||
let {
|
||||
boundsTraverseOrder,
|
||||
intersectsBounds,
|
||||
intersectsRange,
|
||||
intersectsTriangle,
|
||||
} = callbacks;
|
||||
|
||||
// wrap the intersectsRange function
|
||||
if ( intersectsRange && intersectsTriangle ) {
|
||||
|
||||
const originalIntersectsRange = intersectsRange;
|
||||
intersectsRange = ( offset, count, contained, depth, nodeIndex ) => {
|
||||
|
||||
if ( ! originalIntersectsRange( offset, count, contained, depth, nodeIndex ) ) {
|
||||
|
||||
return iterateFunc( offset, count, this, intersectsTriangle, contained, depth, triangle );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
} else if ( ! intersectsRange ) {
|
||||
|
||||
if ( intersectsTriangle ) {
|
||||
|
||||
intersectsRange = ( offset, count, contained, depth ) => {
|
||||
|
||||
return iterateFunc( offset, count, this, intersectsTriangle, contained, depth, triangle );
|
||||
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
intersectsRange = ( offset, count, contained ) => {
|
||||
|
||||
return contained;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// run shapecast
|
||||
let result = false;
|
||||
let byteOffset = 0;
|
||||
const roots = this._roots;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
const root = roots[ i ];
|
||||
result = shapecast( this, i, intersectsBounds, intersectsRange, boundsTraverseOrder, byteOffset );
|
||||
|
||||
if ( result ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
byteOffset += root.byteLength;
|
||||
|
||||
}
|
||||
|
||||
ExtendedTrianglePool.releasePrimitive( triangle );
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
bvhcast( otherBvh, matrixToLocal, callbacks ) {
|
||||
|
||||
let {
|
||||
intersectsRanges,
|
||||
intersectsTriangles,
|
||||
} = callbacks;
|
||||
|
||||
const triangle1 = ExtendedTrianglePool.getPrimitive();
|
||||
const indexAttr1 = this.geometry.index;
|
||||
const positionAttr1 = this.geometry.attributes.position;
|
||||
const assignTriangle1 = this.indirect ?
|
||||
i1 => {
|
||||
|
||||
|
||||
const ti = this.resolveTriangleIndex( i1 );
|
||||
setTriangle( triangle1, ti * 3, indexAttr1, positionAttr1 );
|
||||
|
||||
} :
|
||||
i1 => {
|
||||
|
||||
setTriangle( triangle1, i1 * 3, indexAttr1, positionAttr1 );
|
||||
|
||||
};
|
||||
|
||||
const triangle2 = ExtendedTrianglePool.getPrimitive();
|
||||
const indexAttr2 = otherBvh.geometry.index;
|
||||
const positionAttr2 = otherBvh.geometry.attributes.position;
|
||||
const assignTriangle2 = otherBvh.indirect ?
|
||||
i2 => {
|
||||
|
||||
const ti2 = otherBvh.resolveTriangleIndex( i2 );
|
||||
setTriangle( triangle2, ti2 * 3, indexAttr2, positionAttr2 );
|
||||
|
||||
} :
|
||||
i2 => {
|
||||
|
||||
setTriangle( triangle2, i2 * 3, indexAttr2, positionAttr2 );
|
||||
|
||||
};
|
||||
|
||||
// generate triangle callback if needed
|
||||
if ( intersectsTriangles ) {
|
||||
|
||||
const iterateOverDoubleTriangles = ( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) => {
|
||||
|
||||
for ( let i2 = offset2, l2 = offset2 + count2; i2 < l2; i2 ++ ) {
|
||||
|
||||
assignTriangle2( i2 );
|
||||
|
||||
triangle2.a.applyMatrix4( matrixToLocal );
|
||||
triangle2.b.applyMatrix4( matrixToLocal );
|
||||
triangle2.c.applyMatrix4( matrixToLocal );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i1 = offset1, l1 = offset1 + count1; i1 < l1; i1 ++ ) {
|
||||
|
||||
assignTriangle1( i1 );
|
||||
|
||||
triangle1.needsUpdate = true;
|
||||
|
||||
if ( intersectsTriangles( triangle1, triangle2, i1, i2, depth1, index1, depth2, index2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
if ( intersectsRanges ) {
|
||||
|
||||
const originalIntersectsRanges = intersectsRanges;
|
||||
intersectsRanges = function ( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) {
|
||||
|
||||
if ( ! originalIntersectsRanges( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) ) {
|
||||
|
||||
return iterateOverDoubleTriangles( offset1, count1, offset2, count2, depth1, index1, depth2, index2 );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
intersectsRanges = iterateOverDoubleTriangles;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return bvhcast( this, otherBvh, matrixToLocal, intersectsRanges );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Derived Cast Functions */
|
||||
intersectsBox( box, boxToMesh ) {
|
||||
|
||||
obb.set( box.min, box.max, boxToMesh );
|
||||
obb.needsUpdate = true;
|
||||
|
||||
return this.shapecast(
|
||||
{
|
||||
intersectsBounds: box => obb.intersectsBox( box ),
|
||||
intersectsTriangle: tri => obb.intersectsTriangle( tri )
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
intersectsSphere( sphere ) {
|
||||
|
||||
return this.shapecast(
|
||||
{
|
||||
intersectsBounds: box => sphere.intersectsBox( box ),
|
||||
intersectsTriangle: tri => tri.intersectsSphere( sphere )
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
closestPointToGeometry( otherGeometry, geometryToBvh, target1 = { }, target2 = { }, minThreshold = 0, maxThreshold = Infinity ) {
|
||||
|
||||
const closestPointToGeometryFunc = this.indirect ? closestPointToGeometry_indirect : closestPointToGeometry;
|
||||
return closestPointToGeometryFunc(
|
||||
this,
|
||||
otherGeometry,
|
||||
geometryToBvh,
|
||||
target1,
|
||||
target2,
|
||||
minThreshold,
|
||||
maxThreshold,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
closestPointToPoint( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {
|
||||
|
||||
return closestPointToPoint(
|
||||
this,
|
||||
point,
|
||||
target,
|
||||
minThreshold,
|
||||
maxThreshold,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
getBoundingBox( target ) {
|
||||
|
||||
target.makeEmpty();
|
||||
|
||||
const roots = this._roots;
|
||||
roots.forEach( buffer => {
|
||||
|
||||
arrayToBox( 0, new Float32Array( buffer ), tempBox );
|
||||
target.union( tempBox );
|
||||
|
||||
} );
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
12
node_modules/three-mesh-bvh/src/core/MeshBVHNode.js
generated
vendored
Normal file
12
node_modules/three-mesh-bvh/src/core/MeshBVHNode.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export class MeshBVHNode {
|
||||
|
||||
constructor() {
|
||||
|
||||
// internal nodes have boundingData, left, right, and splitAxis
|
||||
// leaf nodes have offset and count (referring to primitives in the mesh geometry)
|
||||
|
||||
this.boundingData = new Float32Array( 6 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
179
node_modules/three-mesh-bvh/src/core/build/buildTree.js
generated
vendored
Normal file
179
node_modules/three-mesh-bvh/src/core/build/buildTree.js
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
import { ensureIndex, getFullGeometryRange, getRootIndexRanges, getTriCount, hasGroupGaps, } from './geometryUtils.js';
|
||||
import { getBounds, computeTriangleBounds } from './computeBoundsUtils.js';
|
||||
import { getOptimalSplit } from './splitUtils.js';
|
||||
import { MeshBVHNode } from '../MeshBVHNode.js';
|
||||
import { BYTES_PER_NODE } from '../Constants.js';
|
||||
|
||||
import { partition } from './sortUtils.generated.js';
|
||||
import { partition_indirect } from './sortUtils_indirect.generated.js';
|
||||
import { countNodes, populateBuffer } from './buildUtils.js';
|
||||
|
||||
export 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;
|
||||
|
||||
}
|
||||
|
||||
export function buildTree( bvh, triangleBounds, offset, count, options ) {
|
||||
|
||||
// epxand variables
|
||||
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;
|
||||
|
||||
// generate intermediate variables
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// either recursively splits the given node, creating left and right subtrees for it, or makes it a leaf node,
|
||||
// recording the offset and count of its triangles and writing them into the reordered geometry index.
|
||||
function splitNode( node, offset, count, 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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// early out if we've met our capacity
|
||||
if ( count <= maxLeafTris || depth >= maxDepth ) {
|
||||
|
||||
triggerProgress( offset + count );
|
||||
node.offset = offset;
|
||||
node.count = count;
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
// Find where to split the volume
|
||||
const split = getOptimalSplit( node.boundingData, centroidBoundingData, triangleBounds, offset, count, strategy );
|
||||
if ( split.axis === - 1 ) {
|
||||
|
||||
triggerProgress( offset + count );
|
||||
node.offset = offset;
|
||||
node.count = count;
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
const splitOffset = partionFunc( indirectBuffer, indexArray, triangleBounds, offset, count, split );
|
||||
|
||||
// create the two new child nodes
|
||||
if ( splitOffset === offset || splitOffset === offset + count ) {
|
||||
|
||||
triggerProgress( offset + count );
|
||||
node.offset = offset;
|
||||
node.count = count;
|
||||
|
||||
} else {
|
||||
|
||||
node.splitAxis = split.axis;
|
||||
|
||||
// create the left child and compute its bounding box
|
||||
const left = new MeshBVHNode();
|
||||
const lstart = offset;
|
||||
const lcount = splitOffset - offset;
|
||||
node.left = left;
|
||||
|
||||
getBounds( triangleBounds, lstart, lcount, left.boundingData, cacheCentroidBoundingData );
|
||||
splitNode( left, lstart, lcount, cacheCentroidBoundingData, depth + 1 );
|
||||
|
||||
// repeat for right
|
||||
const right = new MeshBVHNode();
|
||||
const rstart = splitOffset;
|
||||
const rcount = count - lcount;
|
||||
node.right = right;
|
||||
|
||||
getBounds( triangleBounds, rstart, rcount, right.boundingData, cacheCentroidBoundingData );
|
||||
splitNode( right, rstart, rcount, cacheCentroidBoundingData, depth + 1 );
|
||||
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export 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;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
103
node_modules/three-mesh-bvh/src/core/build/buildUtils.js
generated
vendored
Normal file
103
node_modules/three-mesh-bvh/src/core/build/buildUtils.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import { BYTES_PER_NODE, IS_LEAFNODE_FLAG } from '../Constants.js';
|
||||
import { IS_LEAF } from '../utils/nodeBufferUtils.js';
|
||||
|
||||
let float32Array, uint32Array, uint16Array, uint8Array;
|
||||
const MAX_POINTER = Math.pow( 2, 32 );
|
||||
|
||||
export function countNodes( node ) {
|
||||
|
||||
if ( 'count' in node ) {
|
||||
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
|
||||
return 1 + countNodes( node.left ) + countNodes( node.right );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export 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 );
|
||||
|
||||
}
|
||||
|
||||
// pack structure
|
||||
// boundingData : 6 float32
|
||||
// right / offset : 1 uint32
|
||||
// splitAxis / isLeaf + count : 1 uint32 / 2 uint16
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
179
node_modules/three-mesh-bvh/src/core/build/computeBoundsUtils.js
generated
vendored
Normal file
179
node_modules/three-mesh-bvh/src/core/build/computeBoundsUtils.js
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
import { FLOAT32_EPSILON } from '../Constants.js';
|
||||
import { getTriCount } from './geometryUtils.js';
|
||||
|
||||
// computes the union of the bounds of all of the given triangles and puts the resulting box in "target".
|
||||
// A bounding box is computed for the centroids of the triangles, as well, and placed in "centroidTarget".
|
||||
// These are computed together to avoid redundant accesses to bounds array.
|
||||
export function getBounds( triangleBounds, offset, count, target, 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;
|
||||
|
||||
}
|
||||
|
||||
target[ 0 ] = minx;
|
||||
target[ 1 ] = miny;
|
||||
target[ 2 ] = minz;
|
||||
|
||||
target[ 3 ] = maxx;
|
||||
target[ 4 ] = maxy;
|
||||
target[ 5 ] = maxz;
|
||||
|
||||
centroidTarget[ 0 ] = cminx;
|
||||
centroidTarget[ 1 ] = cminy;
|
||||
centroidTarget[ 2 ] = cminz;
|
||||
|
||||
centroidTarget[ 3 ] = cmaxx;
|
||||
centroidTarget[ 4 ] = cmaxy;
|
||||
centroidTarget[ 5 ] = cmaxz;
|
||||
|
||||
}
|
||||
|
||||
// precomputes the bounding box for each triangle; required for quickly calculating tree splits.
|
||||
// result is an array of size tris.length * 6 where triangle i maps to a
|
||||
// [x_center, x_delta, y_center, y_delta, z_center, z_delta] tuple starting at index i * 6,
|
||||
// representing the center and half-extent in each dimension of triangle i
|
||||
export function computeTriangleBounds( geo, target = 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 ( target === null ) {
|
||||
|
||||
triangleBounds = new Float32Array( triCount * 6 );
|
||||
offset = 0;
|
||||
count = triCount;
|
||||
|
||||
} else {
|
||||
|
||||
triangleBounds = target;
|
||||
offset = offset || 0;
|
||||
count = count || triCount;
|
||||
|
||||
}
|
||||
|
||||
// used for non-normalized positions
|
||||
const posArr = posAttr.array;
|
||||
|
||||
// support for an interleaved position buffer
|
||||
const bufferOffset = posAttr.offset || 0;
|
||||
let stride = 3;
|
||||
if ( posAttr.isInterleavedBufferAttribute ) {
|
||||
|
||||
stride = posAttr.data.stride;
|
||||
|
||||
}
|
||||
|
||||
// used for normalized positions
|
||||
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 ];
|
||||
|
||||
}
|
||||
|
||||
// we add the stride and offset here since we access the array directly
|
||||
// below for the sake of performance
|
||||
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;
|
||||
|
||||
// Increase the bounds size by float32 epsilon to avoid precision errors when
|
||||
// converting to 32 bit float. Scale the epsilon by the size of the numbers being
|
||||
// worked with.
|
||||
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;
|
||||
|
||||
}
|
||||
131
node_modules/three-mesh-bvh/src/core/build/geometryUtils.js
generated
vendored
Normal file
131
node_modules/three-mesh-bvh/src/core/build/geometryUtils.js
generated
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
import { BufferAttribute } from 'three';
|
||||
|
||||
export function getVertexCount( geo ) {
|
||||
|
||||
return geo.index ? geo.index.count : geo.attributes.position.count;
|
||||
|
||||
}
|
||||
|
||||
export function getTriCount( geo ) {
|
||||
|
||||
return getVertexCount( geo ) / 3;
|
||||
|
||||
}
|
||||
|
||||
export function getIndexArray( vertexCount, BufferConstructor = ArrayBuffer ) {
|
||||
|
||||
if ( vertexCount > 65535 ) {
|
||||
|
||||
return new Uint32Array( new BufferConstructor( 4 * vertexCount ) );
|
||||
|
||||
} else {
|
||||
|
||||
return new Uint16Array( new BufferConstructor( 2 * vertexCount ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ensures that an index is present on the geometry
|
||||
export 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Computes the set of { offset, count } ranges which need independent BVH roots. Each
|
||||
// region in the geometry index that belongs to a different set of material groups requires
|
||||
// a separate BVH root, so that triangles indices belonging to one group never get swapped
|
||||
// with triangle indices belongs to another group. For example, if the groups were like this:
|
||||
//
|
||||
// [-------------------------------------------------------------]
|
||||
// |__________________|
|
||||
// g0 = [0, 20] |______________________||_____________________|
|
||||
// g1 = [16, 40] g2 = [41, 60]
|
||||
//
|
||||
// we would need four BVH roots: [0, 15], [16, 20], [21, 40], [41, 60].
|
||||
export 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 ),
|
||||
} ];
|
||||
|
||||
}
|
||||
|
||||
export function getRootIndexRanges( geo, range ) {
|
||||
|
||||
if ( ! geo.groups || ! geo.groups.length ) {
|
||||
|
||||
return getFullGeometryRange( geo, range );
|
||||
|
||||
}
|
||||
|
||||
const ranges = [];
|
||||
const rangeBoundaries = 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 ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// note that if you don't pass in a comparator, it sorts them lexicographically as strings :-(
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
export 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;
|
||||
|
||||
}
|
||||
67
node_modules/three-mesh-bvh/src/core/build/sortUtils.generated.js
generated
vendored
Normal file
67
node_modules/three-mesh-bvh/src/core/build/sortUtils.generated.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/********************************************************/
|
||||
/* This file is generated from "sortUtils.template.js". */
|
||||
/********************************************************/
|
||||
// reorders `tris` such that for `count` elements after `offset`, elements on the left side of the split
|
||||
// will be on the left and elements on the right side of the split will be on the right. returns the index
|
||||
// of the first element on the right side, or offset + count if there are no elements on the right side.
|
||||
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;
|
||||
|
||||
// hoare partitioning, see e.g. https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
|
||||
while ( true ) {
|
||||
|
||||
while ( left <= right && triangleBounds[ left * 6 + axisOffset ] < pos ) {
|
||||
|
||||
left ++;
|
||||
|
||||
}
|
||||
|
||||
// if a triangle center lies on the partition plane it is considered to be on the right side
|
||||
while ( left <= right && triangleBounds[ right * 6 + axisOffset ] >= pos ) {
|
||||
|
||||
right --;
|
||||
|
||||
}
|
||||
|
||||
if ( left < right ) {
|
||||
|
||||
// we need to swap all of the information associated with the triangles at index
|
||||
// left and right; that's the verts in the geometry index, the bounds,
|
||||
// and perhaps the SAH planes
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// swap bounds
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { partition };
|
||||
70
node_modules/three-mesh-bvh/src/core/build/sortUtils.template.js
generated
vendored
Normal file
70
node_modules/three-mesh-bvh/src/core/build/sortUtils.template.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// reorders `tris` such that for `count` elements after `offset`, elements on the left side of the split
|
||||
// will be on the left and elements on the right side of the split will be on the right. returns the index
|
||||
// of the first element on the right side, or offset + count if there are no elements on the right side.
|
||||
export function partition/* @echo INDIRECT_STRING */( indirectBuffer, index, triangleBounds, offset, count, split ) {
|
||||
|
||||
let left = offset;
|
||||
let right = offset + count - 1;
|
||||
const pos = split.pos;
|
||||
const axisOffset = split.axis * 2;
|
||||
|
||||
// hoare partitioning, see e.g. https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
|
||||
while ( true ) {
|
||||
|
||||
while ( left <= right && triangleBounds[ left * 6 + axisOffset ] < pos ) {
|
||||
|
||||
left ++;
|
||||
|
||||
}
|
||||
|
||||
// if a triangle center lies on the partition plane it is considered to be on the right side
|
||||
while ( left <= right && triangleBounds[ right * 6 + axisOffset ] >= pos ) {
|
||||
|
||||
right --;
|
||||
|
||||
}
|
||||
|
||||
if ( left < right ) {
|
||||
|
||||
// we need to swap all of the information associated with the triangles at index
|
||||
// left and right; that's the verts in the geometry index, the bounds,
|
||||
// and perhaps the SAH planes
|
||||
/* @if INDIRECT */
|
||||
|
||||
let t = indirectBuffer[ left ];
|
||||
indirectBuffer[ left ] = indirectBuffer[ right ];
|
||||
indirectBuffer[ right ] = t;
|
||||
|
||||
/* @else */
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/* @endif */
|
||||
|
||||
// swap bounds
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
62
node_modules/three-mesh-bvh/src/core/build/sortUtils_indirect.generated.js
generated
vendored
Normal file
62
node_modules/three-mesh-bvh/src/core/build/sortUtils_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
/********************************************************/
|
||||
/* This file is generated from "sortUtils.template.js". */
|
||||
/********************************************************/
|
||||
// reorders `tris` such that for `count` elements after `offset`, elements on the left side of the split
|
||||
// will be on the left and elements on the right side of the split will be on the right. returns the index
|
||||
// of the first element on the right side, or offset + count if there are no elements on the right side.
|
||||
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;
|
||||
|
||||
// hoare partitioning, see e.g. https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme
|
||||
while ( true ) {
|
||||
|
||||
while ( left <= right && triangleBounds[ left * 6 + axisOffset ] < pos ) {
|
||||
|
||||
left ++;
|
||||
|
||||
}
|
||||
|
||||
// if a triangle center lies on the partition plane it is considered to be on the right side
|
||||
while ( left <= right && triangleBounds[ right * 6 + axisOffset ] >= pos ) {
|
||||
|
||||
right --;
|
||||
|
||||
}
|
||||
|
||||
if ( left < right ) {
|
||||
|
||||
// we need to swap all of the information associated with the triangles at index
|
||||
// left and right; that's the verts in the geometry index, the bounds,
|
||||
// and perhaps the SAH planes
|
||||
let t = indirectBuffer[ left ];
|
||||
indirectBuffer[ left ] = indirectBuffer[ right ];
|
||||
indirectBuffer[ right ] = t;
|
||||
|
||||
|
||||
// swap bounds
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { partition_indirect };
|
||||
307
node_modules/three-mesh-bvh/src/core/build/splitUtils.js
generated
vendored
Normal file
307
node_modules/three-mesh-bvh/src/core/build/splitUtils.js
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
import { getLongestEdgeIndex, computeSurfaceArea, copyBounds, unionBounds, expandByTriangleBounds } from '../../utils/ArrayBoxUtilities.js';
|
||||
import { CENTER, AVERAGE, SAH, TRIANGLE_INTERSECT_COST, TRAVERSAL_COST } from '../Constants.js';
|
||||
|
||||
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 );
|
||||
|
||||
export function getOptimalSplit( nodeBoundingData, centroidBoundingData, triangleBounds, offset, count, strategy ) {
|
||||
|
||||
let axis = - 1;
|
||||
let pos = 0;
|
||||
|
||||
// Center
|
||||
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;
|
||||
|
||||
// iterate over all axes
|
||||
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 we have fewer triangles than we're planning to split then just check all
|
||||
// the triangle positions because it will be faster.
|
||||
if ( count < BIN_COUNT / 4 ) {
|
||||
|
||||
// initialize the bin candidates
|
||||
const truncatedBins = [ ...sahBins ];
|
||||
truncatedBins.length = count;
|
||||
|
||||
// set the candidates
|
||||
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 );
|
||||
|
||||
// remove redundant splits
|
||||
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 --;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// find the appropriate bin for each triangle and expand the bounds.
|
||||
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 ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// expand all the bounds
|
||||
for ( let bi = 0; bi < splitCount; bi ++ ) {
|
||||
|
||||
const bin = truncatedBins[ bi ];
|
||||
const leftCount = bin.count;
|
||||
const rightCount = count - bin.count;
|
||||
|
||||
// check the cost of this split
|
||||
const leftBounds = bin.leftCacheBounds;
|
||||
const rightBounds = bin.rightCacheBounds;
|
||||
|
||||
let leftProb = 0;
|
||||
if ( leftCount !== 0 ) {
|
||||
|
||||
leftProb = computeSurfaceArea( leftBounds ) / 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 {
|
||||
|
||||
// reset the bins
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// iterate over all center positions
|
||||
for ( let c = cStart; c < cEnd; c += 6 ) {
|
||||
|
||||
const triCenter = triangleBounds[ c + 2 * a ];
|
||||
const relativeCenter = triCenter - axisLeft;
|
||||
|
||||
// in the partition function if the centroid lies on the split plane then it is
|
||||
// considered to be on the right side of the split
|
||||
let binIndex = ~ ~ ( relativeCenter / binWidth );
|
||||
if ( binIndex >= BIN_COUNT ) binIndex = BIN_COUNT - 1;
|
||||
|
||||
const bin = sahBins[ binIndex ];
|
||||
bin.count ++;
|
||||
|
||||
expandByTriangleBounds( c, triangleBounds, bin.bounds );
|
||||
|
||||
}
|
||||
|
||||
// cache the unioned bounds from right to left so we don't have to regenerate them each time
|
||||
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;
|
||||
|
||||
// don't do anything with the bounds if the new bounds have no triangles
|
||||
if ( binCount !== 0 ) {
|
||||
|
||||
if ( leftCount === 0 ) {
|
||||
|
||||
copyBounds( bounds, leftBounds );
|
||||
|
||||
} else {
|
||||
|
||||
unionBounds( bounds, leftBounds, leftBounds );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
leftCount += binCount;
|
||||
|
||||
// check the cost of this split
|
||||
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 };
|
||||
|
||||
}
|
||||
|
||||
// returns the average coordinate on the specified axis of the all the provided triangles
|
||||
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;
|
||||
|
||||
}
|
||||
124
node_modules/three-mesh-bvh/src/core/cast/bvhcast.generated.js
generated
vendored
Normal file
124
node_modules/three-mesh-bvh/src/core/cast/bvhcast.generated.js
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Matrix4, Box3 } from 'three';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
|
||||
/******************************************************/
|
||||
/* This file is generated from "bvhcast.template.js". */
|
||||
/******************************************************/
|
||||
|
||||
const tempMatrix = new Matrix4();
|
||||
const aabb = /* @__PURE__ */ new Box3();
|
||||
const aabb2 = /* @__PURE__ */ new Box3();
|
||||
function bvhcast( bvh, otherBvh, matrixToLocal, callbacks ) {
|
||||
|
||||
// BVHCast function for intersecting two BVHs against each other. Ultimately just uses two recursive shapecast calls rather
|
||||
// than an approach that walks down the tree (see bvhcast.js file for more info).
|
||||
|
||||
let {
|
||||
intersectsRanges,
|
||||
intersectsTriangles,
|
||||
} = callbacks;
|
||||
|
||||
const indexAttr = bvh.geometry.index;
|
||||
const positionAttr = bvh.geometry.attributes.position;
|
||||
|
||||
const otherIndexAttr = otherBvh.geometry.index;
|
||||
const otherPositionAttr = otherBvh.geometry.attributes.position;
|
||||
|
||||
tempMatrix.copy( matrixToLocal ).invert();
|
||||
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const triangle2 = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
if ( intersectsTriangles ) {
|
||||
|
||||
const iterateOverDoubleTriangles = ( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) => {
|
||||
|
||||
for ( let i2 = offset2, l2 = offset2 + count2; i2 < l2; i2 ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle2, i2 * 3, otherIndexAttr, otherPositionAttr );
|
||||
|
||||
|
||||
triangle2.a.applyMatrix4( matrixToLocal );
|
||||
triangle2.b.applyMatrix4( matrixToLocal );
|
||||
triangle2.c.applyMatrix4( matrixToLocal );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i1 = offset1, l1 = offset1 + count1; i1 < l1; i1 ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, i1 * 3, indexAttr, positionAttr );
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
if ( intersectsTriangles( triangle, triangle2, i1, i2, depth1, index1, depth2, index2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
if ( intersectsRanges ) {
|
||||
|
||||
const originalIntersectsRanges = intersectsRanges;
|
||||
intersectsRanges = function ( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) {
|
||||
|
||||
if ( ! originalIntersectsRanges( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) ) {
|
||||
|
||||
return iterateOverDoubleTriangles( offset1, count1, offset2, count2, depth1, index1, depth2, index2 );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
intersectsRanges = iterateOverDoubleTriangles;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
otherBvh.getBoundingBox( aabb2 );
|
||||
aabb2.applyMatrix4( matrixToLocal );
|
||||
const result = bvh.shapecast( {
|
||||
|
||||
intersectsBounds: box => aabb2.intersectsBox( box ),
|
||||
|
||||
intersectsRange: ( offset1, count1, contained, depth1, nodeIndex1, box ) => {
|
||||
|
||||
aabb.copy( box );
|
||||
aabb.applyMatrix4( tempMatrix );
|
||||
return otherBvh.shapecast( {
|
||||
|
||||
intersectsBounds: box => aabb.intersectsBox( box ),
|
||||
|
||||
intersectsRange: ( offset2, count2, contained, depth2, nodeIndex2 ) => {
|
||||
|
||||
return intersectsRanges( offset1, count1, offset2, count2, depth1, nodeIndex1, depth2, nodeIndex2 );
|
||||
|
||||
},
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
ExtendedTrianglePool.releasePrimitive( triangle );
|
||||
ExtendedTrianglePool.releasePrimitive( triangle2 );
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
export { bvhcast };
|
||||
314
node_modules/three-mesh-bvh/src/core/cast/bvhcast.js
generated
vendored
Normal file
314
node_modules/three-mesh-bvh/src/core/cast/bvhcast.js
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
import { Box3, Matrix4 } from 'three';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { BOUNDING_DATA_INDEX, COUNT, IS_LEAF, LEFT_NODE, OFFSET, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { arrayToBox } from '../../utils/ArrayBoxUtilities.js';
|
||||
import { PrimitivePool } from '../../utils/PrimitivePool.js';
|
||||
|
||||
const _bufferStack1 = new BufferStack.constructor();
|
||||
const _bufferStack2 = new BufferStack.constructor();
|
||||
const _boxPool = new PrimitivePool( () => new Box3() );
|
||||
const _leftBox1 = new Box3();
|
||||
const _rightBox1 = new Box3();
|
||||
|
||||
const _leftBox2 = new Box3();
|
||||
const _rightBox2 = new Box3();
|
||||
|
||||
let _active = false;
|
||||
|
||||
export function bvhcast( bvh, otherBvh, matrixToLocal, intersectsRanges ) {
|
||||
|
||||
if ( _active ) {
|
||||
|
||||
throw new Error( 'MeshBVH: Recursive calls to bvhcast not supported.' );
|
||||
|
||||
}
|
||||
|
||||
_active = true;
|
||||
|
||||
const roots = bvh._roots;
|
||||
const otherRoots = otherBvh._roots;
|
||||
let result;
|
||||
let offset1 = 0;
|
||||
let offset2 = 0;
|
||||
const invMat = new Matrix4().copy( matrixToLocal ).invert();
|
||||
|
||||
// iterate over the first set of roots
|
||||
for ( let i = 0, il = roots.length; i < il; i ++ ) {
|
||||
|
||||
_bufferStack1.setBuffer( roots[ i ] );
|
||||
offset2 = 0;
|
||||
|
||||
// prep the initial root box
|
||||
const localBox = _boxPool.getPrimitive();
|
||||
arrayToBox( BOUNDING_DATA_INDEX( 0 ), _bufferStack1.float32Array, localBox );
|
||||
localBox.applyMatrix4( invMat );
|
||||
|
||||
// iterate over the second set of roots
|
||||
for ( let j = 0, jl = otherRoots.length; j < jl; j ++ ) {
|
||||
|
||||
_bufferStack2.setBuffer( otherRoots[ j ] );
|
||||
|
||||
result = _traverse(
|
||||
0, 0, matrixToLocal, invMat, intersectsRanges,
|
||||
offset1, offset2, 0, 0,
|
||||
localBox,
|
||||
);
|
||||
|
||||
_bufferStack2.clearBuffer();
|
||||
offset2 += otherRoots[ j ].length;
|
||||
|
||||
if ( result ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// release stack info
|
||||
_boxPool.releasePrimitive( localBox );
|
||||
_bufferStack1.clearBuffer();
|
||||
offset1 += roots[ i ].length;
|
||||
|
||||
if ( result ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_active = false;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _traverse(
|
||||
node1Index32,
|
||||
node2Index32,
|
||||
matrix2to1,
|
||||
matrix1to2,
|
||||
intersectsRangesFunc,
|
||||
|
||||
// offsets for ids
|
||||
node1IndexByteOffset = 0,
|
||||
node2IndexByteOffset = 0,
|
||||
|
||||
// tree depth
|
||||
depth1 = 0,
|
||||
depth2 = 0,
|
||||
|
||||
currBox = null,
|
||||
reversed = false,
|
||||
|
||||
) {
|
||||
|
||||
// get the buffer stacks associated with the current indices
|
||||
let bufferStack1, bufferStack2;
|
||||
if ( reversed ) {
|
||||
|
||||
bufferStack1 = _bufferStack2;
|
||||
bufferStack2 = _bufferStack1;
|
||||
|
||||
} else {
|
||||
|
||||
bufferStack1 = _bufferStack1;
|
||||
bufferStack2 = _bufferStack2;
|
||||
|
||||
}
|
||||
|
||||
// get the local instances of the typed buffers
|
||||
const
|
||||
float32Array1 = bufferStack1.float32Array,
|
||||
uint32Array1 = bufferStack1.uint32Array,
|
||||
uint16Array1 = bufferStack1.uint16Array,
|
||||
float32Array2 = bufferStack2.float32Array,
|
||||
uint32Array2 = bufferStack2.uint32Array,
|
||||
uint16Array2 = bufferStack2.uint16Array;
|
||||
|
||||
const node1Index16 = node1Index32 * 2;
|
||||
const node2Index16 = node2Index32 * 2;
|
||||
const isLeaf1 = IS_LEAF( node1Index16, uint16Array1 );
|
||||
const isLeaf2 = IS_LEAF( node2Index16, uint16Array2 );
|
||||
let result = false;
|
||||
if ( isLeaf2 && isLeaf1 ) {
|
||||
|
||||
// if both bounds are leaf nodes then fire the callback if the boxes intersect
|
||||
if ( reversed ) {
|
||||
|
||||
result = intersectsRangesFunc(
|
||||
OFFSET( node2Index32, uint32Array2 ), COUNT( node2Index32 * 2, uint16Array2 ),
|
||||
OFFSET( node1Index32, uint32Array1 ), COUNT( node1Index32 * 2, uint16Array1 ),
|
||||
depth2, node2IndexByteOffset + node2Index32,
|
||||
depth1, node1IndexByteOffset + node1Index32,
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
result = intersectsRangesFunc(
|
||||
OFFSET( node1Index32, uint32Array1 ), COUNT( node1Index32 * 2, uint16Array1 ),
|
||||
OFFSET( node2Index32, uint32Array2 ), COUNT( node2Index32 * 2, uint16Array2 ),
|
||||
depth1, node1IndexByteOffset + node1Index32,
|
||||
depth2, node2IndexByteOffset + node2Index32,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
} else if ( isLeaf2 ) {
|
||||
|
||||
// SWAP
|
||||
// If we've traversed to the leaf node on the other bvh then we need to swap over
|
||||
// to traverse down the first one
|
||||
|
||||
// get the new box to use
|
||||
const newBox = _boxPool.getPrimitive();
|
||||
arrayToBox( BOUNDING_DATA_INDEX( node2Index32 ), float32Array2, newBox );
|
||||
newBox.applyMatrix4( matrix2to1 );
|
||||
|
||||
// get the child bounds to check before traversal
|
||||
const cl1 = LEFT_NODE( node1Index32 );
|
||||
const cr1 = RIGHT_NODE( node1Index32, uint32Array1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cl1 ), float32Array1, _leftBox1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cr1 ), float32Array1, _rightBox1 );
|
||||
|
||||
// precompute the intersections otherwise the global boxes will be modified during traversal
|
||||
const intersectCl1 = newBox.intersectsBox( _leftBox1 );
|
||||
const intersectCr1 = newBox.intersectsBox( _rightBox1 );
|
||||
result = (
|
||||
intersectCl1 && _traverse(
|
||||
node2Index32, cl1, matrix1to2, matrix2to1, intersectsRangesFunc,
|
||||
node2IndexByteOffset, node1IndexByteOffset, depth2, depth1 + 1,
|
||||
newBox, ! reversed,
|
||||
)
|
||||
) || (
|
||||
intersectCr1 && _traverse(
|
||||
node2Index32, cr1, matrix1to2, matrix2to1, intersectsRangesFunc,
|
||||
node2IndexByteOffset, node1IndexByteOffset, depth2, depth1 + 1,
|
||||
newBox, ! reversed,
|
||||
)
|
||||
);
|
||||
|
||||
_boxPool.releasePrimitive( newBox );
|
||||
|
||||
} else {
|
||||
|
||||
// if neither are leaves then we should swap if one of the children does not
|
||||
// intersect with the current bounds
|
||||
|
||||
// get the child bounds to check
|
||||
const cl2 = LEFT_NODE( node2Index32 );
|
||||
const cr2 = RIGHT_NODE( node2Index32, uint32Array2 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cl2 ), float32Array2, _leftBox2 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cr2 ), float32Array2, _rightBox2 );
|
||||
|
||||
const leftIntersects = currBox.intersectsBox( _leftBox2 );
|
||||
const rightIntersects = currBox.intersectsBox( _rightBox2 );
|
||||
if ( leftIntersects && rightIntersects ) {
|
||||
|
||||
// continue to traverse both children if they both intersect
|
||||
result = _traverse(
|
||||
node1Index32, cl2, matrix2to1, matrix1to2, intersectsRangesFunc,
|
||||
node1IndexByteOffset, node2IndexByteOffset, depth1, depth2 + 1,
|
||||
currBox, reversed,
|
||||
) || _traverse(
|
||||
node1Index32, cr2, matrix2to1, matrix1to2, intersectsRangesFunc,
|
||||
node1IndexByteOffset, node2IndexByteOffset, depth1, depth2 + 1,
|
||||
currBox, reversed,
|
||||
);
|
||||
|
||||
} else if ( leftIntersects ) {
|
||||
|
||||
if ( isLeaf1 ) {
|
||||
|
||||
// if the current box is a leaf then just continue
|
||||
result = _traverse(
|
||||
node1Index32, cl2, matrix2to1, matrix1to2, intersectsRangesFunc,
|
||||
node1IndexByteOffset, node2IndexByteOffset, depth1, depth2 + 1,
|
||||
currBox, reversed,
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
// SWAP
|
||||
// if only one box intersects then we have to swap to the other bvh to continue
|
||||
const newBox = _boxPool.getPrimitive();
|
||||
newBox.copy( _leftBox2 ).applyMatrix4( matrix2to1 );
|
||||
|
||||
const cl1 = LEFT_NODE( node1Index32 );
|
||||
const cr1 = RIGHT_NODE( node1Index32, uint32Array1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cl1 ), float32Array1, _leftBox1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cr1 ), float32Array1, _rightBox1 );
|
||||
|
||||
// precompute the intersections otherwise the global boxes will be modified during traversal
|
||||
const intersectCl1 = newBox.intersectsBox( _leftBox1 );
|
||||
const intersectCr1 = newBox.intersectsBox( _rightBox1 );
|
||||
result = (
|
||||
intersectCl1 && _traverse(
|
||||
cl2, cl1, matrix1to2, matrix2to1, intersectsRangesFunc,
|
||||
node2IndexByteOffset, node1IndexByteOffset, depth2, depth1 + 1,
|
||||
newBox, ! reversed,
|
||||
)
|
||||
) || (
|
||||
intersectCr1 && _traverse(
|
||||
cl2, cr1, matrix1to2, matrix2to1, intersectsRangesFunc,
|
||||
node2IndexByteOffset, node1IndexByteOffset, depth2, depth1 + 1,
|
||||
newBox, ! reversed,
|
||||
)
|
||||
);
|
||||
|
||||
_boxPool.releasePrimitive( newBox );
|
||||
|
||||
}
|
||||
|
||||
} else if ( rightIntersects ) {
|
||||
|
||||
if ( isLeaf1 ) {
|
||||
|
||||
// if the current box is a leaf then just continue
|
||||
result = _traverse(
|
||||
node1Index32, cr2, matrix2to1, matrix1to2, intersectsRangesFunc,
|
||||
node1IndexByteOffset, node2IndexByteOffset, depth1, depth2 + 1,
|
||||
currBox, reversed,
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
// SWAP
|
||||
// if only one box intersects then we have to swap to the other bvh to continue
|
||||
const newBox = _boxPool.getPrimitive();
|
||||
newBox.copy( _rightBox2 ).applyMatrix4( matrix2to1 );
|
||||
|
||||
const cl1 = LEFT_NODE( node1Index32 );
|
||||
const cr1 = RIGHT_NODE( node1Index32, uint32Array1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cl1 ), float32Array1, _leftBox1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( cr1 ), float32Array1, _rightBox1 );
|
||||
|
||||
// precompute the intersections otherwise the global boxes will be modified during traversal
|
||||
const intersectCl1 = newBox.intersectsBox( _leftBox1 );
|
||||
const intersectCr1 = newBox.intersectsBox( _rightBox1 );
|
||||
result = (
|
||||
intersectCl1 && _traverse(
|
||||
cr2, cl1, matrix1to2, matrix2to1, intersectsRangesFunc,
|
||||
node2IndexByteOffset, node1IndexByteOffset, depth2, depth1 + 1,
|
||||
newBox, ! reversed,
|
||||
)
|
||||
) || (
|
||||
intersectCr1 && _traverse(
|
||||
cr2, cr1, matrix1to2, matrix2to1, intersectsRangesFunc,
|
||||
node2IndexByteOffset, node1IndexByteOffset, depth2, depth1 + 1,
|
||||
newBox, ! reversed,
|
||||
)
|
||||
);
|
||||
|
||||
_boxPool.releasePrimitive( newBox );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
124
node_modules/three-mesh-bvh/src/core/cast/bvhcast_indirect.generated.js
generated
vendored
Normal file
124
node_modules/three-mesh-bvh/src/core/cast/bvhcast_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Matrix4, Box3 } from 'three';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
|
||||
/******************************************************/
|
||||
/* This file is generated from "bvhcast.template.js". */
|
||||
/******************************************************/
|
||||
|
||||
const tempMatrix = new Matrix4();
|
||||
const aabb = /* @__PURE__ */ new Box3();
|
||||
const aabb2 = /* @__PURE__ */ new Box3();
|
||||
function bvhcast_indirect( bvh, otherBvh, matrixToLocal, callbacks ) {
|
||||
|
||||
// BVHCast function for intersecting two BVHs against each other. Ultimately just uses two recursive shapecast calls rather
|
||||
// than an approach that walks down the tree (see bvhcast.js file for more info).
|
||||
|
||||
let {
|
||||
intersectsRanges,
|
||||
intersectsTriangles,
|
||||
} = callbacks;
|
||||
|
||||
const indexAttr = bvh.geometry.index;
|
||||
const positionAttr = bvh.geometry.attributes.position;
|
||||
|
||||
const otherIndexAttr = otherBvh.geometry.index;
|
||||
const otherPositionAttr = otherBvh.geometry.attributes.position;
|
||||
|
||||
tempMatrix.copy( matrixToLocal ).invert();
|
||||
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const triangle2 = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
if ( intersectsTriangles ) {
|
||||
|
||||
const iterateOverDoubleTriangles = ( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) => {
|
||||
|
||||
for ( let i2 = offset2, l2 = offset2 + count2; i2 < l2; i2 ++ ) {
|
||||
|
||||
const ti2 = otherBvh.resolveTriangleIndex( i2 );
|
||||
setTriangle( triangle2, ti2 * 3, otherIndexAttr, otherPositionAttr );
|
||||
|
||||
|
||||
triangle2.a.applyMatrix4( matrixToLocal );
|
||||
triangle2.b.applyMatrix4( matrixToLocal );
|
||||
triangle2.c.applyMatrix4( matrixToLocal );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i1 = offset1, l1 = offset1 + count1; i1 < l1; i1 ++ ) {
|
||||
|
||||
const ti1 = bvh.resolveTriangleIndex( i1 );
|
||||
setTriangle( triangle, ti1 * 3, indexAttr, positionAttr );
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
if ( intersectsTriangles( triangle, triangle2, i1, i2, depth1, index1, depth2, index2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
if ( intersectsRanges ) {
|
||||
|
||||
const originalIntersectsRanges = intersectsRanges;
|
||||
intersectsRanges = function ( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) {
|
||||
|
||||
if ( ! originalIntersectsRanges( offset1, count1, offset2, count2, depth1, index1, depth2, index2 ) ) {
|
||||
|
||||
return iterateOverDoubleTriangles( offset1, count1, offset2, count2, depth1, index1, depth2, index2 );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
intersectsRanges = iterateOverDoubleTriangles;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
otherBvh.getBoundingBox( aabb2 );
|
||||
aabb2.applyMatrix4( matrixToLocal );
|
||||
const result = bvh.shapecast( {
|
||||
|
||||
intersectsBounds: box => aabb2.intersectsBox( box ),
|
||||
|
||||
intersectsRange: ( offset1, count1, contained, depth1, nodeIndex1, box ) => {
|
||||
|
||||
aabb.copy( box );
|
||||
aabb.applyMatrix4( tempMatrix );
|
||||
return otherBvh.shapecast( {
|
||||
|
||||
intersectsBounds: box => aabb.intersectsBox( box ),
|
||||
|
||||
intersectsRange: ( offset2, count2, contained, depth2, nodeIndex2 ) => {
|
||||
|
||||
return intersectsRanges( offset1, count1, offset2, count2, depth1, nodeIndex1, depth2, nodeIndex2 );
|
||||
|
||||
},
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
ExtendedTrianglePool.releasePrimitive( triangle );
|
||||
ExtendedTrianglePool.releasePrimitive( triangle2 );
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
export { bvhcast_indirect };
|
||||
256
node_modules/three-mesh-bvh/src/core/cast/closestPointToGeometry.generated.js
generated
vendored
Normal file
256
node_modules/three-mesh-bvh/src/core/cast/closestPointToGeometry.generated.js
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
import { Matrix4, Vector3 } from 'three';
|
||||
import { OrientedBox } from '../../math/OrientedBox.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { getTriCount } from '../build/geometryUtils.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
|
||||
/*********************************************************************/
|
||||
/* This file is generated from "closestPointToGeometry.template.js". */
|
||||
/*********************************************************************/
|
||||
|
||||
const tempMatrix = /* @__PURE__ */ new Matrix4();
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const obb2 = /* @__PURE__ */ new OrientedBox();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const temp2 = /* @__PURE__ */ new Vector3();
|
||||
const temp3 = /* @__PURE__ */ new Vector3();
|
||||
const temp4 = /* @__PURE__ */ new Vector3();
|
||||
|
||||
function closestPointToGeometry(
|
||||
bvh,
|
||||
otherGeometry,
|
||||
geometryToBvh,
|
||||
target1 = { },
|
||||
target2 = { },
|
||||
minThreshold = 0,
|
||||
maxThreshold = Infinity,
|
||||
) {
|
||||
|
||||
if ( ! otherGeometry.boundingBox ) {
|
||||
|
||||
otherGeometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
obb.set( otherGeometry.boundingBox.min, otherGeometry.boundingBox.max, geometryToBvh );
|
||||
obb.needsUpdate = true;
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const index = geometry.index;
|
||||
const otherPos = otherGeometry.attributes.position;
|
||||
const otherIndex = otherGeometry.index;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const triangle2 = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
let tempTarget1 = temp1;
|
||||
let tempTargetDest1 = temp2;
|
||||
let tempTarget2 = null;
|
||||
let tempTargetDest2 = null;
|
||||
|
||||
if ( target2 ) {
|
||||
|
||||
tempTarget2 = temp3;
|
||||
tempTargetDest2 = temp4;
|
||||
|
||||
}
|
||||
|
||||
let closestDistance = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
let closestDistanceOtherTriIndex = null;
|
||||
tempMatrix.copy( geometryToBvh ).invert();
|
||||
obb2.matrix.copy( tempMatrix );
|
||||
bvh.shapecast(
|
||||
{
|
||||
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
return obb.distanceToBox( box );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
if ( score < closestDistance && score < maxThreshold ) {
|
||||
|
||||
// if we know the triangles of this bounds will be intersected next then
|
||||
// save the bounds to use during triangle checks.
|
||||
if ( isLeaf ) {
|
||||
|
||||
obb2.min.copy( box.min );
|
||||
obb2.max.copy( box.max );
|
||||
obb2.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
intersectsRange: ( offset, count ) => {
|
||||
|
||||
if ( otherGeometry.boundsTree ) {
|
||||
|
||||
// if the other geometry has a bvh then use the accelerated path where we use shapecast to find
|
||||
// the closest bounds in the other geometry to check.
|
||||
const otherBvh = otherGeometry.boundsTree;
|
||||
return otherBvh.shapecast( {
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
return obb2.distanceToBox( box );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
return score < closestDistance && score < maxThreshold;
|
||||
|
||||
},
|
||||
|
||||
intersectsRange: ( otherOffset, otherCount ) => {
|
||||
|
||||
for ( let i2 = otherOffset, l2 = otherOffset + otherCount; i2 < l2; i2 ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle2, 3 * i2, otherIndex, otherPos );
|
||||
|
||||
triangle2.a.applyMatrix4( geometryToBvh );
|
||||
triangle2.b.applyMatrix4( geometryToBvh );
|
||||
triangle2.c.applyMatrix4( geometryToBvh );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, 3 * i, index, pos );
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
const dist = triangle.distanceToTriangle( triangle2, tempTarget1, tempTarget2 );
|
||||
if ( dist < closestDistance ) {
|
||||
|
||||
tempTargetDest1.copy( tempTarget1 );
|
||||
|
||||
if ( tempTargetDest2 ) {
|
||||
|
||||
tempTargetDest2.copy( tempTarget2 );
|
||||
|
||||
}
|
||||
|
||||
closestDistance = dist;
|
||||
closestDistanceTriIndex = i;
|
||||
closestDistanceOtherTriIndex = i2;
|
||||
|
||||
}
|
||||
|
||||
// stop traversal if we find a point that's under the given threshold
|
||||
if ( dist < minThreshold ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
} else {
|
||||
|
||||
// If no bounds tree then we'll just check every triangle.
|
||||
const triCount = getTriCount( otherGeometry );
|
||||
for ( let i2 = 0, l2 = triCount; i2 < l2; i2 ++ ) {
|
||||
|
||||
setTriangle( triangle2, 3 * i2, otherIndex, otherPos );
|
||||
triangle2.a.applyMatrix4( geometryToBvh );
|
||||
triangle2.b.applyMatrix4( geometryToBvh );
|
||||
triangle2.c.applyMatrix4( geometryToBvh );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, 3 * i, index, pos );
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
const dist = triangle.distanceToTriangle( triangle2, tempTarget1, tempTarget2 );
|
||||
if ( dist < closestDistance ) {
|
||||
|
||||
tempTargetDest1.copy( tempTarget1 );
|
||||
|
||||
if ( tempTargetDest2 ) {
|
||||
|
||||
tempTargetDest2.copy( tempTarget2 );
|
||||
|
||||
}
|
||||
|
||||
closestDistance = dist;
|
||||
closestDistanceTriIndex = i;
|
||||
closestDistanceOtherTriIndex = i2;
|
||||
|
||||
}
|
||||
|
||||
// stop traversal if we find a point that's under the given threshold
|
||||
if ( dist < minThreshold ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
ExtendedTrianglePool.releasePrimitive( triangle );
|
||||
ExtendedTrianglePool.releasePrimitive( triangle2 );
|
||||
|
||||
if ( closestDistance === Infinity ) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if ( ! target1.point ) {
|
||||
|
||||
target1.point = tempTargetDest1.clone();
|
||||
|
||||
} else {
|
||||
|
||||
target1.point.copy( tempTargetDest1 );
|
||||
|
||||
}
|
||||
|
||||
target1.distance = closestDistance,
|
||||
target1.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
if ( target2 ) {
|
||||
|
||||
if ( ! target2.point ) target2.point = tempTargetDest2.clone();
|
||||
else target2.point.copy( tempTargetDest2 );
|
||||
target2.point.applyMatrix4( tempMatrix );
|
||||
tempTargetDest1.applyMatrix4( tempMatrix );
|
||||
target2.distance = tempTargetDest1.sub( target2.point ).length();
|
||||
target2.faceIndex = closestDistanceOtherTriIndex;
|
||||
|
||||
}
|
||||
|
||||
return target1;
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToGeometry };
|
||||
271
node_modules/three-mesh-bvh/src/core/cast/closestPointToGeometry.template.js
generated
vendored
Normal file
271
node_modules/three-mesh-bvh/src/core/cast/closestPointToGeometry.template.js
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
import { Vector3, Matrix4 } from 'three';
|
||||
import { OrientedBox } from '../../math/OrientedBox.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { getTriCount } from '../build/geometryUtils.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
|
||||
const tempMatrix = /* @__PURE__ */ new Matrix4();
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const obb2 = /* @__PURE__ */ new OrientedBox();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const temp2 = /* @__PURE__ */ new Vector3();
|
||||
const temp3 = /* @__PURE__ */ new Vector3();
|
||||
const temp4 = /* @__PURE__ */ new Vector3();
|
||||
|
||||
export function closestPointToGeometry/* @echo INDIRECT_STRING */(
|
||||
bvh,
|
||||
otherGeometry,
|
||||
geometryToBvh,
|
||||
target1 = { },
|
||||
target2 = { },
|
||||
minThreshold = 0,
|
||||
maxThreshold = Infinity,
|
||||
) {
|
||||
|
||||
if ( ! otherGeometry.boundingBox ) {
|
||||
|
||||
otherGeometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
obb.set( otherGeometry.boundingBox.min, otherGeometry.boundingBox.max, geometryToBvh );
|
||||
obb.needsUpdate = true;
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const index = geometry.index;
|
||||
const otherPos = otherGeometry.attributes.position;
|
||||
const otherIndex = otherGeometry.index;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const triangle2 = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
let tempTarget1 = temp1;
|
||||
let tempTargetDest1 = temp2;
|
||||
let tempTarget2 = null;
|
||||
let tempTargetDest2 = null;
|
||||
|
||||
if ( target2 ) {
|
||||
|
||||
tempTarget2 = temp3;
|
||||
tempTargetDest2 = temp4;
|
||||
|
||||
}
|
||||
|
||||
let closestDistance = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
let closestDistanceOtherTriIndex = null;
|
||||
tempMatrix.copy( geometryToBvh ).invert();
|
||||
obb2.matrix.copy( tempMatrix );
|
||||
bvh.shapecast(
|
||||
{
|
||||
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
return obb.distanceToBox( box );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
if ( score < closestDistance && score < maxThreshold ) {
|
||||
|
||||
// if we know the triangles of this bounds will be intersected next then
|
||||
// save the bounds to use during triangle checks.
|
||||
if ( isLeaf ) {
|
||||
|
||||
obb2.min.copy( box.min );
|
||||
obb2.max.copy( box.max );
|
||||
obb2.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
intersectsRange: ( offset, count ) => {
|
||||
|
||||
if ( otherGeometry.boundsTree ) {
|
||||
|
||||
// if the other geometry has a bvh then use the accelerated path where we use shapecast to find
|
||||
// the closest bounds in the other geometry to check.
|
||||
const otherBvh = otherGeometry.boundsTree;
|
||||
return otherBvh.shapecast( {
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
return obb2.distanceToBox( box );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
return score < closestDistance && score < maxThreshold;
|
||||
|
||||
},
|
||||
|
||||
intersectsRange: ( otherOffset, otherCount ) => {
|
||||
|
||||
for ( let i2 = otherOffset, l2 = otherOffset + otherCount; i2 < l2; i2 ++ ) {
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
const ti2 = otherBvh.resolveTriangleIndex( i2 );
|
||||
setTriangle( triangle2, 3 * ti2, otherIndex, otherPos );
|
||||
|
||||
/* @else */
|
||||
|
||||
setTriangle( triangle2, 3 * i2, otherIndex, otherPos );
|
||||
|
||||
/* @endif */
|
||||
triangle2.a.applyMatrix4( geometryToBvh );
|
||||
triangle2.b.applyMatrix4( geometryToBvh );
|
||||
triangle2.c.applyMatrix4( geometryToBvh );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
/* @else */
|
||||
|
||||
setTriangle( triangle, 3 * i, index, pos );
|
||||
|
||||
/* @endif */
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
const dist = triangle.distanceToTriangle( triangle2, tempTarget1, tempTarget2 );
|
||||
if ( dist < closestDistance ) {
|
||||
|
||||
tempTargetDest1.copy( tempTarget1 );
|
||||
|
||||
if ( tempTargetDest2 ) {
|
||||
|
||||
tempTargetDest2.copy( tempTarget2 );
|
||||
|
||||
}
|
||||
|
||||
closestDistance = dist;
|
||||
closestDistanceTriIndex = i;
|
||||
closestDistanceOtherTriIndex = i2;
|
||||
|
||||
}
|
||||
|
||||
// stop traversal if we find a point that's under the given threshold
|
||||
if ( dist < minThreshold ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
} else {
|
||||
|
||||
// If no bounds tree then we'll just check every triangle.
|
||||
const triCount = getTriCount( otherGeometry );
|
||||
for ( let i2 = 0, l2 = triCount; i2 < l2; i2 ++ ) {
|
||||
|
||||
setTriangle( triangle2, 3 * i2, otherIndex, otherPos );
|
||||
triangle2.a.applyMatrix4( geometryToBvh );
|
||||
triangle2.b.applyMatrix4( geometryToBvh );
|
||||
triangle2.c.applyMatrix4( geometryToBvh );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
/* @else */
|
||||
|
||||
setTriangle( triangle, 3 * i, index, pos );
|
||||
|
||||
/* @endif */
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
const dist = triangle.distanceToTriangle( triangle2, tempTarget1, tempTarget2 );
|
||||
if ( dist < closestDistance ) {
|
||||
|
||||
tempTargetDest1.copy( tempTarget1 );
|
||||
|
||||
if ( tempTargetDest2 ) {
|
||||
|
||||
tempTargetDest2.copy( tempTarget2 );
|
||||
|
||||
}
|
||||
|
||||
closestDistance = dist;
|
||||
closestDistanceTriIndex = i;
|
||||
closestDistanceOtherTriIndex = i2;
|
||||
|
||||
}
|
||||
|
||||
// stop traversal if we find a point that's under the given threshold
|
||||
if ( dist < minThreshold ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
ExtendedTrianglePool.releasePrimitive( triangle );
|
||||
ExtendedTrianglePool.releasePrimitive( triangle2 );
|
||||
|
||||
if ( closestDistance === Infinity ) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if ( ! target1.point ) {
|
||||
|
||||
target1.point = tempTargetDest1.clone();
|
||||
|
||||
} else {
|
||||
|
||||
target1.point.copy( tempTargetDest1 );
|
||||
|
||||
}
|
||||
|
||||
target1.distance = closestDistance,
|
||||
target1.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
if ( target2 ) {
|
||||
|
||||
if ( ! target2.point ) target2.point = tempTargetDest2.clone();
|
||||
else target2.point.copy( tempTargetDest2 );
|
||||
target2.point.applyMatrix4( tempMatrix );
|
||||
tempTargetDest1.applyMatrix4( tempMatrix );
|
||||
target2.distance = tempTargetDest1.sub( target2.point ).length();
|
||||
target2.faceIndex = closestDistanceOtherTriIndex;
|
||||
|
||||
}
|
||||
|
||||
return target1;
|
||||
|
||||
}
|
||||
256
node_modules/three-mesh-bvh/src/core/cast/closestPointToGeometry_indirect.generated.js
generated
vendored
Normal file
256
node_modules/three-mesh-bvh/src/core/cast/closestPointToGeometry_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
import { Matrix4, Vector3 } from 'three';
|
||||
import { OrientedBox } from '../../math/OrientedBox.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { getTriCount } from '../build/geometryUtils.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
|
||||
/*********************************************************************/
|
||||
/* This file is generated from "closestPointToGeometry.template.js". */
|
||||
/*********************************************************************/
|
||||
|
||||
const tempMatrix = /* @__PURE__ */ new Matrix4();
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const obb2 = /* @__PURE__ */ new OrientedBox();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const temp2 = /* @__PURE__ */ new Vector3();
|
||||
const temp3 = /* @__PURE__ */ new Vector3();
|
||||
const temp4 = /* @__PURE__ */ new Vector3();
|
||||
|
||||
function closestPointToGeometry_indirect(
|
||||
bvh,
|
||||
otherGeometry,
|
||||
geometryToBvh,
|
||||
target1 = { },
|
||||
target2 = { },
|
||||
minThreshold = 0,
|
||||
maxThreshold = Infinity,
|
||||
) {
|
||||
|
||||
if ( ! otherGeometry.boundingBox ) {
|
||||
|
||||
otherGeometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
obb.set( otherGeometry.boundingBox.min, otherGeometry.boundingBox.max, geometryToBvh );
|
||||
obb.needsUpdate = true;
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const index = geometry.index;
|
||||
const otherPos = otherGeometry.attributes.position;
|
||||
const otherIndex = otherGeometry.index;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const triangle2 = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
let tempTarget1 = temp1;
|
||||
let tempTargetDest1 = temp2;
|
||||
let tempTarget2 = null;
|
||||
let tempTargetDest2 = null;
|
||||
|
||||
if ( target2 ) {
|
||||
|
||||
tempTarget2 = temp3;
|
||||
tempTargetDest2 = temp4;
|
||||
|
||||
}
|
||||
|
||||
let closestDistance = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
let closestDistanceOtherTriIndex = null;
|
||||
tempMatrix.copy( geometryToBvh ).invert();
|
||||
obb2.matrix.copy( tempMatrix );
|
||||
bvh.shapecast(
|
||||
{
|
||||
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
return obb.distanceToBox( box );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
if ( score < closestDistance && score < maxThreshold ) {
|
||||
|
||||
// if we know the triangles of this bounds will be intersected next then
|
||||
// save the bounds to use during triangle checks.
|
||||
if ( isLeaf ) {
|
||||
|
||||
obb2.min.copy( box.min );
|
||||
obb2.max.copy( box.max );
|
||||
obb2.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
intersectsRange: ( offset, count ) => {
|
||||
|
||||
if ( otherGeometry.boundsTree ) {
|
||||
|
||||
// if the other geometry has a bvh then use the accelerated path where we use shapecast to find
|
||||
// the closest bounds in the other geometry to check.
|
||||
const otherBvh = otherGeometry.boundsTree;
|
||||
return otherBvh.shapecast( {
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
return obb2.distanceToBox( box );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
return score < closestDistance && score < maxThreshold;
|
||||
|
||||
},
|
||||
|
||||
intersectsRange: ( otherOffset, otherCount ) => {
|
||||
|
||||
for ( let i2 = otherOffset, l2 = otherOffset + otherCount; i2 < l2; i2 ++ ) {
|
||||
|
||||
const ti2 = otherBvh.resolveTriangleIndex( i2 );
|
||||
setTriangle( triangle2, 3 * ti2, otherIndex, otherPos );
|
||||
|
||||
triangle2.a.applyMatrix4( geometryToBvh );
|
||||
triangle2.b.applyMatrix4( geometryToBvh );
|
||||
triangle2.c.applyMatrix4( geometryToBvh );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
const dist = triangle.distanceToTriangle( triangle2, tempTarget1, tempTarget2 );
|
||||
if ( dist < closestDistance ) {
|
||||
|
||||
tempTargetDest1.copy( tempTarget1 );
|
||||
|
||||
if ( tempTargetDest2 ) {
|
||||
|
||||
tempTargetDest2.copy( tempTarget2 );
|
||||
|
||||
}
|
||||
|
||||
closestDistance = dist;
|
||||
closestDistanceTriIndex = i;
|
||||
closestDistanceOtherTriIndex = i2;
|
||||
|
||||
}
|
||||
|
||||
// stop traversal if we find a point that's under the given threshold
|
||||
if ( dist < minThreshold ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
} else {
|
||||
|
||||
// If no bounds tree then we'll just check every triangle.
|
||||
const triCount = getTriCount( otherGeometry );
|
||||
for ( let i2 = 0, l2 = triCount; i2 < l2; i2 ++ ) {
|
||||
|
||||
setTriangle( triangle2, 3 * i2, otherIndex, otherPos );
|
||||
triangle2.a.applyMatrix4( geometryToBvh );
|
||||
triangle2.b.applyMatrix4( geometryToBvh );
|
||||
triangle2.c.applyMatrix4( geometryToBvh );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
const dist = triangle.distanceToTriangle( triangle2, tempTarget1, tempTarget2 );
|
||||
if ( dist < closestDistance ) {
|
||||
|
||||
tempTargetDest1.copy( tempTarget1 );
|
||||
|
||||
if ( tempTargetDest2 ) {
|
||||
|
||||
tempTargetDest2.copy( tempTarget2 );
|
||||
|
||||
}
|
||||
|
||||
closestDistance = dist;
|
||||
closestDistanceTriIndex = i;
|
||||
closestDistanceOtherTriIndex = i2;
|
||||
|
||||
}
|
||||
|
||||
// stop traversal if we find a point that's under the given threshold
|
||||
if ( dist < minThreshold ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
ExtendedTrianglePool.releasePrimitive( triangle );
|
||||
ExtendedTrianglePool.releasePrimitive( triangle2 );
|
||||
|
||||
if ( closestDistance === Infinity ) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
if ( ! target1.point ) {
|
||||
|
||||
target1.point = tempTargetDest1.clone();
|
||||
|
||||
} else {
|
||||
|
||||
target1.point.copy( tempTargetDest1 );
|
||||
|
||||
}
|
||||
|
||||
target1.distance = closestDistance,
|
||||
target1.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
if ( target2 ) {
|
||||
|
||||
if ( ! target2.point ) target2.point = tempTargetDest2.clone();
|
||||
else target2.point.copy( tempTargetDest2 );
|
||||
target2.point.applyMatrix4( tempMatrix );
|
||||
tempTargetDest1.applyMatrix4( tempMatrix );
|
||||
target2.distance = tempTargetDest1.sub( target2.point ).length();
|
||||
target2.faceIndex = closestDistanceOtherTriIndex;
|
||||
|
||||
}
|
||||
|
||||
return target1;
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToGeometry_indirect };
|
||||
116
node_modules/three-mesh-bvh/src/core/cast/closestPointToPoint.generated.js
generated
vendored
Normal file
116
node_modules/three-mesh-bvh/src/core/cast/closestPointToPoint.generated.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Vector3 } from 'three';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
|
||||
|
||||
/******************************************************************/
|
||||
/* This file is generated from "closestPointToPoint.template.js". */
|
||||
/******************************************************************/
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
|
||||
function closestPointToPoint(
|
||||
bvh,
|
||||
root,
|
||||
point,
|
||||
target,
|
||||
minThreshold,
|
||||
maxThreshold
|
||||
) {
|
||||
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
_closestPointToPoint( root );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance;
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
|
||||
// early out if under minThreshold
|
||||
// skip checking if over maxThreshold
|
||||
// set minThreshold = maxThreshold to quickly check if a point is within a threshold
|
||||
// returns Infinity if no value found
|
||||
function _closestPointToPoint( nodeIndex32 ) {
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, i * 3, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance <= rightDistance ) {
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( leftIndex ) ) return true;
|
||||
if ( rightDistance < closestDistanceSq ) return _closestPointToPoint( rightIndex );
|
||||
|
||||
}
|
||||
|
||||
} else if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( rightIndex ) ) return true;
|
||||
if ( leftDistance < closestDistanceSq ) return _closestPointToPoint( leftIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToPoint };
|
||||
78
node_modules/three-mesh-bvh/src/core/cast/closestPointToPoint.js
generated
vendored
Normal file
78
node_modules/three-mesh-bvh/src/core/cast/closestPointToPoint.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Vector3 } from 'three';
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
|
||||
export function closestPointToPoint(
|
||||
bvh,
|
||||
point,
|
||||
target = { },
|
||||
minThreshold = 0,
|
||||
maxThreshold = Infinity,
|
||||
) {
|
||||
|
||||
// early out if under minThreshold
|
||||
// skip checking if over maxThreshold
|
||||
// set minThreshold = maxThreshold to quickly check if a point is within a threshold
|
||||
// returns Infinity if no value found
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
bvh.shapecast(
|
||||
|
||||
{
|
||||
|
||||
boundsTraverseOrder: box => {
|
||||
|
||||
temp.copy( point ).clamp( box.min, box.max );
|
||||
return temp.distanceToSquared( point );
|
||||
|
||||
},
|
||||
|
||||
intersectsBounds: ( box, isLeaf, score ) => {
|
||||
|
||||
return score < closestDistanceSq && score < maxThresholdSq;
|
||||
|
||||
},
|
||||
|
||||
intersectsTriangle: ( tri, triIndex ) => {
|
||||
|
||||
tri.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = triIndex;
|
||||
|
||||
}
|
||||
|
||||
if ( distSq < minThresholdSq ) {
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance,
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
203
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointHybrid.generated.js
generated
vendored
Normal file
203
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointHybrid.generated.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
import { Vector3 } from 'three';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
|
||||
import { SortedListDesc } from '../utils/SortedListDesc.js';
|
||||
|
||||
/************************************************************************/
|
||||
/* This file is generated from "closestPointToPointHybrid.template.js". */
|
||||
/************************************************************************/
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const sortedList = new SortedListDesc();
|
||||
|
||||
function closestPointToPointHybrid(
|
||||
bvh,
|
||||
root,
|
||||
point,
|
||||
target,
|
||||
sortedListMaxCount,
|
||||
minThreshold,
|
||||
maxThreshold
|
||||
) {
|
||||
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
|
||||
if ( sortedListMaxCount === 0 ) {
|
||||
|
||||
_closestPointToPoint( 0 );
|
||||
|
||||
} else {
|
||||
|
||||
_closestPointToPointSorted();
|
||||
|
||||
}
|
||||
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance;
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
|
||||
function _closestPointToPointSorted() {
|
||||
|
||||
sortedList.clear();
|
||||
let count = 0;
|
||||
let node = { nodeIndex32: 0, distance: closestDistanceSquaredPointToBox( 0, float32Array, point ) };
|
||||
|
||||
do {
|
||||
|
||||
const { distance, nodeIndex32 } = node;
|
||||
|
||||
if ( distance >= closestDistanceSq ) return;
|
||||
|
||||
if ( count >= sortedListMaxCount ) {
|
||||
|
||||
if ( _closestPointToPoint( nodeIndex32 ) ) return;
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
count ++;
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, i * 3, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
|
||||
|
||||
}
|
||||
|
||||
if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
|
||||
|
||||
}
|
||||
|
||||
} while ( node = sortedList.pop() );
|
||||
|
||||
}
|
||||
|
||||
|
||||
function _closestPointToPoint( nodeIndex32 ) {
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, i * 3, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance <= rightDistance ) {
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( leftIndex ) ) return true;
|
||||
if ( rightDistance < closestDistanceSq ) return _closestPointToPoint( rightIndex );
|
||||
|
||||
}
|
||||
|
||||
} else if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( rightIndex ) ) return true;
|
||||
if ( leftDistance < closestDistanceSq ) return _closestPointToPoint( leftIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToPointHybrid };
|
||||
203
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointHybrid_indirect.generated.js
generated
vendored
Normal file
203
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointHybrid_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
import { Vector3 } from 'three';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
|
||||
import { SortedListDesc } from '../utils/SortedListDesc.js';
|
||||
|
||||
/************************************************************************/
|
||||
/* This file is generated from "closestPointToPointHybrid.template.js". */
|
||||
/************************************************************************/
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const sortedList = new SortedListDesc();
|
||||
|
||||
function closestPointToPointHybrid_indirect(
|
||||
bvh,
|
||||
root,
|
||||
point,
|
||||
target,
|
||||
sortedListMaxCount,
|
||||
minThreshold,
|
||||
maxThreshold
|
||||
) {
|
||||
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
|
||||
if ( sortedListMaxCount === 0 ) {
|
||||
|
||||
_closestPointToPoint( 0 );
|
||||
|
||||
} else {
|
||||
|
||||
_closestPointToPointSorted();
|
||||
|
||||
}
|
||||
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance;
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
|
||||
function _closestPointToPointSorted() {
|
||||
|
||||
sortedList.clear();
|
||||
let count = 0;
|
||||
let node = { nodeIndex32: 0, distance: closestDistanceSquaredPointToBox( 0, float32Array, point ) };
|
||||
|
||||
do {
|
||||
|
||||
const { distance, nodeIndex32 } = node;
|
||||
|
||||
if ( distance >= closestDistanceSq ) return;
|
||||
|
||||
if ( count >= sortedListMaxCount ) {
|
||||
|
||||
if ( _closestPointToPoint( nodeIndex32 ) ) return;
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
count ++;
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
|
||||
|
||||
}
|
||||
|
||||
if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
|
||||
|
||||
}
|
||||
|
||||
} while ( node = sortedList.pop() );
|
||||
|
||||
}
|
||||
|
||||
|
||||
function _closestPointToPoint( nodeIndex32 ) {
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance <= rightDistance ) {
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( leftIndex ) ) return true;
|
||||
if ( rightDistance < closestDistanceSq ) return _closestPointToPoint( rightIndex );
|
||||
|
||||
}
|
||||
|
||||
} else if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( rightIndex ) ) return true;
|
||||
if ( leftDistance < closestDistanceSq ) return _closestPointToPoint( leftIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToPointHybrid_indirect };
|
||||
122
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointSort.generated.js
generated
vendored
Normal file
122
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointSort.generated.js
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
import { Vector3 } from 'three';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
|
||||
import { SortedListDesc } from '../utils/SortedListDesc.js';
|
||||
|
||||
/**********************************************************************/
|
||||
/* This file is generated from "closestPointToPointSort.template.js". */
|
||||
/**********************************************************************/
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const sortedList = new SortedListDesc();
|
||||
|
||||
function closestPointToPointSort(
|
||||
bvh,
|
||||
root,
|
||||
point,
|
||||
target,
|
||||
minThreshold,
|
||||
maxThreshold
|
||||
) {
|
||||
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
|
||||
_closestPointToPoint();
|
||||
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance;
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
|
||||
function _closestPointToPoint() {
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
sortedList.clear();
|
||||
|
||||
let node = { nodeIndex32: 0, distance: closestDistanceSquaredPointToBox( 0, float32Array, point ) };
|
||||
|
||||
do {
|
||||
|
||||
const { distance, nodeIndex32 } = node;
|
||||
|
||||
if ( distance >= closestDistanceSq ) return;
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
|
||||
setTriangle( triangle, i * 3, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
|
||||
|
||||
}
|
||||
|
||||
if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
|
||||
|
||||
}
|
||||
|
||||
} while ( node = sortedList.pop() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToPointSort };
|
||||
122
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointSort_indirect.generated.js
generated
vendored
Normal file
122
node_modules/three-mesh-bvh/src/core/cast/closestPointToPointSort_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
import { Vector3 } from 'three';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
|
||||
import { SortedListDesc } from '../utils/SortedListDesc.js';
|
||||
|
||||
/**********************************************************************/
|
||||
/* This file is generated from "closestPointToPointSort.template.js". */
|
||||
/**********************************************************************/
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
const sortedList = new SortedListDesc();
|
||||
|
||||
function closestPointToPointSort_indirect(
|
||||
bvh,
|
||||
root,
|
||||
point,
|
||||
target,
|
||||
minThreshold,
|
||||
maxThreshold
|
||||
) {
|
||||
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
|
||||
_closestPointToPoint();
|
||||
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance;
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
|
||||
function _closestPointToPoint() {
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
sortedList.clear();
|
||||
|
||||
let node = { nodeIndex32: 0, distance: closestDistanceSquaredPointToBox( 0, float32Array, point ) };
|
||||
|
||||
do {
|
||||
|
||||
const { distance, nodeIndex32 } = node;
|
||||
|
||||
if ( distance >= closestDistanceSq ) return;
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: leftIndex, distance: leftDistance } );
|
||||
|
||||
}
|
||||
|
||||
if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
sortedList.push( { nodeIndex32: rightIndex, distance: rightDistance } );
|
||||
|
||||
}
|
||||
|
||||
} while ( node = sortedList.pop() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToPointSort_indirect };
|
||||
116
node_modules/three-mesh-bvh/src/core/cast/closestPointToPoint_indirect.generated.js
generated
vendored
Normal file
116
node_modules/three-mesh-bvh/src/core/cast/closestPointToPoint_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Vector3 } from 'three';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
|
||||
|
||||
/******************************************************************/
|
||||
/* This file is generated from "closestPointToPoint.template.js". */
|
||||
/******************************************************************/
|
||||
|
||||
const temp = /* @__PURE__ */ new Vector3();
|
||||
const temp1 = /* @__PURE__ */ new Vector3();
|
||||
|
||||
function closestPointToPoint_indirect(
|
||||
bvh,
|
||||
root,
|
||||
point,
|
||||
target,
|
||||
minThreshold,
|
||||
maxThreshold
|
||||
) {
|
||||
|
||||
const minThresholdSq = minThreshold * minThreshold;
|
||||
const maxThresholdSq = maxThreshold * maxThreshold;
|
||||
let closestDistanceSq = Infinity;
|
||||
let closestDistanceTriIndex = null;
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
const triangle = ExtendedTrianglePool.getPrimitive();
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
_closestPointToPoint( root );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
if ( closestDistanceSq === Infinity ) return null;
|
||||
|
||||
const closestDistance = Math.sqrt( closestDistanceSq );
|
||||
|
||||
if ( ! target.point ) target.point = temp1.clone();
|
||||
else target.point.copy( temp1 );
|
||||
target.distance = closestDistance;
|
||||
target.faceIndex = closestDistanceTriIndex;
|
||||
|
||||
return target;
|
||||
|
||||
|
||||
// early out if under minThreshold
|
||||
// skip checking if over maxThreshold
|
||||
// set minThreshold = maxThreshold to quickly check if a point is within a threshold
|
||||
// returns Infinity if no value found
|
||||
function _closestPointToPoint( nodeIndex32 ) {
|
||||
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, index, pos );
|
||||
|
||||
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
triangle.closestPointToPoint( point, temp );
|
||||
const distSq = point.distanceToSquared( temp );
|
||||
if ( distSq < closestDistanceSq ) {
|
||||
|
||||
temp1.copy( temp );
|
||||
closestDistanceSq = distSq;
|
||||
closestDistanceTriIndex = i;
|
||||
|
||||
if ( distSq < minThresholdSq ) return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
|
||||
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );
|
||||
|
||||
if ( leftDistance <= rightDistance ) {
|
||||
|
||||
if ( leftDistance < closestDistanceSq && leftDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( leftIndex ) ) return true;
|
||||
if ( rightDistance < closestDistanceSq ) return _closestPointToPoint( rightIndex );
|
||||
|
||||
}
|
||||
|
||||
} else if ( rightDistance < closestDistanceSq && rightDistance < maxThresholdSq ) {
|
||||
|
||||
if ( _closestPointToPoint( rightIndex ) ) return true;
|
||||
if ( leftDistance < closestDistanceSq ) return _closestPointToPoint( leftIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { closestPointToPoint_indirect };
|
||||
169
node_modules/three-mesh-bvh/src/core/cast/intersectsGeometry.generated.js
generated
vendored
Normal file
169
node_modules/three-mesh-bvh/src/core/cast/intersectsGeometry.generated.js
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
import { Box3, Matrix4 } from 'three';
|
||||
import { OrientedBox } from '../../math/OrientedBox.js';
|
||||
import { ExtendedTriangle } from '../../math/ExtendedTriangle.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { arrayToBox } from '../../utils/ArrayBoxUtilities.js';
|
||||
import { IS_LEAF, OFFSET, COUNT, BOUNDING_DATA_INDEX } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/* This file is generated from "intersectsGeometry.template.js". */
|
||||
/*****************************************************************/
|
||||
/* eslint-disable indent */
|
||||
|
||||
const boundingBox = /* @__PURE__ */ new Box3();
|
||||
const triangle = /* @__PURE__ */ new ExtendedTriangle();
|
||||
const triangle2 = /* @__PURE__ */ new ExtendedTriangle();
|
||||
const invertedMat = /* @__PURE__ */ new Matrix4();
|
||||
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const obb2 = /* @__PURE__ */ new OrientedBox();
|
||||
|
||||
function intersectsGeometry( bvh, root, otherGeometry, geometryToBvh ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const result = _intersectsGeometry( 0, bvh, otherGeometry, geometryToBvh );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _intersectsGeometry( nodeIndex32, bvh, otherGeometry, geometryToBvh, cachedObb = null ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
if ( cachedObb === null ) {
|
||||
|
||||
if ( ! otherGeometry.boundingBox ) {
|
||||
|
||||
otherGeometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
obb.set( otherGeometry.boundingBox.min, otherGeometry.boundingBox.max, geometryToBvh );
|
||||
cachedObb = obb;
|
||||
|
||||
}
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const thisGeometry = bvh.geometry;
|
||||
const thisIndex = thisGeometry.index;
|
||||
const thisPos = thisGeometry.attributes.position;
|
||||
|
||||
const index = otherGeometry.index;
|
||||
const pos = otherGeometry.attributes.position;
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
// get the inverse of the geometry matrix so we can transform our triangles into the
|
||||
// geometry space we're trying to test. We assume there are fewer triangles being checked
|
||||
// here.
|
||||
invertedMat.copy( geometryToBvh ).invert();
|
||||
|
||||
if ( otherGeometry.boundsTree ) {
|
||||
|
||||
// if there's a bounds tree
|
||||
arrayToBox( BOUNDING_DATA_INDEX( nodeIndex32 ), float32Array, obb2 );
|
||||
obb2.matrix.copy( invertedMat );
|
||||
obb2.needsUpdate = true;
|
||||
|
||||
// TODO: use a triangle iteration function here
|
||||
const res = otherGeometry.boundsTree.shapecast( {
|
||||
|
||||
intersectsBounds: box => obb2.intersectsBox( box ),
|
||||
|
||||
intersectsTriangle: tri => {
|
||||
|
||||
tri.a.applyMatrix4( geometryToBvh );
|
||||
tri.b.applyMatrix4( geometryToBvh );
|
||||
tri.c.applyMatrix4( geometryToBvh );
|
||||
tri.needsUpdate = true;
|
||||
|
||||
|
||||
for ( let i = offset * 3, l = ( count + offset ) * 3; i < l; i += 3 ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
setTriangle( triangle2, i, thisIndex, thisPos );
|
||||
triangle2.needsUpdate = true;
|
||||
if ( tri.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return res;
|
||||
|
||||
} else {
|
||||
|
||||
// if we're just dealing with raw geometry
|
||||
|
||||
for ( let i = offset * 3, l = ( count + offset ) * 3; i < l; i += 3 ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
setTriangle( triangle, i, thisIndex, thisPos );
|
||||
|
||||
|
||||
triangle.a.applyMatrix4( invertedMat );
|
||||
triangle.b.applyMatrix4( invertedMat );
|
||||
triangle.c.applyMatrix4( invertedMat );
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
for ( let i2 = 0, l2 = index.count; i2 < l2; i2 += 3 ) {
|
||||
|
||||
setTriangle( triangle2, i2, index, pos );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
if ( triangle.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const left = nodeIndex32 + 8;
|
||||
const right = uint32Array[ nodeIndex32 + 6 ];
|
||||
|
||||
arrayToBox( BOUNDING_DATA_INDEX( left ), float32Array, boundingBox );
|
||||
const leftIntersection =
|
||||
cachedObb.intersectsBox( boundingBox ) &&
|
||||
_intersectsGeometry( left, bvh, otherGeometry, geometryToBvh, cachedObb );
|
||||
|
||||
if ( leftIntersection ) return true;
|
||||
|
||||
arrayToBox( BOUNDING_DATA_INDEX( right ), float32Array, boundingBox );
|
||||
const rightIntersection =
|
||||
cachedObb.intersectsBox( boundingBox ) &&
|
||||
_intersectsGeometry( right, bvh, otherGeometry, geometryToBvh, cachedObb );
|
||||
|
||||
if ( rightIntersection ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { intersectsGeometry };
|
||||
196
node_modules/three-mesh-bvh/src/core/cast/intersectsGeometry.template.js
generated
vendored
Normal file
196
node_modules/three-mesh-bvh/src/core/cast/intersectsGeometry.template.js
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
/* eslint-disable indent */
|
||||
import { Box3, Matrix4 } from 'three';
|
||||
import { OrientedBox } from '../../math/OrientedBox.js';
|
||||
import { ExtendedTriangle } from '../../math/ExtendedTriangle.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { arrayToBox } from '../../utils/ArrayBoxUtilities.js';
|
||||
import { COUNT, OFFSET, IS_LEAF, BOUNDING_DATA_INDEX } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
|
||||
const boundingBox = /* @__PURE__ */ new Box3();
|
||||
const triangle = /* @__PURE__ */ new ExtendedTriangle();
|
||||
const triangle2 = /* @__PURE__ */ new ExtendedTriangle();
|
||||
const invertedMat = /* @__PURE__ */ new Matrix4();
|
||||
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const obb2 = /* @__PURE__ */ new OrientedBox();
|
||||
|
||||
export function intersectsGeometry/* @echo INDIRECT_STRING */( bvh, root, otherGeometry, geometryToBvh ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const result = _intersectsGeometry( 0, bvh, otherGeometry, geometryToBvh );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _intersectsGeometry( nodeIndex32, bvh, otherGeometry, geometryToBvh, cachedObb = null ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
if ( cachedObb === null ) {
|
||||
|
||||
if ( ! otherGeometry.boundingBox ) {
|
||||
|
||||
otherGeometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
obb.set( otherGeometry.boundingBox.min, otherGeometry.boundingBox.max, geometryToBvh );
|
||||
cachedObb = obb;
|
||||
|
||||
}
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const thisGeometry = bvh.geometry;
|
||||
const thisIndex = thisGeometry.index;
|
||||
const thisPos = thisGeometry.attributes.position;
|
||||
|
||||
const index = otherGeometry.index;
|
||||
const pos = otherGeometry.attributes.position;
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
// get the inverse of the geometry matrix so we can transform our triangles into the
|
||||
// geometry space we're trying to test. We assume there are fewer triangles being checked
|
||||
// here.
|
||||
invertedMat.copy( geometryToBvh ).invert();
|
||||
|
||||
if ( otherGeometry.boundsTree ) {
|
||||
|
||||
// if there's a bounds tree
|
||||
arrayToBox( BOUNDING_DATA_INDEX( nodeIndex32 ), float32Array, obb2 );
|
||||
obb2.matrix.copy( invertedMat );
|
||||
obb2.needsUpdate = true;
|
||||
|
||||
// TODO: use a triangle iteration function here
|
||||
const res = otherGeometry.boundsTree.shapecast( {
|
||||
|
||||
intersectsBounds: box => obb2.intersectsBox( box ),
|
||||
|
||||
intersectsTriangle: tri => {
|
||||
|
||||
tri.a.applyMatrix4( geometryToBvh );
|
||||
tri.b.applyMatrix4( geometryToBvh );
|
||||
tri.c.applyMatrix4( geometryToBvh );
|
||||
tri.needsUpdate = true;
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
setTriangle( triangle2, 3 * bvh.resolveTriangleIndex( i ), thisIndex, thisPos );
|
||||
triangle2.needsUpdate = true;
|
||||
if ( tri.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* @else */
|
||||
|
||||
for ( let i = offset * 3, l = ( count + offset ) * 3; i < l; i += 3 ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
setTriangle( triangle2, i, thisIndex, thisPos );
|
||||
triangle2.needsUpdate = true;
|
||||
if ( tri.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* @endif */
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return res;
|
||||
|
||||
} else {
|
||||
|
||||
// if we're just dealing with raw geometry
|
||||
/* @if INDIRECT */
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, thisIndex, thisPos );
|
||||
|
||||
/* @else */
|
||||
|
||||
for ( let i = offset * 3, l = ( count + offset ) * 3; i < l; i += 3 ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
setTriangle( triangle, i, thisIndex, thisPos );
|
||||
|
||||
/* @endif */
|
||||
|
||||
triangle.a.applyMatrix4( invertedMat );
|
||||
triangle.b.applyMatrix4( invertedMat );
|
||||
triangle.c.applyMatrix4( invertedMat );
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
for ( let i2 = 0, l2 = index.count; i2 < l2; i2 += 3 ) {
|
||||
|
||||
setTriangle( triangle2, i2, index, pos );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
if ( triangle.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
}
|
||||
|
||||
/* @else */
|
||||
|
||||
}
|
||||
|
||||
/* @endif */
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const left = nodeIndex32 + 8;
|
||||
const right = uint32Array[ nodeIndex32 + 6 ];
|
||||
|
||||
arrayToBox( BOUNDING_DATA_INDEX( left ), float32Array, boundingBox );
|
||||
const leftIntersection =
|
||||
cachedObb.intersectsBox( boundingBox ) &&
|
||||
_intersectsGeometry( left, bvh, otherGeometry, geometryToBvh, cachedObb );
|
||||
|
||||
if ( leftIntersection ) return true;
|
||||
|
||||
arrayToBox( BOUNDING_DATA_INDEX( right ), float32Array, boundingBox );
|
||||
const rightIntersection =
|
||||
cachedObb.intersectsBox( boundingBox ) &&
|
||||
_intersectsGeometry( right, bvh, otherGeometry, geometryToBvh, cachedObb );
|
||||
|
||||
if ( rightIntersection ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
167
node_modules/three-mesh-bvh/src/core/cast/intersectsGeometry_indirect.generated.js
generated
vendored
Normal file
167
node_modules/three-mesh-bvh/src/core/cast/intersectsGeometry_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
import { Box3, Matrix4 } from 'three';
|
||||
import { OrientedBox } from '../../math/OrientedBox.js';
|
||||
import { ExtendedTriangle } from '../../math/ExtendedTriangle.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
import { arrayToBox } from '../../utils/ArrayBoxUtilities.js';
|
||||
import { IS_LEAF, OFFSET, COUNT, BOUNDING_DATA_INDEX } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
|
||||
/*****************************************************************/
|
||||
/* This file is generated from "intersectsGeometry.template.js". */
|
||||
/*****************************************************************/
|
||||
/* eslint-disable indent */
|
||||
|
||||
const boundingBox = /* @__PURE__ */ new Box3();
|
||||
const triangle = /* @__PURE__ */ new ExtendedTriangle();
|
||||
const triangle2 = /* @__PURE__ */ new ExtendedTriangle();
|
||||
const invertedMat = /* @__PURE__ */ new Matrix4();
|
||||
|
||||
const obb = /* @__PURE__ */ new OrientedBox();
|
||||
const obb2 = /* @__PURE__ */ new OrientedBox();
|
||||
|
||||
function intersectsGeometry_indirect( bvh, root, otherGeometry, geometryToBvh ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const result = _intersectsGeometry( 0, bvh, otherGeometry, geometryToBvh );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _intersectsGeometry( nodeIndex32, bvh, otherGeometry, geometryToBvh, cachedObb = null ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
if ( cachedObb === null ) {
|
||||
|
||||
if ( ! otherGeometry.boundingBox ) {
|
||||
|
||||
otherGeometry.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
obb.set( otherGeometry.boundingBox.min, otherGeometry.boundingBox.max, geometryToBvh );
|
||||
cachedObb = obb;
|
||||
|
||||
}
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const thisGeometry = bvh.geometry;
|
||||
const thisIndex = thisGeometry.index;
|
||||
const thisPos = thisGeometry.attributes.position;
|
||||
|
||||
const index = otherGeometry.index;
|
||||
const pos = otherGeometry.attributes.position;
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
// get the inverse of the geometry matrix so we can transform our triangles into the
|
||||
// geometry space we're trying to test. We assume there are fewer triangles being checked
|
||||
// here.
|
||||
invertedMat.copy( geometryToBvh ).invert();
|
||||
|
||||
if ( otherGeometry.boundsTree ) {
|
||||
|
||||
// if there's a bounds tree
|
||||
arrayToBox( BOUNDING_DATA_INDEX( nodeIndex32 ), float32Array, obb2 );
|
||||
obb2.matrix.copy( invertedMat );
|
||||
obb2.needsUpdate = true;
|
||||
|
||||
// TODO: use a triangle iteration function here
|
||||
const res = otherGeometry.boundsTree.shapecast( {
|
||||
|
||||
intersectsBounds: box => obb2.intersectsBox( box ),
|
||||
|
||||
intersectsTriangle: tri => {
|
||||
|
||||
tri.a.applyMatrix4( geometryToBvh );
|
||||
tri.b.applyMatrix4( geometryToBvh );
|
||||
tri.c.applyMatrix4( geometryToBvh );
|
||||
tri.needsUpdate = true;
|
||||
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
setTriangle( triangle2, 3 * bvh.resolveTriangleIndex( i ), thisIndex, thisPos );
|
||||
triangle2.needsUpdate = true;
|
||||
if ( tri.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return res;
|
||||
|
||||
} else {
|
||||
|
||||
// if we're just dealing with raw geometry
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
// this triangle needs to be transformed into the current BVH coordinate frame
|
||||
const ti = bvh.resolveTriangleIndex( i );
|
||||
setTriangle( triangle, 3 * ti, thisIndex, thisPos );
|
||||
|
||||
|
||||
triangle.a.applyMatrix4( invertedMat );
|
||||
triangle.b.applyMatrix4( invertedMat );
|
||||
triangle.c.applyMatrix4( invertedMat );
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
for ( let i2 = 0, l2 = index.count; i2 < l2; i2 += 3 ) {
|
||||
|
||||
setTriangle( triangle2, i2, index, pos );
|
||||
triangle2.needsUpdate = true;
|
||||
|
||||
if ( triangle.intersectsTriangle( triangle2 ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const left = nodeIndex32 + 8;
|
||||
const right = uint32Array[ nodeIndex32 + 6 ];
|
||||
|
||||
arrayToBox( BOUNDING_DATA_INDEX( left ), float32Array, boundingBox );
|
||||
const leftIntersection =
|
||||
cachedObb.intersectsBox( boundingBox ) &&
|
||||
_intersectsGeometry( left, bvh, otherGeometry, geometryToBvh, cachedObb );
|
||||
|
||||
if ( leftIntersection ) return true;
|
||||
|
||||
arrayToBox( BOUNDING_DATA_INDEX( right ), float32Array, boundingBox );
|
||||
const rightIntersection =
|
||||
cachedObb.intersectsBox( boundingBox ) &&
|
||||
_intersectsGeometry( right, bvh, otherGeometry, geometryToBvh, cachedObb );
|
||||
|
||||
if ( rightIntersection ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { intersectsGeometry_indirect };
|
||||
53
node_modules/three-mesh-bvh/src/core/cast/raycast.generated.js
generated
vendored
Normal file
53
node_modules/three-mesh-bvh/src/core/cast/raycast.generated.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import { intersectRay } from '../utils/intersectUtils.js';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { intersectTris } from '../utils/iterationUtils.generated.js';
|
||||
import '../utils/iterationUtils_indirect.generated.js';
|
||||
|
||||
/******************************************************/
|
||||
/* This file is generated from "raycast.template.js". */
|
||||
/******************************************************/
|
||||
|
||||
function raycast( bvh, root, side, ray, intersects, near, far ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
_raycast( 0, bvh, side, ray, intersects, near, far );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
}
|
||||
|
||||
function _raycast( nodeIndex32, bvh, side, ray, intersects, near, far ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
|
||||
intersectTris( bvh, side, ray, offset, count, intersects, near, far );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
if ( intersectRay( leftIndex, float32Array, ray, near, far ) ) {
|
||||
|
||||
_raycast( leftIndex, bvh, side, ray, intersects, near, far );
|
||||
|
||||
}
|
||||
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
if ( intersectRay( rightIndex, float32Array, ray, near, far ) ) {
|
||||
|
||||
_raycast( rightIndex, bvh, side, ray, intersects, near, far );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { raycast };
|
||||
53
node_modules/three-mesh-bvh/src/core/cast/raycast.template.js
generated
vendored
Normal file
53
node_modules/three-mesh-bvh/src/core/cast/raycast.template.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
import { intersectRay } from '../utils/intersectUtils.js';
|
||||
import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { intersectTris } from '../utils/iterationUtils.generated.js';
|
||||
import { intersectTris_indirect } from '../utils/iterationUtils_indirect.generated.js';
|
||||
|
||||
export function raycast/* @echo INDIRECT_STRING */( bvh, root, side, ray, intersects, near, far ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
_raycast( 0, bvh, side, ray, intersects, near, far );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
}
|
||||
|
||||
function _raycast( nodeIndex32, bvh, side, ray, intersects, near, far ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
intersectTris_indirect( bvh, side, ray, offset, count, intersects, near, far );
|
||||
|
||||
/* @else */
|
||||
|
||||
intersectTris( bvh, side, ray, offset, count, intersects, near, far );
|
||||
|
||||
/* @endif */
|
||||
|
||||
} else {
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
if ( intersectRay( leftIndex, float32Array, ray, near, far ) ) {
|
||||
|
||||
_raycast( leftIndex, bvh, side, ray, intersects, near, far );
|
||||
|
||||
}
|
||||
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
if ( intersectRay( rightIndex, float32Array, ray, near, far ) ) {
|
||||
|
||||
_raycast( rightIndex, bvh, side, ray, intersects, near, far );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
103
node_modules/three-mesh-bvh/src/core/cast/raycastFirst.generated.js
generated
vendored
Normal file
103
node_modules/three-mesh-bvh/src/core/cast/raycastFirst.generated.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import { IS_LEAF, OFFSET, COUNT, SPLIT_AXIS, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { intersectRay } from '../utils/intersectUtils.js';
|
||||
import { intersectClosestTri } from '../utils/iterationUtils.generated.js';
|
||||
import '../utils/iterationUtils_indirect.generated.js';
|
||||
|
||||
/***********************************************************/
|
||||
/* This file is generated from "raycastFirst.template.js". */
|
||||
/***********************************************************/
|
||||
|
||||
const _xyzFields = [ 'x', 'y', 'z' ];
|
||||
|
||||
function raycastFirst( bvh, root, side, ray, near, far ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const result = _raycastFirst( 0, bvh, side, ray, near, far );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _raycastFirst( nodeIndex32, bvh, side, ray, near, far ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
|
||||
// eslint-disable-next-line no-unreachable
|
||||
return intersectClosestTri( bvh, side, ray, offset, count, near, far );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// consider the position of the split plane with respect to the oncoming ray; whichever direction
|
||||
// the ray is coming from, look for an intersection among that side of the tree first
|
||||
const splitAxis = SPLIT_AXIS( nodeIndex32, uint32Array );
|
||||
const xyzAxis = _xyzFields[ splitAxis ];
|
||||
const rayDir = ray.direction[ xyzAxis ];
|
||||
const leftToRight = rayDir >= 0;
|
||||
|
||||
// c1 is the child to check first
|
||||
let c1, c2;
|
||||
if ( leftToRight ) {
|
||||
|
||||
c1 = LEFT_NODE( nodeIndex32 );
|
||||
c2 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
} else {
|
||||
|
||||
c1 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
c2 = LEFT_NODE( nodeIndex32 );
|
||||
|
||||
}
|
||||
|
||||
const c1Intersection = intersectRay( c1, float32Array, ray, near, far );
|
||||
const c1Result = c1Intersection ? _raycastFirst( c1, bvh, side, ray, near, far ) : null;
|
||||
|
||||
// if we got an intersection in the first node and it's closer than the second node's bounding
|
||||
// box, we don't need to consider the second node because it couldn't possibly be a better result
|
||||
if ( c1Result ) {
|
||||
|
||||
// check if the point is within the second bounds
|
||||
// "point" is in the local frame of the bvh
|
||||
const point = c1Result.point[ xyzAxis ];
|
||||
const isOutside = leftToRight ?
|
||||
point <= float32Array[ c2 + splitAxis ] : // min bounding data
|
||||
point >= float32Array[ c2 + splitAxis + 3 ]; // max bounding data
|
||||
|
||||
if ( isOutside ) {
|
||||
|
||||
return c1Result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// either there was no intersection in the first node, or there could still be a closer
|
||||
// intersection in the second, so check the second node and then take the better of the two
|
||||
const c2Intersection = intersectRay( c2, float32Array, ray, near, far );
|
||||
const c2Result = c2Intersection ? _raycastFirst( c2, bvh, side, ray, near, far ) : null;
|
||||
|
||||
if ( c1Result && c2Result ) {
|
||||
|
||||
return c1Result.distance <= c2Result.distance ? c1Result : c2Result;
|
||||
|
||||
} else {
|
||||
|
||||
return c1Result || c2Result || null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { raycastFirst };
|
||||
103
node_modules/three-mesh-bvh/src/core/cast/raycastFirst.template.js
generated
vendored
Normal file
103
node_modules/three-mesh-bvh/src/core/cast/raycastFirst.template.js
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF, SPLIT_AXIS } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { intersectRay } from '../utils/intersectUtils.js';
|
||||
import { intersectClosestTri } from '../utils/iterationUtils.generated.js';
|
||||
import { intersectClosestTri_indirect } from '../utils/iterationUtils_indirect.generated.js';
|
||||
|
||||
const _xyzFields = [ 'x', 'y', 'z' ];
|
||||
|
||||
export function raycastFirst/* @echo INDIRECT_STRING */( bvh, root, side, ray, near, far ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const result = _raycastFirst( 0, bvh, side, ray, near, far );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _raycastFirst( nodeIndex32, bvh, side, ray, near, far ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
return intersectClosestTri_indirect( bvh, side, ray, offset, count, near, far );
|
||||
|
||||
/* @else */
|
||||
|
||||
// eslint-disable-next-line no-unreachable
|
||||
return intersectClosestTri( bvh, side, ray, offset, count, near, far );
|
||||
|
||||
/* @endif */
|
||||
|
||||
} else {
|
||||
|
||||
// consider the position of the split plane with respect to the oncoming ray; whichever direction
|
||||
// the ray is coming from, look for an intersection among that side of the tree first
|
||||
const splitAxis = SPLIT_AXIS( nodeIndex32, uint32Array );
|
||||
const xyzAxis = _xyzFields[ splitAxis ];
|
||||
const rayDir = ray.direction[ xyzAxis ];
|
||||
const leftToRight = rayDir >= 0;
|
||||
|
||||
// c1 is the child to check first
|
||||
let c1, c2;
|
||||
if ( leftToRight ) {
|
||||
|
||||
c1 = LEFT_NODE( nodeIndex32 );
|
||||
c2 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
} else {
|
||||
|
||||
c1 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
c2 = LEFT_NODE( nodeIndex32 );
|
||||
|
||||
}
|
||||
|
||||
const c1Intersection = intersectRay( c1, float32Array, ray, near, far );
|
||||
const c1Result = c1Intersection ? _raycastFirst( c1, bvh, side, ray, near, far ) : null;
|
||||
|
||||
// if we got an intersection in the first node and it's closer than the second node's bounding
|
||||
// box, we don't need to consider the second node because it couldn't possibly be a better result
|
||||
if ( c1Result ) {
|
||||
|
||||
// check if the point is within the second bounds
|
||||
// "point" is in the local frame of the bvh
|
||||
const point = c1Result.point[ xyzAxis ];
|
||||
const isOutside = leftToRight ?
|
||||
point <= float32Array[ c2 + splitAxis ] : // min bounding data
|
||||
point >= float32Array[ c2 + splitAxis + 3 ]; // max bounding data
|
||||
|
||||
if ( isOutside ) {
|
||||
|
||||
return c1Result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// either there was no intersection in the first node, or there could still be a closer
|
||||
// intersection in the second, so check the second node and then take the better of the two
|
||||
const c2Intersection = intersectRay( c2, float32Array, ray, near, far );
|
||||
const c2Result = c2Intersection ? _raycastFirst( c2, bvh, side, ray, near, far ) : null;
|
||||
|
||||
if ( c1Result && c2Result ) {
|
||||
|
||||
return c1Result.distance <= c2Result.distance ? c1Result : c2Result;
|
||||
|
||||
} else {
|
||||
|
||||
return c1Result || c2Result || null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
101
node_modules/three-mesh-bvh/src/core/cast/raycastFirst_indirect.generated.js
generated
vendored
Normal file
101
node_modules/three-mesh-bvh/src/core/cast/raycastFirst_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
import { IS_LEAF, OFFSET, COUNT, SPLIT_AXIS, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import { intersectRay } from '../utils/intersectUtils.js';
|
||||
import '../utils/iterationUtils.generated.js';
|
||||
import { intersectClosestTri_indirect } from '../utils/iterationUtils_indirect.generated.js';
|
||||
|
||||
/***********************************************************/
|
||||
/* This file is generated from "raycastFirst.template.js". */
|
||||
/***********************************************************/
|
||||
|
||||
const _xyzFields = [ 'x', 'y', 'z' ];
|
||||
|
||||
function raycastFirst_indirect( bvh, root, side, ray, near, far ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
const result = _raycastFirst( 0, bvh, side, ray, near, far );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function _raycastFirst( nodeIndex32, bvh, side, ray, near, far ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
return intersectClosestTri_indirect( bvh, side, ray, offset, count, near, far );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// consider the position of the split plane with respect to the oncoming ray; whichever direction
|
||||
// the ray is coming from, look for an intersection among that side of the tree first
|
||||
const splitAxis = SPLIT_AXIS( nodeIndex32, uint32Array );
|
||||
const xyzAxis = _xyzFields[ splitAxis ];
|
||||
const rayDir = ray.direction[ xyzAxis ];
|
||||
const leftToRight = rayDir >= 0;
|
||||
|
||||
// c1 is the child to check first
|
||||
let c1, c2;
|
||||
if ( leftToRight ) {
|
||||
|
||||
c1 = LEFT_NODE( nodeIndex32 );
|
||||
c2 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
|
||||
} else {
|
||||
|
||||
c1 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
c2 = LEFT_NODE( nodeIndex32 );
|
||||
|
||||
}
|
||||
|
||||
const c1Intersection = intersectRay( c1, float32Array, ray, near, far );
|
||||
const c1Result = c1Intersection ? _raycastFirst( c1, bvh, side, ray, near, far ) : null;
|
||||
|
||||
// if we got an intersection in the first node and it's closer than the second node's bounding
|
||||
// box, we don't need to consider the second node because it couldn't possibly be a better result
|
||||
if ( c1Result ) {
|
||||
|
||||
// check if the point is within the second bounds
|
||||
// "point" is in the local frame of the bvh
|
||||
const point = c1Result.point[ xyzAxis ];
|
||||
const isOutside = leftToRight ?
|
||||
point <= float32Array[ c2 + splitAxis ] : // min bounding data
|
||||
point >= float32Array[ c2 + splitAxis + 3 ]; // max bounding data
|
||||
|
||||
if ( isOutside ) {
|
||||
|
||||
return c1Result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// either there was no intersection in the first node, or there could still be a closer
|
||||
// intersection in the second, so check the second node and then take the better of the two
|
||||
const c2Intersection = intersectRay( c2, float32Array, ray, near, far );
|
||||
const c2Result = c2Intersection ? _raycastFirst( c2, bvh, side, ray, near, far ) : null;
|
||||
|
||||
if ( c1Result && c2Result ) {
|
||||
|
||||
return c1Result.distance <= c2Result.distance ? c1Result : c2Result;
|
||||
|
||||
} else {
|
||||
|
||||
return c1Result || c2Result || null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { raycastFirst_indirect };
|
||||
52
node_modules/three-mesh-bvh/src/core/cast/raycast_indirect.generated.js
generated
vendored
Normal file
52
node_modules/three-mesh-bvh/src/core/cast/raycast_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import { intersectRay } from '../utils/intersectUtils.js';
|
||||
import { IS_LEAF, OFFSET, COUNT, LEFT_NODE, RIGHT_NODE } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
import '../utils/iterationUtils.generated.js';
|
||||
import { intersectTris_indirect } from '../utils/iterationUtils_indirect.generated.js';
|
||||
|
||||
/******************************************************/
|
||||
/* This file is generated from "raycast.template.js". */
|
||||
/******************************************************/
|
||||
|
||||
function raycast_indirect( bvh, root, side, ray, intersects, near, far ) {
|
||||
|
||||
BufferStack.setBuffer( bvh._roots[ root ] );
|
||||
_raycast( 0, bvh, side, ray, intersects, near, far );
|
||||
BufferStack.clearBuffer();
|
||||
|
||||
}
|
||||
|
||||
function _raycast( nodeIndex32, bvh, side, ray, intersects, near, far ) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
intersectTris_indirect( bvh, side, ray, offset, count, intersects, near, far );
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
const leftIndex = LEFT_NODE( nodeIndex32 );
|
||||
if ( intersectRay( leftIndex, float32Array, ray, near, far ) ) {
|
||||
|
||||
_raycast( leftIndex, bvh, side, ray, intersects, near, far );
|
||||
|
||||
}
|
||||
|
||||
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
if ( intersectRay( rightIndex, float32Array, ray, near, far ) ) {
|
||||
|
||||
_raycast( rightIndex, bvh, side, ray, intersects, near, far );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { raycast_indirect };
|
||||
172
node_modules/three-mesh-bvh/src/core/cast/refit.generated.js
generated
vendored
Normal file
172
node_modules/three-mesh-bvh/src/core/cast/refit.generated.js
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
import { IS_LEAFNODE_FLAG } from '../Constants.js';
|
||||
|
||||
/****************************************************/
|
||||
/* This file is generated from "refit.template.js". */
|
||||
/****************************************************/
|
||||
|
||||
function refit( bvh, nodeIndices = null ) {
|
||||
|
||||
if ( nodeIndices && Array.isArray( nodeIndices ) ) {
|
||||
|
||||
nodeIndices = new Set( nodeIndices );
|
||||
|
||||
}
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const indexArr = geometry.index ? geometry.index.array : null;
|
||||
const posAttr = geometry.attributes.position;
|
||||
|
||||
let buffer, uint32Array, uint16Array, float32Array;
|
||||
let byteOffset = 0;
|
||||
const roots = bvh._roots;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
buffer = roots[ i ];
|
||||
uint32Array = new Uint32Array( buffer );
|
||||
uint16Array = new Uint16Array( buffer );
|
||||
float32Array = new Float32Array( buffer );
|
||||
|
||||
_traverse( 0, byteOffset );
|
||||
byteOffset += buffer.byteLength;
|
||||
|
||||
}
|
||||
|
||||
function _traverse( node32Index, byteOffset, force = false ) {
|
||||
|
||||
const node16Index = node32Index * 2;
|
||||
const isLeaf = uint16Array[ node16Index + 15 ] === IS_LEAFNODE_FLAG;
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = uint32Array[ node32Index + 6 ];
|
||||
const count = uint16Array[ node16Index + 14 ];
|
||||
|
||||
let minx = Infinity;
|
||||
let miny = Infinity;
|
||||
let minz = Infinity;
|
||||
let maxx = - Infinity;
|
||||
let maxy = - Infinity;
|
||||
let maxz = - Infinity;
|
||||
|
||||
|
||||
for ( let i = 3 * offset, l = 3 * ( offset + count ); i < l; i ++ ) {
|
||||
|
||||
let index = indexArr[ i ];
|
||||
const x = posAttr.getX( index );
|
||||
const y = posAttr.getY( index );
|
||||
const z = posAttr.getZ( index );
|
||||
|
||||
if ( x < minx ) minx = x;
|
||||
if ( x > maxx ) maxx = x;
|
||||
|
||||
if ( y < miny ) miny = y;
|
||||
if ( y > maxy ) maxy = y;
|
||||
|
||||
if ( z < minz ) minz = z;
|
||||
if ( z > maxz ) maxz = z;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
float32Array[ node32Index + 0 ] !== minx ||
|
||||
float32Array[ node32Index + 1 ] !== miny ||
|
||||
float32Array[ node32Index + 2 ] !== minz ||
|
||||
|
||||
float32Array[ node32Index + 3 ] !== maxx ||
|
||||
float32Array[ node32Index + 4 ] !== maxy ||
|
||||
float32Array[ node32Index + 5 ] !== maxz
|
||||
) {
|
||||
|
||||
float32Array[ node32Index + 0 ] = minx;
|
||||
float32Array[ node32Index + 1 ] = miny;
|
||||
float32Array[ node32Index + 2 ] = minz;
|
||||
|
||||
float32Array[ node32Index + 3 ] = maxx;
|
||||
float32Array[ node32Index + 4 ] = maxy;
|
||||
float32Array[ node32Index + 5 ] = maxz;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const left = node32Index + 8;
|
||||
const right = uint32Array[ node32Index + 6 ];
|
||||
|
||||
// the identifying node indices provided by the shapecast function include offsets of all
|
||||
// root buffers to guarantee they're unique between roots so offset left and right indices here.
|
||||
const offsetLeft = left + byteOffset;
|
||||
const offsetRight = right + byteOffset;
|
||||
let forceChildren = force;
|
||||
let includesLeft = false;
|
||||
let includesRight = false;
|
||||
|
||||
if ( nodeIndices ) {
|
||||
|
||||
// if we see that neither the left or right child are included in the set that need to be updated
|
||||
// then we assume that all children need to be updated.
|
||||
if ( ! forceChildren ) {
|
||||
|
||||
includesLeft = nodeIndices.has( offsetLeft );
|
||||
includesRight = nodeIndices.has( offsetRight );
|
||||
forceChildren = ! includesLeft && ! includesRight;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
includesLeft = true;
|
||||
includesRight = true;
|
||||
|
||||
}
|
||||
|
||||
const traverseLeft = forceChildren || includesLeft;
|
||||
const traverseRight = forceChildren || includesRight;
|
||||
|
||||
let leftChange = false;
|
||||
if ( traverseLeft ) {
|
||||
|
||||
leftChange = _traverse( left, byteOffset, forceChildren );
|
||||
|
||||
}
|
||||
|
||||
let rightChange = false;
|
||||
if ( traverseRight ) {
|
||||
|
||||
rightChange = _traverse( right, byteOffset, forceChildren );
|
||||
|
||||
}
|
||||
|
||||
const didChange = leftChange || rightChange;
|
||||
if ( didChange ) {
|
||||
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
const lefti = left + i;
|
||||
const righti = right + i;
|
||||
const minLeftValue = float32Array[ lefti ];
|
||||
const maxLeftValue = float32Array[ lefti + 3 ];
|
||||
const minRightValue = float32Array[ righti ];
|
||||
const maxRightValue = float32Array[ righti + 3 ];
|
||||
|
||||
float32Array[ node32Index + i ] = minLeftValue < minRightValue ? minLeftValue : minRightValue;
|
||||
float32Array[ node32Index + i + 3 ] = maxLeftValue > maxRightValue ? maxLeftValue : maxRightValue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return didChange;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { refit };
|
||||
196
node_modules/three-mesh-bvh/src/core/cast/refit.template.js
generated
vendored
Normal file
196
node_modules/three-mesh-bvh/src/core/cast/refit.template.js
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
import { IS_LEAFNODE_FLAG } from '../Constants.js';
|
||||
|
||||
export function refit/* @echo INDIRECT_STRING */( bvh, nodeIndices = null ) {
|
||||
|
||||
if ( nodeIndices && Array.isArray( nodeIndices ) ) {
|
||||
|
||||
nodeIndices = new Set( nodeIndices );
|
||||
|
||||
}
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const indexArr = geometry.index ? geometry.index.array : null;
|
||||
const posAttr = geometry.attributes.position;
|
||||
|
||||
let buffer, uint32Array, uint16Array, float32Array;
|
||||
let byteOffset = 0;
|
||||
const roots = bvh._roots;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
buffer = roots[ i ];
|
||||
uint32Array = new Uint32Array( buffer );
|
||||
uint16Array = new Uint16Array( buffer );
|
||||
float32Array = new Float32Array( buffer );
|
||||
|
||||
_traverse( 0, byteOffset );
|
||||
byteOffset += buffer.byteLength;
|
||||
|
||||
}
|
||||
|
||||
function _traverse( node32Index, byteOffset, force = false ) {
|
||||
|
||||
const node16Index = node32Index * 2;
|
||||
const isLeaf = uint16Array[ node16Index + 15 ] === IS_LEAFNODE_FLAG;
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = uint32Array[ node32Index + 6 ];
|
||||
const count = uint16Array[ node16Index + 14 ];
|
||||
|
||||
let minx = Infinity;
|
||||
let miny = Infinity;
|
||||
let minz = Infinity;
|
||||
let maxx = - Infinity;
|
||||
let maxy = - Infinity;
|
||||
let maxz = - Infinity;
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
const t = 3 * bvh.resolveTriangleIndex( i );
|
||||
for ( let j = 0; j < 3; j ++ ) {
|
||||
|
||||
let index = t + j;
|
||||
index = indexArr ? indexArr[ index ] : index;
|
||||
|
||||
const x = posAttr.getX( index );
|
||||
const y = posAttr.getY( index );
|
||||
const z = posAttr.getZ( index );
|
||||
|
||||
if ( x < minx ) minx = x;
|
||||
if ( x > maxx ) maxx = x;
|
||||
|
||||
if ( y < miny ) miny = y;
|
||||
if ( y > maxy ) maxy = y;
|
||||
|
||||
if ( z < minz ) minz = z;
|
||||
if ( z > maxz ) maxz = z;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* @else */
|
||||
|
||||
for ( let i = 3 * offset, l = 3 * ( offset + count ); i < l; i ++ ) {
|
||||
|
||||
let index = indexArr[ i ];
|
||||
const x = posAttr.getX( index );
|
||||
const y = posAttr.getY( index );
|
||||
const z = posAttr.getZ( index );
|
||||
|
||||
if ( x < minx ) minx = x;
|
||||
if ( x > maxx ) maxx = x;
|
||||
|
||||
if ( y < miny ) miny = y;
|
||||
if ( y > maxy ) maxy = y;
|
||||
|
||||
if ( z < minz ) minz = z;
|
||||
if ( z > maxz ) maxz = z;
|
||||
|
||||
}
|
||||
|
||||
/* @endif */
|
||||
|
||||
if (
|
||||
float32Array[ node32Index + 0 ] !== minx ||
|
||||
float32Array[ node32Index + 1 ] !== miny ||
|
||||
float32Array[ node32Index + 2 ] !== minz ||
|
||||
|
||||
float32Array[ node32Index + 3 ] !== maxx ||
|
||||
float32Array[ node32Index + 4 ] !== maxy ||
|
||||
float32Array[ node32Index + 5 ] !== maxz
|
||||
) {
|
||||
|
||||
float32Array[ node32Index + 0 ] = minx;
|
||||
float32Array[ node32Index + 1 ] = miny;
|
||||
float32Array[ node32Index + 2 ] = minz;
|
||||
|
||||
float32Array[ node32Index + 3 ] = maxx;
|
||||
float32Array[ node32Index + 4 ] = maxy;
|
||||
float32Array[ node32Index + 5 ] = maxz;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const left = node32Index + 8;
|
||||
const right = uint32Array[ node32Index + 6 ];
|
||||
|
||||
// the identifying node indices provided by the shapecast function include offsets of all
|
||||
// root buffers to guarantee they're unique between roots so offset left and right indices here.
|
||||
const offsetLeft = left + byteOffset;
|
||||
const offsetRight = right + byteOffset;
|
||||
let forceChildren = force;
|
||||
let includesLeft = false;
|
||||
let includesRight = false;
|
||||
|
||||
if ( nodeIndices ) {
|
||||
|
||||
// if we see that neither the left or right child are included in the set that need to be updated
|
||||
// then we assume that all children need to be updated.
|
||||
if ( ! forceChildren ) {
|
||||
|
||||
includesLeft = nodeIndices.has( offsetLeft );
|
||||
includesRight = nodeIndices.has( offsetRight );
|
||||
forceChildren = ! includesLeft && ! includesRight;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
includesLeft = true;
|
||||
includesRight = true;
|
||||
|
||||
}
|
||||
|
||||
const traverseLeft = forceChildren || includesLeft;
|
||||
const traverseRight = forceChildren || includesRight;
|
||||
|
||||
let leftChange = false;
|
||||
if ( traverseLeft ) {
|
||||
|
||||
leftChange = _traverse( left, byteOffset, forceChildren );
|
||||
|
||||
}
|
||||
|
||||
let rightChange = false;
|
||||
if ( traverseRight ) {
|
||||
|
||||
rightChange = _traverse( right, byteOffset, forceChildren );
|
||||
|
||||
}
|
||||
|
||||
const didChange = leftChange || rightChange;
|
||||
if ( didChange ) {
|
||||
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
const lefti = left + i;
|
||||
const righti = right + i;
|
||||
const minLeftValue = float32Array[ lefti ];
|
||||
const maxLeftValue = float32Array[ lefti + 3 ];
|
||||
const minRightValue = float32Array[ righti ];
|
||||
const maxRightValue = float32Array[ righti + 3 ];
|
||||
|
||||
float32Array[ node32Index + i ] = minLeftValue < minRightValue ? minLeftValue : minRightValue;
|
||||
float32Array[ node32Index + i + 3 ] = maxLeftValue > maxRightValue ? maxLeftValue : maxRightValue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return didChange;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
179
node_modules/three-mesh-bvh/src/core/cast/refit_indirect.generated.js
generated
vendored
Normal file
179
node_modules/three-mesh-bvh/src/core/cast/refit_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
import { IS_LEAFNODE_FLAG } from '../Constants.js';
|
||||
|
||||
/****************************************************/
|
||||
/* This file is generated from "refit.template.js". */
|
||||
/****************************************************/
|
||||
|
||||
function refit_indirect( bvh, nodeIndices = null ) {
|
||||
|
||||
if ( nodeIndices && Array.isArray( nodeIndices ) ) {
|
||||
|
||||
nodeIndices = new Set( nodeIndices );
|
||||
|
||||
}
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const indexArr = geometry.index ? geometry.index.array : null;
|
||||
const posAttr = geometry.attributes.position;
|
||||
|
||||
let buffer, uint32Array, uint16Array, float32Array;
|
||||
let byteOffset = 0;
|
||||
const roots = bvh._roots;
|
||||
for ( let i = 0, l = roots.length; i < l; i ++ ) {
|
||||
|
||||
buffer = roots[ i ];
|
||||
uint32Array = new Uint32Array( buffer );
|
||||
uint16Array = new Uint16Array( buffer );
|
||||
float32Array = new Float32Array( buffer );
|
||||
|
||||
_traverse( 0, byteOffset );
|
||||
byteOffset += buffer.byteLength;
|
||||
|
||||
}
|
||||
|
||||
function _traverse( node32Index, byteOffset, force = false ) {
|
||||
|
||||
const node16Index = node32Index * 2;
|
||||
const isLeaf = uint16Array[ node16Index + 15 ] === IS_LEAFNODE_FLAG;
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = uint32Array[ node32Index + 6 ];
|
||||
const count = uint16Array[ node16Index + 14 ];
|
||||
|
||||
let minx = Infinity;
|
||||
let miny = Infinity;
|
||||
let minz = Infinity;
|
||||
let maxx = - Infinity;
|
||||
let maxy = - Infinity;
|
||||
let maxz = - Infinity;
|
||||
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
const t = 3 * bvh.resolveTriangleIndex( i );
|
||||
for ( let j = 0; j < 3; j ++ ) {
|
||||
|
||||
let index = t + j;
|
||||
index = indexArr ? indexArr[ index ] : index;
|
||||
|
||||
const x = posAttr.getX( index );
|
||||
const y = posAttr.getY( index );
|
||||
const z = posAttr.getZ( index );
|
||||
|
||||
if ( x < minx ) minx = x;
|
||||
if ( x > maxx ) maxx = x;
|
||||
|
||||
if ( y < miny ) miny = y;
|
||||
if ( y > maxy ) maxy = y;
|
||||
|
||||
if ( z < minz ) minz = z;
|
||||
if ( z > maxz ) maxz = z;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
float32Array[ node32Index + 0 ] !== minx ||
|
||||
float32Array[ node32Index + 1 ] !== miny ||
|
||||
float32Array[ node32Index + 2 ] !== minz ||
|
||||
|
||||
float32Array[ node32Index + 3 ] !== maxx ||
|
||||
float32Array[ node32Index + 4 ] !== maxy ||
|
||||
float32Array[ node32Index + 5 ] !== maxz
|
||||
) {
|
||||
|
||||
float32Array[ node32Index + 0 ] = minx;
|
||||
float32Array[ node32Index + 1 ] = miny;
|
||||
float32Array[ node32Index + 2 ] = minz;
|
||||
|
||||
float32Array[ node32Index + 3 ] = maxx;
|
||||
float32Array[ node32Index + 4 ] = maxy;
|
||||
float32Array[ node32Index + 5 ] = maxz;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const left = node32Index + 8;
|
||||
const right = uint32Array[ node32Index + 6 ];
|
||||
|
||||
// the identifying node indices provided by the shapecast function include offsets of all
|
||||
// root buffers to guarantee they're unique between roots so offset left and right indices here.
|
||||
const offsetLeft = left + byteOffset;
|
||||
const offsetRight = right + byteOffset;
|
||||
let forceChildren = force;
|
||||
let includesLeft = false;
|
||||
let includesRight = false;
|
||||
|
||||
if ( nodeIndices ) {
|
||||
|
||||
// if we see that neither the left or right child are included in the set that need to be updated
|
||||
// then we assume that all children need to be updated.
|
||||
if ( ! forceChildren ) {
|
||||
|
||||
includesLeft = nodeIndices.has( offsetLeft );
|
||||
includesRight = nodeIndices.has( offsetRight );
|
||||
forceChildren = ! includesLeft && ! includesRight;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
includesLeft = true;
|
||||
includesRight = true;
|
||||
|
||||
}
|
||||
|
||||
const traverseLeft = forceChildren || includesLeft;
|
||||
const traverseRight = forceChildren || includesRight;
|
||||
|
||||
let leftChange = false;
|
||||
if ( traverseLeft ) {
|
||||
|
||||
leftChange = _traverse( left, byteOffset, forceChildren );
|
||||
|
||||
}
|
||||
|
||||
let rightChange = false;
|
||||
if ( traverseRight ) {
|
||||
|
||||
rightChange = _traverse( right, byteOffset, forceChildren );
|
||||
|
||||
}
|
||||
|
||||
const didChange = leftChange || rightChange;
|
||||
if ( didChange ) {
|
||||
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
const lefti = left + i;
|
||||
const righti = right + i;
|
||||
const minLeftValue = float32Array[ lefti ];
|
||||
const maxLeftValue = float32Array[ lefti + 3 ];
|
||||
const minRightValue = float32Array[ righti ];
|
||||
const maxRightValue = float32Array[ righti + 3 ];
|
||||
|
||||
float32Array[ node32Index + i ] = minLeftValue < minRightValue ? minLeftValue : minRightValue;
|
||||
float32Array[ node32Index + i + 3 ] = maxLeftValue > maxRightValue ? maxLeftValue : maxRightValue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return didChange;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { refit_indirect };
|
||||
214
node_modules/three-mesh-bvh/src/core/cast/shapecast.js
generated
vendored
Normal file
214
node_modules/three-mesh-bvh/src/core/cast/shapecast.js
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
import { Box3 } from 'three';
|
||||
import { CONTAINED } from '../Constants.js';
|
||||
import { arrayToBox } from '../../utils/ArrayBoxUtilities.js';
|
||||
import { PrimitivePool } from '../../utils/PrimitivePool.js';
|
||||
import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF, BOUNDING_DATA_INDEX } from '../utils/nodeBufferUtils.js';
|
||||
import { BufferStack } from '../utils/BufferStack.js';
|
||||
|
||||
let _box1, _box2;
|
||||
const boxStack = [];
|
||||
const boxPool = /* @__PURE__ */ new PrimitivePool( () => new Box3() );
|
||||
|
||||
export function shapecast( bvh, root, intersectsBounds, intersectsRange, boundsTraverseOrder, byteOffset ) {
|
||||
|
||||
// setup
|
||||
_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 );
|
||||
|
||||
// cleanup
|
||||
BufferStack.clearBuffer();
|
||||
boxPool.releasePrimitive( _box1 );
|
||||
boxPool.releasePrimitive( _box2 );
|
||||
boxStack.pop();
|
||||
boxStack.pop();
|
||||
|
||||
const length = boxStack.length;
|
||||
if ( length > 0 ) {
|
||||
|
||||
_box2 = boxStack[ length - 1 ];
|
||||
_box1 = boxStack[ length - 2 ];
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function shapecastTraverse(
|
||||
nodeIndex32,
|
||||
geometry,
|
||||
intersectsBoundsFunc,
|
||||
intersectsRangeFunc,
|
||||
nodeScoreFunc = null,
|
||||
nodeIndexByteOffset = 0, // offset for unique node identifier
|
||||
depth = 0
|
||||
) {
|
||||
|
||||
const { float32Array, uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
const isLeaf = IS_LEAF( nodeIndex16, uint16Array );
|
||||
if ( isLeaf ) {
|
||||
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( nodeIndex32 ), float32Array, _box1 );
|
||||
return intersectsRangeFunc( offset, count, false, depth, nodeIndexByteOffset + nodeIndex32, _box1 );
|
||||
|
||||
} else {
|
||||
|
||||
const left = LEFT_NODE( nodeIndex32 );
|
||||
const right = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
let c1 = left;
|
||||
let c2 = right;
|
||||
|
||||
let score1, score2;
|
||||
let box1, box2;
|
||||
if ( nodeScoreFunc ) {
|
||||
|
||||
box1 = _box1;
|
||||
box2 = _box2;
|
||||
|
||||
// bounding data is not offset
|
||||
arrayToBox( BOUNDING_DATA_INDEX( c1 ), float32Array, box1 );
|
||||
arrayToBox( BOUNDING_DATA_INDEX( c2 ), float32Array, box2 );
|
||||
|
||||
score1 = nodeScoreFunc( box1 );
|
||||
score2 = nodeScoreFunc( box2 );
|
||||
|
||||
if ( score2 < score1 ) {
|
||||
|
||||
c1 = right;
|
||||
c2 = left;
|
||||
|
||||
const temp = score1;
|
||||
score1 = score2;
|
||||
score2 = temp;
|
||||
|
||||
box1 = box2;
|
||||
// box2 is always set before use below
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check box 1 intersection
|
||||
if ( ! box1 ) {
|
||||
|
||||
box1 = _box1;
|
||||
arrayToBox( BOUNDING_DATA_INDEX( c1 ), float32Array, box1 );
|
||||
|
||||
}
|
||||
|
||||
const isC1Leaf = IS_LEAF( c1 * 2, uint16Array );
|
||||
const c1Intersection = intersectsBoundsFunc( box1, isC1Leaf, score1, depth + 1, nodeIndexByteOffset + c1 );
|
||||
|
||||
let c1StopTraversal;
|
||||
if ( c1Intersection === CONTAINED ) {
|
||||
|
||||
const offset = getLeftOffset( c1 );
|
||||
const end = getRightEndOffset( c1 );
|
||||
const count = end - offset;
|
||||
|
||||
c1StopTraversal = intersectsRangeFunc( offset, count, true, depth + 1, nodeIndexByteOffset + c1, box1 );
|
||||
|
||||
} else {
|
||||
|
||||
c1StopTraversal =
|
||||
c1Intersection &&
|
||||
shapecastTraverse(
|
||||
c1,
|
||||
geometry,
|
||||
intersectsBoundsFunc,
|
||||
intersectsRangeFunc,
|
||||
nodeScoreFunc,
|
||||
nodeIndexByteOffset,
|
||||
depth + 1
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ( c1StopTraversal ) return true;
|
||||
|
||||
// Check box 2 intersection
|
||||
// cached box2 will have been overwritten by previous traversal
|
||||
box2 = _box2;
|
||||
arrayToBox( BOUNDING_DATA_INDEX( c2 ), float32Array, box2 );
|
||||
|
||||
const isC2Leaf = IS_LEAF( c2 * 2, uint16Array );
|
||||
const c2Intersection = intersectsBoundsFunc( box2, isC2Leaf, score2, depth + 1, nodeIndexByteOffset + c2 );
|
||||
|
||||
let c2StopTraversal;
|
||||
if ( c2Intersection === CONTAINED ) {
|
||||
|
||||
const offset = getLeftOffset( c2 );
|
||||
const end = getRightEndOffset( c2 );
|
||||
const count = end - offset;
|
||||
|
||||
c2StopTraversal = intersectsRangeFunc( offset, count, true, depth + 1, nodeIndexByteOffset + c2, box2 );
|
||||
|
||||
} else {
|
||||
|
||||
c2StopTraversal =
|
||||
c2Intersection &&
|
||||
shapecastTraverse(
|
||||
c2,
|
||||
geometry,
|
||||
intersectsBoundsFunc,
|
||||
intersectsRangeFunc,
|
||||
nodeScoreFunc,
|
||||
nodeIndexByteOffset,
|
||||
depth + 1
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if ( c2StopTraversal ) return true;
|
||||
|
||||
return false;
|
||||
|
||||
// Define these inside the function so it has access to the local variables needed
|
||||
// when converting to the buffer equivalents
|
||||
function getLeftOffset( nodeIndex32 ) {
|
||||
|
||||
const { uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
// traverse until we find a leaf
|
||||
while ( ! IS_LEAF( nodeIndex16, uint16Array ) ) {
|
||||
|
||||
nodeIndex32 = LEFT_NODE( nodeIndex32 );
|
||||
nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
}
|
||||
|
||||
return OFFSET( nodeIndex32, uint32Array );
|
||||
|
||||
}
|
||||
|
||||
function getRightEndOffset( nodeIndex32 ) {
|
||||
|
||||
const { uint16Array, uint32Array } = BufferStack;
|
||||
let nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
// traverse until we find a leaf
|
||||
while ( ! IS_LEAF( nodeIndex16, uint16Array ) ) {
|
||||
|
||||
// adjust offset to point to the right node
|
||||
nodeIndex32 = RIGHT_NODE( nodeIndex32, uint32Array );
|
||||
nodeIndex16 = nodeIndex32 * 2;
|
||||
|
||||
}
|
||||
|
||||
// return the end offset of the triangle range
|
||||
return OFFSET( nodeIndex32, uint32Array ) + COUNT( nodeIndex16, uint16Array );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
45
node_modules/three-mesh-bvh/src/core/utils/BufferStack.js
generated
vendored
Normal file
45
node_modules/three-mesh-bvh/src/core/utils/BufferStack.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
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() );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const BufferStack = new _BufferStack();
|
||||
78
node_modules/three-mesh-bvh/src/core/utils/intersectUtils.js
generated
vendored
Normal file
78
node_modules/three-mesh-bvh/src/core/utils/intersectUtils.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* This function performs intersection tests similar to Ray.intersectBox in three.js,
|
||||
* with the difference that the box values are read from an array to improve performance.
|
||||
*/
|
||||
export function intersectRay( nodeIndex32, array, ray, near, far ) {
|
||||
|
||||
let tmin, tmax, tymin, tymax, tzmin, tzmax;
|
||||
|
||||
const invdirx = 1 / ray.direction.x,
|
||||
invdiry = 1 / ray.direction.y,
|
||||
invdirz = 1 / ray.direction.z;
|
||||
|
||||
const ox = ray.origin.x;
|
||||
const oy = ray.origin.y;
|
||||
const oz = ray.origin.z;
|
||||
|
||||
let minx = array[ nodeIndex32 ];
|
||||
let maxx = array[ nodeIndex32 + 3 ];
|
||||
|
||||
let miny = array[ nodeIndex32 + 1 ];
|
||||
let maxy = array[ nodeIndex32 + 3 + 1 ];
|
||||
|
||||
let minz = array[ nodeIndex32 + 2 ];
|
||||
let maxz = array[ nodeIndex32 + 3 + 2 ];
|
||||
|
||||
if ( invdirx >= 0 ) {
|
||||
|
||||
tmin = ( minx - ox ) * invdirx;
|
||||
tmax = ( maxx - ox ) * invdirx;
|
||||
|
||||
} else {
|
||||
|
||||
tmin = ( maxx - ox ) * invdirx;
|
||||
tmax = ( minx - ox ) * invdirx;
|
||||
|
||||
}
|
||||
|
||||
if ( invdiry >= 0 ) {
|
||||
|
||||
tymin = ( miny - oy ) * invdiry;
|
||||
tymax = ( maxy - oy ) * invdiry;
|
||||
|
||||
} else {
|
||||
|
||||
tymin = ( maxy - oy ) * invdiry;
|
||||
tymax = ( miny - oy ) * invdiry;
|
||||
|
||||
}
|
||||
|
||||
if ( ( tmin > tymax ) || ( tymin > tmax ) ) return false;
|
||||
|
||||
if ( tymin > tmin || isNaN( tmin ) ) tmin = tymin;
|
||||
|
||||
if ( tymax < tmax || isNaN( tmax ) ) tmax = tymax;
|
||||
|
||||
if ( invdirz >= 0 ) {
|
||||
|
||||
tzmin = ( minz - oz ) * invdirz;
|
||||
tzmax = ( maxz - oz ) * invdirz;
|
||||
|
||||
} else {
|
||||
|
||||
tzmin = ( maxz - oz ) * invdirz;
|
||||
tzmax = ( minz - oz ) * invdirz;
|
||||
|
||||
}
|
||||
|
||||
if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return false;
|
||||
|
||||
if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;
|
||||
|
||||
if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;
|
||||
|
||||
//return point closest to the ray (positive side)
|
||||
|
||||
return tmin <= far && tmax >= near;
|
||||
|
||||
}
|
||||
81
node_modules/three-mesh-bvh/src/core/utils/iterationUtils.generated.js
generated
vendored
Normal file
81
node_modules/three-mesh-bvh/src/core/utils/iterationUtils.generated.js
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
import { intersectTri } from '../../utils/ThreeRayIntersectUtilities.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
|
||||
/*************************************************************/
|
||||
/* This file is generated from "iterationUtils.template.js". */
|
||||
/*************************************************************/
|
||||
/* eslint-disable indent */
|
||||
|
||||
function intersectTris( bvh, side, ray, offset, count, intersections, near, far ) {
|
||||
|
||||
const { geometry, _indirectBuffer } = bvh;
|
||||
for ( let i = offset, end = offset + count; i < end; i ++ ) {
|
||||
|
||||
|
||||
intersectTri( geometry, side, ray, i, intersections, near, far );
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function intersectClosestTri( bvh, side, ray, offset, count, near, far ) {
|
||||
|
||||
const { geometry, _indirectBuffer } = bvh;
|
||||
let dist = Infinity;
|
||||
let res = null;
|
||||
for ( let i = offset, end = offset + count; i < end; i ++ ) {
|
||||
|
||||
let intersection;
|
||||
|
||||
intersection = intersectTri( geometry, side, ray, i, null, near, far );
|
||||
|
||||
|
||||
if ( intersection && intersection.distance < dist ) {
|
||||
|
||||
res = intersection;
|
||||
dist = intersection.distance;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
function iterateOverTriangles(
|
||||
offset,
|
||||
count,
|
||||
bvh,
|
||||
intersectsTriangleFunc,
|
||||
contained,
|
||||
depth,
|
||||
triangle
|
||||
) {
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
let tri;
|
||||
|
||||
tri = i;
|
||||
|
||||
setTriangle( triangle, tri * 3, index, pos );
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
if ( intersectsTriangleFunc( triangle, tri, contained, depth ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
export { intersectClosestTri, intersectTris, iterateOverTriangles };
|
||||
94
node_modules/three-mesh-bvh/src/core/utils/iterationUtils.template.js
generated
vendored
Normal file
94
node_modules/three-mesh-bvh/src/core/utils/iterationUtils.template.js
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/* eslint-disable indent */
|
||||
import { intersectTri } from '../../utils/ThreeRayIntersectUtilities.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
|
||||
export function intersectTris/* @echo INDIRECT_STRING */( bvh, side, ray, offset, count, intersections, near, far ) {
|
||||
|
||||
const { geometry, _indirectBuffer } = bvh;
|
||||
for ( let i = offset, end = offset + count; i < end; i ++ ) {
|
||||
|
||||
/* @if INDIRECT */
|
||||
|
||||
let vi = _indirectBuffer ? _indirectBuffer[ i ] : i;
|
||||
intersectTri( geometry, side, ray, vi, intersections, near, far );
|
||||
|
||||
/* @else */
|
||||
|
||||
intersectTri( geometry, side, ray, i, intersections, near, far );
|
||||
|
||||
/* @endif */
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function intersectClosestTri/* @echo INDIRECT_STRING */( bvh, side, ray, offset, count, near, far ) {
|
||||
|
||||
const { geometry, _indirectBuffer } = bvh;
|
||||
let dist = Infinity;
|
||||
let res = null;
|
||||
for ( let i = offset, end = offset + count; i < end; i ++ ) {
|
||||
|
||||
let intersection;
|
||||
/* @if INDIRECT */
|
||||
|
||||
intersection = intersectTri( geometry, side, ray, _indirectBuffer ? _indirectBuffer[ i ] : i, null, near, far );
|
||||
|
||||
/* @else */
|
||||
|
||||
intersection = intersectTri( geometry, side, ray, i, null, near, far );
|
||||
|
||||
/* @endif */
|
||||
|
||||
if ( intersection && intersection.distance < dist ) {
|
||||
|
||||
res = intersection;
|
||||
dist = intersection.distance;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
export function iterateOverTriangles/* @echo INDIRECT_STRING */(
|
||||
offset,
|
||||
count,
|
||||
bvh,
|
||||
intersectsTriangleFunc,
|
||||
contained,
|
||||
depth,
|
||||
triangle
|
||||
) {
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
let tri;
|
||||
/* @if INDIRECT */
|
||||
|
||||
tri = bvh.resolveTriangleIndex( i );
|
||||
|
||||
/* @else */
|
||||
|
||||
tri = i;
|
||||
|
||||
/* @endif */
|
||||
setTriangle( triangle, tri * 3, index, pos );
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
if ( intersectsTriangleFunc( triangle, tri, contained, depth ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
79
node_modules/three-mesh-bvh/src/core/utils/iterationUtils_indirect.generated.js
generated
vendored
Normal file
79
node_modules/three-mesh-bvh/src/core/utils/iterationUtils_indirect.generated.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
import { intersectTri } from '../../utils/ThreeRayIntersectUtilities.js';
|
||||
import { setTriangle } from '../../utils/TriangleUtilities.js';
|
||||
|
||||
/*************************************************************/
|
||||
/* This file is generated from "iterationUtils.template.js". */
|
||||
/*************************************************************/
|
||||
/* eslint-disable indent */
|
||||
|
||||
function intersectTris_indirect( bvh, side, ray, offset, count, intersections, near, far ) {
|
||||
|
||||
const { geometry, _indirectBuffer } = bvh;
|
||||
for ( let i = offset, end = offset + count; i < end; i ++ ) {
|
||||
|
||||
let vi = _indirectBuffer ? _indirectBuffer[ i ] : i;
|
||||
intersectTri( geometry, side, ray, vi, intersections, near, far );
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function intersectClosestTri_indirect( bvh, side, ray, offset, count, near, far ) {
|
||||
|
||||
const { geometry, _indirectBuffer } = bvh;
|
||||
let dist = Infinity;
|
||||
let res = null;
|
||||
for ( let i = offset, end = offset + count; i < end; i ++ ) {
|
||||
|
||||
let intersection;
|
||||
intersection = intersectTri( geometry, side, ray, _indirectBuffer ? _indirectBuffer[ i ] : i, null, near, far );
|
||||
|
||||
|
||||
if ( intersection && intersection.distance < dist ) {
|
||||
|
||||
res = intersection;
|
||||
dist = intersection.distance;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
function iterateOverTriangles_indirect(
|
||||
offset,
|
||||
count,
|
||||
bvh,
|
||||
intersectsTriangleFunc,
|
||||
contained,
|
||||
depth,
|
||||
triangle
|
||||
) {
|
||||
|
||||
const { geometry } = bvh;
|
||||
const { index } = geometry;
|
||||
const pos = geometry.attributes.position;
|
||||
for ( let i = offset, l = count + offset; i < l; i ++ ) {
|
||||
|
||||
let tri;
|
||||
tri = bvh.resolveTriangleIndex( i );
|
||||
|
||||
setTriangle( triangle, tri * 3, index, pos );
|
||||
triangle.needsUpdate = true;
|
||||
|
||||
if ( intersectsTriangleFunc( triangle, tri, contained, depth ) ) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
export { intersectClosestTri_indirect, intersectTris_indirect, iterateOverTriangles_indirect };
|
||||
41
node_modules/three-mesh-bvh/src/core/utils/nodeBufferUtils.js
generated
vendored
Normal file
41
node_modules/three-mesh-bvh/src/core/utils/nodeBufferUtils.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
export function IS_LEAF( n16, uint16Array ) {
|
||||
|
||||
return uint16Array[ n16 + 15 ] === 0xFFFF;
|
||||
|
||||
}
|
||||
|
||||
export function OFFSET( n32, uint32Array ) {
|
||||
|
||||
return uint32Array[ n32 + 6 ];
|
||||
|
||||
}
|
||||
|
||||
export function COUNT( n16, uint16Array ) {
|
||||
|
||||
return uint16Array[ n16 + 14 ];
|
||||
|
||||
}
|
||||
|
||||
export function LEFT_NODE( n32 ) {
|
||||
|
||||
return n32 + 8;
|
||||
|
||||
}
|
||||
|
||||
export function RIGHT_NODE( n32, uint32Array ) {
|
||||
|
||||
return uint32Array[ n32 + 6 ];
|
||||
|
||||
}
|
||||
|
||||
export function SPLIT_AXIS( n32, uint32Array ) {
|
||||
|
||||
return uint32Array[ n32 + 7 ];
|
||||
|
||||
}
|
||||
|
||||
export function BOUNDING_DATA_INDEX( n32 ) {
|
||||
|
||||
return n32;
|
||||
|
||||
}
|
||||
292
node_modules/three-mesh-bvh/src/debug/Debug.js
generated
vendored
Normal file
292
node_modules/three-mesh-bvh/src/debug/Debug.js
generated
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
import { Box3, Vector3 } from 'three';
|
||||
import { TRAVERSAL_COST, TRIANGLE_INTERSECT_COST } from '../core/Constants.js';
|
||||
import { arrayToBox } from '../utils/ArrayBoxUtilities.js';
|
||||
import { isSharedArrayBufferSupported } from '../utils/BufferUtils.js';
|
||||
|
||||
const _box1 = /* @__PURE__ */ new Box3();
|
||||
const _box2 = /* @__PURE__ */ new Box3();
|
||||
const _vec = /* @__PURE__ */ new Vector3();
|
||||
|
||||
// https://stackoverflow.com/questions/1248302/how-to-get-the-size-of-a-javascript-object
|
||||
function getPrimitiveSize( el ) {
|
||||
|
||||
switch ( typeof el ) {
|
||||
|
||||
case 'number':
|
||||
return 8;
|
||||
case 'string':
|
||||
return el.length * 2;
|
||||
case 'boolean':
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function isTypedArray( arr ) {
|
||||
|
||||
const regex = /(Uint|Int|Float)(8|16|32)Array/;
|
||||
return regex.test( arr.constructor.name );
|
||||
|
||||
}
|
||||
|
||||
function getRootExtremes( bvh, group ) {
|
||||
|
||||
const result = {
|
||||
nodeCount: 0,
|
||||
leafNodeCount: 0,
|
||||
|
||||
depth: {
|
||||
min: Infinity, max: - Infinity
|
||||
},
|
||||
tris: {
|
||||
min: Infinity, max: - Infinity
|
||||
},
|
||||
splits: [ 0, 0, 0 ],
|
||||
surfaceAreaScore: 0,
|
||||
};
|
||||
|
||||
bvh.traverse( ( depth, isLeaf, boundingData, offsetOrSplit, count ) => {
|
||||
|
||||
const l0 = boundingData[ 0 + 3 ] - boundingData[ 0 ];
|
||||
const l1 = boundingData[ 1 + 3 ] - boundingData[ 1 ];
|
||||
const l2 = boundingData[ 2 + 3 ] - boundingData[ 2 ];
|
||||
|
||||
const surfaceArea = 2 * ( l0 * l1 + l1 * l2 + l2 * l0 );
|
||||
|
||||
result.nodeCount ++;
|
||||
if ( isLeaf ) {
|
||||
|
||||
result.leafNodeCount ++;
|
||||
|
||||
result.depth.min = Math.min( depth, result.depth.min );
|
||||
result.depth.max = Math.max( depth, result.depth.max );
|
||||
|
||||
result.tris.min = Math.min( count, result.tris.min );
|
||||
result.tris.max = Math.max( count, result.tris.max );
|
||||
|
||||
result.surfaceAreaScore += surfaceArea * TRIANGLE_INTERSECT_COST * count;
|
||||
|
||||
} else {
|
||||
|
||||
result.splits[ offsetOrSplit ] ++;
|
||||
|
||||
result.surfaceAreaScore += surfaceArea * TRAVERSAL_COST;
|
||||
|
||||
}
|
||||
|
||||
}, group );
|
||||
|
||||
// If there are no leaf nodes because the tree hasn't finished generating yet.
|
||||
if ( result.tris.min === Infinity ) {
|
||||
|
||||
result.tris.min = 0;
|
||||
result.tris.max = 0;
|
||||
|
||||
}
|
||||
|
||||
if ( result.depth.min === Infinity ) {
|
||||
|
||||
result.depth.min = 0;
|
||||
result.depth.max = 0;
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
function getBVHExtremes( bvh ) {
|
||||
|
||||
return bvh._roots.map( ( root, i ) => getRootExtremes( bvh, i ) );
|
||||
|
||||
}
|
||||
|
||||
function estimateMemoryInBytes( obj ) {
|
||||
|
||||
const traversed = new Set();
|
||||
const stack = [ obj ];
|
||||
let bytes = 0;
|
||||
|
||||
while ( stack.length ) {
|
||||
|
||||
const curr = stack.pop();
|
||||
if ( traversed.has( curr ) ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
traversed.add( curr );
|
||||
|
||||
for ( let key in curr ) {
|
||||
|
||||
if ( ! Object.hasOwn( curr, key ) ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
bytes += getPrimitiveSize( key );
|
||||
|
||||
const value = curr[ key ];
|
||||
if ( value && ( typeof value === 'object' || typeof value === 'function' ) ) {
|
||||
|
||||
if ( isTypedArray( value ) ) {
|
||||
|
||||
bytes += value.byteLength;
|
||||
|
||||
} else if ( isSharedArrayBufferSupported() && value instanceof SharedArrayBuffer ) {
|
||||
|
||||
bytes += value.byteLength;
|
||||
|
||||
} else if ( value instanceof ArrayBuffer ) {
|
||||
|
||||
bytes += value.byteLength;
|
||||
|
||||
} else {
|
||||
|
||||
stack.push( value );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
bytes += getPrimitiveSize( value );
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return bytes;
|
||||
|
||||
}
|
||||
|
||||
function validateBounds( bvh ) {
|
||||
|
||||
const geometry = bvh.geometry;
|
||||
const depthStack = [];
|
||||
const index = geometry.index;
|
||||
const position = geometry.getAttribute( 'position' );
|
||||
let passes = true;
|
||||
|
||||
bvh.traverse( ( depth, isLeaf, boundingData, offset, count ) => {
|
||||
|
||||
const info = {
|
||||
depth,
|
||||
isLeaf,
|
||||
boundingData,
|
||||
offset,
|
||||
count,
|
||||
};
|
||||
depthStack[ depth ] = info;
|
||||
|
||||
arrayToBox( 0, boundingData, _box1 );
|
||||
const parent = depthStack[ depth - 1 ];
|
||||
|
||||
if ( isLeaf ) {
|
||||
|
||||
// check triangles
|
||||
for ( let i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
const triIndex = bvh.resolveTriangleIndex( i );
|
||||
let i0 = 3 * triIndex;
|
||||
let i1 = 3 * triIndex + 1;
|
||||
let i2 = 3 * triIndex + 2;
|
||||
|
||||
if ( index ) {
|
||||
|
||||
i0 = index.getX( i0 );
|
||||
i1 = index.getX( i1 );
|
||||
i2 = index.getX( i2 );
|
||||
|
||||
}
|
||||
|
||||
let isContained;
|
||||
|
||||
_vec.fromBufferAttribute( position, i0 );
|
||||
isContained = _box1.containsPoint( _vec );
|
||||
|
||||
_vec.fromBufferAttribute( position, i1 );
|
||||
isContained = isContained && _box1.containsPoint( _vec );
|
||||
|
||||
_vec.fromBufferAttribute( position, i2 );
|
||||
isContained = isContained && _box1.containsPoint( _vec );
|
||||
|
||||
console.assert( isContained, 'Leaf bounds does not fully contain triangle.' );
|
||||
passes = passes && isContained;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( parent ) {
|
||||
|
||||
// check if my bounds fit in my parents
|
||||
arrayToBox( 0, boundingData, _box2 );
|
||||
|
||||
const isContained = _box2.containsBox( _box1 );
|
||||
console.assert( isContained, 'Parent bounds does not fully contain child.' );
|
||||
passes = passes && isContained;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return passes;
|
||||
|
||||
}
|
||||
|
||||
// Returns a simple, human readable object that represents the BVH.
|
||||
function getJSONStructure( bvh ) {
|
||||
|
||||
const depthStack = [];
|
||||
|
||||
bvh.traverse( ( depth, isLeaf, boundingData, offset, count ) => {
|
||||
|
||||
const info = {
|
||||
bounds: arrayToBox( 0, boundingData, new Box3() ),
|
||||
};
|
||||
|
||||
if ( isLeaf ) {
|
||||
|
||||
info.count = count;
|
||||
info.offset = offset;
|
||||
|
||||
} else {
|
||||
|
||||
info.left = null;
|
||||
info.right = null;
|
||||
|
||||
}
|
||||
|
||||
depthStack[ depth ] = info;
|
||||
|
||||
// traversal hits the left then right node
|
||||
const parent = depthStack[ depth - 1 ];
|
||||
if ( parent ) {
|
||||
|
||||
if ( parent.left === null ) {
|
||||
|
||||
parent.left = info;
|
||||
|
||||
} else {
|
||||
|
||||
parent.right = info;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return depthStack[ 0 ];
|
||||
|
||||
}
|
||||
|
||||
export { estimateMemoryInBytes, getBVHExtremes, validateBounds, getJSONStructure };
|
||||
4
node_modules/three-mesh-bvh/src/gpu/BVHShaderGLSL.js
generated
vendored
Normal file
4
node_modules/three-mesh-bvh/src/gpu/BVHShaderGLSL.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './glsl/common_functions.glsl.js';
|
||||
export * from './glsl/bvh_distance_functions.glsl.js';
|
||||
export * from './glsl/bvh_ray_functions.glsl.js';
|
||||
export * from './glsl/bvh_struct_definitions.glsl.js';
|
||||
191
node_modules/three-mesh-bvh/src/gpu/MeshBVHUniformStruct.js
generated
vendored
Normal file
191
node_modules/three-mesh-bvh/src/gpu/MeshBVHUniformStruct.js
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
import {
|
||||
DataTexture,
|
||||
FloatType,
|
||||
UnsignedIntType,
|
||||
RGBAFormat,
|
||||
RGIntegerFormat,
|
||||
NearestFilter,
|
||||
BufferAttribute,
|
||||
} from 'three';
|
||||
import {
|
||||
FloatVertexAttributeTexture,
|
||||
UIntVertexAttributeTexture,
|
||||
} from './VertexAttributeTexture.js';
|
||||
import { BYTES_PER_NODE } from '../core/Constants.js';
|
||||
import {
|
||||
BOUNDING_DATA_INDEX,
|
||||
COUNT,
|
||||
IS_LEAF,
|
||||
RIGHT_NODE,
|
||||
OFFSET,
|
||||
SPLIT_AXIS,
|
||||
} from '../core/utils/nodeBufferUtils.js';
|
||||
import { getIndexArray, getVertexCount } from '../core/build/geometryUtils.js';
|
||||
|
||||
export class MeshBVHUniformStruct {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.index = new UIntVertexAttributeTexture();
|
||||
this.position = new FloatVertexAttributeTexture();
|
||||
this.bvhBounds = new DataTexture();
|
||||
this.bvhContents = new DataTexture();
|
||||
this._cachedIndexAttr = null;
|
||||
|
||||
this.index.overrideItemSize = 3;
|
||||
|
||||
}
|
||||
|
||||
updateFrom( bvh ) {
|
||||
|
||||
const { geometry } = bvh;
|
||||
bvhToTextures( bvh, this.bvhBounds, this.bvhContents );
|
||||
|
||||
this.position.updateFrom( geometry.attributes.position );
|
||||
|
||||
// dereference a new index attribute if we're using indirect storage
|
||||
if ( bvh.indirect ) {
|
||||
|
||||
const indirectBuffer = bvh._indirectBuffer;
|
||||
if (
|
||||
this._cachedIndexAttr === null ||
|
||||
this._cachedIndexAttr.count !== indirectBuffer.length
|
||||
) {
|
||||
|
||||
if ( geometry.index ) {
|
||||
|
||||
this._cachedIndexAttr = geometry.index.clone();
|
||||
|
||||
} else {
|
||||
|
||||
const array = getIndexArray( getVertexCount( geometry ) );
|
||||
this._cachedIndexAttr = new BufferAttribute( array, 1, false );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dereferenceIndex( geometry, indirectBuffer, this._cachedIndexAttr );
|
||||
this.index.updateFrom( this._cachedIndexAttr );
|
||||
|
||||
} else {
|
||||
|
||||
this.index.updateFrom( geometry.index );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
const { index, position, bvhBounds, bvhContents } = this;
|
||||
|
||||
if ( index ) index.dispose();
|
||||
if ( position ) position.dispose();
|
||||
if ( bvhBounds ) bvhBounds.dispose();
|
||||
if ( bvhContents ) bvhContents.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function dereferenceIndex( geometry, indirectBuffer, target ) {
|
||||
|
||||
const unpacked = target.array;
|
||||
const indexArray = geometry.index ? geometry.index.array : null;
|
||||
for ( let i = 0, l = indirectBuffer.length; i < l; i ++ ) {
|
||||
|
||||
const i3 = 3 * i;
|
||||
const v3 = 3 * indirectBuffer[ i ];
|
||||
for ( let c = 0; c < 3; c ++ ) {
|
||||
|
||||
unpacked[ i3 + c ] = indexArray ? indexArray[ v3 + c ] : v3 + c;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function bvhToTextures( bvh, boundsTexture, contentsTexture ) {
|
||||
|
||||
const roots = bvh._roots;
|
||||
|
||||
if ( roots.length !== 1 ) {
|
||||
|
||||
throw new Error( 'MeshBVHUniformStruct: Multi-root BVHs not supported.' );
|
||||
|
||||
}
|
||||
|
||||
const root = roots[ 0 ];
|
||||
const uint16Array = new Uint16Array( root );
|
||||
const uint32Array = new Uint32Array( root );
|
||||
const float32Array = new Float32Array( root );
|
||||
|
||||
// Both bounds need two elements per node so compute the height so it's twice as long as
|
||||
// the width so we can expand the row by two and still have a square texture
|
||||
const nodeCount = root.byteLength / BYTES_PER_NODE;
|
||||
const boundsDimension = 2 * Math.ceil( Math.sqrt( nodeCount / 2 ) );
|
||||
const boundsArray = new Float32Array( 4 * boundsDimension * boundsDimension );
|
||||
|
||||
const contentsDimension = Math.ceil( Math.sqrt( nodeCount ) );
|
||||
const contentsArray = new Uint32Array( 2 * contentsDimension * contentsDimension );
|
||||
|
||||
for ( let i = 0; i < nodeCount; i ++ ) {
|
||||
|
||||
const nodeIndex32 = i * BYTES_PER_NODE / 4;
|
||||
const nodeIndex16 = nodeIndex32 * 2;
|
||||
const boundsIndex = BOUNDING_DATA_INDEX( nodeIndex32 );
|
||||
for ( let b = 0; b < 3; b ++ ) {
|
||||
|
||||
boundsArray[ 8 * i + 0 + b ] = float32Array[ boundsIndex + 0 + b ];
|
||||
boundsArray[ 8 * i + 4 + b ] = float32Array[ boundsIndex + 3 + b ];
|
||||
|
||||
}
|
||||
|
||||
if ( IS_LEAF( nodeIndex16, uint16Array ) ) {
|
||||
|
||||
const count = COUNT( nodeIndex16, uint16Array );
|
||||
const offset = OFFSET( nodeIndex32, uint32Array );
|
||||
|
||||
const mergedLeafCount = 0xffff0000 | count;
|
||||
contentsArray[ i * 2 + 0 ] = mergedLeafCount;
|
||||
contentsArray[ i * 2 + 1 ] = offset;
|
||||
|
||||
} else {
|
||||
|
||||
const rightIndex = 4 * RIGHT_NODE( nodeIndex32, uint32Array ) / BYTES_PER_NODE;
|
||||
const splitAxis = SPLIT_AXIS( nodeIndex32, uint32Array );
|
||||
|
||||
contentsArray[ i * 2 + 0 ] = splitAxis;
|
||||
contentsArray[ i * 2 + 1 ] = rightIndex;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
boundsTexture.image.data = boundsArray;
|
||||
boundsTexture.image.width = boundsDimension;
|
||||
boundsTexture.image.height = boundsDimension;
|
||||
boundsTexture.format = RGBAFormat;
|
||||
boundsTexture.type = FloatType;
|
||||
boundsTexture.internalFormat = 'RGBA32F';
|
||||
boundsTexture.minFilter = NearestFilter;
|
||||
boundsTexture.magFilter = NearestFilter;
|
||||
boundsTexture.generateMipmaps = false;
|
||||
boundsTexture.needsUpdate = true;
|
||||
boundsTexture.dispose();
|
||||
|
||||
contentsTexture.image.data = contentsArray;
|
||||
contentsTexture.image.width = contentsDimension;
|
||||
contentsTexture.image.height = contentsDimension;
|
||||
contentsTexture.format = RGIntegerFormat;
|
||||
contentsTexture.type = UnsignedIntType;
|
||||
contentsTexture.internalFormat = 'RG32UI';
|
||||
contentsTexture.minFilter = NearestFilter;
|
||||
contentsTexture.magFilter = NearestFilter;
|
||||
contentsTexture.generateMipmaps = false;
|
||||
contentsTexture.needsUpdate = true;
|
||||
contentsTexture.dispose();
|
||||
|
||||
}
|
||||
309
node_modules/three-mesh-bvh/src/gpu/VertexAttributeTexture.js
generated
vendored
Normal file
309
node_modules/three-mesh-bvh/src/gpu/VertexAttributeTexture.js
generated
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
import {
|
||||
DataTexture,
|
||||
FloatType,
|
||||
IntType,
|
||||
UnsignedIntType,
|
||||
ByteType,
|
||||
UnsignedByteType,
|
||||
ShortType,
|
||||
UnsignedShortType,
|
||||
|
||||
RedFormat,
|
||||
RGFormat,
|
||||
RGBAFormat,
|
||||
|
||||
RedIntegerFormat,
|
||||
RGIntegerFormat,
|
||||
RGBAIntegerFormat,
|
||||
|
||||
NearestFilter,
|
||||
} from 'three';
|
||||
|
||||
function countToStringFormat( count ) {
|
||||
|
||||
switch ( count ) {
|
||||
|
||||
case 1: return 'R';
|
||||
case 2: return 'RG';
|
||||
case 3: return 'RGBA';
|
||||
case 4: return 'RGBA';
|
||||
|
||||
}
|
||||
|
||||
throw new Error();
|
||||
|
||||
}
|
||||
|
||||
function countToFormat( count ) {
|
||||
|
||||
switch ( count ) {
|
||||
|
||||
case 1: return RedFormat;
|
||||
case 2: return RGFormat;
|
||||
case 3: return RGBAFormat;
|
||||
case 4: return RGBAFormat;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function countToIntFormat( count ) {
|
||||
|
||||
switch ( count ) {
|
||||
|
||||
case 1: return RedIntegerFormat;
|
||||
case 2: return RGIntegerFormat;
|
||||
case 3: return RGBAIntegerFormat;
|
||||
case 4: return RGBAIntegerFormat;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class VertexAttributeTexture extends DataTexture {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this.minFilter = NearestFilter;
|
||||
this.magFilter = NearestFilter;
|
||||
this.generateMipmaps = false;
|
||||
this.overrideItemSize = null;
|
||||
this._forcedType = null;
|
||||
|
||||
}
|
||||
|
||||
updateFrom( attr ) {
|
||||
|
||||
const overrideItemSize = this.overrideItemSize;
|
||||
const originalItemSize = attr.itemSize;
|
||||
const originalCount = attr.count;
|
||||
if ( overrideItemSize !== null ) {
|
||||
|
||||
if ( ( originalItemSize * originalCount ) % overrideItemSize !== 0.0 ) {
|
||||
|
||||
throw new Error( 'VertexAttributeTexture: overrideItemSize must divide evenly into buffer length.' );
|
||||
|
||||
}
|
||||
|
||||
attr.itemSize = overrideItemSize;
|
||||
attr.count = originalCount * originalItemSize / overrideItemSize;
|
||||
|
||||
}
|
||||
|
||||
const itemSize = attr.itemSize;
|
||||
const count = attr.count;
|
||||
const normalized = attr.normalized;
|
||||
const originalBufferCons = attr.array.constructor;
|
||||
const byteCount = originalBufferCons.BYTES_PER_ELEMENT;
|
||||
let targetType = this._forcedType;
|
||||
let finalStride = itemSize;
|
||||
|
||||
// derive the type of texture this should be in the shader
|
||||
if ( targetType === null ) {
|
||||
|
||||
switch ( originalBufferCons ) {
|
||||
|
||||
case Float32Array:
|
||||
targetType = FloatType;
|
||||
break;
|
||||
|
||||
case Uint8Array:
|
||||
case Uint16Array:
|
||||
case Uint32Array:
|
||||
targetType = UnsignedIntType;
|
||||
break;
|
||||
|
||||
case Int8Array:
|
||||
case Int16Array:
|
||||
case Int32Array:
|
||||
targetType = IntType;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// get the target format to store the texture as
|
||||
let type, format, normalizeValue, targetBufferCons;
|
||||
let internalFormat = countToStringFormat( itemSize );
|
||||
switch ( targetType ) {
|
||||
|
||||
case FloatType:
|
||||
normalizeValue = 1.0;
|
||||
format = countToFormat( itemSize );
|
||||
|
||||
if ( normalized && byteCount === 1 ) {
|
||||
|
||||
targetBufferCons = originalBufferCons;
|
||||
internalFormat += '8';
|
||||
|
||||
if ( originalBufferCons === Uint8Array ) {
|
||||
|
||||
type = UnsignedByteType;
|
||||
|
||||
} else {
|
||||
|
||||
type = ByteType;
|
||||
internalFormat += '_SNORM';
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
targetBufferCons = Float32Array;
|
||||
internalFormat += '32F';
|
||||
type = FloatType;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IntType:
|
||||
internalFormat += byteCount * 8 + 'I';
|
||||
normalizeValue = normalized ? Math.pow( 2, originalBufferCons.BYTES_PER_ELEMENT * 8 - 1 ) : 1.0;
|
||||
format = countToIntFormat( itemSize );
|
||||
|
||||
if ( byteCount === 1 ) {
|
||||
|
||||
targetBufferCons = Int8Array;
|
||||
type = ByteType;
|
||||
|
||||
} else if ( byteCount === 2 ) {
|
||||
|
||||
targetBufferCons = Int16Array;
|
||||
type = ShortType;
|
||||
|
||||
} else {
|
||||
|
||||
targetBufferCons = Int32Array;
|
||||
type = IntType;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UnsignedIntType:
|
||||
internalFormat += byteCount * 8 + 'UI';
|
||||
normalizeValue = normalized ? Math.pow( 2, originalBufferCons.BYTES_PER_ELEMENT * 8 - 1 ) : 1.0;
|
||||
format = countToIntFormat( itemSize );
|
||||
|
||||
if ( byteCount === 1 ) {
|
||||
|
||||
targetBufferCons = Uint8Array;
|
||||
type = UnsignedByteType;
|
||||
|
||||
} else if ( byteCount === 2 ) {
|
||||
|
||||
targetBufferCons = Uint16Array;
|
||||
type = UnsignedShortType;
|
||||
|
||||
} else {
|
||||
|
||||
targetBufferCons = Uint32Array;
|
||||
type = UnsignedIntType;
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// there will be a mismatch between format length and final length because
|
||||
// RGBFormat and RGBIntegerFormat was removed
|
||||
if ( finalStride === 3 && ( format === RGBAFormat || format === RGBAIntegerFormat ) ) {
|
||||
|
||||
finalStride = 4;
|
||||
|
||||
}
|
||||
|
||||
// copy the data over to the new texture array
|
||||
const dimension = Math.ceil( Math.sqrt( count ) ) || 1;
|
||||
const length = finalStride * dimension * dimension;
|
||||
const dataArray = new targetBufferCons( length );
|
||||
|
||||
// temporarily set the normalized state to false since we have custom normalization logic
|
||||
const originalNormalized = attr.normalized;
|
||||
attr.normalized = false;
|
||||
for ( let i = 0; i < count; i ++ ) {
|
||||
|
||||
const ii = finalStride * i;
|
||||
dataArray[ ii ] = attr.getX( i ) / normalizeValue;
|
||||
|
||||
if ( itemSize >= 2 ) {
|
||||
|
||||
dataArray[ ii + 1 ] = attr.getY( i ) / normalizeValue;
|
||||
|
||||
}
|
||||
|
||||
if ( itemSize >= 3 ) {
|
||||
|
||||
dataArray[ ii + 2 ] = attr.getZ( i ) / normalizeValue;
|
||||
|
||||
if ( finalStride === 4 ) {
|
||||
|
||||
dataArray[ ii + 3 ] = 1.0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( itemSize >= 4 ) {
|
||||
|
||||
dataArray[ ii + 3 ] = attr.getW( i ) / normalizeValue;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
attr.normalized = originalNormalized;
|
||||
|
||||
this.internalFormat = internalFormat;
|
||||
this.format = format;
|
||||
this.type = type;
|
||||
this.image.width = dimension;
|
||||
this.image.height = dimension;
|
||||
this.image.data = dataArray;
|
||||
this.needsUpdate = true;
|
||||
this.dispose();
|
||||
|
||||
attr.itemSize = originalItemSize;
|
||||
attr.count = originalCount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class UIntVertexAttributeTexture extends VertexAttributeTexture {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._forcedType = UnsignedIntType;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class IntVertexAttributeTexture extends VertexAttributeTexture {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._forcedType = IntType;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class FloatVertexAttributeTexture extends VertexAttributeTexture {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
this._forcedType = FloatType;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
195
node_modules/three-mesh-bvh/src/gpu/glsl/bvh_distance_functions.glsl.js
generated
vendored
Normal file
195
node_modules/three-mesh-bvh/src/gpu/glsl/bvh_distance_functions.glsl.js
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
// Distance to Point
|
||||
export const bvh_distance_functions = /* glsl */`
|
||||
|
||||
float dot2( vec3 v ) {
|
||||
|
||||
return dot( v, v );
|
||||
|
||||
}
|
||||
|
||||
// https://www.shadertoy.com/view/ttfGWl
|
||||
vec3 closestPointToTriangle( vec3 p, vec3 v0, vec3 v1, vec3 v2, out vec3 barycoord ) {
|
||||
|
||||
vec3 v10 = v1 - v0;
|
||||
vec3 v21 = v2 - v1;
|
||||
vec3 v02 = v0 - v2;
|
||||
|
||||
vec3 p0 = p - v0;
|
||||
vec3 p1 = p - v1;
|
||||
vec3 p2 = p - v2;
|
||||
|
||||
vec3 nor = cross( v10, v02 );
|
||||
|
||||
// method 2, in barycentric space
|
||||
vec3 q = cross( nor, p0 );
|
||||
float d = 1.0 / dot2( nor );
|
||||
float u = d * dot( q, v02 );
|
||||
float v = d * dot( q, v10 );
|
||||
float w = 1.0 - u - v;
|
||||
|
||||
if( u < 0.0 ) {
|
||||
|
||||
w = clamp( dot( p2, v02 ) / dot2( v02 ), 0.0, 1.0 );
|
||||
u = 0.0;
|
||||
v = 1.0 - w;
|
||||
|
||||
} else if( v < 0.0 ) {
|
||||
|
||||
u = clamp( dot( p0, v10 ) / dot2( v10 ), 0.0, 1.0 );
|
||||
v = 0.0;
|
||||
w = 1.0 - u;
|
||||
|
||||
} else if( w < 0.0 ) {
|
||||
|
||||
v = clamp( dot( p1, v21 ) / dot2( v21 ), 0.0, 1.0 );
|
||||
w = 0.0;
|
||||
u = 1.0-v;
|
||||
|
||||
}
|
||||
|
||||
barycoord = vec3( u, v, w );
|
||||
return u * v1 + v * v2 + w * v0;
|
||||
|
||||
}
|
||||
|
||||
float distanceToTriangles(
|
||||
// geometry info and triangle range
|
||||
sampler2D positionAttr, usampler2D indexAttr, uint offset, uint count,
|
||||
|
||||
// point and cut off range
|
||||
vec3 point, float closestDistanceSquared,
|
||||
|
||||
// outputs
|
||||
inout uvec4 faceIndices, inout vec3 faceNormal, inout vec3 barycoord, inout float side, inout vec3 outPoint
|
||||
) {
|
||||
|
||||
bool found = false;
|
||||
vec3 localBarycoord;
|
||||
for ( uint i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
uvec3 indices = uTexelFetch1D( indexAttr, i ).xyz;
|
||||
vec3 a = texelFetch1D( positionAttr, indices.x ).rgb;
|
||||
vec3 b = texelFetch1D( positionAttr, indices.y ).rgb;
|
||||
vec3 c = texelFetch1D( positionAttr, indices.z ).rgb;
|
||||
|
||||
// get the closest point and barycoord
|
||||
vec3 closestPoint = closestPointToTriangle( point, a, b, c, localBarycoord );
|
||||
vec3 delta = point - closestPoint;
|
||||
float sqDist = dot2( delta );
|
||||
if ( sqDist < closestDistanceSquared ) {
|
||||
|
||||
// set the output results
|
||||
closestDistanceSquared = sqDist;
|
||||
faceIndices = uvec4( indices.xyz, i );
|
||||
faceNormal = normalize( cross( a - b, b - c ) );
|
||||
barycoord = localBarycoord;
|
||||
outPoint = closestPoint;
|
||||
side = sign( dot( faceNormal, delta ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return closestDistanceSquared;
|
||||
|
||||
}
|
||||
|
||||
float distanceSqToBounds( vec3 point, vec3 boundsMin, vec3 boundsMax ) {
|
||||
|
||||
vec3 clampedPoint = clamp( point, boundsMin, boundsMax );
|
||||
vec3 delta = point - clampedPoint;
|
||||
return dot( delta, delta );
|
||||
|
||||
}
|
||||
|
||||
float distanceSqToBVHNodeBoundsPoint( vec3 point, sampler2D bvhBounds, uint currNodeIndex ) {
|
||||
|
||||
uint cni2 = currNodeIndex * 2u;
|
||||
vec3 boundsMin = texelFetch1D( bvhBounds, cni2 ).xyz;
|
||||
vec3 boundsMax = texelFetch1D( bvhBounds, cni2 + 1u ).xyz;
|
||||
return distanceSqToBounds( point, boundsMin, boundsMax );
|
||||
|
||||
}
|
||||
|
||||
// use a macro to hide the fact that we need to expand the struct into separate fields
|
||||
#define\
|
||||
bvhClosestPointToPoint(\
|
||||
bvh,\
|
||||
point, faceIndices, faceNormal, barycoord, side, outPoint\
|
||||
)\
|
||||
_bvhClosestPointToPoint(\
|
||||
bvh.position, bvh.index, bvh.bvhBounds, bvh.bvhContents,\
|
||||
point, faceIndices, faceNormal, barycoord, side, outPoint\
|
||||
)
|
||||
|
||||
float _bvhClosestPointToPoint(
|
||||
// bvh info
|
||||
sampler2D bvh_position, usampler2D bvh_index, sampler2D bvh_bvhBounds, usampler2D bvh_bvhContents,
|
||||
|
||||
// point to check
|
||||
vec3 point,
|
||||
|
||||
// output variables
|
||||
inout uvec4 faceIndices, inout vec3 faceNormal, inout vec3 barycoord,
|
||||
inout float side, inout vec3 outPoint
|
||||
) {
|
||||
|
||||
// stack needs to be twice as long as the deepest tree we expect because
|
||||
// we push both the left and right child onto the stack every traversal
|
||||
int ptr = 0;
|
||||
uint stack[ BVH_STACK_DEPTH ];
|
||||
stack[ 0 ] = 0u;
|
||||
|
||||
float closestDistanceSquared = pow( 100000.0, 2.0 );
|
||||
bool found = false;
|
||||
while ( ptr > - 1 && ptr < BVH_STACK_DEPTH ) {
|
||||
|
||||
uint currNodeIndex = stack[ ptr ];
|
||||
ptr --;
|
||||
|
||||
// check if we intersect the current bounds
|
||||
float boundsHitDistance = distanceSqToBVHNodeBoundsPoint( point, bvh_bvhBounds, currNodeIndex );
|
||||
if ( boundsHitDistance > closestDistanceSquared ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
uvec2 boundsInfo = uTexelFetch1D( bvh_bvhContents, currNodeIndex ).xy;
|
||||
bool isLeaf = bool( boundsInfo.x & 0xffff0000u );
|
||||
if ( isLeaf ) {
|
||||
|
||||
uint count = boundsInfo.x & 0x0000ffffu;
|
||||
uint offset = boundsInfo.y;
|
||||
closestDistanceSquared = distanceToTriangles(
|
||||
bvh_position, bvh_index, offset, count, point, closestDistanceSquared,
|
||||
|
||||
// outputs
|
||||
faceIndices, faceNormal, barycoord, side, outPoint
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
uint leftIndex = currNodeIndex + 1u;
|
||||
uint splitAxis = boundsInfo.x & 0x0000ffffu;
|
||||
uint rightIndex = boundsInfo.y;
|
||||
bool leftToRight = distanceSqToBVHNodeBoundsPoint( point, bvh_bvhBounds, leftIndex ) < distanceSqToBVHNodeBoundsPoint( point, bvh_bvhBounds, rightIndex );//rayDirection[ splitAxis ] >= 0.0;
|
||||
uint c1 = leftToRight ? leftIndex : rightIndex;
|
||||
uint c2 = leftToRight ? rightIndex : leftIndex;
|
||||
|
||||
// set c2 in the stack so we traverse it later. We need to keep track of a pointer in
|
||||
// the stack while we traverse. The second pointer added is the one that will be
|
||||
// traversed first
|
||||
ptr ++;
|
||||
stack[ ptr ] = c2;
|
||||
ptr ++;
|
||||
stack[ ptr ] = c1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sqrt( closestDistanceSquared );
|
||||
|
||||
}
|
||||
`;
|
||||
213
node_modules/three-mesh-bvh/src/gpu/glsl/bvh_ray_functions.glsl.js
generated
vendored
Normal file
213
node_modules/three-mesh-bvh/src/gpu/glsl/bvh_ray_functions.glsl.js
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
export const bvh_ray_functions = /* glsl */`
|
||||
|
||||
#ifndef TRI_INTERSECT_EPSILON
|
||||
#define TRI_INTERSECT_EPSILON 1e-5
|
||||
#endif
|
||||
|
||||
// Raycasting
|
||||
bool intersectsBounds( vec3 rayOrigin, vec3 rayDirection, vec3 boundsMin, vec3 boundsMax, out float dist ) {
|
||||
|
||||
// https://www.reddit.com/r/opengl/comments/8ntzz5/fast_glsl_ray_box_intersection/
|
||||
// https://tavianator.com/2011/ray_box.html
|
||||
vec3 invDir = 1.0 / rayDirection;
|
||||
|
||||
// find intersection distances for each plane
|
||||
vec3 tMinPlane = invDir * ( boundsMin - rayOrigin );
|
||||
vec3 tMaxPlane = invDir * ( boundsMax - rayOrigin );
|
||||
|
||||
// get the min and max distances from each intersection
|
||||
vec3 tMinHit = min( tMaxPlane, tMinPlane );
|
||||
vec3 tMaxHit = max( tMaxPlane, tMinPlane );
|
||||
|
||||
// get the furthest hit distance
|
||||
vec2 t = max( tMinHit.xx, tMinHit.yz );
|
||||
float t0 = max( t.x, t.y );
|
||||
|
||||
// get the minimum hit distance
|
||||
t = min( tMaxHit.xx, tMaxHit.yz );
|
||||
float t1 = min( t.x, t.y );
|
||||
|
||||
// set distance to 0.0 if the ray starts inside the box
|
||||
dist = max( t0, 0.0 );
|
||||
|
||||
return t1 >= dist;
|
||||
|
||||
}
|
||||
|
||||
bool intersectsTriangle(
|
||||
vec3 rayOrigin, vec3 rayDirection, vec3 a, vec3 b, vec3 c,
|
||||
out vec3 barycoord, out vec3 norm, out float dist, out float side
|
||||
) {
|
||||
|
||||
// https://stackoverflow.com/questions/42740765/intersection-between-line-and-triangle-in-3d
|
||||
vec3 edge1 = b - a;
|
||||
vec3 edge2 = c - a;
|
||||
norm = cross( edge1, edge2 );
|
||||
|
||||
float det = - dot( rayDirection, norm );
|
||||
float invdet = 1.0 / det;
|
||||
|
||||
vec3 AO = rayOrigin - a;
|
||||
vec3 DAO = cross( AO, rayDirection );
|
||||
|
||||
vec4 uvt;
|
||||
uvt.x = dot( edge2, DAO ) * invdet;
|
||||
uvt.y = - dot( edge1, DAO ) * invdet;
|
||||
uvt.z = dot( AO, norm ) * invdet;
|
||||
uvt.w = 1.0 - uvt.x - uvt.y;
|
||||
|
||||
// set the hit information
|
||||
barycoord = uvt.wxy; // arranged in A, B, C order
|
||||
dist = uvt.z;
|
||||
side = sign( det );
|
||||
norm = side * normalize( norm );
|
||||
|
||||
// add an epsilon to avoid misses between triangles
|
||||
uvt += vec4( TRI_INTERSECT_EPSILON );
|
||||
|
||||
return all( greaterThanEqual( uvt, vec4( 0.0 ) ) );
|
||||
|
||||
}
|
||||
|
||||
bool intersectTriangles(
|
||||
// geometry info and triangle range
|
||||
sampler2D positionAttr, usampler2D indexAttr, uint offset, uint count,
|
||||
|
||||
// ray
|
||||
vec3 rayOrigin, vec3 rayDirection,
|
||||
|
||||
// outputs
|
||||
inout float minDistance, inout uvec4 faceIndices, inout vec3 faceNormal, inout vec3 barycoord,
|
||||
inout float side, inout float dist
|
||||
) {
|
||||
|
||||
bool found = false;
|
||||
vec3 localBarycoord, localNormal;
|
||||
float localDist, localSide;
|
||||
for ( uint i = offset, l = offset + count; i < l; i ++ ) {
|
||||
|
||||
uvec3 indices = uTexelFetch1D( indexAttr, i ).xyz;
|
||||
vec3 a = texelFetch1D( positionAttr, indices.x ).rgb;
|
||||
vec3 b = texelFetch1D( positionAttr, indices.y ).rgb;
|
||||
vec3 c = texelFetch1D( positionAttr, indices.z ).rgb;
|
||||
|
||||
if (
|
||||
intersectsTriangle( rayOrigin, rayDirection, a, b, c, localBarycoord, localNormal, localDist, localSide )
|
||||
&& localDist < minDistance
|
||||
) {
|
||||
|
||||
found = true;
|
||||
minDistance = localDist;
|
||||
|
||||
faceIndices = uvec4( indices.xyz, i );
|
||||
faceNormal = localNormal;
|
||||
|
||||
side = localSide;
|
||||
barycoord = localBarycoord;
|
||||
dist = localDist;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return found;
|
||||
|
||||
}
|
||||
|
||||
bool intersectsBVHNodeBounds( vec3 rayOrigin, vec3 rayDirection, sampler2D bvhBounds, uint currNodeIndex, out float dist ) {
|
||||
|
||||
uint cni2 = currNodeIndex * 2u;
|
||||
vec3 boundsMin = texelFetch1D( bvhBounds, cni2 ).xyz;
|
||||
vec3 boundsMax = texelFetch1D( bvhBounds, cni2 + 1u ).xyz;
|
||||
return intersectsBounds( rayOrigin, rayDirection, boundsMin, boundsMax, dist );
|
||||
|
||||
}
|
||||
|
||||
// use a macro to hide the fact that we need to expand the struct into separate fields
|
||||
#define\
|
||||
bvhIntersectFirstHit(\
|
||||
bvh,\
|
||||
rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist\
|
||||
)\
|
||||
_bvhIntersectFirstHit(\
|
||||
bvh.position, bvh.index, bvh.bvhBounds, bvh.bvhContents,\
|
||||
rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist\
|
||||
)
|
||||
|
||||
bool _bvhIntersectFirstHit(
|
||||
// bvh info
|
||||
sampler2D bvh_position, usampler2D bvh_index, sampler2D bvh_bvhBounds, usampler2D bvh_bvhContents,
|
||||
|
||||
// ray
|
||||
vec3 rayOrigin, vec3 rayDirection,
|
||||
|
||||
// output variables split into separate variables due to output precision
|
||||
inout uvec4 faceIndices, inout vec3 faceNormal, inout vec3 barycoord,
|
||||
inout float side, inout float dist
|
||||
) {
|
||||
|
||||
// stack needs to be twice as long as the deepest tree we expect because
|
||||
// we push both the left and right child onto the stack every traversal
|
||||
int ptr = 0;
|
||||
uint stack[ BVH_STACK_DEPTH ];
|
||||
stack[ 0 ] = 0u;
|
||||
|
||||
float triangleDistance = INFINITY;
|
||||
bool found = false;
|
||||
while ( ptr > - 1 && ptr < BVH_STACK_DEPTH ) {
|
||||
|
||||
uint currNodeIndex = stack[ ptr ];
|
||||
ptr --;
|
||||
|
||||
// check if we intersect the current bounds
|
||||
float boundsHitDistance;
|
||||
if (
|
||||
! intersectsBVHNodeBounds( rayOrigin, rayDirection, bvh_bvhBounds, currNodeIndex, boundsHitDistance )
|
||||
|| boundsHitDistance > triangleDistance
|
||||
) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
uvec2 boundsInfo = uTexelFetch1D( bvh_bvhContents, currNodeIndex ).xy;
|
||||
bool isLeaf = bool( boundsInfo.x & 0xffff0000u );
|
||||
|
||||
if ( isLeaf ) {
|
||||
|
||||
uint count = boundsInfo.x & 0x0000ffffu;
|
||||
uint offset = boundsInfo.y;
|
||||
|
||||
found = intersectTriangles(
|
||||
bvh_position, bvh_index, offset, count,
|
||||
rayOrigin, rayDirection, triangleDistance,
|
||||
faceIndices, faceNormal, barycoord, side, dist
|
||||
) || found;
|
||||
|
||||
} else {
|
||||
|
||||
uint leftIndex = currNodeIndex + 1u;
|
||||
uint splitAxis = boundsInfo.x & 0x0000ffffu;
|
||||
uint rightIndex = boundsInfo.y;
|
||||
|
||||
bool leftToRight = rayDirection[ splitAxis ] >= 0.0;
|
||||
uint c1 = leftToRight ? leftIndex : rightIndex;
|
||||
uint c2 = leftToRight ? rightIndex : leftIndex;
|
||||
|
||||
// set c2 in the stack so we traverse it later. We need to keep track of a pointer in
|
||||
// the stack while we traverse. The second pointer added is the one that will be
|
||||
// traversed first
|
||||
ptr ++;
|
||||
stack[ ptr ] = c2;
|
||||
|
||||
ptr ++;
|
||||
stack[ ptr ] = c1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return found;
|
||||
|
||||
}
|
||||
`;
|
||||
14
node_modules/three-mesh-bvh/src/gpu/glsl/bvh_struct_definitions.glsl.js
generated
vendored
Normal file
14
node_modules/three-mesh-bvh/src/gpu/glsl/bvh_struct_definitions.glsl.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Note that a struct cannot be used for the hit record including faceIndices, faceNormal, barycoord,
|
||||
// side, and dist because on some mobile GPUS (such as Adreno) numbers are afforded less precision specifically
|
||||
// when in a struct leading to inaccurate hit results. See KhronosGroup/WebGL#3351 for more details.
|
||||
export const bvh_struct_definitions = /* glsl */`
|
||||
struct BVH {
|
||||
|
||||
usampler2D index;
|
||||
sampler2D position;
|
||||
|
||||
sampler2D bvhBounds;
|
||||
usampler2D bvhContents;
|
||||
|
||||
};
|
||||
`;
|
||||
83
node_modules/three-mesh-bvh/src/gpu/glsl/common_functions.glsl.js
generated
vendored
Normal file
83
node_modules/three-mesh-bvh/src/gpu/glsl/common_functions.glsl.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
export const common_functions = /* glsl */`
|
||||
|
||||
// A stack of uint32 indices can can store the indices for
|
||||
// a perfectly balanced tree with a depth up to 31. Lower stack
|
||||
// depth gets higher performance.
|
||||
//
|
||||
// However not all trees are balanced. Best value to set this to
|
||||
// is the trees max depth.
|
||||
#ifndef BVH_STACK_DEPTH
|
||||
#define BVH_STACK_DEPTH 60
|
||||
#endif
|
||||
|
||||
#ifndef INFINITY
|
||||
#define INFINITY 1e20
|
||||
#endif
|
||||
|
||||
// Utilities
|
||||
uvec4 uTexelFetch1D( usampler2D tex, uint index ) {
|
||||
|
||||
uint width = uint( textureSize( tex, 0 ).x );
|
||||
uvec2 uv;
|
||||
uv.x = index % width;
|
||||
uv.y = index / width;
|
||||
|
||||
return texelFetch( tex, ivec2( uv ), 0 );
|
||||
|
||||
}
|
||||
|
||||
ivec4 iTexelFetch1D( isampler2D tex, uint index ) {
|
||||
|
||||
uint width = uint( textureSize( tex, 0 ).x );
|
||||
uvec2 uv;
|
||||
uv.x = index % width;
|
||||
uv.y = index / width;
|
||||
|
||||
return texelFetch( tex, ivec2( uv ), 0 );
|
||||
|
||||
}
|
||||
|
||||
vec4 texelFetch1D( sampler2D tex, uint index ) {
|
||||
|
||||
uint width = uint( textureSize( tex, 0 ).x );
|
||||
uvec2 uv;
|
||||
uv.x = index % width;
|
||||
uv.y = index / width;
|
||||
|
||||
return texelFetch( tex, ivec2( uv ), 0 );
|
||||
|
||||
}
|
||||
|
||||
vec4 textureSampleBarycoord( sampler2D tex, vec3 barycoord, uvec3 faceIndices ) {
|
||||
|
||||
return
|
||||
barycoord.x * texelFetch1D( tex, faceIndices.x ) +
|
||||
barycoord.y * texelFetch1D( tex, faceIndices.y ) +
|
||||
barycoord.z * texelFetch1D( tex, faceIndices.z );
|
||||
|
||||
}
|
||||
|
||||
void ndcToCameraRay(
|
||||
vec2 coord, mat4 cameraWorld, mat4 invProjectionMatrix,
|
||||
out vec3 rayOrigin, out vec3 rayDirection
|
||||
) {
|
||||
|
||||
// get camera look direction and near plane for camera clipping
|
||||
vec4 lookDirection = cameraWorld * vec4( 0.0, 0.0, - 1.0, 0.0 );
|
||||
vec4 nearVector = invProjectionMatrix * vec4( 0.0, 0.0, - 1.0, 1.0 );
|
||||
float near = abs( nearVector.z / nearVector.w );
|
||||
|
||||
// get the camera direction and position from camera matrices
|
||||
vec4 origin = cameraWorld * vec4( 0.0, 0.0, 0.0, 1.0 );
|
||||
vec4 direction = invProjectionMatrix * vec4( coord, 0.5, 1.0 );
|
||||
direction /= direction.w;
|
||||
direction = cameraWorld * direction - origin;
|
||||
|
||||
// slide the origin along the ray until it sits at the near clip plane position
|
||||
origin.xyz += direction.xyz * near / dot( direction, lookDirection );
|
||||
|
||||
rayOrigin = origin.xyz;
|
||||
rayDirection = direction.xyz;
|
||||
|
||||
}
|
||||
`;
|
||||
360
node_modules/three-mesh-bvh/src/index.d.ts
generated
vendored
Normal file
360
node_modules/three-mesh-bvh/src/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,360 @@
|
||||
import { BufferGeometry, Vector3, Side, Material, Ray, Sphere, Matrix4, Color,
|
||||
Intersection, Box3, Triangle, Vector2, Raycaster, MeshBasicMaterial, Group,
|
||||
LineBasicMaterial, Mesh, DataTexture, BufferAttribute, Line3, Object3D } from 'three';
|
||||
|
||||
// Contants
|
||||
export enum SplitStrategy {}
|
||||
export const CENTER: SplitStrategy;
|
||||
export const AVERAGE: SplitStrategy;
|
||||
export const SAH: SplitStrategy;
|
||||
|
||||
export enum ShapecastIntersection {}
|
||||
export const NOT_INTERSECTED: ShapecastIntersection;
|
||||
export const INTERSECTED: ShapecastIntersection;
|
||||
export const CONTAINED: ShapecastIntersection;
|
||||
|
||||
// MeshBVH
|
||||
export interface HitPointInfo {
|
||||
point: Vector3;
|
||||
distance: number;
|
||||
faceIndex: number;
|
||||
}
|
||||
|
||||
export interface MeshBVHOptions {
|
||||
strategy?: SplitStrategy;
|
||||
maxDepth?: number;
|
||||
maxLeafTris?: number;
|
||||
setBoundingBox?: boolean;
|
||||
useSharedArrayBuffer?: boolean;
|
||||
verbose?: boolean;
|
||||
onProgress?: ( progress: number ) => void;
|
||||
range?: { start: number; count: number };
|
||||
}
|
||||
|
||||
export interface MeshBVHSerializeOptions {
|
||||
cloneBuffers?: boolean;
|
||||
}
|
||||
|
||||
export interface MeshBVHDeserializeOptions {
|
||||
setIndex?: boolean;
|
||||
}
|
||||
|
||||
|
||||
export class MeshBVH {
|
||||
|
||||
readonly geometry: BufferGeometry;
|
||||
|
||||
static serialize( bvh: MeshBVH, options?: MeshBVHSerializeOptions ): SerializedBVH;
|
||||
|
||||
static deserialize(
|
||||
data: SerializedBVH,
|
||||
geometry: BufferGeometry,
|
||||
options?: MeshBVHDeserializeOptions
|
||||
): MeshBVH;
|
||||
|
||||
constructor( geometry: BufferGeometry, options?: MeshBVHOptions );
|
||||
|
||||
raycast( ray: Ray, materialOrSide?: Side | Array<Material> | Material, near?: number, far?: number ): Array<Intersection>
|
||||
|
||||
raycastFirst( ray: Ray, materialOrSide?: Side | Array<Material> | Material, near?: number, far?: number ): Intersection;
|
||||
|
||||
intersectsSphere( sphere: Sphere ): boolean;
|
||||
|
||||
intersectsBox( box: Box3, boxToMesh: Matrix4 ): boolean;
|
||||
|
||||
intersectsGeometry( geometry: BufferGeometry, geometryToBvh: Matrix4 ): boolean;
|
||||
|
||||
closestPointToPoint(
|
||||
point: Vector3,
|
||||
target?: HitPointInfo,
|
||||
minThreshold?: number,
|
||||
maxThreshold?: number
|
||||
): HitPointInfo | null;
|
||||
|
||||
closestPointToGeometry(
|
||||
geometry: BufferGeometry,
|
||||
geometryToBvh: Matrix4,
|
||||
target1?: HitPointInfo,
|
||||
target2?: HitPointInfo,
|
||||
minThreshold?: number,
|
||||
maxThreshold?: number
|
||||
): HitPointInfo | null;
|
||||
|
||||
// union types to enable at least one of two functions:
|
||||
// https://stackoverflow.com/a/60617060/9838891
|
||||
shapecast(
|
||||
callbacks: {
|
||||
|
||||
intersectsBounds: (
|
||||
box: Box3,
|
||||
isLeaf: boolean,
|
||||
score: number | undefined,
|
||||
depth: number,
|
||||
nodeIndex: number
|
||||
) => ShapecastIntersection|boolean,
|
||||
|
||||
boundsTraverseOrder?: (
|
||||
box: Box3
|
||||
) => number,
|
||||
|
||||
} & ( {
|
||||
|
||||
intersectsRange: (
|
||||
triangleOffset: number,
|
||||
triangleCount: number,
|
||||
contained: boolean,
|
||||
depth: number,
|
||||
nodeIndex: number,
|
||||
box: Box3
|
||||
) => boolean,
|
||||
|
||||
} | {
|
||||
|
||||
intersectsTriangle: (
|
||||
triangle: ExtendedTriangle,
|
||||
triangleIndex: number,
|
||||
contained: boolean,
|
||||
depth: number
|
||||
) => boolean|void
|
||||
|
||||
} )
|
||||
): boolean;
|
||||
|
||||
// union types to enable at least one of two functions:
|
||||
// https://stackoverflow.com/a/60617060/9838891
|
||||
bvhcast(
|
||||
otherBVH: MeshBVH,
|
||||
matrixToLocal: Matrix4,
|
||||
callbacks: ( {
|
||||
|
||||
intersectsRanges: (
|
||||
offset1: number,
|
||||
count1: number,
|
||||
offset2: number,
|
||||
count2: number,
|
||||
depth1: number,
|
||||
index1: number,
|
||||
depth2: number,
|
||||
index2: number
|
||||
) => boolean
|
||||
|
||||
} | {
|
||||
|
||||
intersectsTriangles: (
|
||||
triangle1: ExtendedTriangle,
|
||||
triangle2: ExtendedTriangle,
|
||||
i1: number,
|
||||
i2: number,
|
||||
depth1: number,
|
||||
index1: number,
|
||||
depth2: number,
|
||||
index2: number,
|
||||
) => boolean,
|
||||
|
||||
} )
|
||||
): boolean;
|
||||
|
||||
traverse(
|
||||
callback: (
|
||||
depth: number,
|
||||
isLeaf: boolean,
|
||||
boundingData: ArrayBuffer,
|
||||
offsetOrSplit: number,
|
||||
count: number
|
||||
) => void,
|
||||
rootIndex?: number
|
||||
): void;
|
||||
|
||||
refit( nodeIndices?: Array<number> | Set<number> ): void;
|
||||
|
||||
getBoundingBox( target: Box3 ): Box3;
|
||||
|
||||
}
|
||||
|
||||
// SerializedBVH
|
||||
export class SerializedBVH {
|
||||
|
||||
roots: Array<ArrayBuffer>;
|
||||
index: ArrayBufferView;
|
||||
|
||||
}
|
||||
|
||||
// MeshBVHHelper
|
||||
export class MeshBVHHelper extends Group {
|
||||
|
||||
opacity: number;
|
||||
depth: number;
|
||||
displayParents: boolean;
|
||||
displayEdges: boolean;
|
||||
edgeMaterial: LineBasicMaterial;
|
||||
meshMaterial: MeshBasicMaterial;
|
||||
|
||||
constructor( mesh: Mesh, depth?: number );
|
||||
|
||||
update(): void;
|
||||
|
||||
get color(): Color;
|
||||
|
||||
}
|
||||
|
||||
// THREE.js Extensions
|
||||
|
||||
export function computeBoundsTree( options?: MeshBVHOptions ): MeshBVH;
|
||||
|
||||
export function disposeBoundsTree(): void;
|
||||
|
||||
export function computeBatchedBoundsTree( index?: Number, options?: MeshBVHOptions ): MeshBVH | MeshBVH[];
|
||||
|
||||
export function disposeBatchedBoundsTree( index?: Number ): void;
|
||||
|
||||
export function acceleratedRaycast(
|
||||
raycaster: Raycaster,
|
||||
intersects: Array<Intersection>
|
||||
): void;
|
||||
|
||||
declare module 'three' {
|
||||
export interface BufferGeometry {
|
||||
boundsTree?: MeshBVH;
|
||||
computeBoundsTree: typeof computeBoundsTree;
|
||||
disposeBoundsTree: typeof disposeBoundsTree;
|
||||
}
|
||||
|
||||
export interface BatchedMesh {
|
||||
boundsTrees?: Array<MeshBVH | null>;
|
||||
computeBoundsTree: typeof computeBatchedBoundsTree;
|
||||
disposeBoundsTree: typeof disposeBatchedBoundsTree;
|
||||
}
|
||||
|
||||
export interface Raycaster {
|
||||
firstHitOnly?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateMeshBVHWorker
|
||||
// export class GenerateMeshBVHWorker {
|
||||
|
||||
// running: boolean;
|
||||
|
||||
// generate( geometry: BufferGeometry, options?: MeshBVHOptions ): Promise<MeshBVH>;
|
||||
|
||||
// terminate(): boolean;
|
||||
|
||||
// }
|
||||
|
||||
// Debug functions
|
||||
export function estimateMemoryInBytes( bvh: MeshBVH ): number;
|
||||
|
||||
export interface ExtremeInfo {
|
||||
nodeCount: number;
|
||||
leafNodeCount: number;
|
||||
surfaceAreaScore: number;
|
||||
depth: {min: number, max: number};
|
||||
tris: {min: number, max: number};
|
||||
splits: [number, number, number];
|
||||
}
|
||||
|
||||
export function getBVHExtremes( bvh :MeshBVH ): Array<ExtremeInfo>;
|
||||
|
||||
export function validateBounds( bvh: MeshBVH ): boolean;
|
||||
|
||||
export interface TreeNode {
|
||||
bounds: Box3;
|
||||
count: number;
|
||||
offset: number;
|
||||
left?: TreeNode;
|
||||
right?: TreeNode;
|
||||
}
|
||||
|
||||
export function getJSONStructure( bvh: MeshBVH ): TreeNode;
|
||||
|
||||
// Triangle Utilities
|
||||
export interface HitTriangleInfo {
|
||||
face: {
|
||||
a: number,
|
||||
b: number,
|
||||
c: number,
|
||||
materialIndex: number,
|
||||
normal: Vector3
|
||||
},
|
||||
uv: Vector2
|
||||
}
|
||||
|
||||
export function getTriangleHitPointInfo(
|
||||
point: Vector3,
|
||||
geometry : BufferGeometry,
|
||||
triangleIndex: number,
|
||||
target?: HitTriangleInfo
|
||||
): HitTriangleInfo
|
||||
|
||||
// Shader Utilities
|
||||
declare class VertexAttributeTexture extends DataTexture {
|
||||
|
||||
overrideItemSize: number | null;
|
||||
updateFrom( attribute: BufferAttribute ): void;
|
||||
|
||||
}
|
||||
|
||||
export class FloatVertexAttributeTexture extends VertexAttributeTexture {}
|
||||
export class UIntVertexAttributeTexture extends VertexAttributeTexture {}
|
||||
export class IntVertexAttributeTexture extends VertexAttributeTexture {}
|
||||
|
||||
export class MeshBVHUniformStruct {
|
||||
|
||||
updateFrom( bvh: MeshBVH ): void;
|
||||
dispose(): void;
|
||||
|
||||
}
|
||||
|
||||
export const BVHShaderGLSL: {
|
||||
bvh_distance_functions: string;
|
||||
bvh_ray_functions: string;
|
||||
bvh_struct_definitions: string;
|
||||
common_functions: string;
|
||||
};
|
||||
|
||||
// backwards compatibility
|
||||
export const shaderStructs: string;
|
||||
export const shaderDistanceFunction: string;
|
||||
export const shaderIntersectFunction: string;
|
||||
|
||||
// Math classes
|
||||
export class ExtendedTriangle extends Triangle {
|
||||
|
||||
needsUpdate : boolean;
|
||||
|
||||
intersectsTriangle( other : Triangle, target? : Line3 ) : boolean;
|
||||
intersectsSphere( sphere : Sphere ) : boolean;
|
||||
closestPointToSegment( segment : Line3, target1? : Vector3, target2? : Vector3 ) : number;
|
||||
distanceToPoint( point : Vector3 ) : number;
|
||||
distanceToTriangle( tri : Triangle ) : number;
|
||||
|
||||
}
|
||||
|
||||
export class OrientedBox {
|
||||
|
||||
min: Vector3;
|
||||
max: Vector3;
|
||||
matrix : Matrix4;
|
||||
needsUpdate : boolean;
|
||||
|
||||
constructor( min : Vector3, max : Vector3 );
|
||||
set( min : Vector3, max : Vector3, matrix : Matrix4 ) : OrientedBox;
|
||||
intersectsBox( box : Box3 ) : boolean;
|
||||
intersectsTriangle( tri : Triangle ) : boolean;
|
||||
closestPointToPoint( point : Vector3, target? : Vector3 ) : number;
|
||||
distanceToPoint( point : Vector3 ) : number;
|
||||
distanceToBox( box : Box3, threshold? : number, target1? : Vector3, target2? : Vector3 ) : number;
|
||||
|
||||
}
|
||||
|
||||
export class StaticGeometryGenerator {
|
||||
|
||||
useGroups : boolean;
|
||||
attributes : Array<string>;
|
||||
applyWorldTransforms : boolean;
|
||||
|
||||
constructor( objects : Array<Object3D> | Object3D );
|
||||
getMaterials() : Array<Material>;
|
||||
generate( target? : BufferGeometry ) : BufferGeometry;
|
||||
|
||||
}
|
||||
21
node_modules/three-mesh-bvh/src/index.js
generated
vendored
Normal file
21
node_modules/three-mesh-bvh/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
export { MeshBVH } from './core/MeshBVH.js';
|
||||
export { MeshBVHHelper } from './objects/MeshBVHHelper.js';
|
||||
export { CENTER, AVERAGE, SAH, NOT_INTERSECTED, INTERSECTED, CONTAINED } from './core/Constants.js';
|
||||
export { getBVHExtremes, estimateMemoryInBytes, getJSONStructure, validateBounds } from './debug/Debug.js';
|
||||
export * from './utils/ExtensionUtilities.js';
|
||||
export { getTriangleHitPointInfo } from './utils/TriangleUtilities.js';
|
||||
export * from './math/ExtendedTriangle.js';
|
||||
export * from './math/OrientedBox.js';
|
||||
export * from './gpu/MeshBVHUniformStruct.js';
|
||||
export * from './gpu/VertexAttributeTexture.js';
|
||||
export * from './utils/StaticGeometryGenerator.js';
|
||||
export * as BVHShaderGLSL from './gpu/BVHShaderGLSL.js';
|
||||
|
||||
// backwards compatibility
|
||||
import * as BVHShaderGLSL from './gpu/BVHShaderGLSL.js';
|
||||
export const shaderStructs = BVHShaderGLSL.bvh_struct_definitions;
|
||||
export const shaderDistanceFunction = BVHShaderGLSL.bvh_distance_functions;
|
||||
export const shaderIntersectFunction = `
|
||||
${ BVHShaderGLSL.common_functions }
|
||||
${ BVHShaderGLSL.bvh_ray_functions }
|
||||
`;
|
||||
514
node_modules/three-mesh-bvh/src/math/ExtendedTriangle.js
generated
vendored
Normal file
514
node_modules/three-mesh-bvh/src/math/ExtendedTriangle.js
generated
vendored
Normal file
@@ -0,0 +1,514 @@
|
||||
import { Triangle, Vector3, Line3, Sphere, Plane } from 'three';
|
||||
import { SeparatingAxisBounds } from './SeparatingAxisBounds.js';
|
||||
import { closestPointsSegmentToSegment, sphereIntersectTriangle } from './MathUtilities.js';
|
||||
|
||||
const ZERO_EPSILON = 1e-15;
|
||||
function isNearZero( value ) {
|
||||
|
||||
return Math.abs( value ) < ZERO_EPSILON;
|
||||
|
||||
}
|
||||
|
||||
export 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;
|
||||
|
||||
// check the triangle edges
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check end points
|
||||
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 ) {
|
||||
|
||||
// find the edge that intersects the other triangle plane
|
||||
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 ) {
|
||||
|
||||
// if the edge lies on the plane then take the line
|
||||
targetEdge.copy( edge );
|
||||
count = 2;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// check if the start point is near the plane because "intersectLine" is not robust to that case
|
||||
const doesIntersect = plane.intersectLine( edge, tempPoint );
|
||||
if ( ! doesIntersect && startIntersects ) {
|
||||
|
||||
tempPoint.copy( start );
|
||||
|
||||
}
|
||||
|
||||
// ignore the end point
|
||||
if ( ( doesIntersect || startIntersects ) && ! isNearZero( tempPoint.distanceTo( end ) ) ) {
|
||||
|
||||
if ( count <= 1 ) {
|
||||
|
||||
// assign to the start or end point and save which index was snapped to
|
||||
// the start point if necessary
|
||||
const point = count === 1 ? targetEdge.start : targetEdge.end;
|
||||
point.copy( tempPoint );
|
||||
if ( startIntersects ) {
|
||||
|
||||
startPointIntersection = count;
|
||||
|
||||
}
|
||||
|
||||
} else if ( count >= 2 ) {
|
||||
|
||||
// if we're here that means that there must have been one point that had
|
||||
// snapped to the start point so replace it here
|
||||
const point = startPointIntersection === 1 ? targetEdge.start : targetEdge.end;
|
||||
point.copy( tempPoint );
|
||||
count = 2;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
count ++;
|
||||
if ( count === 2 && startPointIntersection === - 1 ) {
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
// TODO: If the triangles are coplanar and intersecting the target is nonsensical. It should at least
|
||||
// be a line contained by both triangles if not a different special case somehow represented in the return result.
|
||||
return function intersectsTriangle( other, target = 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.0 - 1e-10 ) {
|
||||
|
||||
// perform separating axis intersection test only for coplanar triangles
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
// check crossed axes
|
||||
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 ( target ) {
|
||||
|
||||
// TODO find two points that intersect on the edges and make that the result
|
||||
if ( ! suppressLog ) {
|
||||
|
||||
console.warn( 'ExtendedTriangle.intersectsTriangle: Triangles are coplanar which does not support an output edge. Setting edge to 0, 0, 0.' );
|
||||
|
||||
}
|
||||
|
||||
target.start.set( 0, 0, 0 );
|
||||
target.end.set( 0, 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// find the edge that intersects the other triangle plane
|
||||
const count1 = triIntersectPlane( this, plane2, edge1 );
|
||||
if ( count1 === 1 && other.containsPoint( edge1.end ) ) {
|
||||
|
||||
if ( target ) {
|
||||
|
||||
target.start.copy( edge1.end );
|
||||
target.end.copy( edge1.end );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if ( count1 !== 2 ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// find the other triangles edge that intersects this plane
|
||||
const count2 = triIntersectPlane( other, plane1, edge2 );
|
||||
if ( count2 === 1 && this.containsPoint( edge2.end ) ) {
|
||||
|
||||
if ( target ) {
|
||||
|
||||
target.start.copy( edge2.end );
|
||||
target.end.copy( edge2.end );
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if ( count2 !== 2 ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// find swap the second edge so both lines are running the same direction
|
||||
edge1.delta( dir1 );
|
||||
edge2.delta( dir2 );
|
||||
|
||||
if ( dir1.dot( dir2 ) < 0 ) {
|
||||
|
||||
let tmp = edge2.start;
|
||||
edge2.start = edge2.end;
|
||||
edge2.end = tmp;
|
||||
|
||||
}
|
||||
|
||||
// check if the edges are overlapping
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
// assign the target output
|
||||
if ( target ) {
|
||||
|
||||
tempDir.subVectors( edge1.start, edge2.start );
|
||||
if ( tempDir.dot( dir1 ) > 0 ) {
|
||||
|
||||
target.start.copy( edge1.start );
|
||||
|
||||
} else {
|
||||
|
||||
target.start.copy( edge2.start );
|
||||
|
||||
}
|
||||
|
||||
tempDir.subVectors( edge1.end, edge2.end );
|
||||
if ( tempDir.dot( dir1 ) < 0 ) {
|
||||
|
||||
target.end.copy( edge1.end );
|
||||
|
||||
} else {
|
||||
|
||||
target.end.copy( edge2.end );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
|
||||
ExtendedTriangle.prototype.distanceToPoint = ( function () {
|
||||
|
||||
const target = new Vector3();
|
||||
return function distanceToPoint( point ) {
|
||||
|
||||
this.closestPointToPoint( point, target );
|
||||
return point.distanceTo( target );
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// check all point distances
|
||||
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 );
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
203
node_modules/three-mesh-bvh/src/math/MathUtilities.js
generated
vendored
Normal file
203
node_modules/three-mesh-bvh/src/math/MathUtilities.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
import { Vector3, Vector2, Plane, Line3 } from 'three';
|
||||
|
||||
export const closestPointLineToLine = ( function () {
|
||||
|
||||
// https://github.com/juj/MathGeoLib/blob/master/src/Geometry/Line.cpp#L56
|
||||
const dir1 = new Vector3();
|
||||
const dir2 = new Vector3();
|
||||
const v02 = new Vector3();
|
||||
return function closestPointLineToLine( 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 );
|
||||
|
||||
// float d0232 = v02.Dot(v32);
|
||||
const d0232 = v02.dot( v32 );
|
||||
|
||||
// float d3210 = v32.Dot(v10);
|
||||
const d3210 = v32.dot( v10 );
|
||||
|
||||
// float d3232 = v32.Dot(v32);
|
||||
const d3232 = v32.dot( v32 );
|
||||
|
||||
// float d0210 = v02.Dot(v10);
|
||||
const d0210 = v02.dot( v10 );
|
||||
|
||||
// float d1010 = v10.Dot(v10);
|
||||
const d1010 = v10.dot( v10 );
|
||||
|
||||
// float denom = d1010*d3232 - d3210*d3210;
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
export const closestPointsSegmentToSegment = ( function () {
|
||||
|
||||
// https://github.com/juj/MathGeoLib/blob/master/src/Geometry/LineSegment.cpp#L187
|
||||
const paramResult = new Vector2();
|
||||
const temp1 = new Vector3();
|
||||
const temp2 = new Vector3();
|
||||
return function closestPointsSegmentToSegment( 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 ) {
|
||||
|
||||
// Only d2 is out of bounds.
|
||||
if ( d2 < 0 ) {
|
||||
|
||||
l2.at( 0, target2 );
|
||||
|
||||
} else {
|
||||
|
||||
l2.at( 1, target2 );
|
||||
|
||||
}
|
||||
|
||||
l1.closestPointToPoint( target2, true, target1 );
|
||||
return;
|
||||
|
||||
} else if ( d2 >= 0 && d2 <= 1 ) {
|
||||
|
||||
// Only d is out of bounds.
|
||||
if ( d < 0 ) {
|
||||
|
||||
l1.at( 0, target1 );
|
||||
|
||||
} else {
|
||||
|
||||
l1.at( 1, target1 );
|
||||
|
||||
}
|
||||
|
||||
l2.closestPointToPoint( target1, true, target2 );
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
// Both u and u2 are out of bounds.
|
||||
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 = temp1;
|
||||
const closestPoint2 = temp2;
|
||||
l1.closestPointToPoint( p2, true, temp1 );
|
||||
l2.closestPointToPoint( p, true, temp2 );
|
||||
|
||||
if ( closestPoint.distanceToSquared( p2 ) <= closestPoint2.distanceToSquared( p ) ) {
|
||||
|
||||
target1.copy( closestPoint );
|
||||
target2.copy( p2 );
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
target1.copy( p );
|
||||
target2.copy( closestPoint2 );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
|
||||
export const sphereIntersectTriangle = ( function () {
|
||||
|
||||
// https://stackoverflow.com/questions/34043955/detect-collision-between-sphere-and-triangle-in-three-js
|
||||
const closestPointTemp = new Vector3();
|
||||
const projectedPointTemp = new Vector3();
|
||||
const planeTemp = new Plane();
|
||||
const lineTemp = new Line3();
|
||||
return function sphereIntersectTriangle( sphere, triangle ) {
|
||||
|
||||
const { radius, center } = sphere;
|
||||
const { a, b, c } = triangle;
|
||||
|
||||
// phase 1
|
||||
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;
|
||||
|
||||
// phase 2
|
||||
const plane = triangle.getPlane( planeTemp );
|
||||
const dp = Math.abs( plane.distanceToPoint( center ) );
|
||||
if ( dp <= radius ) {
|
||||
|
||||
const pp = plane.projectPoint( center, projectedPointTemp );
|
||||
const cp = triangle.containsPoint( pp );
|
||||
if ( cp ) return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
421
node_modules/three-mesh-bvh/src/math/OrientedBox.js
generated
vendored
Normal file
421
node_modules/three-mesh-bvh/src/math/OrientedBox.js
generated
vendored
Normal file
@@ -0,0 +1,421 @@
|
||||
import { Vector3, Matrix4, Line3 } from 'three';
|
||||
import { SeparatingAxisBounds } from './SeparatingAxisBounds.js';
|
||||
import { ExtendedTriangle } from './ExtendedTriangle.js';
|
||||
import { closestPointsSegmentToSegment } from './MathUtilities.js';
|
||||
|
||||
export 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 = ( 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 v = points[ i ];
|
||||
v.x = x ? max.x : min.x;
|
||||
v.y = y ? max.y : min.y;
|
||||
v.z = z ? max.z : min.z;
|
||||
|
||||
v.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 ) {
|
||||
|
||||
// TODO: should this be doing SAT against the AABB?
|
||||
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( triangle ) {
|
||||
|
||||
if ( this.needsUpdate ) {
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
if ( ! triangle.isExtendedTriangle ) {
|
||||
|
||||
saTri.copy( triangle );
|
||||
saTri.update();
|
||||
triangle = saTri;
|
||||
|
||||
} else if ( triangle.needsUpdate ) {
|
||||
|
||||
triangle.update();
|
||||
|
||||
}
|
||||
|
||||
const satBounds = this.satBounds;
|
||||
const satAxes = this.satAxes;
|
||||
|
||||
pointsArr[ 0 ] = triangle.a;
|
||||
pointsArr[ 1 ] = triangle.b;
|
||||
pointsArr[ 2 ] = triangle.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 = triangle.satBounds;
|
||||
const triSatAxes = triangle.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;
|
||||
|
||||
}
|
||||
|
||||
// check crossed axes
|
||||
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 = ( function () {
|
||||
|
||||
return function closestPointToPoint( 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 target = new Vector3();
|
||||
return function distanceToPoint( point ) {
|
||||
|
||||
this.closestPointToPoint( point, target );
|
||||
return point.distanceTo( target );
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
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();
|
||||
|
||||
// early out if we find a value below threshold
|
||||
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;
|
||||
|
||||
|
||||
// iterate over every edge and compare distances
|
||||
let closestDistanceSq = Infinity;
|
||||
|
||||
// check over all these points
|
||||
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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// generate and check all line segment distances
|
||||
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;
|
||||
|
||||
// get obb line segments
|
||||
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 );
|
||||
|
||||
|
||||
// get aabb line segments
|
||||
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 ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check all the other boxes point
|
||||
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 );
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
127
node_modules/three-mesh-bvh/src/math/SeparatingAxisBounds.js
generated
vendored
Normal file
127
node_modules/three-mesh-bvh/src/math/SeparatingAxisBounds.js
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
import { Vector3 } from 'three';
|
||||
|
||||
export 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;
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
|
||||
export const areIntersecting = ( function () {
|
||||
|
||||
const cacheSatBounds = new SeparatingAxisBounds();
|
||||
return function areIntersecting( shape1, shape2 ) {
|
||||
|
||||
const points1 = shape1.points;
|
||||
const satAxes1 = shape1.satAxes;
|
||||
const satBounds1 = shape1.satBounds;
|
||||
|
||||
const points2 = shape2.points;
|
||||
const satAxes2 = shape2.satAxes;
|
||||
const satBounds2 = shape2.satBounds;
|
||||
|
||||
// check axes of the first shape
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
const sb = satBounds1[ i ];
|
||||
const sa = satAxes1[ i ];
|
||||
cacheSatBounds.setFromPoints( sa, points2 );
|
||||
if ( sb.isSeparated( cacheSatBounds ) ) return false;
|
||||
|
||||
}
|
||||
|
||||
// check axes of the second shape
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
const sb = satBounds2[ i ];
|
||||
const sa = satAxes2[ i ];
|
||||
cacheSatBounds.setFromPoints( sa, points1 );
|
||||
if ( sb.isSeparated( cacheSatBounds ) ) return false;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} )();
|
||||
422
node_modules/three-mesh-bvh/src/objects/MeshBVHHelper.js
generated
vendored
Normal file
422
node_modules/three-mesh-bvh/src/objects/MeshBVHHelper.js
generated
vendored
Normal file
@@ -0,0 +1,422 @@
|
||||
import { LineBasicMaterial, BufferAttribute, Box3, Group, MeshBasicMaterial, Object3D, BufferGeometry, Mesh, Matrix4 } from 'three';
|
||||
import { arrayToBox } from '../utils/ArrayBoxUtilities.js';
|
||||
import { MeshBVH } from '../core/MeshBVH.js';
|
||||
|
||||
const boundingBox = /* @__PURE__ */ new Box3();
|
||||
const matrix = /* @__PURE__ */ new Matrix4();
|
||||
|
||||
class MeshBVHRootHelper extends Object3D {
|
||||
|
||||
get isMesh() {
|
||||
|
||||
return ! this.displayEdges;
|
||||
|
||||
}
|
||||
|
||||
get isLineSegments() {
|
||||
|
||||
return this.displayEdges;
|
||||
|
||||
}
|
||||
|
||||
get isLine() {
|
||||
|
||||
return this.displayEdges;
|
||||
|
||||
}
|
||||
|
||||
getVertexPosition( ...args ) {
|
||||
|
||||
// implement this function so it works with Box3.setFromObject
|
||||
return Mesh.prototype.getVertexPosition.call( this, ...args );
|
||||
|
||||
}
|
||||
|
||||
constructor( bvh, material, depth = 10, group = 0 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.material = material;
|
||||
this.geometry = new BufferGeometry();
|
||||
this.name = 'MeshBVHRootHelper';
|
||||
this.depth = depth;
|
||||
this.displayParents = false;
|
||||
this.bvh = bvh;
|
||||
this.displayEdges = true;
|
||||
this._group = group;
|
||||
|
||||
}
|
||||
|
||||
raycast() {}
|
||||
|
||||
update() {
|
||||
|
||||
const geometry = this.geometry;
|
||||
const boundsTree = this.bvh;
|
||||
const group = this._group;
|
||||
geometry.dispose();
|
||||
this.visible = false;
|
||||
if ( boundsTree ) {
|
||||
|
||||
// count the number of bounds required
|
||||
const targetDepth = this.depth - 1;
|
||||
const displayParents = this.displayParents;
|
||||
let boundsCount = 0;
|
||||
boundsTree.traverse( ( depth, isLeaf ) => {
|
||||
|
||||
if ( depth >= targetDepth || isLeaf ) {
|
||||
|
||||
boundsCount ++;
|
||||
return true;
|
||||
|
||||
} else if ( displayParents ) {
|
||||
|
||||
boundsCount ++;
|
||||
|
||||
}
|
||||
|
||||
}, group );
|
||||
|
||||
// fill in the position buffer with the bounds corners
|
||||
let posIndex = 0;
|
||||
const positionArray = new Float32Array( 8 * 3 * boundsCount );
|
||||
boundsTree.traverse( ( depth, isLeaf, boundingData ) => {
|
||||
|
||||
const terminate = depth >= targetDepth || isLeaf;
|
||||
if ( terminate || displayParents ) {
|
||||
|
||||
arrayToBox( 0, boundingData, boundingBox );
|
||||
|
||||
const { min, max } = boundingBox;
|
||||
for ( let x = - 1; x <= 1; x += 2 ) {
|
||||
|
||||
const xVal = x < 0 ? min.x : max.x;
|
||||
for ( let y = - 1; y <= 1; y += 2 ) {
|
||||
|
||||
const yVal = y < 0 ? min.y : max.y;
|
||||
for ( let z = - 1; z <= 1; z += 2 ) {
|
||||
|
||||
const zVal = z < 0 ? min.z : max.z;
|
||||
positionArray[ posIndex + 0 ] = xVal;
|
||||
positionArray[ posIndex + 1 ] = yVal;
|
||||
positionArray[ posIndex + 2 ] = zVal;
|
||||
|
||||
posIndex += 3;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return terminate;
|
||||
|
||||
}
|
||||
|
||||
}, group );
|
||||
|
||||
let indexArray;
|
||||
let indices;
|
||||
if ( this.displayEdges ) {
|
||||
|
||||
// fill in the index buffer to point to the corner points
|
||||
indices = new Uint8Array( [
|
||||
// x axis
|
||||
0, 4,
|
||||
1, 5,
|
||||
2, 6,
|
||||
3, 7,
|
||||
|
||||
// y axis
|
||||
0, 2,
|
||||
1, 3,
|
||||
4, 6,
|
||||
5, 7,
|
||||
|
||||
// z axis
|
||||
0, 1,
|
||||
2, 3,
|
||||
4, 5,
|
||||
6, 7,
|
||||
] );
|
||||
|
||||
} else {
|
||||
|
||||
indices = new Uint8Array( [
|
||||
|
||||
// X-, X+
|
||||
0, 1, 2,
|
||||
2, 1, 3,
|
||||
|
||||
4, 6, 5,
|
||||
6, 7, 5,
|
||||
|
||||
// Y-, Y+
|
||||
1, 4, 5,
|
||||
0, 4, 1,
|
||||
|
||||
2, 3, 6,
|
||||
3, 7, 6,
|
||||
|
||||
// Z-, Z+
|
||||
0, 2, 4,
|
||||
2, 6, 4,
|
||||
|
||||
1, 5, 3,
|
||||
3, 5, 7,
|
||||
|
||||
] );
|
||||
|
||||
}
|
||||
|
||||
if ( positionArray.length > 65535 ) {
|
||||
|
||||
indexArray = new Uint32Array( indices.length * boundsCount );
|
||||
|
||||
} else {
|
||||
|
||||
indexArray = new Uint16Array( indices.length * boundsCount );
|
||||
|
||||
}
|
||||
|
||||
const indexLength = indices.length;
|
||||
for ( let i = 0; i < boundsCount; i ++ ) {
|
||||
|
||||
const posOffset = i * 8;
|
||||
const indexOffset = i * indexLength;
|
||||
for ( let j = 0; j < indexLength; j ++ ) {
|
||||
|
||||
indexArray[ indexOffset + j ] = posOffset + indices[ j ];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update the geometry
|
||||
geometry.setIndex(
|
||||
new BufferAttribute( indexArray, 1, false ),
|
||||
);
|
||||
geometry.setAttribute(
|
||||
'position',
|
||||
new BufferAttribute( positionArray, 3, false ),
|
||||
);
|
||||
this.visible = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MeshBVHHelper extends Group {
|
||||
|
||||
get color() {
|
||||
|
||||
return this.edgeMaterial.color;
|
||||
|
||||
}
|
||||
|
||||
get opacity() {
|
||||
|
||||
return this.edgeMaterial.opacity;
|
||||
|
||||
}
|
||||
|
||||
set opacity( v ) {
|
||||
|
||||
this.edgeMaterial.opacity = v;
|
||||
this.meshMaterial.opacity = v;
|
||||
|
||||
}
|
||||
|
||||
constructor( mesh = null, bvh = null, depth = 10 ) {
|
||||
|
||||
// handle bvh, depth signature
|
||||
if ( mesh instanceof MeshBVH ) {
|
||||
|
||||
depth = bvh || 10;
|
||||
bvh = mesh;
|
||||
mesh = null;
|
||||
|
||||
}
|
||||
|
||||
// handle mesh, depth signature
|
||||
if ( typeof bvh === 'number' ) {
|
||||
|
||||
depth = bvh;
|
||||
bvh = null;
|
||||
|
||||
}
|
||||
|
||||
super();
|
||||
|
||||
this.name = 'MeshBVHHelper';
|
||||
this.depth = depth;
|
||||
this.mesh = mesh;
|
||||
this.bvh = bvh;
|
||||
this.displayParents = false;
|
||||
this.displayEdges = true;
|
||||
this.objectIndex = 0;
|
||||
this._roots = [];
|
||||
|
||||
const edgeMaterial = new LineBasicMaterial( {
|
||||
color: 0x00FF88,
|
||||
transparent: true,
|
||||
opacity: 0.3,
|
||||
depthWrite: false,
|
||||
} );
|
||||
|
||||
const meshMaterial = new MeshBasicMaterial( {
|
||||
color: 0x00FF88,
|
||||
transparent: true,
|
||||
opacity: 0.3,
|
||||
depthWrite: false,
|
||||
} );
|
||||
|
||||
meshMaterial.color = edgeMaterial.color;
|
||||
|
||||
this.edgeMaterial = edgeMaterial;
|
||||
this.meshMaterial = meshMaterial;
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const mesh = this.mesh;
|
||||
let bvh = this.bvh || mesh.geometry.boundsTree || null;
|
||||
if ( mesh.isBatchedMesh && mesh.boundsTrees && ! bvh ) {
|
||||
|
||||
// get the bvh from a batchedMesh if not provided
|
||||
// TODO: we should have an official way to get the geometry index cleanly
|
||||
const drawInfo = mesh._drawInfo[ this.objectIndex ];
|
||||
if ( drawInfo ) {
|
||||
|
||||
bvh = mesh.boundsTrees[ drawInfo.geometryIndex ] || bvh;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const totalRoots = bvh ? bvh._roots.length : 0;
|
||||
while ( this._roots.length > totalRoots ) {
|
||||
|
||||
const root = this._roots.pop();
|
||||
root.geometry.dispose();
|
||||
this.remove( root );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < totalRoots; i ++ ) {
|
||||
|
||||
const { depth, edgeMaterial, meshMaterial, displayParents, displayEdges } = this;
|
||||
|
||||
if ( i >= this._roots.length ) {
|
||||
|
||||
const root = new MeshBVHRootHelper( bvh, edgeMaterial, depth, i );
|
||||
this.add( root );
|
||||
this._roots.push( root );
|
||||
|
||||
}
|
||||
|
||||
const root = this._roots[ i ];
|
||||
root.bvh = bvh;
|
||||
root.depth = depth;
|
||||
root.displayParents = displayParents;
|
||||
root.displayEdges = displayEdges;
|
||||
root.material = displayEdges ? edgeMaterial : meshMaterial;
|
||||
root.update();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateMatrixWorld( ...args ) {
|
||||
|
||||
const mesh = this.mesh;
|
||||
const parent = this.parent;
|
||||
|
||||
if ( mesh !== null ) {
|
||||
|
||||
mesh.updateWorldMatrix( true, false );
|
||||
|
||||
if ( parent ) {
|
||||
|
||||
this.matrix
|
||||
.copy( parent.matrixWorld )
|
||||
.invert()
|
||||
.multiply( mesh.matrixWorld );
|
||||
|
||||
} else {
|
||||
|
||||
this.matrix
|
||||
.copy( mesh.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
// handle batched and instanced mesh bvhs
|
||||
if ( mesh.isInstancedMesh || mesh.isBatchedMesh ) {
|
||||
|
||||
mesh.getMatrixAt( this.objectIndex, matrix );
|
||||
this.matrix.multiply( matrix );
|
||||
|
||||
}
|
||||
|
||||
this.matrix.decompose(
|
||||
this.position,
|
||||
this.quaternion,
|
||||
this.scale,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
super.updateMatrixWorld( ...args );
|
||||
|
||||
}
|
||||
|
||||
copy( source ) {
|
||||
|
||||
this.depth = source.depth;
|
||||
this.mesh = source.mesh;
|
||||
this.bvh = source.bvh;
|
||||
this.opacity = source.opacity;
|
||||
this.color.copy( source.color );
|
||||
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
||||
return new MeshBVHHelper( this.mesh, this.bvh, this.depth );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.edgeMaterial.dispose();
|
||||
this.meshMaterial.dispose();
|
||||
|
||||
const children = this.children;
|
||||
for ( let i = 0, l = children.length; i < l; i ++ ) {
|
||||
|
||||
children[ i ].geometry.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class MeshBVHVisualizer extends MeshBVHHelper {
|
||||
|
||||
constructor( ...args ) {
|
||||
|
||||
super( ...args );
|
||||
|
||||
console.warn( 'MeshBVHVisualizer: MeshBVHVisualizer has been deprecated. Use MeshBVHHelper, instead.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { MeshBVHHelper };
|
||||
108
node_modules/three-mesh-bvh/src/utils/ArrayBoxUtilities.js
generated
vendored
Normal file
108
node_modules/three-mesh-bvh/src/utils/ArrayBoxUtilities.js
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
export function arrayToBox( nodeIndex32, array, target ) {
|
||||
|
||||
target.min.x = array[ nodeIndex32 ];
|
||||
target.min.y = array[ nodeIndex32 + 1 ];
|
||||
target.min.z = array[ nodeIndex32 + 2 ];
|
||||
|
||||
target.max.x = array[ nodeIndex32 + 3 ];
|
||||
target.max.y = array[ nodeIndex32 + 4 ];
|
||||
target.max.z = array[ nodeIndex32 + 5 ];
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
export function makeEmptyBounds( target ) {
|
||||
|
||||
target[ 0 ] = target[ 1 ] = target[ 2 ] = Infinity;
|
||||
target[ 3 ] = target[ 4 ] = target[ 5 ] = - Infinity;
|
||||
|
||||
}
|
||||
|
||||
export 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;
|
||||
|
||||
}
|
||||
|
||||
// copies bounds a into bounds b
|
||||
export function copyBounds( source, target ) {
|
||||
|
||||
target.set( source );
|
||||
|
||||
}
|
||||
|
||||
// sets bounds target to the union of bounds a and b
|
||||
export function unionBounds( a, b, target ) {
|
||||
|
||||
let aVal, bVal;
|
||||
for ( let d = 0; d < 3; d ++ ) {
|
||||
|
||||
const d3 = d + 3;
|
||||
|
||||
// set the minimum values
|
||||
aVal = a[ d ];
|
||||
bVal = b[ d ];
|
||||
target[ d ] = aVal < bVal ? aVal : bVal;
|
||||
|
||||
// set the max values
|
||||
aVal = a[ d3 ];
|
||||
bVal = b[ d3 ];
|
||||
target[ d3 ] = aVal > bVal ? aVal : bVal;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// expands the given bounds by the provided triangle bounds
|
||||
export 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// compute bounds surface area
|
||||
export 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 );
|
||||
|
||||
}
|
||||
41
node_modules/three-mesh-bvh/src/utils/BufferUtils.js
generated
vendored
Normal file
41
node_modules/three-mesh-bvh/src/utils/BufferUtils.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
export function isSharedArrayBufferSupported() {
|
||||
|
||||
return typeof SharedArrayBuffer !== 'undefined';
|
||||
|
||||
}
|
||||
|
||||
export function convertToBufferType( array, BufferConstructor ) {
|
||||
|
||||
if ( array === null ) {
|
||||
|
||||
return array;
|
||||
|
||||
} else if ( array.buffer ) {
|
||||
|
||||
const buffer = array.buffer;
|
||||
if ( buffer.constructor === BufferConstructor ) {
|
||||
|
||||
return array;
|
||||
|
||||
}
|
||||
|
||||
const ArrayConstructor = array.constructor;
|
||||
const result = new ArrayConstructor( new BufferConstructor( buffer.byteLength ) );
|
||||
result.set( array );
|
||||
return result;
|
||||
|
||||
} else {
|
||||
|
||||
if ( array.constructor === BufferConstructor ) {
|
||||
|
||||
return array;
|
||||
|
||||
}
|
||||
|
||||
const result = new BufferConstructor( array.byteLength );
|
||||
new Uint8Array( result ).set( new Uint8Array( array ) );
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
14
node_modules/three-mesh-bvh/src/utils/ExtendedTrianglePool.js
generated
vendored
Normal file
14
node_modules/three-mesh-bvh/src/utils/ExtendedTrianglePool.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ExtendedTriangle } from '../math/ExtendedTriangle.js';
|
||||
import { PrimitivePool } from './PrimitivePool.js';
|
||||
|
||||
class ExtendedTrianglePoolBase extends PrimitivePool {
|
||||
|
||||
constructor() {
|
||||
|
||||
super( () => new ExtendedTriangle() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const ExtendedTrianglePool = /* @__PURE__ */ new ExtendedTrianglePoolBase();
|
||||
245
node_modules/three-mesh-bvh/src/utils/ExtensionUtilities.js
generated
vendored
Normal file
245
node_modules/three-mesh-bvh/src/utils/ExtensionUtilities.js
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
import { Ray, Matrix4, Mesh, Vector3, Sphere, BatchedMesh, REVISION } from 'three';
|
||||
import { convertRaycastIntersect } from './GeometryRayIntersectUtilities.js';
|
||||
import { MeshBVH } from '../core/MeshBVH.js';
|
||||
|
||||
const IS_REVISION_166 = parseInt( REVISION ) >= 166;
|
||||
const ray = /* @__PURE__ */ new Ray();
|
||||
const direction = /* @__PURE__ */ new Vector3();
|
||||
const tmpInverseMatrix = /* @__PURE__ */ new Matrix4();
|
||||
const origMeshRaycastFunc = Mesh.prototype.raycast;
|
||||
const origBatchedRaycastFunc = BatchedMesh.prototype.raycast;
|
||||
const _worldScale = /* @__PURE__ */ new Vector3();
|
||||
const _mesh = /* @__PURE__ */ new Mesh();
|
||||
const _batchIntersects = [];
|
||||
|
||||
export function acceleratedRaycast( raycaster, intersects ) {
|
||||
|
||||
if ( this.isBatchedMesh ) {
|
||||
|
||||
acceleratedBatchedMeshRaycast.call( this, raycaster, intersects );
|
||||
|
||||
} else {
|
||||
|
||||
acceleratedMeshRaycast.call( this, raycaster, intersects );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function acceleratedBatchedMeshRaycast( raycaster, intersects ) {
|
||||
|
||||
if ( this.boundsTrees ) {
|
||||
|
||||
// TODO: remove use of geometry info, instance info when r170 is minimum version
|
||||
const boundsTrees = this.boundsTrees;
|
||||
const drawInfo = this._drawInfo || this._instanceInfo;
|
||||
const drawRanges = this._drawRanges || this._geometryInfo;
|
||||
const matrixWorld = this.matrixWorld;
|
||||
|
||||
_mesh.material = this.material;
|
||||
_mesh.geometry = this.geometry;
|
||||
|
||||
const oldBoundsTree = _mesh.geometry.boundsTree;
|
||||
const oldDrawRange = _mesh.geometry.drawRange;
|
||||
|
||||
if ( _mesh.geometry.boundingSphere === null ) {
|
||||
|
||||
_mesh.geometry.boundingSphere = new Sphere();
|
||||
|
||||
}
|
||||
|
||||
// TODO: provide new method to get instances count instead of 'drawInfo.length'
|
||||
for ( let i = 0, l = drawInfo.length; i < l; i ++ ) {
|
||||
|
||||
if ( ! this.getVisibleAt( i ) ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// TODO: use getGeometryIndex
|
||||
const geometryId = drawInfo[ i ].geometryIndex;
|
||||
|
||||
_mesh.geometry.boundsTree = boundsTrees[ geometryId ];
|
||||
|
||||
this.getMatrixAt( i, _mesh.matrixWorld ).premultiply( matrixWorld );
|
||||
|
||||
if ( ! _mesh.geometry.boundsTree ) {
|
||||
|
||||
this.getBoundingBoxAt( geometryId, _mesh.geometry.boundingBox );
|
||||
this.getBoundingSphereAt( geometryId, _mesh.geometry.boundingSphere );
|
||||
|
||||
const drawRange = drawRanges[ geometryId ];
|
||||
_mesh.geometry.setDrawRange( drawRange.start, drawRange.count );
|
||||
|
||||
}
|
||||
|
||||
_mesh.raycast( raycaster, _batchIntersects );
|
||||
|
||||
for ( let j = 0, l = _batchIntersects.length; j < l; j ++ ) {
|
||||
|
||||
const intersect = _batchIntersects[ j ];
|
||||
intersect.object = this;
|
||||
intersect.batchId = i;
|
||||
intersects.push( intersect );
|
||||
|
||||
}
|
||||
|
||||
_batchIntersects.length = 0;
|
||||
|
||||
}
|
||||
|
||||
_mesh.geometry.boundsTree = oldBoundsTree;
|
||||
_mesh.geometry.drawRange = oldDrawRange;
|
||||
_mesh.material = null;
|
||||
_mesh.geometry = null;
|
||||
|
||||
} else {
|
||||
|
||||
origBatchedRaycastFunc.call( this, raycaster, intersects );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function acceleratedMeshRaycast( raycaster, intersects ) {
|
||||
|
||||
if ( this.geometry.boundsTree ) {
|
||||
|
||||
if ( this.material === undefined ) return;
|
||||
|
||||
tmpInverseMatrix.copy( this.matrixWorld ).invert();
|
||||
ray.copy( raycaster.ray ).applyMatrix4( tmpInverseMatrix );
|
||||
|
||||
_worldScale.setFromMatrixScale( this.matrixWorld );
|
||||
direction.copy( ray.direction ).multiply( _worldScale );
|
||||
|
||||
const scaleFactor = direction.length();
|
||||
const near = raycaster.near / scaleFactor;
|
||||
const far = raycaster.far / scaleFactor;
|
||||
|
||||
const bvh = this.geometry.boundsTree;
|
||||
if ( raycaster.firstHitOnly === true ) {
|
||||
|
||||
const hit = convertRaycastIntersect( bvh.raycastFirst( ray, this.material, near, far ), this, raycaster );
|
||||
if ( hit ) {
|
||||
|
||||
intersects.push( hit );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const hits = bvh.raycast( ray, this.material, near, far );
|
||||
for ( let i = 0, l = hits.length; i < l; i ++ ) {
|
||||
|
||||
const hit = convertRaycastIntersect( hits[ i ], this, raycaster );
|
||||
if ( hit ) {
|
||||
|
||||
intersects.push( hit );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
origMeshRaycastFunc.call( this, raycaster, intersects );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function computeBoundsTree( options = {} ) {
|
||||
|
||||
this.boundsTree = new MeshBVH( this, options );
|
||||
return this.boundsTree;
|
||||
|
||||
}
|
||||
|
||||
export function disposeBoundsTree() {
|
||||
|
||||
this.boundsTree = null;
|
||||
|
||||
}
|
||||
|
||||
export function computeBatchedBoundsTree( index = - 1, options = {} ) {
|
||||
|
||||
if ( ! IS_REVISION_166 ) {
|
||||
|
||||
throw new Error( 'BatchedMesh: Three r166+ is required to compute bounds trees.' );
|
||||
|
||||
}
|
||||
|
||||
if ( options.indirect ) {
|
||||
|
||||
console.warn( '"Indirect" is set to false because it is not supported for BatchedMesh.' );
|
||||
|
||||
}
|
||||
|
||||
options = {
|
||||
...options,
|
||||
indirect: false,
|
||||
range: null
|
||||
};
|
||||
|
||||
const drawRanges = this._drawRanges || this._geometryInfo;
|
||||
const geometryCount = this._geometryCount;
|
||||
if ( ! this.boundsTrees ) {
|
||||
|
||||
this.boundsTrees = new Array( geometryCount ).fill( null );
|
||||
|
||||
}
|
||||
|
||||
const boundsTrees = this.boundsTrees;
|
||||
while ( boundsTrees.length < geometryCount ) {
|
||||
|
||||
boundsTrees.push( null );
|
||||
|
||||
}
|
||||
|
||||
if ( index < 0 ) {
|
||||
|
||||
for ( let i = 0; i < geometryCount; i ++ ) {
|
||||
|
||||
options.range = drawRanges[ i ];
|
||||
boundsTrees[ i ] = new MeshBVH( this.geometry, options );
|
||||
|
||||
}
|
||||
|
||||
return boundsTrees;
|
||||
|
||||
} else {
|
||||
|
||||
if ( index < drawRanges.length ) {
|
||||
|
||||
options.range = drawRanges[ index ];
|
||||
boundsTrees[ index ] = new MeshBVH( this.geometry, options );
|
||||
|
||||
}
|
||||
|
||||
return boundsTrees[ index ] || null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function disposeBatchedBoundsTree( index = - 1 ) {
|
||||
|
||||
if ( index < 0 ) {
|
||||
|
||||
this.boundsTrees.fill( null );
|
||||
|
||||
} else {
|
||||
|
||||
if ( index < this.boundsTree.length ) {
|
||||
|
||||
this.boundsTrees[ index ] = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
17
node_modules/three-mesh-bvh/src/utils/GeometryRayIntersectUtilities.js
generated
vendored
Normal file
17
node_modules/three-mesh-bvh/src/utils/GeometryRayIntersectUtilities.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// converts the given BVH raycast intersection to align with the three.js raycast
|
||||
// structure (include object, world space distance and point).
|
||||
export function convertRaycastIntersect( hit, object, raycaster ) {
|
||||
|
||||
if ( hit === null ) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
hit.point.applyMatrix4( object.matrixWorld );
|
||||
hit.distance = hit.point.distanceTo( raycaster.ray.origin );
|
||||
hit.object = object;
|
||||
|
||||
return hit;
|
||||
|
||||
}
|
||||
31
node_modules/three-mesh-bvh/src/utils/PrimitivePool.js
generated
vendored
Normal file
31
node_modules/three-mesh-bvh/src/utils/PrimitivePool.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
export 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 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
774
node_modules/three-mesh-bvh/src/utils/StaticGeometryGenerator.js
generated
vendored
Normal file
774
node_modules/three-mesh-bvh/src/utils/StaticGeometryGenerator.js
generated
vendored
Normal file
@@ -0,0 +1,774 @@
|
||||
import { BufferAttribute, BufferGeometry, Vector3, Vector4, Matrix4, Matrix3 } from 'three';
|
||||
|
||||
const _positionVector = /*@__PURE__*/ new Vector3();
|
||||
const _normalVector = /*@__PURE__*/ new Vector3();
|
||||
const _tangentVector = /*@__PURE__*/ new Vector3();
|
||||
const _tangentVector4 = /*@__PURE__*/ new Vector4();
|
||||
|
||||
const _morphVector = /*@__PURE__*/ new Vector3();
|
||||
const _temp = /*@__PURE__*/ new Vector3();
|
||||
|
||||
const _skinIndex = /*@__PURE__*/ new Vector4();
|
||||
const _skinWeight = /*@__PURE__*/ new Vector4();
|
||||
const _matrix = /*@__PURE__*/ new Matrix4();
|
||||
const _boneMatrix = /*@__PURE__*/ new Matrix4();
|
||||
|
||||
// Confirms that the two provided attributes are compatible
|
||||
function validateAttributes( attr1, attr2 ) {
|
||||
|
||||
if ( ! attr1 && ! attr2 ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const sameCount = attr1.count === attr2.count;
|
||||
const sameNormalized = attr1.normalized === attr2.normalized;
|
||||
const sameType = attr1.array.constructor === attr2.array.constructor;
|
||||
const sameItemSize = attr1.itemSize === attr2.itemSize;
|
||||
|
||||
if ( ! sameCount || ! sameNormalized || ! sameType || ! sameItemSize ) {
|
||||
|
||||
throw new Error();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Clones the given attribute with a new compatible buffer attribute but no data
|
||||
function createAttributeClone( attr, countOverride = null ) {
|
||||
|
||||
const cons = attr.array.constructor;
|
||||
const normalized = attr.normalized;
|
||||
const itemSize = attr.itemSize;
|
||||
const count = countOverride === null ? attr.count : countOverride;
|
||||
|
||||
return new BufferAttribute( new cons( itemSize * count ), itemSize, normalized );
|
||||
|
||||
}
|
||||
|
||||
// target offset is the number of elements in the target buffer stride to skip before copying the
|
||||
// attributes contents in to.
|
||||
function copyAttributeContents( attr, target, targetOffset = 0 ) {
|
||||
|
||||
if ( attr.isInterleavedBufferAttribute ) {
|
||||
|
||||
const itemSize = attr.itemSize;
|
||||
for ( let i = 0, l = attr.count; i < l; i ++ ) {
|
||||
|
||||
const io = i + targetOffset;
|
||||
target.setX( io, attr.getX( i ) );
|
||||
if ( itemSize >= 2 ) target.setY( io, attr.getY( i ) );
|
||||
if ( itemSize >= 3 ) target.setZ( io, attr.getZ( i ) );
|
||||
if ( itemSize >= 4 ) target.setW( io, attr.getW( i ) );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const array = target.array;
|
||||
const cons = array.constructor;
|
||||
const byteOffset = array.BYTES_PER_ELEMENT * attr.itemSize * targetOffset;
|
||||
const temp = new cons( array.buffer, byteOffset, attr.array.length );
|
||||
temp.set( attr.array );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Adds the "matrix" multiplied by "scale" to "target"
|
||||
function addScaledMatrix( target, matrix, scale ) {
|
||||
|
||||
const targetArray = target.elements;
|
||||
const matrixArray = matrix.elements;
|
||||
for ( let i = 0, l = matrixArray.length; i < l; i ++ ) {
|
||||
|
||||
targetArray[ i ] += matrixArray[ i ] * scale;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// A version of "SkinnedMesh.boneTransform" for normals
|
||||
function boneNormalTransform( mesh, index, target ) {
|
||||
|
||||
const skeleton = mesh.skeleton;
|
||||
const geometry = mesh.geometry;
|
||||
const bones = skeleton.bones;
|
||||
const boneInverses = skeleton.boneInverses;
|
||||
|
||||
_skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );
|
||||
_skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );
|
||||
|
||||
_matrix.elements.fill( 0 );
|
||||
|
||||
for ( let i = 0; i < 4; i ++ ) {
|
||||
|
||||
const weight = _skinWeight.getComponent( i );
|
||||
|
||||
if ( weight !== 0 ) {
|
||||
|
||||
const boneIndex = _skinIndex.getComponent( i );
|
||||
_boneMatrix.multiplyMatrices( bones[ boneIndex ].matrixWorld, boneInverses[ boneIndex ] );
|
||||
|
||||
addScaledMatrix( _matrix, _boneMatrix, weight );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_matrix.multiply( mesh.bindMatrix ).premultiply( mesh.bindMatrixInverse );
|
||||
target.transformDirection( _matrix );
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
// Applies the morph target data to the target vector
|
||||
function applyMorphTarget( morphData, morphInfluences, morphTargetsRelative, i, target ) {
|
||||
|
||||
_morphVector.set( 0, 0, 0 );
|
||||
for ( let j = 0, jl = morphData.length; j < jl; j ++ ) {
|
||||
|
||||
const influence = morphInfluences[ j ];
|
||||
const morphAttribute = morphData[ j ];
|
||||
|
||||
if ( influence === 0 ) continue;
|
||||
|
||||
_temp.fromBufferAttribute( morphAttribute, i );
|
||||
|
||||
if ( morphTargetsRelative ) {
|
||||
|
||||
_morphVector.addScaledVector( _temp, influence );
|
||||
|
||||
} else {
|
||||
|
||||
_morphVector.addScaledVector( _temp.sub( target ), influence );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
target.add( _morphVector );
|
||||
|
||||
}
|
||||
|
||||
// Modified version of BufferGeometryUtils.mergeBufferGeometries that ignores morph targets and updates a attributes in place
|
||||
function mergeBufferGeometries( geometries, options = { useGroups: false, updateIndex: false, skipAttributes: [] }, targetGeometry = new BufferGeometry() ) {
|
||||
|
||||
const isIndexed = geometries[ 0 ].index !== null;
|
||||
const { useGroups = false, updateIndex = false, skipAttributes = [] } = options;
|
||||
|
||||
const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) );
|
||||
const attributes = {};
|
||||
|
||||
let offset = 0;
|
||||
|
||||
targetGeometry.clearGroups();
|
||||
for ( let i = 0; i < geometries.length; ++ i ) {
|
||||
|
||||
const geometry = geometries[ i ];
|
||||
let attributesCount = 0;
|
||||
|
||||
// ensure that all geometries are indexed, or none
|
||||
if ( isIndexed !== ( geometry.index !== null ) ) {
|
||||
|
||||
throw new Error( 'StaticGeometryGenerator: All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
|
||||
|
||||
}
|
||||
|
||||
// gather attributes, exit early if they're different
|
||||
for ( const name in geometry.attributes ) {
|
||||
|
||||
if ( ! attributesUsed.has( name ) ) {
|
||||
|
||||
throw new Error( 'StaticGeometryGenerator: All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
|
||||
|
||||
}
|
||||
|
||||
if ( attributes[ name ] === undefined ) {
|
||||
|
||||
attributes[ name ] = [];
|
||||
|
||||
}
|
||||
|
||||
attributes[ name ].push( geometry.attributes[ name ] );
|
||||
attributesCount ++;
|
||||
|
||||
}
|
||||
|
||||
// ensure geometries have the same number of attributes
|
||||
if ( attributesCount !== attributesUsed.size ) {
|
||||
|
||||
throw new Error( 'StaticGeometryGenerator: Make sure all geometries have the same number of attributes.' );
|
||||
|
||||
}
|
||||
|
||||
if ( useGroups ) {
|
||||
|
||||
let count;
|
||||
if ( isIndexed ) {
|
||||
|
||||
count = geometry.index.count;
|
||||
|
||||
} else if ( geometry.attributes.position !== undefined ) {
|
||||
|
||||
count = geometry.attributes.position.count;
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'StaticGeometryGenerator: The geometry must have either an index or a position attribute' );
|
||||
|
||||
}
|
||||
|
||||
targetGeometry.addGroup( offset, count, i );
|
||||
offset += count;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// merge indices
|
||||
if ( isIndexed ) {
|
||||
|
||||
let forceUpdateIndex = false;
|
||||
if ( ! targetGeometry.index ) {
|
||||
|
||||
let indexCount = 0;
|
||||
for ( let i = 0; i < geometries.length; ++ i ) {
|
||||
|
||||
indexCount += geometries[ i ].index.count;
|
||||
|
||||
}
|
||||
|
||||
targetGeometry.setIndex( new BufferAttribute( new Uint32Array( indexCount ), 1, false ) );
|
||||
forceUpdateIndex = true;
|
||||
|
||||
}
|
||||
|
||||
if ( updateIndex || forceUpdateIndex ) {
|
||||
|
||||
const targetIndex = targetGeometry.index;
|
||||
let targetOffset = 0;
|
||||
let indexOffset = 0;
|
||||
for ( let i = 0; i < geometries.length; ++ i ) {
|
||||
|
||||
const geometry = geometries[ i ];
|
||||
const index = geometry.index;
|
||||
if ( skipAttributes[ i ] !== true ) {
|
||||
|
||||
for ( let j = 0; j < index.count; ++ j ) {
|
||||
|
||||
targetIndex.setX( targetOffset, index.getX( j ) + indexOffset );
|
||||
targetOffset ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
indexOffset += geometry.attributes.position.count;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// merge attributes
|
||||
for ( const name in attributes ) {
|
||||
|
||||
const attrList = attributes[ name ];
|
||||
if ( ! ( name in targetGeometry.attributes ) ) {
|
||||
|
||||
let count = 0;
|
||||
for ( const key in attrList ) {
|
||||
|
||||
count += attrList[ key ].count;
|
||||
|
||||
}
|
||||
|
||||
targetGeometry.setAttribute( name, createAttributeClone( attributes[ name ][ 0 ], count ) );
|
||||
|
||||
}
|
||||
|
||||
const targetAttribute = targetGeometry.attributes[ name ];
|
||||
let offset = 0;
|
||||
for ( let i = 0, l = attrList.length; i < l; i ++ ) {
|
||||
|
||||
const attr = attrList[ i ];
|
||||
if ( skipAttributes[ i ] !== true ) {
|
||||
|
||||
copyAttributeContents( attr, targetAttribute, offset );
|
||||
|
||||
}
|
||||
|
||||
offset += attr.count;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return targetGeometry;
|
||||
|
||||
}
|
||||
|
||||
function checkTypedArrayEquality( a, b ) {
|
||||
|
||||
if ( a === null || b === null ) {
|
||||
|
||||
return a === b;
|
||||
|
||||
}
|
||||
|
||||
if ( a.length !== b.length ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0, l = a.length; i < l; i ++ ) {
|
||||
|
||||
if ( a[ i ] !== b[ i ] ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function invertGeometry( geometry ) {
|
||||
|
||||
const { index, attributes } = geometry;
|
||||
if ( index ) {
|
||||
|
||||
for ( let i = 0, l = index.count; i < l; i += 3 ) {
|
||||
|
||||
const v0 = index.getX( i );
|
||||
const v2 = index.getX( i + 2 );
|
||||
index.setX( i, v2 );
|
||||
index.setX( i + 2, v0 );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( const key in attributes ) {
|
||||
|
||||
const attr = attributes[ key ];
|
||||
const itemSize = attr.itemSize;
|
||||
for ( let i = 0, l = attr.count; i < l; i += 3 ) {
|
||||
|
||||
for ( let j = 0; j < itemSize; j ++ ) {
|
||||
|
||||
const v0 = attr.getComponent( i, j );
|
||||
const v2 = attr.getComponent( i + 2, j );
|
||||
attr.setComponent( i, j, v2 );
|
||||
attr.setComponent( i + 2, j, v0 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return geometry;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Checks whether the geometry changed between this and last evaluation
|
||||
class GeometryDiff {
|
||||
|
||||
constructor( mesh ) {
|
||||
|
||||
this.matrixWorld = new Matrix4();
|
||||
this.geometryHash = null;
|
||||
this.boneMatrices = null;
|
||||
this.primitiveCount = - 1;
|
||||
this.mesh = mesh;
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const mesh = this.mesh;
|
||||
const geometry = mesh.geometry;
|
||||
const skeleton = mesh.skeleton;
|
||||
const primitiveCount = ( geometry.index ? geometry.index.count : geometry.attributes.position.count ) / 3;
|
||||
this.matrixWorld.copy( mesh.matrixWorld );
|
||||
this.geometryHash = geometry.attributes.position.version;
|
||||
this.primitiveCount = primitiveCount;
|
||||
|
||||
if ( skeleton ) {
|
||||
|
||||
// ensure the bone matrix array is updated to the appropriate length
|
||||
if ( ! skeleton.boneTexture ) {
|
||||
|
||||
skeleton.computeBoneTexture();
|
||||
|
||||
}
|
||||
|
||||
skeleton.update();
|
||||
|
||||
// copy data if possible otherwise clone it
|
||||
const boneMatrices = skeleton.boneMatrices;
|
||||
if ( ! this.boneMatrices || this.boneMatrices.length !== boneMatrices.length ) {
|
||||
|
||||
this.boneMatrices = boneMatrices.slice();
|
||||
|
||||
} else {
|
||||
|
||||
this.boneMatrices.set( boneMatrices );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
this.boneMatrices = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
didChange() {
|
||||
|
||||
const mesh = this.mesh;
|
||||
const geometry = mesh.geometry;
|
||||
const primitiveCount = ( geometry.index ? geometry.index.count : geometry.attributes.position.count ) / 3;
|
||||
const identical =
|
||||
this.matrixWorld.equals( mesh.matrixWorld ) &&
|
||||
this.geometryHash === geometry.attributes.position.version &&
|
||||
checkTypedArrayEquality( mesh.skeleton && mesh.skeleton.boneMatrices || null, this.boneMatrices ) &&
|
||||
this.primitiveCount === primitiveCount;
|
||||
|
||||
return ! identical;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class StaticGeometryGenerator {
|
||||
|
||||
constructor( meshes ) {
|
||||
|
||||
if ( ! Array.isArray( meshes ) ) {
|
||||
|
||||
meshes = [ meshes ];
|
||||
|
||||
}
|
||||
|
||||
const finalMeshes = [];
|
||||
meshes.forEach( object => {
|
||||
|
||||
object.traverseVisible( c => {
|
||||
|
||||
if ( c.isMesh ) {
|
||||
|
||||
finalMeshes.push( c );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
this.meshes = finalMeshes;
|
||||
this.useGroups = true;
|
||||
this.applyWorldTransforms = true;
|
||||
this.attributes = [ 'position', 'normal', 'color', 'tangent', 'uv', 'uv2' ];
|
||||
this._intermediateGeometry = new Array( finalMeshes.length ).fill().map( () => new BufferGeometry() );
|
||||
this._diffMap = new WeakMap();
|
||||
|
||||
}
|
||||
|
||||
getMaterials() {
|
||||
|
||||
const materials = [];
|
||||
this.meshes.forEach( mesh => {
|
||||
|
||||
if ( Array.isArray( mesh.material ) ) {
|
||||
|
||||
materials.push( ...mesh.material );
|
||||
|
||||
} else {
|
||||
|
||||
materials.push( mesh.material );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
return materials;
|
||||
|
||||
}
|
||||
|
||||
generate( targetGeometry = new BufferGeometry() ) {
|
||||
|
||||
// track which attributes have been updated and which to skip to avoid unnecessary attribute copies
|
||||
let skipAttributes = [];
|
||||
const { meshes, useGroups, _intermediateGeometry, _diffMap } = this;
|
||||
for ( let i = 0, l = meshes.length; i < l; i ++ ) {
|
||||
|
||||
const mesh = meshes[ i ];
|
||||
const geom = _intermediateGeometry[ i ];
|
||||
const diff = _diffMap.get( mesh );
|
||||
if ( ! diff || diff.didChange( mesh ) ) {
|
||||
|
||||
this._convertToStaticGeometry( mesh, geom );
|
||||
skipAttributes.push( false );
|
||||
|
||||
if ( ! diff ) {
|
||||
|
||||
_diffMap.set( mesh, new GeometryDiff( mesh ) );
|
||||
|
||||
} else {
|
||||
|
||||
diff.update();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
skipAttributes.push( true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( _intermediateGeometry.length === 0 ) {
|
||||
|
||||
// if there are no geometries then just create a fake empty geometry to provide
|
||||
targetGeometry.setIndex( null );
|
||||
|
||||
// remove all geometry
|
||||
const attrs = targetGeometry.attributes;
|
||||
for ( const key in attrs ) {
|
||||
|
||||
targetGeometry.deleteAttribute( key );
|
||||
|
||||
}
|
||||
|
||||
// create dummy attributes
|
||||
for ( const key in this.attributes ) {
|
||||
|
||||
targetGeometry.setAttribute( this.attributes[ key ], new BufferAttribute( new Float32Array( 0 ), 4, false ) );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
mergeBufferGeometries( _intermediateGeometry, { useGroups, skipAttributes }, targetGeometry );
|
||||
|
||||
}
|
||||
|
||||
for ( const key in targetGeometry.attributes ) {
|
||||
|
||||
targetGeometry.attributes[ key ].needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
return targetGeometry;
|
||||
|
||||
}
|
||||
|
||||
_convertToStaticGeometry( mesh, targetGeometry = new BufferGeometry() ) {
|
||||
|
||||
const geometry = mesh.geometry;
|
||||
const applyWorldTransforms = this.applyWorldTransforms;
|
||||
const includeNormal = this.attributes.includes( 'normal' );
|
||||
const includeTangent = this.attributes.includes( 'tangent' );
|
||||
const attributes = geometry.attributes;
|
||||
const targetAttributes = targetGeometry.attributes;
|
||||
|
||||
// initialize the attributes if they don't exist
|
||||
if ( ! targetGeometry.index && geometry.index ) {
|
||||
|
||||
targetGeometry.index = geometry.index.clone();
|
||||
|
||||
}
|
||||
|
||||
if ( ! targetAttributes.position ) {
|
||||
|
||||
targetGeometry.setAttribute( 'position', createAttributeClone( attributes.position ) );
|
||||
|
||||
}
|
||||
|
||||
if ( includeNormal && ! targetAttributes.normal && attributes.normal ) {
|
||||
|
||||
targetGeometry.setAttribute( 'normal', createAttributeClone( attributes.normal ) );
|
||||
|
||||
}
|
||||
|
||||
if ( includeTangent && ! targetAttributes.tangent && attributes.tangent ) {
|
||||
|
||||
targetGeometry.setAttribute( 'tangent', createAttributeClone( attributes.tangent ) );
|
||||
|
||||
}
|
||||
|
||||
// ensure the attributes are consistent
|
||||
validateAttributes( geometry.index, targetGeometry.index );
|
||||
validateAttributes( attributes.position, targetAttributes.position );
|
||||
|
||||
if ( includeNormal ) {
|
||||
|
||||
validateAttributes( attributes.normal, targetAttributes.normal );
|
||||
|
||||
}
|
||||
|
||||
if ( includeTangent ) {
|
||||
|
||||
validateAttributes( attributes.tangent, targetAttributes.tangent );
|
||||
|
||||
}
|
||||
|
||||
// generate transformed vertex attribute data
|
||||
const position = attributes.position;
|
||||
const normal = includeNormal ? attributes.normal : null;
|
||||
const tangent = includeTangent ? attributes.tangent : null;
|
||||
const morphPosition = geometry.morphAttributes.position;
|
||||
const morphNormal = geometry.morphAttributes.normal;
|
||||
const morphTangent = geometry.morphAttributes.tangent;
|
||||
const morphTargetsRelative = geometry.morphTargetsRelative;
|
||||
const morphInfluences = mesh.morphTargetInfluences;
|
||||
const normalMatrix = new Matrix3();
|
||||
normalMatrix.getNormalMatrix( mesh.matrixWorld );
|
||||
|
||||
// copy the index
|
||||
if ( geometry.index ) {
|
||||
|
||||
targetGeometry.index.array.set( geometry.index.array );
|
||||
|
||||
}
|
||||
|
||||
// copy and apply other attributes
|
||||
for ( let i = 0, l = attributes.position.count; i < l; i ++ ) {
|
||||
|
||||
_positionVector.fromBufferAttribute( position, i );
|
||||
if ( normal ) {
|
||||
|
||||
_normalVector.fromBufferAttribute( normal, i );
|
||||
|
||||
}
|
||||
|
||||
if ( tangent ) {
|
||||
|
||||
_tangentVector4.fromBufferAttribute( tangent, i );
|
||||
_tangentVector.fromBufferAttribute( tangent, i );
|
||||
|
||||
}
|
||||
|
||||
// apply morph target transform
|
||||
if ( morphInfluences ) {
|
||||
|
||||
if ( morphPosition ) {
|
||||
|
||||
applyMorphTarget( morphPosition, morphInfluences, morphTargetsRelative, i, _positionVector );
|
||||
|
||||
}
|
||||
|
||||
if ( morphNormal ) {
|
||||
|
||||
applyMorphTarget( morphNormal, morphInfluences, morphTargetsRelative, i, _normalVector );
|
||||
|
||||
}
|
||||
|
||||
if ( morphTangent ) {
|
||||
|
||||
applyMorphTarget( morphTangent, morphInfluences, morphTargetsRelative, i, _tangentVector );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// apply bone transform
|
||||
if ( mesh.isSkinnedMesh ) {
|
||||
|
||||
mesh.applyBoneTransform( i, _positionVector );
|
||||
if ( normal ) {
|
||||
|
||||
boneNormalTransform( mesh, i, _normalVector );
|
||||
|
||||
}
|
||||
|
||||
if ( tangent ) {
|
||||
|
||||
boneNormalTransform( mesh, i, _tangentVector );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update the vectors of the attributes
|
||||
if ( applyWorldTransforms ) {
|
||||
|
||||
_positionVector.applyMatrix4( mesh.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
targetAttributes.position.setXYZ( i, _positionVector.x, _positionVector.y, _positionVector.z );
|
||||
|
||||
if ( normal ) {
|
||||
|
||||
if ( applyWorldTransforms ) {
|
||||
|
||||
_normalVector.applyNormalMatrix( normalMatrix );
|
||||
|
||||
}
|
||||
|
||||
targetAttributes.normal.setXYZ( i, _normalVector.x, _normalVector.y, _normalVector.z );
|
||||
|
||||
}
|
||||
|
||||
if ( tangent ) {
|
||||
|
||||
if ( applyWorldTransforms ) {
|
||||
|
||||
_tangentVector.transformDirection( mesh.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
targetAttributes.tangent.setXYZW( i, _tangentVector.x, _tangentVector.y, _tangentVector.z, _tangentVector4.w );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// copy other attributes over
|
||||
for ( const i in this.attributes ) {
|
||||
|
||||
const key = this.attributes[ i ];
|
||||
if ( key === 'position' || key === 'tangent' || key === 'normal' || ! ( key in attributes ) ) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if ( ! targetAttributes[ key ] ) {
|
||||
|
||||
targetGeometry.setAttribute( key, createAttributeClone( attributes[ key ] ) );
|
||||
|
||||
}
|
||||
|
||||
validateAttributes( attributes[ key ], targetAttributes[ key ] );
|
||||
copyAttributeContents( attributes[ key ], targetAttributes[ key ] );
|
||||
|
||||
}
|
||||
|
||||
if ( mesh.matrixWorld.determinant() < 0 ) {
|
||||
|
||||
invertGeometry( targetGeometry );
|
||||
|
||||
}
|
||||
|
||||
return targetGeometry;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
153
node_modules/three-mesh-bvh/src/utils/ThreeRayIntersectUtilities.js
generated
vendored
Normal file
153
node_modules/three-mesh-bvh/src/utils/ThreeRayIntersectUtilities.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
import { Vector3, Vector2, Triangle, DoubleSide, BackSide, REVISION } from 'three';
|
||||
|
||||
const IS_GT_REVISION_169 = parseInt( REVISION ) >= 169;
|
||||
|
||||
// Ripped and modified From THREE.js Mesh raycast
|
||||
// https://github.com/mrdoob/three.js/blob/0aa87c999fe61e216c1133fba7a95772b503eddf/src/objects/Mesh.js#L115
|
||||
const _vA = /* @__PURE__ */ new Vector3();
|
||||
const _vB = /* @__PURE__ */ new Vector3();
|
||||
const _vC = /* @__PURE__ */ new Vector3();
|
||||
|
||||
const _uvA = /* @__PURE__ */ new Vector2();
|
||||
const _uvB = /* @__PURE__ */ new Vector2();
|
||||
const _uvC = /* @__PURE__ */ new Vector2();
|
||||
|
||||
const _normalA = /* @__PURE__ */ new Vector3();
|
||||
const _normalB = /* @__PURE__ */ new Vector3();
|
||||
const _normalC = /* @__PURE__ */ new Vector3();
|
||||
|
||||
const _intersectionPoint = /* @__PURE__ */ new Vector3();
|
||||
function checkIntersection( ray, pA, pB, pC, point, side, near, far ) {
|
||||
|
||||
let intersect;
|
||||
if ( side === BackSide ) {
|
||||
|
||||
intersect = ray.intersectTriangle( pC, pB, pA, true, point );
|
||||
|
||||
} else {
|
||||
|
||||
intersect = ray.intersectTriangle( pA, pB, pC, side !== DoubleSide, point );
|
||||
|
||||
}
|
||||
|
||||
if ( intersect === null ) return null;
|
||||
|
||||
const distance = ray.origin.distanceTo( point );
|
||||
|
||||
if ( distance < near || distance > far ) return null;
|
||||
|
||||
return {
|
||||
|
||||
distance: distance,
|
||||
point: point.clone(),
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function checkBufferGeometryIntersection( ray, position, normal, uv, uv1, a, b, c, side, near, far ) {
|
||||
|
||||
_vA.fromBufferAttribute( position, a );
|
||||
_vB.fromBufferAttribute( position, b );
|
||||
_vC.fromBufferAttribute( position, c );
|
||||
|
||||
const intersection = checkIntersection( ray, _vA, _vB, _vC, _intersectionPoint, side, near, far );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
const barycoord = new Vector3();
|
||||
Triangle.getBarycoord( _intersectionPoint, _vA, _vB, _vC, barycoord );
|
||||
|
||||
if ( uv ) {
|
||||
|
||||
_uvA.fromBufferAttribute( uv, a );
|
||||
_uvB.fromBufferAttribute( uv, b );
|
||||
_uvC.fromBufferAttribute( uv, c );
|
||||
|
||||
intersection.uv = Triangle.getInterpolation( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() );
|
||||
|
||||
}
|
||||
|
||||
if ( uv1 ) {
|
||||
|
||||
_uvA.fromBufferAttribute( uv1, a );
|
||||
_uvB.fromBufferAttribute( uv1, b );
|
||||
_uvC.fromBufferAttribute( uv1, c );
|
||||
|
||||
intersection.uv1 = Triangle.getInterpolation( _intersectionPoint, _vA, _vB, _vC, _uvA, _uvB, _uvC, new Vector2() );
|
||||
|
||||
}
|
||||
|
||||
if ( normal ) {
|
||||
|
||||
_normalA.fromBufferAttribute( normal, a );
|
||||
_normalB.fromBufferAttribute( normal, b );
|
||||
_normalC.fromBufferAttribute( normal, c );
|
||||
|
||||
intersection.normal = Triangle.getInterpolation( _intersectionPoint, _vA, _vB, _vC, _normalA, _normalB, _normalC, new Vector3() );
|
||||
if ( intersection.normal.dot( ray.direction ) > 0 ) {
|
||||
|
||||
intersection.normal.multiplyScalar( - 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const face = {
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
normal: new Vector3(),
|
||||
materialIndex: 0
|
||||
};
|
||||
|
||||
Triangle.getNormal( _vA, _vB, _vC, face.normal );
|
||||
|
||||
intersection.face = face;
|
||||
intersection.faceIndex = a;
|
||||
|
||||
if ( IS_GT_REVISION_169 ) {
|
||||
|
||||
intersection.barycoord = barycoord;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return intersection;
|
||||
|
||||
}
|
||||
|
||||
// https://github.com/mrdoob/three.js/blob/0aa87c999fe61e216c1133fba7a95772b503eddf/src/objects/Mesh.js#L258
|
||||
function intersectTri( geo, side, ray, tri, intersections, near, far ) {
|
||||
|
||||
const triOffset = tri * 3;
|
||||
let a = triOffset + 0;
|
||||
let b = triOffset + 1;
|
||||
let c = triOffset + 2;
|
||||
|
||||
const index = geo.index;
|
||||
if ( geo.index ) {
|
||||
|
||||
a = index.getX( a );
|
||||
b = index.getX( b );
|
||||
c = index.getX( c );
|
||||
|
||||
}
|
||||
|
||||
const { position, normal, uv, uv1 } = geo.attributes;
|
||||
const intersection = checkBufferGeometryIntersection( ray, position, normal, uv, uv1, a, b, c, side, near, far );
|
||||
|
||||
if ( intersection ) {
|
||||
|
||||
intersection.faceIndex = tri;
|
||||
if ( intersections ) intersections.push( intersection );
|
||||
return intersection;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
export { intersectTri };
|
||||
125
node_modules/three-mesh-bvh/src/utils/TriangleUtilities.js
generated
vendored
Normal file
125
node_modules/three-mesh-bvh/src/utils/TriangleUtilities.js
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
import { Vector2, Vector3, Triangle } from 'three';
|
||||
|
||||
// sets the vertices of triangle `tri` with the 3 vertices after i
|
||||
export function setTriangle( tri, i, index, pos ) {
|
||||
|
||||
const ta = tri.a;
|
||||
const tb = tri.b;
|
||||
const tc = tri.c;
|
||||
|
||||
let i0 = i;
|
||||
let i1 = i + 1;
|
||||
let i2 = i + 2;
|
||||
if ( index ) {
|
||||
|
||||
i0 = index.getX( i0 );
|
||||
i1 = index.getX( i1 );
|
||||
i2 = index.getX( i2 );
|
||||
|
||||
}
|
||||
|
||||
ta.x = pos.getX( i0 );
|
||||
ta.y = pos.getY( i0 );
|
||||
ta.z = pos.getZ( i0 );
|
||||
|
||||
tb.x = pos.getX( i1 );
|
||||
tb.y = pos.getY( i1 );
|
||||
tb.z = pos.getZ( i1 );
|
||||
|
||||
tc.x = pos.getX( i2 );
|
||||
tc.y = pos.getY( i2 );
|
||||
tc.z = pos.getZ( i2 );
|
||||
|
||||
}
|
||||
|
||||
const tempV1 = /* @__PURE__ */ new Vector3();
|
||||
const tempV2 = /* @__PURE__ */ new Vector3();
|
||||
const tempV3 = /* @__PURE__ */ new Vector3();
|
||||
const tempUV1 = /* @__PURE__ */ new Vector2();
|
||||
const tempUV2 = /* @__PURE__ */ new Vector2();
|
||||
const tempUV3 = /* @__PURE__ */ new Vector2();
|
||||
|
||||
export function getTriangleHitPointInfo( point, geometry, triangleIndex, target ) {
|
||||
|
||||
const indices = geometry.getIndex().array;
|
||||
const positions = geometry.getAttribute( 'position' );
|
||||
const uvs = geometry.getAttribute( 'uv' );
|
||||
|
||||
const a = indices[ triangleIndex * 3 ];
|
||||
const b = indices[ triangleIndex * 3 + 1 ];
|
||||
const c = indices[ triangleIndex * 3 + 2 ];
|
||||
|
||||
tempV1.fromBufferAttribute( positions, a );
|
||||
tempV2.fromBufferAttribute( positions, b );
|
||||
tempV3.fromBufferAttribute( positions, c );
|
||||
|
||||
// find the associated material index
|
||||
let materialIndex = 0;
|
||||
const groups = geometry.groups;
|
||||
const firstVertexIndex = triangleIndex * 3;
|
||||
for ( let i = 0, l = groups.length; i < l; i ++ ) {
|
||||
|
||||
const group = groups[ i ];
|
||||
const { start, count } = group;
|
||||
if ( firstVertexIndex >= start && firstVertexIndex < start + count ) {
|
||||
|
||||
materialIndex = group.materialIndex;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// extract barycoord
|
||||
const barycoord = target && target.barycoord ? target.barycoord : new Vector3();
|
||||
Triangle.getBarycoord( point, tempV1, tempV2, tempV3, barycoord );
|
||||
|
||||
// extract uvs
|
||||
let uv = null;
|
||||
if ( uvs ) {
|
||||
|
||||
tempUV1.fromBufferAttribute( uvs, a );
|
||||
tempUV2.fromBufferAttribute( uvs, b );
|
||||
tempUV3.fromBufferAttribute( uvs, c );
|
||||
|
||||
if ( target && target.uv ) uv = target.uv;
|
||||
else uv = new Vector2();
|
||||
|
||||
Triangle.getInterpolation( point, tempV1, tempV2, tempV3, tempUV1, tempUV2, tempUV3, uv );
|
||||
|
||||
}
|
||||
|
||||
// adjust the provided target or create a new one
|
||||
if ( target ) {
|
||||
|
||||
if ( ! target.face ) target.face = { };
|
||||
target.face.a = a;
|
||||
target.face.b = b;
|
||||
target.face.c = c;
|
||||
target.face.materialIndex = materialIndex;
|
||||
if ( ! target.face.normal ) target.face.normal = new Vector3();
|
||||
Triangle.getNormal( tempV1, tempV2, tempV3, target.face.normal );
|
||||
|
||||
if ( uv ) target.uv = uv;
|
||||
target.barycoord = barycoord;
|
||||
|
||||
return target;
|
||||
|
||||
} else {
|
||||
|
||||
return {
|
||||
face: {
|
||||
a: a,
|
||||
b: b,
|
||||
c: c,
|
||||
materialIndex: materialIndex,
|
||||
normal: Triangle.getNormal( tempV1, tempV2, tempV3, new Vector3() )
|
||||
},
|
||||
uv: uv,
|
||||
barycoord: barycoord,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
120
node_modules/three-mesh-bvh/src/workers/GenerateMeshBVHWorker.js
generated
vendored
Normal file
120
node_modules/three-mesh-bvh/src/workers/GenerateMeshBVHWorker.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import { Box3, BufferAttribute } from 'three';
|
||||
import { MeshBVH } from '../core/MeshBVH.js';
|
||||
import { WorkerBase } from './utils/WorkerBase.js';
|
||||
|
||||
export class GenerateMeshBVHWorker extends WorkerBase {
|
||||
|
||||
constructor() {
|
||||
|
||||
const worker = new Worker( new URL( './generateMeshBVH.worker.js', import.meta.url ), { type: 'module' } );
|
||||
super( worker );
|
||||
this.name = 'GenerateMeshBVHWorker';
|
||||
|
||||
}
|
||||
|
||||
runTask( worker, geometry, options = {} ) {
|
||||
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
|
||||
if (
|
||||
geometry.getAttribute( 'position' ).isInterleavedBufferAttribute ||
|
||||
geometry.index && geometry.index.isInterleavedBufferAttribute
|
||||
) {
|
||||
|
||||
throw new Error( 'GenerateMeshBVHWorker: InterleavedBufferAttribute are not supported for the geometry attributes.' );
|
||||
|
||||
}
|
||||
|
||||
worker.onerror = e => {
|
||||
|
||||
reject( new Error( `GenerateMeshBVHWorker: ${ e.message }` ) );
|
||||
|
||||
};
|
||||
|
||||
worker.onmessage = e => {
|
||||
|
||||
const { data } = e;
|
||||
|
||||
if ( data.error ) {
|
||||
|
||||
reject( new Error( data.error ) );
|
||||
worker.onmessage = null;
|
||||
|
||||
} else if ( data.serialized ) {
|
||||
|
||||
const { serialized, position } = data;
|
||||
const bvh = MeshBVH.deserialize( serialized, geometry, { setIndex: false } );
|
||||
const boundsOptions = Object.assign( {
|
||||
|
||||
setBoundingBox: true,
|
||||
|
||||
}, options );
|
||||
|
||||
// we need to replace the arrays because they're neutered entirely by the
|
||||
// webworker transfer.
|
||||
geometry.attributes.position.array = position;
|
||||
if ( serialized.index ) {
|
||||
|
||||
if ( geometry.index ) {
|
||||
|
||||
geometry.index.array = serialized.index;
|
||||
|
||||
} else {
|
||||
|
||||
const newIndex = new BufferAttribute( serialized.index, 1, false );
|
||||
geometry.setIndex( newIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( boundsOptions.setBoundingBox ) {
|
||||
|
||||
geometry.boundingBox = bvh.getBoundingBox( new Box3() );
|
||||
|
||||
}
|
||||
|
||||
if ( options.onProgress ) {
|
||||
|
||||
options.onProgress( data.progress );
|
||||
|
||||
}
|
||||
|
||||
resolve( bvh );
|
||||
worker.onmessage = null;
|
||||
|
||||
} else if ( options.onProgress ) {
|
||||
|
||||
options.onProgress( data.progress );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const index = geometry.index ? geometry.index.array : null;
|
||||
const position = geometry.attributes.position.array;
|
||||
const transferable = [ position ];
|
||||
if ( index ) {
|
||||
|
||||
transferable.push( index );
|
||||
|
||||
}
|
||||
|
||||
worker.postMessage( {
|
||||
|
||||
index,
|
||||
position,
|
||||
options: {
|
||||
...options,
|
||||
onProgress: null,
|
||||
includedProgressCallback: Boolean( options.onProgress ),
|
||||
groups: [ ... geometry.groups ],
|
||||
},
|
||||
|
||||
}, transferable.map( arr => arr.buffer ).filter( v => ( typeof SharedArrayBuffer === 'undefined' ) || ! ( v instanceof SharedArrayBuffer ) ) );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
154
node_modules/three-mesh-bvh/src/workers/ParallelMeshBVHWorker.js
generated
vendored
Normal file
154
node_modules/three-mesh-bvh/src/workers/ParallelMeshBVHWorker.js
generated
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
import { Box3, BufferAttribute } from 'three';
|
||||
import { MeshBVH } from '../core/MeshBVH.js';
|
||||
import { WorkerBase } from './utils/WorkerBase.js';
|
||||
import { convertToBufferType, isSharedArrayBufferSupported } from '../utils/BufferUtils.js';
|
||||
import { GenerateMeshBVHWorker } from './GenerateMeshBVHWorker.js';
|
||||
import { ensureIndex } from '../core/build/geometryUtils.js';
|
||||
|
||||
const DEFAULT_WORKER_COUNT = typeof navigator !== 'undefined' ? navigator.hardwareConcurrency : 4;
|
||||
class _ParallelMeshBVHWorker extends WorkerBase {
|
||||
|
||||
constructor() {
|
||||
|
||||
const worker = new Worker( new URL( './parallelMeshBVH.worker.js', import.meta.url ), { type: 'module' } );
|
||||
super( worker );
|
||||
|
||||
this.name = 'ParallelMeshBVHWorker';
|
||||
this.maxWorkerCount = Math.max( DEFAULT_WORKER_COUNT, 4 );
|
||||
|
||||
if ( ! isSharedArrayBufferSupported() ) {
|
||||
|
||||
throw new Error( 'ParallelMeshBVHWorker: Shared Array Buffers are not supported.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
runTask( worker, geometry, options = {} ) {
|
||||
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
|
||||
if ( ! geometry.index && ! options.indirect ) {
|
||||
|
||||
ensureIndex( geometry, options );
|
||||
|
||||
}
|
||||
|
||||
if (
|
||||
geometry.getAttribute( 'position' ).isInterleavedBufferAttribute ||
|
||||
geometry.index && geometry.index.isInterleavedBufferAttribute
|
||||
) {
|
||||
|
||||
throw new Error( 'ParallelMeshBVHWorker: InterleavedBufferAttribute are not supported for the geometry attributes.' );
|
||||
|
||||
}
|
||||
|
||||
worker.onerror = e => {
|
||||
|
||||
reject( new Error( `ParallelMeshBVHWorker: ${ e.message }` ) );
|
||||
|
||||
};
|
||||
|
||||
worker.onmessage = e => {
|
||||
|
||||
const { data } = e;
|
||||
|
||||
if ( data.error ) {
|
||||
|
||||
reject( new Error( data.error ) );
|
||||
worker.onmessage = null;
|
||||
|
||||
} else if ( data.serialized ) {
|
||||
|
||||
const { serialized, position } = data;
|
||||
const bvh = MeshBVH.deserialize( serialized, geometry, { setIndex: false } );
|
||||
const boundsOptions = {
|
||||
setBoundingBox: true,
|
||||
...options,
|
||||
};
|
||||
|
||||
// we need to replace the arrays because they're neutered entirely by the
|
||||
// webworker transfer.
|
||||
geometry.attributes.position.array = position;
|
||||
if ( serialized.index ) {
|
||||
|
||||
if ( geometry.index ) {
|
||||
|
||||
geometry.index.array = serialized.index;
|
||||
|
||||
} else {
|
||||
|
||||
const newIndex = new BufferAttribute( serialized.index, 1, false );
|
||||
geometry.setIndex( newIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( boundsOptions.setBoundingBox ) {
|
||||
|
||||
geometry.boundingBox = bvh.getBoundingBox( new Box3() );
|
||||
|
||||
}
|
||||
|
||||
if ( options.onProgress ) {
|
||||
|
||||
options.onProgress( data.progress );
|
||||
|
||||
}
|
||||
|
||||
resolve( bvh );
|
||||
worker.onmessage = null;
|
||||
|
||||
} else if ( options.onProgress ) {
|
||||
|
||||
options.onProgress( data.progress );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const index = geometry.index ? geometry.index.array : null;
|
||||
const position = geometry.attributes.position.array;
|
||||
worker.postMessage( {
|
||||
|
||||
operation: 'BUILD_BVH',
|
||||
maxWorkerCount: this.maxWorkerCount,
|
||||
index: convertToBufferType( index, SharedArrayBuffer ),
|
||||
position: convertToBufferType( position, SharedArrayBuffer ),
|
||||
options: {
|
||||
...options,
|
||||
onProgress: null,
|
||||
includedProgressCallback: Boolean( options.onProgress ),
|
||||
groups: [ ... geometry.groups ],
|
||||
},
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class ParallelMeshBVHWorker {
|
||||
|
||||
constructor() {
|
||||
|
||||
if ( isSharedArrayBufferSupported() ) {
|
||||
|
||||
return new _ParallelMeshBVHWorker();
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'ParallelMeshBVHWorker: SharedArrayBuffers not supported. Falling back to single-threaded GenerateMeshBVHWorker.' );
|
||||
|
||||
const object = new GenerateMeshBVHWorker();
|
||||
object.maxWorkerCount = DEFAULT_WORKER_COUNT;
|
||||
return object;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
100
node_modules/three-mesh-bvh/src/workers/generateMeshBVH.worker.js
generated
vendored
Normal file
100
node_modules/three-mesh-bvh/src/workers/generateMeshBVH.worker.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
BufferAttribute,
|
||||
} from 'three';
|
||||
import { MeshBVH } from '../core/MeshBVH.js';
|
||||
|
||||
onmessage = ( { data } ) => {
|
||||
|
||||
let prevTime = performance.now();
|
||||
function onProgressCallback( progress ) {
|
||||
|
||||
// account for error
|
||||
progress = Math.min( progress, 1 );
|
||||
|
||||
const currTime = performance.now();
|
||||
if ( currTime - prevTime >= 10 && progress !== 1.0 ) {
|
||||
|
||||
postMessage( {
|
||||
|
||||
error: null,
|
||||
serialized: null,
|
||||
position: null,
|
||||
progress,
|
||||
|
||||
} );
|
||||
prevTime = currTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const { index, position, options } = data;
|
||||
try {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
geometry.setAttribute( 'position', new BufferAttribute( position, 3, false ) );
|
||||
if ( index ) {
|
||||
|
||||
geometry.setIndex( new BufferAttribute( index, 1, false ) );
|
||||
|
||||
}
|
||||
|
||||
if ( options.includedProgressCallback ) {
|
||||
|
||||
options.onProgress = onProgressCallback;
|
||||
|
||||
}
|
||||
|
||||
if ( options.groups ) {
|
||||
|
||||
const groups = options.groups;
|
||||
for ( const i in groups ) {
|
||||
|
||||
const group = groups[ i ];
|
||||
geometry.addGroup( group.start, group.count, group.materialIndex );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const bvh = new MeshBVH( geometry, options );
|
||||
const serialized = MeshBVH.serialize( bvh, { copyIndexBuffer: false } );
|
||||
let toTransfer = [ position.buffer, ...serialized.roots ];
|
||||
if ( serialized.index ) {
|
||||
|
||||
toTransfer.push( serialized.index.buffer );
|
||||
|
||||
}
|
||||
|
||||
toTransfer = toTransfer.filter( v => ( typeof SharedArrayBuffer === 'undefined' ) || ! ( v instanceof SharedArrayBuffer ) );
|
||||
|
||||
if ( bvh._indirectBuffer ) {
|
||||
|
||||
toTransfer.push( serialized.indirectBuffer.buffer );
|
||||
|
||||
}
|
||||
|
||||
postMessage( {
|
||||
|
||||
error: null,
|
||||
serialized,
|
||||
position,
|
||||
progress: 1,
|
||||
|
||||
}, toTransfer );
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
postMessage( {
|
||||
|
||||
error,
|
||||
serialized: null,
|
||||
position: null,
|
||||
progress: 1,
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
304
node_modules/three-mesh-bvh/src/workers/parallelMeshBVH.worker.js
generated
vendored
Normal file
304
node_modules/three-mesh-bvh/src/workers/parallelMeshBVH.worker.js
generated
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
import { MathUtils, BufferGeometry, BufferAttribute } from 'three';
|
||||
import { WorkerPool } from './utils/WorkerPool.js';
|
||||
import { BYTES_PER_NODE } from '../core/Constants.js';
|
||||
import { buildTree, generateIndirectBuffer } from '../core/build/buildTree.js';
|
||||
import { countNodes, populateBuffer } from '../core/build/buildUtils.js';
|
||||
import { computeTriangleBounds } from '../core/build/computeBoundsUtils.js';
|
||||
import { getFullGeometryRange, getRootIndexRanges, getTriCount } from '../core/build/geometryUtils.js';
|
||||
import { DEFAULT_OPTIONS } from '../core/MeshBVH.js';
|
||||
|
||||
let isRunning = false;
|
||||
let prevTime = 0;
|
||||
const workerPool = new WorkerPool( () => new Worker( new URL( './parallelMeshBVH.worker.js', import.meta.url ), { type: 'module' } ) );
|
||||
|
||||
onmessage = async ( { data } ) => {
|
||||
|
||||
if ( isRunning ) {
|
||||
|
||||
throw new Error( 'Worker is already running a task.' );
|
||||
|
||||
}
|
||||
|
||||
const { operation } = data;
|
||||
if ( operation === 'BUILD_BVH' ) {
|
||||
|
||||
isRunning = true;
|
||||
|
||||
const {
|
||||
maxWorkerCount,
|
||||
index,
|
||||
position,
|
||||
options,
|
||||
} = data;
|
||||
|
||||
// initialize the number of workers balanced for a binary tree
|
||||
workerPool.setWorkerCount( MathUtils.floorPowerOfTwo( maxWorkerCount ) );
|
||||
|
||||
// generate necessary buffers and objects
|
||||
const geometry = getGeometry( index, position );
|
||||
const geometryRanges = options.indirect ? getFullGeometryRange( geometry, options.range ) : getRootIndexRanges( geometry, options.range );
|
||||
const indirectBuffer = options.indirect ? generateIndirectBuffer( geometry, true ) : null;
|
||||
const triCount = getTriCount( geometry );
|
||||
const triangleBounds = new Float32Array( new SharedArrayBuffer( triCount * 6 * 4 ) );
|
||||
|
||||
// generate portions of the triangle bounds buffer over multiple frames
|
||||
const boundsPromises = [];
|
||||
for ( let i = 0, l = workerPool.workerCount; i < l; i ++ ) {
|
||||
|
||||
const countPerWorker = Math.ceil( triCount / l );
|
||||
const offset = i * countPerWorker;
|
||||
const count = Math.min( countPerWorker, triCount - offset );
|
||||
|
||||
boundsPromises.push( workerPool.runSubTask(
|
||||
i,
|
||||
{
|
||||
operation: 'BUILD_TRIANGLE_BOUNDS',
|
||||
offset,
|
||||
count,
|
||||
index,
|
||||
position,
|
||||
triangleBounds,
|
||||
}
|
||||
) );
|
||||
|
||||
}
|
||||
|
||||
await Promise.all( boundsPromises );
|
||||
|
||||
// create a proxy bvh structure
|
||||
const proxyBvh = {
|
||||
_indirectBuffer: indirectBuffer,
|
||||
geometry: geometry,
|
||||
};
|
||||
|
||||
let totalProgress = 0;
|
||||
|
||||
const localOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options,
|
||||
verbose: false,
|
||||
maxDepth: Math.round( Math.log2( workerPool.workerCount ) ),
|
||||
onProgress: options.includedProgressCallback ?
|
||||
getOnProgressDeltaCallback( delta => {
|
||||
|
||||
totalProgress += 0.1 * delta;
|
||||
triggerOnProgress( totalProgress );
|
||||
|
||||
} ) :
|
||||
null,
|
||||
};
|
||||
|
||||
// generate the ranges for all roots asynchronously
|
||||
const packedRoots = [];
|
||||
for ( let i = 0, l = geometryRanges.length; i < l; i ++ ) {
|
||||
|
||||
// build the tree down to the necessary depth
|
||||
const promises = [];
|
||||
const range = geometryRanges[ i ];
|
||||
const root = buildTree( proxyBvh, triangleBounds, range.offset, range.count, localOptions );
|
||||
const flatNodes = flattenNodes( root );
|
||||
let bufferLengths = 0;
|
||||
let remainingNodes = 0;
|
||||
let nextWorker = 0;
|
||||
|
||||
// trigger workers for each generated leaf node
|
||||
for ( let j = 0, l = flatNodes.length; j < l; j ++ ) {
|
||||
|
||||
const node = flatNodes[ j ];
|
||||
const isLeaf = Boolean( node.count );
|
||||
if ( isLeaf ) {
|
||||
|
||||
// adjust the maxDepth to account for the depth we've already traversed
|
||||
const workerOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options
|
||||
};
|
||||
|
||||
workerOptions.maxDepth = workerOptions.maxDepth - node.depth;
|
||||
|
||||
const pr = workerPool.runSubTask(
|
||||
nextWorker ++,
|
||||
{
|
||||
operation: 'BUILD_SUBTREE',
|
||||
offset: node.offset,
|
||||
count: node.count,
|
||||
indirectBuffer,
|
||||
index,
|
||||
position,
|
||||
triangleBounds,
|
||||
options: workerOptions,
|
||||
},
|
||||
getOnProgressDeltaCallback( delta => {
|
||||
|
||||
totalProgress += 0.9 * delta / nextWorker;
|
||||
triggerOnProgress( totalProgress );
|
||||
|
||||
} ),
|
||||
).then( data => {
|
||||
|
||||
const buffer = data.buffer;
|
||||
node.buffer = buffer;
|
||||
bufferLengths += buffer.byteLength;
|
||||
|
||||
} );
|
||||
|
||||
promises.push( pr );
|
||||
|
||||
} else {
|
||||
|
||||
remainingNodes ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// wait for the sub trees to complete
|
||||
await Promise.all( promises );
|
||||
|
||||
const BufferConstructor = options.useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
|
||||
const buffer = new BufferConstructor( bufferLengths + remainingNodes * BYTES_PER_NODE );
|
||||
populateBuffer( 0, root, buffer );
|
||||
|
||||
packedRoots.push( buffer );
|
||||
|
||||
}
|
||||
|
||||
// transfer the data back
|
||||
postMessage( {
|
||||
error: null,
|
||||
serialized: {
|
||||
roots: packedRoots,
|
||||
index: index,
|
||||
indirectBuffer: indirectBuffer,
|
||||
},
|
||||
position,
|
||||
progress: 1,
|
||||
} );
|
||||
|
||||
isRunning = false;
|
||||
|
||||
} else if ( operation === 'BUILD_SUBTREE' ) {
|
||||
|
||||
const {
|
||||
offset,
|
||||
count,
|
||||
indirectBuffer,
|
||||
index,
|
||||
position,
|
||||
triangleBounds,
|
||||
options,
|
||||
} = data;
|
||||
|
||||
const proxyBvh = {
|
||||
_indirectBuffer: indirectBuffer,
|
||||
geometry: getGeometry( index, position ),
|
||||
};
|
||||
|
||||
const localOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options,
|
||||
onProgress: options.includedProgressCallback ? triggerOnProgress : null,
|
||||
};
|
||||
|
||||
const root = buildTree( proxyBvh, triangleBounds, offset, count, localOptions );
|
||||
const nodeCount = countNodes( root );
|
||||
const buffer = new ArrayBuffer( BYTES_PER_NODE * nodeCount );
|
||||
populateBuffer( 0, root, buffer );
|
||||
postMessage( { type: 'result', buffer, progress: 1 }, [ buffer ] );
|
||||
|
||||
} else if ( operation === 'BUILD_TRIANGLE_BOUNDS' ) {
|
||||
|
||||
const {
|
||||
index,
|
||||
position,
|
||||
triangleBounds,
|
||||
offset,
|
||||
count,
|
||||
} = data;
|
||||
|
||||
const geometry = getGeometry( index, position );
|
||||
computeTriangleBounds( geometry, triangleBounds, offset, count );
|
||||
postMessage( { type: 'result' } );
|
||||
|
||||
} else if ( operation === 'REFIT' ) {
|
||||
|
||||
// TODO
|
||||
|
||||
} else if ( operation === 'REFIT_SUBTREE' ) {
|
||||
|
||||
// TODO
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Helper functions and utils
|
||||
function getOnProgressDeltaCallback( cb ) {
|
||||
|
||||
let lastProgress = 0;
|
||||
return function onProgressDeltaCallback( progress ) {
|
||||
|
||||
cb( progress - lastProgress );
|
||||
lastProgress = progress;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function triggerOnProgress( progress ) {
|
||||
|
||||
// account for error
|
||||
progress = Math.min( progress, 1 );
|
||||
|
||||
const currTime = performance.now();
|
||||
if ( currTime - prevTime >= 10 && progress !== 1.0 ) {
|
||||
|
||||
postMessage( {
|
||||
|
||||
error: null,
|
||||
progress,
|
||||
type: 'progress'
|
||||
|
||||
} );
|
||||
prevTime = currTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getGeometry( index, position ) {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
if ( index ) {
|
||||
|
||||
geometry.index = new BufferAttribute( index, 1, false );
|
||||
|
||||
}
|
||||
|
||||
geometry.setAttribute( 'position', new BufferAttribute( position, 3 ) );
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
function flattenNodes( node ) {
|
||||
|
||||
const arr = [];
|
||||
traverse( node );
|
||||
return arr;
|
||||
|
||||
function traverse( node, depth = 0 ) {
|
||||
|
||||
node.depth = depth;
|
||||
arr.push( node );
|
||||
|
||||
const isLeaf = Boolean( node.count );
|
||||
if ( ! isLeaf ) {
|
||||
|
||||
traverse( node.left, depth + 1 );
|
||||
traverse( node.right, depth + 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
60
node_modules/three-mesh-bvh/src/workers/utils/WorkerBase.js
generated
vendored
Normal file
60
node_modules/three-mesh-bvh/src/workers/utils/WorkerBase.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
export class WorkerBase {
|
||||
|
||||
constructor( worker ) {
|
||||
|
||||
this.name = 'WorkerBase';
|
||||
this.running = false;
|
||||
this.worker = worker;
|
||||
this.worker.onerror = e => {
|
||||
|
||||
if ( e.message ) {
|
||||
|
||||
throw new Error( `${ this.name }: Could not create Web Worker with error "${ e.message }"` );
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( `${ this.name }: Could not create Web Worker.` );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
runTask() {}
|
||||
|
||||
generate( ...args ) {
|
||||
|
||||
if ( this.running ) {
|
||||
|
||||
throw new Error( 'GenerateMeshBVHWorker: Already running job.' );
|
||||
|
||||
}
|
||||
|
||||
if ( this.worker === null ) {
|
||||
|
||||
throw new Error( 'GenerateMeshBVHWorker: Worker has been disposed.' );
|
||||
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
|
||||
const promise = this.runTask( this.worker, ...args );
|
||||
promise.finally( () => {
|
||||
|
||||
this.running = false;
|
||||
|
||||
} );
|
||||
|
||||
return promise;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.worker.terminate();
|
||||
this.worker = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
82
node_modules/three-mesh-bvh/src/workers/utils/WorkerPool.js
generated
vendored
Normal file
82
node_modules/three-mesh-bvh/src/workers/utils/WorkerPool.js
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
export class WorkerPool {
|
||||
|
||||
get workerCount() {
|
||||
|
||||
return this.workers.length;
|
||||
|
||||
}
|
||||
|
||||
constructor( getWorkerCallback ) {
|
||||
|
||||
this.workers = [];
|
||||
this._getWorker = getWorkerCallback;
|
||||
|
||||
}
|
||||
|
||||
setWorkerCount( count ) {
|
||||
|
||||
const workers = this.workers;
|
||||
while ( workers.length < count ) {
|
||||
|
||||
workers.push( this._getWorker() );
|
||||
|
||||
}
|
||||
|
||||
while ( workers.length > count ) {
|
||||
|
||||
workers.pop().terminate();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
runSubTask( i, msg, onProgress ) {
|
||||
|
||||
return new Promise( ( resolve, reject ) => {
|
||||
|
||||
const worker = this.workers[ i ];
|
||||
if ( worker.isRunning ) {
|
||||
|
||||
throw new Error( `${ this.name }: Worker ${ i } is already running.` );
|
||||
|
||||
}
|
||||
|
||||
worker.isRunning = true;
|
||||
worker.postMessage( msg );
|
||||
worker.onerror = e => {
|
||||
|
||||
worker.isRunning = false;
|
||||
reject( e );
|
||||
|
||||
};
|
||||
|
||||
worker.onmessage = e => {
|
||||
|
||||
if ( e.data.type === 'progress' ) {
|
||||
|
||||
if ( onProgress ) {
|
||||
|
||||
onProgress( e.data.progress );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( onProgress ) {
|
||||
|
||||
onProgress( 1 );
|
||||
|
||||
}
|
||||
|
||||
worker.isRunning = false;
|
||||
resolve( e.data );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user