How would you go about Blade & Sorcery -style climbing?

Godot Version

Ideally 4.7, but this isn't a version-specific ask.

Question

Blade & Sorcery’s VR climbing mechanic is great: https://youtu.be/nP797pQuo10?t=225

You can grab any static collider’s corner and, if it’s a sufficiently acute angle, it works like a grab point in more traditional VR climbing games.

That’s it. Brilliantly simple, and it’s really immersive. And, once you have the system in place, it’s really easy to build levels for!

How would yall go about implementing something like this? Ultimately you need to get the normals of all collision faces intersecting with the player’s hand, but how do you get those normals? I’ve been mulling it over, and I have a few ideas.

  1. At startup, split every static body into a bunch of smaller static bodies, one for each mesh face. Then you have each newly-mint static body emit a signal to a handler singleton when the player’s hand collides with them. The singleton tracks which faces are being grabbed, and if two faces have the right angle between their normals, you know the player should be climbing.
    1. The obvious optimization here would be turn the above startup script into a tool that I would run on the maps before shipping.
    2. The big downside here is the node cost. If you’ve got moderately-detailed geometry, you’re adding potentially many thousands of nodes to your scene which don’t feel like they should be necessary.
  2. As a similar idea to the above (with similar issues): at startup, open up every mesh in something like an ArrayMesh and iterate over the faces. If two neighboring faces’ normals are at the right angle, instantiate a long, thin, cylindrical Area3D along the connecting edge. If the player’s hand intersects with any of those Area3Ds, you’re climbing.
  3. get_normal() seems like the obvious way forward, but I don’t think it would actually work out-of-the-box. You need to get the normal for every colliding face of the static mesh, not just the first point of contact between the hand and the level geometry.
  4. Shapecasting also looks promising at first, but I don’t think it would work for this. It also only provides a single normal, when we need the normal of every face of the mesh which intersects with the hand collider.
  5. You could surround the player’s hand with a swarm of Area3Ds, and detect which ones are intersecting with a static body.
    1. For downsides:
      1. You’d need to do more math here to figure out what the face normals are.
      2. You might run into performance issues with having a swarm of ~100 Area3Ds on each hand at all times.
      3. Small static bodies (eg. the rung of a ladder) might not collide with any of the swarm Area3Ds, and thus no climbing would be initiated.

This feels like it must be a more solvable problem. I think that #2 is the best solution I’ve come up with, but I really feel like I’m overengineering this.

O wise oracles, what do you think?