Skip to content

Commit

Permalink
Added sort
Browse files Browse the repository at this point in the history
  • Loading branch information
agargaro committed Aug 27, 2024
1 parent 781dede commit 06e5cc5
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 48 deletions.
62 changes: 36 additions & 26 deletions example/testToRemove.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import { computeBoundsTree, SAH } from '../src';

THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;

const radius = 100;
const tube = 0.5;
// const tube = 0.5 * radius;
const tubularSegments = 800;
const radialSegments = 200;
const geometry = new THREE.TorusKnotGeometry( radius, tube, tubularSegments, radialSegments );

geometry.computeBoundsTree( {
maxLeafTris: 5,
strategy: SAH,
} );
const spawnPointRadius = 10;
const radius = 100; // if radius 100 and tube 0.1, sort works really good.
const tube = 0.1;
const segmentsMultiplier = 8;
const maxLeafTris = 5;
const strategy = SAH;

const seed = 20000;

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

export class PRNG {

Expand All @@ -40,36 +42,35 @@ export class PRNG {

}


const bvh = geometry.boundsTree;
const target = new THREE.Vector3();
const target2 = new THREE.Vector3();

Check warning on line 47 in example/testToRemove.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'target2' is assigned a value but never used

Check notice

Code scanning / CodeQL

Unused variable, import, function or class Note test

Unused variable target2.

const count = 10000;
const r = new PRNG( 10000 );
const count = 5000;
const r = new PRNG( seed );

const points = new Array( count );
for ( let i = 0; i < count; i ++ ) {

points[ i ] = new THREE.Vector3( r.next(), r.next(), r.next() ).multiplyScalar( 5 ).subScalar( 2.5 );
points[ i ] = new THREE.Vector3( r.range( - spawnPointRadius, spawnPointRadius ), r.range( - spawnPointRadius, spawnPointRadius ), r.range( - spawnPointRadius, spawnPointRadius ) );

}

// TEST EQUALS RESULTS
// // TEST EQUALS RESULTS

for ( let i = 0; i < count; i ++ ) {
// for ( let i = 0; i < count; i ++ ) {

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

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

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

}
// }

}
// }

// TEST PERFORMANCE

Expand All @@ -93,10 +94,19 @@ function benchmark() {
}

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

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

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

}

const endSort = performance.now() - startSort;

console.log( `New: ${endNew.toFixed( 1 )} / Old: ${endOld.toFixed( 1 )} / Diff: ${( ( 1 - ( endOld / endNew ) ) * 100).toFixed( 2 )} %` );
const bestEnd = Math.min( endSort, endNew );

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

}

Expand Down
27 changes: 27 additions & 0 deletions src/core/MeshBVH.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { ExtendedTrianglePool } from '../utils/ExtendedTrianglePool.js';
import { shapecast } from './cast/shapecast.js';
import { closestPointToPoint } from './cast/closestPointToPoint.generated.js';
import { closestPointToPoint_indirect } from './cast/closestPointToPoint_indirect.generated.js';
import { closestPointToPointSort } from './cast/closestPointToPointSort.generated.js';
import { closestPointToPointSort_indirect } from './cast/closestPointToPointSort_indirect.generated.js';
import { closestPointToPointOld } from './cast/closestPointToPoint.js'; // REMOVE AFTER TEST

import { iterateOverTriangles } from './utils/iterationUtils.generated.js';
Expand Down Expand Up @@ -544,6 +546,31 @@ export class MeshBVH {

}

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

const closestPointToPointFunc = this.indirect ? closestPointToPointSort_indirect : closestPointToPointSort;
const roots = this._roots;
let result = null;

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

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

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

}

return result;

}

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

Expand Down
26 changes: 4 additions & 22 deletions src/core/cast/closestPointToPoint.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF } from '../utils/nodeBuff
import { BufferStack } from '../utils/BufferStack.js';
import { ExtendedTrianglePool } from '../../utils/ExtendedTrianglePool.js';
import { setTriangle } from '../../utils/TriangleUtilities.js';
import { closestDistanceSquaredPointToBox } from '../utils/distanceUtils.js';

