three.js
three.js copied to clipboard
SceneUtils: Add sortInstancedMesh(mesh, fn)
After implementing a function for sorting instances of a THREE.InstancedMesh, it turned out to be more code than expected and I thought we might want a helper function here. The new method takes an InstancedMesh and a comparator function:
import { sortInstancedMesh } from 'three/examples/jsm/utils/SceneUtils.js';
sortInstancedMesh( mesh, ( indexA, indexB ) => {
let condition;
// ...
return condition ? 1 : -1;
} );
This operation can be expensive for lots of instances, and shouldn't be done on every frame, but can certainly be useful as a bit of pre-processing to get transparency arranged correctly.
This contribution is funded by The New York Times.
I'd missed #23348 and the removal of unit tests on examples/jsm/*
, but for posterity here are tests on SceneUtils.sortInstancedMesh:
import { sortInstancedMesh } from '../../../../examples/jsm/utils/SceneUtils.js';
import { BufferAttribute } from '../../../../src/core/BufferAttribute.js';
import { BoxGeometry } from '../../../../src/geometries/BoxGeometry.js';
import { InstancedBufferAttribute } from '../../../../src/core/InstancedBufferAttribute.js';
import { InstancedMesh } from '../../../../src/objects/InstancedMesh.js';
import { Color } from '../../../../src/math/Color.js';
import { Matrix4 } from '../../../../src/math/Matrix4.js';
const color = new Color();
const matrix = new Matrix4();
export default QUnit.module( 'Utils', () => {
QUnit.module( 'SceneUtils', () => {
QUnit.test( 'sortInstancedMesh()', ( assert ) => {
const valueA = new InstancedBufferAttribute( new Uint8Array( [ 2, 7, 1 ] ), 1 );
const valueB = new InstancedBufferAttribute( new Float32Array( [ 1, 1, 1, 2, 2, 2, 3, 3, 3 ] ), 3 );
const geometry = new BoxGeometry()
.setAttribute( 'instanceValueA', valueA )
.setAttribute( 'instanceValueB', valueB );
const mesh = new InstancedMesh( geometry, undefined, 3 );
mesh.setMatrixAt( 0, matrix.makeTranslation( 10, 0, 0 ) );
mesh.setMatrixAt( 1, matrix.makeTranslation( 0, 10, 0 ) );
mesh.setMatrixAt( 2, matrix.makeTranslation( 0, 0, 10 ) );
mesh.setColorAt( 0, color.setRGB( 1, 0, 0 ) );
mesh.setColorAt( 1, color.setRGB( 0, 1, 0 ) );
mesh.setColorAt( 2, color.setRGB( 0, 0, 1 ) );
sortInstancedMesh( mesh, ( a, b ) => valueA.getX( a ) - valueA.getX( b ) );
//
assert.smartEqual( Array.from( mesh.instanceMatrix.array ), [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 10, 1,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
10, 0, 0, 1,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 10, 0, 1,
], 'sorted instanceMatrix' );
assert.smartEqual( Array.from( mesh.instanceColor.array ), [
0, 0, 1,
1, 0, 0,
0, 1, 0,
], 'sorted instanceColor' );
assert.smartEqual( Array.from( valueA.array ), [ 1, 2, 7 ], 'sorted instanceValueA' );
assert.smartEqual( Array.from( valueB.array ), [ 3, 3, 3, 1, 1, 1, 2, 2, 2 ], 'sorted instanceValueB' );
} );
} );
} );
Thanks for making a useful function.
This operation can be expensive for lots of instances, and shouldn't be done on every frame,
Nit: I think it would be useful for users to write this note in the code or documents.
Agreed! I'll add documentation for sortInstancedMesh
if the PR is accepted.
- Documentation: https://github.com/mrdoob/three.js/pull/24833