Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions example/testToRemove.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>three-mesh-bvh - Complex Geometry Raycasting</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

<style type="text/css">
html, body {
padding: 0;
margin: 0;
overflow: hidden;
}

canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<script type="module" src="./testToRemove.js"></script>
</body>
</html>
127 changes: 127 additions & 0 deletions example/testToRemove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import * as THREE from 'three';
import { computeBoundsTree, CENTER } from '../src';

THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;

class PRNG {

constructor( seed ) {

this._seed = seed;

}

next() {

let t = ( this._seed += 0x6d2b79f5 );
t = Math.imul( t ^ ( t >>> 15 ), t | 1 );
t ^= t + Math.imul( t ^ ( t >>> 7 ), t | 61 );
return ( ( t ^ ( t >>> 14 ) ) >>> 0 ) / 4294967296;

}

range( min, max ) {

return min + ( max - min ) * this.next();

}

}


const maxSpawnPointRadius = 2;
const maxLeafTris = 4;
const strategy = CENTER;

const tries = 1000;
const seed = 123456;

const radius = 10; // if radius 100 and tube 0.1 and spawnRadius 100, sort works really good.
const tube = 0.1;
const segmentsMultiplier = 32;

// const geometry = new THREE.SphereGeometry( radius, 8 * segmentsMultiplier, 4 * segmentsMultiplier );
const geometry = new THREE.TorusKnotGeometry( radius, tube, 64 * segmentsMultiplier, 8 * segmentsMultiplier );

geometry.computeBoundsTree( { maxLeafTris, strategy } );

geometry.computeBoundsTree( { maxLeafTris, strategy } );

const bvh = geometry.boundsTree;
const target = {};

const r = new PRNG( seed );
const points = new Array( tries );

function generatePoints() {

for ( let i = 0; i < tries; i ++ ) {

points[ i ] = new THREE.Vector3( r.range( - maxSpawnPointRadius, maxSpawnPointRadius ), r.range( - maxSpawnPointRadius, maxSpawnPointRadius ), r.range( - maxSpawnPointRadius, maxSpawnPointRadius ) );

}

}


// TEST EQUALS RESULTS

// generatePoints();
// const target2 = {};
// for ( let i = 0; i < tries; i ++ ) {

// bvh.closestPointToPoint( points[ i ], target );
// bvh.closestPointToPointHybrid( points[ i ], target2 );

// if ( target.distance !== target2.distance ) {

// const diff = target.distance - target2.distance;
// console.error( "error: " + ( diff / target2.distance * 100 ) + "%" );

// }

// }

// TEST PERFORMANCE

function benchmark() {

generatePoints();

const startOld = performance.now();

for ( let i = 0; i < tries; i ++ ) {

bvh.closestPointToPointOld( points[ i ], target );

}

const endOld = performance.now() - startOld;
const startNew = performance.now();

for ( let i = 0; i < tries; i ++ ) {

bvh.closestPointToPoint( points[ i ], target );

}

const endNew = performance.now() - startNew;
const startSort = performance.now();

for ( let i = 0; i < tries; i ++ ) {

bvh.closestPointToPointSort( points[ i ], target );

}

const endSort = performance.now() - startSort;

const bestEnd = Math.min( endSort, endNew );
const best = bestEnd === endSort ? "Sorted" : "New";

console.log( `New: ${endNew.toFixed( 1 )}ms / Sorted: ${endSort.toFixed( 1 )}ms / Old: ${endOld.toFixed( 1 )}ms / Diff: ${( ( 1 - ( endOld / bestEnd ) ) * 100 ).toFixed( 2 )} % / Best: ${best}` );

}

benchmark();
setInterval( () => benchmark(), 2000 );
59 changes: 57 additions & 2 deletions src/core/MeshBVH.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ 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 { closestPointToPoint } from './cast/closestPointToPointNew.js';
import { closestPointToPointSort } from './cast/closestPointToPointSort.js';
import { closestPointToPointOld } from './cast/closestPointToPoint.js'; // REMOVE AFTER TEST

import { iterateOverTriangles } from './utils/iterationUtils.generated.js';
import { refit } from './cast/refit.generated.js';
Expand Down Expand Up @@ -519,7 +521,60 @@ export class MeshBVH {

closestPointToPoint( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {

return closestPointToPoint(
const roots = this._roots;
let result = null;

for ( let i = 0, l = roots.length; i < l; i ++ ) {

result = closestPointToPoint(
this,
i,
point,
target,
minThreshold,
maxThreshold,
);

// fix here, check old result and new

if ( result && result.distance <= minThreshold ) break;

}

return result;

}

closestPointToPointSort( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {

const roots = this._roots;
let result = null;

for ( let i = 0, l = roots.length; i < l; i ++ ) {

result = closestPointToPointSort(
this,
i,
point,
target,
minThreshold,
maxThreshold,
);

// fix here, check old result and new

if ( result && result.distance <= minThreshold ) break;

}

return result;

}

// REMOVE AFTER TEST
closestPointToPointOld( point, target = { }, minThreshold = 0, maxThreshold = Infinity ) {

return closestPointToPointOld(
this,
point,
target,
Expand Down
14 changes: 4 additions & 10 deletions src/core/cast/closestPointToPoint.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Vector3 } from 'three';

// DELETE THIS AFTER TEST

const temp = /* @__PURE__ */ new Vector3();
const temp1 = /* @__PURE__ */ new Vector3();

export function closestPointToPoint(
export function closestPointToPointOld(
bvh,
point,
target = { },
Expand Down Expand Up @@ -48,15 +50,7 @@ export function closestPointToPoint(

}

if ( distSq < minThresholdSq ) {

return true;

} else {

return false;

}
return distSq < minThresholdSq;

},

Expand Down
109 changes: 109 additions & 0 deletions src/core/cast/closestPointToPointNew.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Vector3 } from 'three';
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
import { BufferStack } from '../utils/BufferStack.js';
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';
import { iterateOverTriangles } from '../utils/iterationUtils.generated.js';
import { iterateOverTriangles_indirect } from '../utils/iterationUtils_indirect.generated.js';
import { COUNT, IS_LEAF, LEFT_NODE, OFFSET, RIGHT_NODE } from '../utils/nodeBufferUtils.js';

const temp = /* @__PURE__ */ new Vector3();
const temp1 = /* @__PURE__ */ new Vector3();

export function closestPointToPoint/* @echo INDIRECT_STRING */(
bvh,
root,
point,
target,
minThreshold,
maxThreshold
) {

const minThresholdSq = minThreshold * minThreshold;
const maxThresholdSq = maxThreshold * maxThreshold;
let closestDistanceSq = Infinity;
let closestDistanceTriIndex = null;

const triangle = ExtendedTrianglePool.getPrimitive();

const iterateOverTrianglesFunc = bvh.indirect ? iterateOverTriangles_indirect : iterateOverTriangles;

BufferStack.setBuffer( bvh._roots[ root ] );
const { float32Array, uint16Array, uint32Array } = BufferStack;

_closestPointToPoint( root );

BufferStack.clearBuffer();
ExtendedTrianglePool.releasePrimitive( triangle );

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 );

return iterateOverTrianglesFunc( offset, count, bvh, intersectTriangle, null, null, triangle );

}

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 );

}

return false;

}

function intersectTriangle( triangle, triIndex ) {

triangle.closestPointToPoint( point, temp );
const distSq = point.distanceToSquared( temp );
if ( distSq < closestDistanceSq ) {

temp1.copy( temp );
closestDistanceSq = distSq;
closestDistanceTriIndex = triIndex;

}

return distSq < minThresholdSq;

}

}
Loading
Loading