mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102k stars 35.33k forks source link

BatchedMesh: Provide approach for sharing geometry and transforms between BatchedMesh #27203

Open gkjohnson opened 10 months ago

gkjohnson commented 10 months ago

Description

Right now you can render a number of geometries with a single material using a BatchedMesh but you may want to swap materials of an individual mesh for things like selection, highlighting, etc. Right now that means creating and maintaining a completely different BatchedMesh which is slower and takes more memory. Instead it would be best if two BatchedMeshes could share geometry and potentially transform data so they can both specify different materials and visibility arrays.

Solution

I'm not exactly sure of some of the options here - but one solution is to add a "BatchedBufferGeometry" object that BatchedMesh references and stores all the geometry, draw ranges, etc. The BatchedMesh would still bookkeep the multidraw buffers, etc:

const mesh1 = new BatchedMesh( maxGeometryCount, maxVertexCount, maxIndexCount, material1 );
const index = mesh1.geometry.addGeometry( ... );
mesh1.setMatrixAt( ... );

const mesh2 = new BatchedMesh( mesh1.geometry, material2 );

mesh1.setVisibilityAt( 0, false );
mesh1.setVisibilityAt( 1, true );
scene.add( mesh1, mesh2 );

Alternatives

Instead the end user could just use something like a proxy or wrapper class that forwards all common information between the BatchedMeshes while reimplementing all needed:

const mesh1 = new BatchedMesh( ... );
const mesh2 = new BatchedMeshWrapper( mesh1 );

Or - possibly the better approach - BatchedMesh could support different and arbitrary material properties and textures but this can get complicated. It's possible this would be best done with node materials in the future.

Additional context

User mentioned this need in three-mesh-bvh: https://github.com/gkjohnson/three-mesh-bvh/issues/513

gkjohnson commented 10 months ago

The demo referenced in https://github.com/mrdoob/three.js/issues/27170#issuecomment-1823855592 is relevant to this discussion as it affords different materials across different objects in a single BatchedMesh. It shows highlighting objects with mouseover, as well. It won't fully help with transparent materials, though.

donmccurdy commented 10 months ago

Hm, that is hard. I am hoping we could do this without a new subclass of BufferGeometry... I suppose the hard part is that the book-keeping of the two BatchedMesh classes needs to be shared?

Could we keep plain BufferGeometry objects underneath BatchedMesh, but allow syncing their full state with batchMesh1.copy(batchMesh2)?

gkjohnson commented 10 months ago

In the long term I think a solution that enables multiple different material properties in a single draw call like what I've shown in https://github.com/mrdoob/three.js/issues/27170#issuecomment-1823855592 is most viable option. That won't work if someone want's to render hover or highlight state with completely different shaders - though thats likely much more of a corner case.

Could we keep plain BufferGeometry objects underneath BatchedMesh, but allow syncing their full state with batchMesh1.copy(batchMesh2)?

This an option - it just feels messy and maybe error prone. The geometry, draw ranges, reserved ranges, bounds, active state, etc would have to be synced between the meshes.