Skip to content

Commit

Permalink
BatchedMesh: Add support for per-object opaque and transparent sorting (
Browse files Browse the repository at this point in the history
#27168)

* Add support for sorting objects to BatchedMesh

* Add transparency to the demo

* Class rename

* Comments, removed variables, fixed condition

* Add copy and toJSON support for object sorting
  • Loading branch information
gkjohnson committed Nov 17, 2023
1 parent f02b948 commit fec1161
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 18 deletions.
147 changes: 132 additions & 15 deletions examples/jsm/objects/BatchedMesh.js
Expand Up @@ -15,6 +15,63 @@ import {
Vector3,
} from 'three';

function sortOpaque( a, b ) {

return a.z - b.z;

}

function sortTransparent( a, b ) {

return b.z - a.z;

}

class MultiDrawRenderList {

constructor() {

this.index = 0;
this.pool = [];
this.list = [];

}

push( drawRange, z ) {

const pool = this.pool;
const list = this.list;
if ( this.index >= pool.length ) {

pool.push( {

start: - 1,
count: - 1,
z: - 1,

} );

}

const item = pool[ this.index ];
list.push( item );
this.index ++;

item.start = drawRange.start;
item.count = drawRange.count;
item.z = z;

}

reset() {

this.list.length = 0;
this.index = 0;

}

}

const ID_ATTR_NAME = 'batchId';
const _matrix = new Matrix4();
const _identityMatrix = new Matrix4();
Expand All @@ -23,6 +80,7 @@ const _frustum = new Frustum();
const _box = new Box3();
const _sphere = new Sphere();
const _vector = new Vector3();
const _renderList = new MultiDrawRenderList();
const _mesh = new Mesh();
const _batchIntersects = [];

Expand Down Expand Up @@ -72,6 +130,7 @@ class BatchedMesh extends Mesh {

this.isBatchedMesh = true;
this.perObjectFrustumCulled = true;
this.sortObjects = true;
this.boundingBox = null;
this.boundingSphere = null;

Expand Down Expand Up @@ -799,6 +858,7 @@ class BatchedMesh extends Mesh {

this.geometry = source.geometry.clone();
this.perObjectFrustumCulled = source.perObjectFrustumCulled;
this.sortObjects = source.sortObjects;
this.boundingBox = source.boundingBox !== null ? source.boundingBox.clone() : null;
this.boundingSphere = source.boundingSphere !== null ? source.boundingSphere.clone() : null;

Expand Down Expand Up @@ -869,30 +929,87 @@ class BatchedMesh extends Mesh {
}

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

if ( visible[ i ] ) {
if ( this.sortObjects ) {

// determine whether the batched geometry is within the frustum
let culled = false;
if ( perObjectFrustumCulled ) {
// get the camera position
_vector.setFromMatrixPosition( camera.matrixWorld );

// get the bounds in camera space
this.getMatrixAt( i, _matrix );
for ( let i = 0, l = visible.length; i < l; i ++ ) {

// get the bounds
this.getBoundingBoxAt( i, _box ).applyMatrix4( _matrix );
if ( visible[ i ] ) {

this.getMatrixAt( i, _matrix );
this.getBoundingSphereAt( i, _sphere ).applyMatrix4( _matrix );
culled = ! _frustum.intersectsBox( _box ) || ! _frustum.intersectsSphere( _sphere );

// determine whether the batched geometry is within the frustum
let culled = false;
if ( perObjectFrustumCulled ) {

// get the bounds in camera space
this.getMatrixAt( i, _matrix );

// get the bounds
this.getBoundingBoxAt( i, _box ).applyMatrix4( _matrix );
culled = ! _frustum.intersectsBox( _box ) || ! _frustum.intersectsSphere( _sphere );

}

if ( ! culled ) {

// get the distance from camera used for sorting
const z = _vector.distanceToSquared( _sphere.center );
_renderList.push( drawRanges[ i ], z );

}

}

if ( ! culled ) {
}

// Sort the draw ranges and prep for rendering
const list = _renderList.list;
list.sort( material.transparent ? sortTransparent : sortOpaque );

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

const item = list[ i ];
multiDrawStarts[ count ] = item.start * bytesPerElement;
multiDrawCounts[ count ] = item.count;
count ++;

}

_renderList.reset();

} else {

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

if ( visible[ i ] ) {

// determine whether the batched geometry is within the frustum
let culled = false;
if ( perObjectFrustumCulled ) {

// get the bounds in camera space
this.getMatrixAt( i, _matrix );

// get the bounds
this.getBoundingBoxAt( i, _box ).applyMatrix4( _matrix );
this.getBoundingSphereAt( i, _sphere ).applyMatrix4( _matrix );
culled = ! _frustum.intersectsBox( _box ) || ! _frustum.intersectsSphere( _sphere );

}

if ( ! culled ) {

const range = drawRanges[ i ];
multiDrawStarts[ count ] = range.start * bytesPerElement;
multiDrawCounts[ count ] = range.count;
count ++;

const range = drawRanges[ i ];
multiDrawStarts[ count ] = range.start * bytesPerElement;
multiDrawCounts[ count ] = range.count;
count ++;
}

}

Expand Down
41 changes: 38 additions & 3 deletions examples/webgl_mesh_batch.html
Expand Up @@ -39,7 +39,7 @@

let stats, gui, guiStatsEl;
let camera, controls, scene, renderer;
let geometries, mesh;
let geometries, mesh, material;
const ids = [];
const matrix = new THREE.Matrix4();

Expand All @@ -62,7 +62,10 @@
const api = {
method: Method.BATCHED,
count: 256,
dynamic: 16
dynamic: 16,

sortObjects: true,
opacity: 1,
};

init();
Expand Down Expand Up @@ -111,7 +114,14 @@

function createMaterial() {

return new THREE.MeshNormalMaterial();
if ( ! material ) {

material = new THREE.MeshNormalMaterial();
material.opacity = 0.1;

}

return material;

}

Expand Down Expand Up @@ -238,6 +248,25 @@
gui.add( api, 'count', 1, MAX_GEOMETRY_COUNT ).step( 1 ).onChange( initMesh );
gui.add( api, 'dynamic', 0, MAX_GEOMETRY_COUNT ).step( 1 );
gui.add( api, 'method', Method ).onChange( initMesh );
gui.add( api, 'opacity', 0, 1 ).onChange( v => {

if ( v < 1 ) {

material.transparent = true;
material.depthWrite = false;

} else {

material.transparent = false;
material.depthWrite = true;

}

material.opacity = v;
material.needsUpdate = true;

} );
gui.add( api, 'sortObjects' );

guiStatsEl = document.createElement( 'li' );
guiStatsEl.classList.add( 'gui-stats' );
Expand Down Expand Up @@ -313,6 +342,12 @@

function render() {

if ( mesh.isBatchedMesh ) {

mesh.sortObjects = api.sortObjects;

}

renderer.render( scene, camera );

}
Expand Down
1 change: 1 addition & 0 deletions src/core/Object3D.js
Expand Up @@ -723,6 +723,7 @@ class Object3D extends EventDispatcher {

object.type = 'BatchedMesh';
object.perObjectFrustumCulled = this.perObjectFrustumCulled;
object.sortObjects = this.sortObjects;

object.drawRanges = this._drawRanges;
object.reservedRanges = this._reservedRanges;
Expand Down

0 comments on commit fec1161

Please sign in to comment.