One method i've been using for static geometry, (that is quite literally geometry that doesn't and never will move like walls and floors)
is to make 1 big GeometryArray to hold the data rather than using shared Geometry.
This annuls the frustum culler and is hence a bad idea.
I don't know, what this has to do with shared geometry. Well, I also don't know your dungeon

.
Let's say, you have 30000 objects, of which some are walls and others are torches or furniture, etc. Then you should not model each quad of the walls as its own Geometry and Shape. It's always a trade off. Create each furniture as its own shape and pieces of the walls as separate shapes (not each quad, but larger pieces). Limit the size of each object to 4096 vertices. This is the range, where a vertex buffer operates optimally. It depends on the size and detail level, how many vertices you should put into one Geometry.
The Frustum Culler must have a chance to do its job. 550 FPS may sound like a lot, but I am sure, you can get much more, if you carefully group quads and give the Frustum Culler the chance to sort out large parts of the world at an early stage in the rendering process.
Also use sub groups to group nearby objects, that are likely to be rendered all or none, but don't use 50 levels of grouping. Groups and sub groups are a scenegraph's strength.
I also suggest to use triangles instead of quads, which the GL can do a lot faster. And use strips instead of plain triangles. This saves data and is faster.
Use display lists, where ever possible. For the static parts of a world, this will always be possible. If using display lists, the geometry type doesn't even matter.
Don't put the static objects into TransformGroups, but use StaticTransform to statically move them into place.
If you have some tunnels in your dungeon, you could also consider to use a Switch group and decide on application level, which large groups of objects are seeable from the current position. This will gain a lot of FPS.
These are the simple parts of scenegraph optimization, though you need some careful adjustments to make it optimal. But don't cheat the scenegraph and deny it doing its job, by using one big geometry and letting the graphics card render everything always.
The problem with TransformGroup is, that they may be optimized and don't calculate the inner matrices every single frame, if they remain static, but the metrices need to be sent to the GL, which can mean a lot, if there are a lot of them, which are not sorted out early.
Good luck with optimization. And please report back, what it gave you.
Marvin