---
title: "Inspect navigation-mesh build stages in C#"
url: https://recursiveai.org/onescope/cookbook/inspect-navmesh-build-stages/
description: "Inspect navigation-mesh build stages in C# with OneScope and OnePath. Discover stage axes and view intermediate work without moving the replay cursor."
version: "1.0.0"
---

# Inspect navigation-mesh build stages in C#

**OneScope** is a free, open-source viewer for simulation recordings, with a desktop window or command-line control, from Recursive AI. MIT or Apache-2.0.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneScope.Sdk) · [GitHub](https://github.com/recursive-ai-oss/onescope-csharp) · [Docs](https://recursiveai.org/onescope/docs/) · [Download example (.zip)](https://recursiveai.org/onescope/cookbook/inspect-navmesh-build-stages/inspect-navmesh-build-stages.zip)

OnePath’s navigation-mesh build can be viewed at each of its seven stages, so you can look at the partial result.

[OnePath](https://recursiveai.org/onepath/) is Recursive AI’s pathfinding and navigation-mesh library; OnePath.Scope plugs it into OneScope.

```shell
dotnet add package RecursiveAI.OneScope.Sdk
dotnet add package RecursiveAI.OnePath.Scope
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

A recording has a time axis for moving between steps; this build also has a separate stage axis for stepping through the work inside a step.

Stage numbers are 1: voxels, 2: filtered spans, 3: compact spans, 4: regions, 5: contours, 6: polygons and 7: detail. `Seek("stage", 2)` selects filtered spans; position 0 returns to the time view.

Run the snippet from the unzipped example folder: it needs the included `floor.opbi`. This input is a flat 10 by 10 square made of two triangles; the full program shows all its vertices and indices.

```csharp
using OneScope.Sdk;
using RecursiveAI.OnePath.Scope;

var source = new OnePathModule().Open(
    File.ReadAllBytes("floor.opbi"), "floor.opbi");
var history = new History(source);
history.Seek("stage", 2);
var entities = new List<EntityInfo>();
history.View.CollectEntities(entities);
var stage = entities.Single(e => e.Name == "2. Filtered spans");
var properties = new PropertyList();
if (!history.View.Inspect(stage.Id, properties))
    throw new InvalidOperationException("Stage not found.");
Console.WriteLine(stage.Name);
Console.WriteLine(properties.Rows.Single(
    p => p.Name == "summary").Value.GetText());
```

```text
2. Filtered spans
1156 spans, 1024 walkable, after the low-obstacle, ledge and low-height filters; grid 34 x 34 x 12
```

Unzip [the example](https://recursiveai.org/onescope/cookbook/inspect-navmesh-build-stages/inspect-navmesh-build-stages.zip), then `dotnet run`.

## The full program

The example creates the input file, discovers its axes, reads each selected stage’s summary from `History.View`, and checks that the time state stays unchanged.

```csharp
using System.Buffers;
using OneScope.Sdk;
using RecursiveAI.OnePath.Scope;

// A flat 10 by 10 square, two upward-facing triangles.
var bytes = new ArrayBufferWriter<byte>();
BuildInput.Write([0,0,0, 10,0,0, 10,0,10, 0,0,10],
    [0,2,1, 0,3,2], null, bytes);
File.WriteAllBytes("floor.opbi", bytes.WrittenSpan);
var source = new OnePathModule().Open(bytes.WrittenMemory, "floor.opbi");
var history = new History(source);
long time = history.CurrentStep;
ulong hash = history.StateHash;
foreach (AxisInfo axis in source.Info.Axes)
{
    Console.WriteLine($"Axis: {axis.Id}, positions: {axis.PositionCount}");
    if (axis.Id == AxisIds.Time) continue;
    foreach (int position in new[] { 1, axis.PositionCount, 2, 0 })
    {
        history.Seek(axis.Id, position);
        bool unchanged = history.CurrentStep == time && history.StateHash == hash;
        if (!unchanged) throw new InvalidOperationException("Time state changed.");
        Console.WriteLine($"View: {history.ViewAxis}/{history.ViewPosition}, time unchanged: {unchanged}");
        if (position == 0) continue;
        var entities = new List<EntityInfo>();
        history.View.CollectEntities(entities);
        var stage = entities.Single(e => e.Kind == "stage" &&
            e.Name.StartsWith($"{position}. ", StringComparison.Ordinal));
        var properties = new PropertyList();
        if (!history.View.Inspect(stage.Id, properties))
            throw new InvalidOperationException("Stage not found.");
        Console.WriteLine(stage.Name);
        Console.WriteLine(properties.Rows.Single(
            p => p.Name == "summary").Value.GetText());
    }
}
if (source.Info.Axes.All(axis => axis.Id == AxisIds.Time))
    Console.WriteLine("This source has only time; no intermediate views.");
```

`dotnet run` prints:

```text
Axis: time, positions: 0
Axis: stage, positions: 7
View: stage/1, time unchanged: True
1. Voxels
1156 spans, 1156 walkable, after rasterizing; grid 34 x 34 x 12
View: stage/7, time unchanged: True
7. Detail
2 detail triangles, 0 extra vertices
View: stage/2, time unchanged: True
2. Filtered spans
1156 spans, 1024 walkable, after the low-obstacle, ledge and low-height filters; grid 34 x 34 x 12
View: time/0, time unchanged: True
```

An unknown axis throws `ArgumentException`; an out-of-range position throws `ArgumentOutOfRangeException`, and a positive axis seek at the recording’s end throws `InvalidOperationException`.

## Related recipes

- [View a physics replay](https://recursiveai.org/onescope/cookbook/view-physics-replay/)
- [Limit replay keyframe memory](https://recursiveai.org/onescope/cookbook/bound-replay-keyframe-memory/)

## The short answer

Use a source that declares a non-time axis, then call `History.Seek(axisId, position)`. Read the intermediate state from `History.View`, not `History.World`. The time cursor and `StateHash` still describe the main replay state before the next step. The released OnePath module declares a `stage` axis for navigation-mesh build inputs. The full program discovers it through `source.Info.Axes`; sources that expose only time print a message instead.

## How do you inspect navigation-mesh build stages?

A [OnePath build input](https://github.com/recursive-ai-oss/onepath-csharp/blob/9da11631e6c0d85abbb302e51f0511b74fa728fd/src/OnePath.Scope/BuildInput.cs) stores triangle coordinates, indices and build settings. The full program creates those bytes directly. Passing null settings selects `NavSettings.Humanoid`. No crowd replay or external geometry file is needed for this square.

The module declares seven stage positions: voxels, filtered spans, compact spans, regions, contours, polygons and detail. These are the module’s stage outputs, in that order, as documented in its [published package](https://www.nuget.org/packages/RecursiveAI.OnePath.Scope/1.0.0). This build recording starts at time step 0 and ends at step 1. Leave the time cursor at 0 while selecting stages: after seeking time step 1, a positive stage seek throws `InvalidOperationException` because no next step remains to inspect.

Position 0 clears the intermediate view and returns to the time view. Positive positions run partway through the next step on a scratch world restored from the cursor’s snapshot. Read or draw `history.View` to inspect that scratch world. Reading `StateHash` alone will not tell you which build stage is visible: it deliberately remains the main replay world’s hash.

The full program visits stages 1, 7 and 2, then clears the view. It checks that both the main cursor and main hash remain unchanged after each visit. Returning to an earlier stage does not undo the recording or create a branch. The [history contract](https://recursiveai.org/onescope/docs/history/) says the same step and axis position give the same hash by any path.

For opening navigation recordings in the desktop window, see [Visualize a navmesh and crowds](https://recursiveai.org/onescope/cookbook/visualize-navmesh-and-crowds/).
