I’ve digged a little deeper since i’ve implemented it in my engine now. Basically a very simple approach is to generate a second mesh out of the original as i described, you need to store each vertex and the shared neighbours to create a list of all the triangles centroids and the 2 vertices a neighbour face shares for the portals (but i guess patrol generates this for you)
For the second mesh you filter out the relevant faces, like simply the face normal with a slope tolerance for floor faces, then merge close faces like those from stairs to get a closed ramp. Smaller faces can be merged in this process too, but this isn’t quite what Recast for example does. The topology may not be optimal, and it doesn’t use non-connected surfaces like a crate on a large 2 triangle quad for a floor.
The technique used by Recast is more complex, but i’ll implement another i made for procedurally tiled spline extrusion by outlining the filtered faces and creating a new topology and inserting scene objects contours. It should be generated realtime at reasonable performance and low memory cost instead a static external one that won’t adapt to changes.