const temp = /* @__PURE__ */ new Vector3();
const temp1 = /* @__PURE__ */ new Vector3();
Expand All @@ -27,7 +28,7 @@ export function closestPointToPoint/* @echo INDIRECT_STRING */(
const triangle = ExtendedTrianglePool.getPrimitive();

BufferStack.setBuffer( bvh._roots[ root ] );
const { float32Array, uint16Array, uint32Array } = BufferStack; // moved try bench
const { float32Array, uint16Array, uint32Array } = BufferStack;
_closestPointToPoint( root );
BufferStack.clearBuffer();

Expand Down Expand Up @@ -92,8 +93,8 @@ export function closestPointToPoint/* @echo INDIRECT_STRING */(
const leftIndex = LEFT_NODE( nodeIndex32 );
const rightIndex = RIGHT_NODE( nodeIndex32, uint32Array );

const leftDistance = distanceSquaredPointToBox( leftIndex, float32Array, point );
const rightDistance = distanceSquaredPointToBox( rightIndex, float32Array, point );
const leftDistance = closestDistanceSquaredPointToBox( leftIndex, float32Array, point );
const rightDistance = closestDistanceSquaredPointToBox( rightIndex, float32Array, point );

if ( leftDistance <= rightDistance ) {

Expand All @@ -114,22 +115,3 @@ export function closestPointToPoint/* @echo INDIRECT_STRING */(
}

}

// TODO move it
export function distanceSquaredPointToBox( nodeIndex32, array, point ) {

const xMin = array[ nodeIndex32 + 0 ] - point.x;
const xMax = point.x - array[ nodeIndex32 + 3 ];
const dx = xMin > xMax ? ( xMin > 0 ? xMin : 0 ) : ( xMax > 0 ? xMax : 0 );

const yMin = array[ nodeIndex32 + 1 ] - point.y;
const yMax = point.y - array[ nodeIndex32 + 4 ];
const dy = yMin > yMax ? ( yMin > 0 ? yMin : 0 ) : ( yMax > 0 ? yMax : 0 );

const zMin = array[ nodeIndex32 + 2 ] - point.z;
const zMax = point.z - array[ nodeIndex32 + 5 ];
const dz = zMin > zMax ? ( zMin > 0 ? zMin : 0 ) : ( zMax > 0 ? zMax : 0 );

return dx * dx + dy * dy + dz * dz;

}
119 changes: 119 additions & 0 deletions src/core/cast/closestPointToPointSort.template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { Vector3 } from 'three';
import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF } 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';

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

export function closestPointToPointSort/* @echo INDIRECT_STRING */(
bvh,
root,
point,
target = { },
minThreshold = 0,
maxThreshold = Infinity
) {

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

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

let node = { nodeIndex32: 0, distance: closestDistanceSquaredPointToBox( 0, float32Array, point ) };

do {

const { distance } = node;

if ( distance >= closestDistanceSq ) return;

const { nodeIndex32 } = node;

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 ++ ) {

/* @if INDIRECT */

const ti = bvh.resolveTriangleIndex( i );
setTriangle( triangle, 3 * ti, index, pos );

/* @else */

setTriangle( triangle, i * 3, index, pos );

/* @endif */

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;

}

}

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

Check failure on line 104 in src/core/cast/closestPointToPointSort.template.js

View workflow job for this annotation

GitHub Actions / build (20.x)

Expected a conditional expression and instead saw an assignment

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;

}
53 changes: 53 additions & 0 deletions src/core/utils/SortedListDesc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export class SortedListDesc {

constructor() {

this.array = [];

}

clear() {

this.array = [];

}


push( node ) {

const index = this.binarySearch( node.distance );
this.array.splice( index, 0, node );

}

pop() {

return this.array.pop();

}

binarySearch( value ) {

const array = this.array;

let start = 0;
let end = array.length - 1;
let index = 0;

while ( start <= end ) {

index = Math.ceil( ( start + end ) / 2 );

if ( index === 0 ) break;
if ( array[ index ].distance <= value && array[ index - 1 ].distance >= value ) return index;

if ( value > array[ index ].distance ) end = index - 1;
else start = index + 1;

}

return value < array[ index ]?.distance ? index + 1 : index;

}

}
20 changes: 20 additions & 0 deletions src/core/utils/distanceUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export function closestDistanceSquaredPointToBox( nodeIndex32, array, point ) {

const xMin = array[ nodeIndex32 + 0 ] - point.x;
const xMax = point.x - array[ nodeIndex32 + 3 ];
let dx = xMin > xMax ? xMin : xMax;
dx = dx > 0 ? dx : 0;

const yMin = array[ nodeIndex32 + 1 ] - point.y;
const yMax = point.y - array[ nodeIndex32 + 4 ];
let dy = yMin > yMax ? yMin : yMax;
dy = dy > 0 ? dy : 0;

const zMin = array[ nodeIndex32 + 2 ] - point.z;
const zMax = point.z - array[ nodeIndex32 + 5 ];
let dz = zMin > zMax ? zMin : zMax;
dz = dz > 0 ? dz : 0;

return dx * dx + dy * dy + dz * dz;

}

0 comments on commit 06e5cc5

Please sign in to comment.