Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BatchedMesh: Add support for per-object opaque and transparent sorting #27168

Merged
merged 6 commits into from
Nov 17, 2023
Merged
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
117 changes: 117 additions & 0 deletions examples/jsm/objects/BatchedMesh.js
Original file line number Diff line number Diff line change
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 @@ -29,6 +86,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 @@ -77,6 +135,7 @@ class BatchedMesh extends Mesh {

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

Expand Down Expand Up @@ -816,6 +875,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 @@ -886,6 +946,61 @@ class BatchedMesh extends Mesh {
}

let count = 0;

if ( this.sortObjects ) {

// get the camera position
_vector.setFromMatrixPosition( camera.matrixWorld );

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

if ( visible[ i ] ) {

this.getMatrixAt( i, _matrix );
this.getBoundingSphereAt( i, _sphere ).applyMatrix4( _matrix );

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

}

}

}

// 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 ] ) {
Expand Down Expand Up @@ -917,6 +1032,8 @@ class BatchedMesh extends Mesh {

}

}

this._multiDrawCount = count;

// @TODO: Implement geometry sorting for transparent and opaque materials
Expand Down
41 changes: 38 additions & 3 deletions examples/webgl_mesh_batch.html
Original file line number Diff line number Diff line change
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;
gkjohnson marked this conversation as resolved.
Show resolved Hide resolved

} );
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
Original file line number Diff line number Diff line change
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