VIS (Visibility Graph)
VIS is an ASCII graph structure used extensively by the rendering engine to calculate occlusion culling. It plots mathematical relationships defining which room meshes are visible from any given observer room.
At a Glance
| Property | Value |
|---|---|
| Extension(s) | .vis |
| Magic Signature | None |
| Type | Room Graph |
| Rust Reference | View rakata_formats::Vis in Rustdocs |
File Layout
Plain ASCII, no header, no magic, no terminator. The whole file is a sequence of blocks, one per observer room:
m01aa_08c 4
m01aa_08a
m01aa_08b
m01aa_07a
m01aa_07b
An unindented line names an observer room and how many rooms it can see. The lines that follow are those rooms, one per line, each indented by exactly two spaces. The next unindented line begins the next block. Room names carry no extension and refer to the rooms in the module’s LYT.
Measured across every .vis in chitin.key: the indent is two spaces in every child line without exception, and no file contains a blank line.
Warning
Do not trust the count, and do not assume the graph is symmetric This is the counted case from counted, terminated, or neither, with the twist that the count is not reliable, so the run has to be read as if it were terminated by the indent.
Both look like invariants and neither is one.
The count disagrees with the block in a handful of shipped files, so a reader that seeks forward by the declared number rather than reading until the indent stops will desynchronise and attribute one room’s visibility to another. Read to the end of the indented run; treat the number as a hint.
A shipped file is not a symmetric graph. Hundreds of edges name a room that does not name the source back. The engine makes the relation symmetric itself, at load, by inserting each direction as it reads (see the audit below), so the loaded result is symmetric and the file is not. A reader reproducing engine behaviour has to mirror; a tool round-tripping the file must not, or it will write back edges BioWare never shipped.
One further shape occurs. A single shipped file carries an observer line with no count at all, just the room name, which is worth tolerating rather than rejecting.
Engine Audits & Decompilation
Read from Scene::LoadVisibility at 0x004568d0 in swkotor.exe. Provenance: derived, not attested. The rows below have not been separately re-derived, so they sit on the reverse-engineering queue.
| Pipeline Event | Ghidra Provenance & Engine Behavior |
|---|---|
| Text Loading | Function: Scene::LoadVisibility (0x004568d0)The .vis file is executed purely as raw text. The engine continuously extracts observer and child string pairs by looping AurResGetNextLine() over the file buffer. |
| Silent Forgiveness | Function: Scene::LoadVisibility (0x004568d0)If the parser extracts a room reference (either observer or child) that does not exist in the active area layout (which it verifies via a FindRoom call), the visibility entry is quietly dropped without crashing or generating logs. |
| Bidirectional Application | Function: Scene::SetVisibilityCalling SetVisibility(room_a, room_b, 1) inherently maps both visualization paths. The function inserts room_b into room_a’s visibility list, and immediately mirrors by adding room_a to room_b’s list while executing native deduplication. |
| Write Generation | Function: Scene::SaveVisibilityWhen generating a .vis file natively, the engine relies on an _sscanf block structure mapping to "%s%d" and uniformly pads a dual-space indent onto all child elements beneath observer headers. |