Implementation
@override
OnRender? get onBeforeRender =>({
WebGLRenderer? renderer,
RenderTarget? renderTarget,
Object3D? mesh,
Scene? scene,
Camera? camera,
BufferGeometry? geometry,
Material? material,
Map<String, dynamic>? group
}){
// if visibility has not changed and frustum culling and object sorting is not required
// then skip iterating over all items
if ( ! this._visibilityChanged && ! this.perObjectFrustumCulled && ! this.sortObjects ) {
return null;
}
// the indexed version of the multi draw function requires specifying the start
// offset in bytes.
final index = geometry!.getIndex();
final bytesPerElement = index == null ? 1 : index.array.BYTES_PER_ELEMENT;
final instanceInfo = _instanceInfo;
final multiDrawStarts = this.multiDrawStarts;
final multiDrawCounts = this.multiDrawCounts;
final geometryInfoList = _geometryInfo;
final perObjectFrustumCulled = this.perObjectFrustumCulled;
final indirectTexture = this.indirectTexture;
final indirectArray = indirectTexture?.image.data;
// prepare the frustum in the local frame
if ( perObjectFrustumCulled ) {
_matrix.multiply2( camera!.projectionMatrix, camera.matrixWorldInverse )
.multiply( this.matrixWorld );
_frustum.setFromMatrix(
_matrix,
renderer!.coordinateSystem
);
}
int multiDrawCount = 0;
if ( this.sortObjects ) {
// get the camera position in the local frame
_matrix.setFrom( this.matrixWorld ).invert();
_vector.setFromMatrixPosition( camera!.matrixWorld ).applyMatrix4( _matrix );
_forward.setValues( 0, 0, - 1 ).transformDirection( camera.matrixWorld ).transformDirection( _matrix );
for (int i = 0, l = instanceInfo.length; i < l; i ++ ) {
if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) {
final geometryId = instanceInfo[ i ].geometryIndex;
// get the bounds in world space
this.getMatrixAt( i, _matrix );
this.getBoundingSphereAt( geometryId, _sphere ).applyMatrix4( _matrix );
// determine whether the batched geometry is within the frustum
bool culled = false;
if ( perObjectFrustumCulled ) {
culled = ! _frustum.intersectsSphere( _sphere );
}
if ( ! culled ) {
// get the distance from camera used for sorting
final geometryInfo = geometryInfoList[ geometryId ];
final z = _temp.sub2( _sphere.center, _vector ).dot( _forward );
_renderList.push( geometryInfo.start, geometryInfo.count, z.toInt(), i );
}
}
}
// Sort the draw ranges and prep for rendering
final list = _renderList.list;
final customSort = this.customSort;
if ( customSort == null ) {
list.sort( material?.transparent != null? sortTransparent : sortOpaque );
} else {
customSort.call( this, list, camera );
}
for (int i = 0, l = list.length; i < l; i ++ ) {
final item = list[ i ];
multiDrawStarts[ multiDrawCount ] = item.start * bytesPerElement;
multiDrawCounts[ multiDrawCount ] = item.count;
indirectArray[ multiDrawCount ] = item.index;
multiDrawCount ++;
}
_renderList.reset();
} else {
for ( int i = 0, l = instanceInfo.length; i < l; i ++ ) {
if ( instanceInfo[ i ].visible && instanceInfo[ i ].active ) {
final geometryId = instanceInfo[ i ].geometryIndex;
// determine whether the batched geometry is within the frustum
bool culled = false;
if ( perObjectFrustumCulled ) {
// get the bounds in world space
this.getMatrixAt( i, _matrix );
this.getBoundingSphereAt( geometryId, _sphere ).applyMatrix4( _matrix );
culled = ! _frustum.intersectsSphere( _sphere );
}
if ( ! culled ) {
final geometryInfo = geometryInfoList[ geometryId ];
multiDrawStarts[ multiDrawCount ] = geometryInfo.start * bytesPerElement;
multiDrawCounts[ multiDrawCount ] = geometryInfo.count;
indirectArray[ multiDrawCount ] = i;
multiDrawCount ++;
}
}
}
}
indirectTexture?.needsUpdate = true;
this.multiDrawCount = multiDrawCount;
this._visibilityChanged = false;
return null;
};