Nav Mesh Generation Ideas - noodlecollie/afterburner-game GitHub Wiki

This research is gathered from the leaked Source Engine code from April 2020.

How the Source Engine Does Nav Generation

The interface to the Source Engine navigation mesh is defined within CNavMesh (tf2_src/game/server/nav_mesh.h). When generating, this acts as a big state machine that performs a certain amount of work on each frame.

When generating a nav mesh from scratch (not incrementally), the function call stack looks something like the following. This gives us a cross-section of the multitude of processing that is required in order to build the entire mesh.

Note that processing happens across frames, and is time-limited per frame. The function call tree stack below is simplified to remove the asynchronous details.

// 1. Beginning generation

CNavMesh::BeginGeneration()
    CNavMesh::m_generationState = SAMPLE_WALKABLE_SPACE
    CNavMesh::DestroyNavigationMesh()
    CNavMesh::BuildLadders()
    CNavMesh::AddWalkableSeeds()
// 2. Sampling walkable space

CNavMesh::UpdateGeneration()
    CNavMesh::SampleStep()
        CNavMesh::GetNextWalkableSeedNode()

        for each dir in (north, east, south, west):
            pos = CNavMesh::m_currentNode->pos + (dir * GenerationStepSize)
            CNavMesh::m_currentNode->MarkAsVisited(dir)

            CNavMesh::TraceAdjacentNode()
                UTIL_TraceHull()

            if TraceAdjacentNode() not successful:
                re-trace with manual height adjustments etc.
                if still not successful, return

            CNavMesh::AddNode()

    CNavMesh::m_generationState = CREATE_AREAS_FROM_SAMPLES
// 3. Creating areas from samples

CNavMesh::UpdateGeneration()
    CNavMesh::CreateNavAreasFromNodes()
        CNavMesh::TestArea()
        CNavMesh::BuildArea()
            CNavMesh::CreateArea()
            CNavArea::Build()

        CNavMesh::AllocateGrid()

        for each area built earlier:
            CNavMesh::AddNavArea(area)

        CNavMesh::ConnectGeneratedAreas()
            CNavMesh::StitchGeneratedAreas()

        CNavMesh::MarkPlayerClipAreas() (only applicable in L4D)
        CNavMesh::MarkJumpAreas()
        CNavMesh::MergeGeneratedAreas()
        CNavMesh::SplitAreasUnderOverhangs()
        CNavMesh::SquareUpAreas()

        CNavMesh::MarkStairAreas()
            CNavArea::TestStairs()
                IsStairs()

        CNavMesh::StichAndRemoveJumpAreas()
            for each jump area:
                JumpConnector::operator ()()

        CNavMesh::HandleObstacleTopAreas()
            CNavMesh::RaiseAreasWithInternalObstacles()
            CNavMesh::CreateObstacleTopAreas()
                CNavMesh::CreateObstacleTopAreaIfNecessary()
            CNavMesh::RemoveOverlappingObstacleTopAreas()

        CNavMesh::FixUpGeneratedAreas()
            CNavMesh::FixCornerOnCornerAreas();
            CNavMesh::FixConnections();

        for each ladder:
            CNavLadder::ConnectGeneratedLadder()

    CNavMesh::DestroyHidingSpots()
    CNavMesh::m_generationState = FIND_HIDING_SPOTS
// 4. Finding hiding spots

CNavMesh::UpdateGeneration()
    for each nav area:
        CNavArea::ComputeHidingSpots()

    CNavMesh::m_generationState = FIND_ENCOUNTER_SPOTS
// 5. Finding encounter spots

CNavMesh::UpdateGeneration()
    for each nav area:
        CNavArea::ComputeSpotEncounters()
            CNavArea::AddSpotEncounters()

    CNavMesh::m_generationState = FIND_SNIPER_SPOTS
// 6. Finding sniper spots

CNavMesh::UpdateGeneration()
    for each nav area:
        CNavArea::ComputeSniperSpots()
            CNavArea::ClassifySniperSpot()

    CNavMesh::m_generationState = COMPUTE_MESH_VISIBILITY

    CNavMesh::BeginVisibilityComputations()
        for each nav area:
            CNavArea::ResetPotentiallyVisibleAreas()
// 7. Computing mesh visibility

CNavMesh::UpdateGeneration()
    for each nav area:
        CNavArea::ComputeVisibilityToMesh()
            for each nav area in nav_max_view_distance radius:
                NavAreaCollector::operator ()()

            CNavArea::SetupPVS()

            call CNavArea::ComputeVisToArea in parallel for each area in radius:
                CNavArea::ComputeVisibility()

    CNavMesh::EndVisibilityComputations()
    CNavMesh::::m_generationState = FIND_EARLIEST_OCCUPY_TIMES
// 8a. Finding earliest occupy times

CNavMesh::UpdateGeneration()
    for each nav area:
        CNavMesh::ComputeEarliestOccupyTimes() // (only applicable in CS)
            NavAreaTravelDistance()

    if step for finding light intensity is enabled:
        CNavMesh::m_generationState = FIND_LIGHT_INTENSITY
        CNavArea::MakeNewMarker()
    else:
        CNavMesh::m_generationState = CUSTOM
// 8b. Finding light intensity

CNavMesh::UpdateGeneration()
    for each nav area:
        CNavArea::ComputeLighting()

    CNavMesh::m_generationState = CUSTOM
// 9. Custom game-specific analysis

CNavMesh::UpdateGeneration()
    CNavMesh::BeginCustomAnalysis()

    for each nav area:
        CNavArea::CustomAnalysis()

    CNavMesh::PostCustomAnalysis()
    CNavMesh::EndCustomAnalysis()
    CNavMesh::m_generationState = SAVE_NAV_MESH
// 10. Saving mesh

CNavMesh::UpdateGeneration()
    CNavMesh::ClearWalkableSeeds()
    CNavMesh::HideAnalysisProgress()
    CNavMesh::Save()
        ??? No implementation available