I propose that I add an instance method
List<Spatial> Spatial.descendantMatch(Class<? extends Spatial> spatialSubclass, String nameRegex>()
with convenience wrappers
List<Spatial> Spatial.descendantMatch(Class<? extends Spatial>>()
List<Spatial> Spatial.descendantMatch(String>()
Methods will alway return a list, possibly a 0-element list, so it will always be safe to use loops like:
for (Spatial s : scene.descendantMatch(".+Bush") {...}
The purpose is to make it easy to perform operations on a subset of scene spatials based on Spatial type and/or based on game-specific naming conventions. It's a Spatial method instead of a Node method just so it can be used as easily as possible by methods which work on Spatials, e.g. a custom boolean isTerrainColliding(Spatial).
Snippets to justify usefulness:
for (Spatial s : scene.descendantMatch(CustomBush.class)) s.removeFromParent();
// Removes all custom Bush Spatials from scene
for (Spatial s : subScene.descendantMatch(Node.class, "avatar.+")) s.updateRenderState();
// Updates render state for all Nodes in specified subgraph with name starting with "avatar"
for (Spatial s : scene.descendantMatch(Bone.class)) {
Bone skel = (Bone) s;
if (skel.getControllerCount() < 1) continue;
...
// Do some action to all animation controllers, e.g. stop all active animations