I wrote some code to perform selection queries, similar to document.querySelectorAll, on an Object3D and its children.
How to use it
scene.querySelectorAll('Mesh'); // Selects all the meshes in the scene.
scene.querySelectorAll('.even'); // Selects all Object3D that have the 'even' tag.
scene.querySelectorAll('[name=box]'); // Selects all Object3D that have 'box' as their name.
scene.querySelectorAll('[name*=box]'); // Selects all Object3D that have 'box' anywhere in their name.
scene.querySelectorAll('[name^=box]'); // Selects all Object3D that have a name starting with 'box'.
scene.querySelectorAll('[name$=box]'); // Selects all Object3D that have a name ending with 'box'.
scene.querySelectorAll('Mesh.even'); // Selects meshes with both 'Mesh' type and 'even' tag.
scene.querySelectorAll('Group .even'); // Selects all Object3D with 'even' tag that are children of a 'Group'.
scene.querySelectorAll('Group > .even'); // Selects all direct children with 'even' tag under a 'Group'.
scene.querySelectorAll('Mesh, SkinnedMesh'); // Selects all meshes and skinned meshes in the scene.
Comparison
for (const obj of scene.querySelectorAll('Mesh[name*=box]')) {
obj.castShadow = true;
}
I currently added it in my three.ez library (if you use three.js vanilla I suggest you take a look at it), but I would like to release a package soon for anyone who wants to use it.
I just have to decide whether I will patch the Object3D to be able to use it as in three.ez, or whether there will simply be a querySelectorAll(target, query) function.
How to add tags
In three.ez each Object3D has a property tags of type Set already instantiated.
obj.tags.add('even').add('customTag');
Performance
I tried to make it as fast as possible, all 3D objects are iterated once.
Obviously the query has to be parsed and then executed on all objects.
For queries like scene.querySelectorAll('.even'), where are tags stored? I’m guessing each object3D would have a tags property that’s a string array?
Can you write more comples queries such as scene.querySelectorAll('Group.house.green > Mesh[name=window].glass.broken')?
In terms of suggestions, the obvious thing to do is extend it to support other CSS selectors / pseudoselectors so you could do scene.querySelectorAll(':not(SkinnedMesh)'), or scene.querySelectorAll('Mesh[material=MeshBasicMaterial]') etc
Since you’re replicating CSS queries and document.querySelector syntax, I would expect the syntax to match CSS syntax. scene.querySelectorAll(':not(SkinnedMesh)') has my preference, I don’t think scene.querySelectorAll('!SkinnedMesh') is friendlier towards JS devs as it’s not what most people will be used to writing. If the API uses the same name as the DOM API and the rest of its syntax is CSS like, switching to a different syntax for a single use case will create unecessary confusion.