Documentation on .streamingsector files
Last documented update: January 27, 2024 by
StreamingSectors are the files that define the world in Cyberpunk, they tell the engine what goes where, and define everything from the meshes you see and how you can interact with them, to the locations of lights, sounds and fx. They call up the NPCs, they define the minimap.
The fundamental bits of the structure are 2 main lists, the nodes, and the nodeData. The nodes define the assets that make up the sector, and the nodeData tells the engine where to put them. Theres a nodeRef list as well, which seem to be the link that quests use to tie in to triggers etc via the .scene files. Theres also some bits that define variants, that seem to be how sectors change after missions, not worked those out yet.
Streaming Sectors contain the world's data, holding a node tree that includes all its models and entities.
Other than the world environment, streaming sector files can also define
navigation
sound
collision
illumination
as there are several types.
The world is broken up into a grid, with several sizes of squares available (bit like graph paper with major and minor grid lines). The size of the grid in use is dependent on the Level of Detail (LOD) of the sector file your looking at, which is the last digit of the filename.
Chunk sizes are as below.
For every location, there can be multiple overlapping levels of LOD sectors with progressively more detail as you go down the levels.
For instance, Lizzies bar is located at approximately -1200, 1562, 22:
Filenames are structured as sectortype_X_Y_Z.streamingsector
. If you preview a sector in wkit, the axes are shown as Z=-Y and Y=Z.
I've left the detail of the process below for record, but if you just want a list of sectors I've created a python script to find the ones that a set of co-ordinates are in. You can find it HERE. Simply enter the co-ordinates in the player_loc dict in block 3 then run all blocks. You'll be given 2 lists containing of all the interior and exterior sectors which contain the co-ords within their bounding boxes. The list also shows the distance from the centre of the sectors to the co-ords given.
From those co-ordinates we can calculate the sector files for interior/exterior sectors by dividing by the grid size for the LOD and rounding. (ie 1200/32=38 etc)
At the level 0 files the whole building isn't covered by 1 sector, so you end up needing 4, conversely the exterior level 2 is several city blocks.
For example, the loft apartment uses these files:
interior_-24_-16_1_1.streamingsector
: inner walls, floors, collision
interior_-48_-31_2_0.streamingsector
: interior decoration, clutter
Note that the numbers are simply duplicated and rounded down. No, I don't know how you round down 2*16 to 31 either.
Some locations are partially defined in quest sectors, where parts of the location are locked away behind story triggers (for example half of V's apartment, as a lot of decoration will only be shown after completing quests). The exact process isn't fully understood as of May 2023.
A steamingsector can split its nodes into ranges called variants. A variant is used to display/enable an amount of nodes in a specific situation (like during or after a quest). All sectors and their variants need to be declared somewhere; this place is the streamingblock.
We will see how they are declared and how they are linked together.
The game contains only 3 streamingblocks that gather more than 26300 sectors.
We will focus on the main one and take a look at the first sector inside (exterior_-18_3_-12_0
).
When creating a mod with sectors, you will need to create your own block that will list your new sectors.
This sector has 10 variants, in this case, each one is related to a quest state.
numNodeRanges
indicate the number of node ranges the sector contains; it always corresponds to the number of variant + sector's default range (so here 10 variants + 1 default).
When opening a variant definition, we can find the range index (to find the right range inside the sector file). rangeIndex
cannot be 0, 0 is used as sector's default range.
Variant's name is only used to identify the variant within the streamingblock.
enableDyDefault
is self-explanatory; it indicates whether it should be enabled by default.
The link between quests and sectors' variants is not well-researched as of Jan 2024 - if you know anything more about this, please get in touch via Discord or update the wiki!
The sector file uses 2 arrays to define ranges: variantIndices
and variantNodes
.
variantNodes
contains as many empty arrays as variants (in this case, 10).
variantIndices
contains as many values as ranges needed (10 variants + 1 default range)
A variant indice indicates the range starting nodeData index. This range goes from the index specified to the start of the following range. If we look at variant indice #7, the range starts at node index 14 (included) and ends at node index 46 (excluded).
A sector always has a default range (variant indice 0); this default range indicates which nodes must always be visible/enable, whatever are the variants state. In our example, the default range goes from 0 to 1, so only the first node is always enabled.
Here is an overview of files relations:
LOD | Interior | Exterior |
---|---|---|
LOD | Interior | Exterior |
---|---|---|
0
32
64
1
64
128
2
128
256?
0
-38_49_0
-19_24_0
1
-19_24_0
-9_12_0
2
-9_12_0
-5_6_0
Published: Mar 02 2024 by Last documented update: Mar 02 2024 by
This page explains how shadows are configured for nodes in streamingsectors.
To learn how components (equipment) casts shadows, check Meshes, Shadows, and Shadow Meshes
The shadow properties of the environment are configured in the properties of each node inside the .streamingsector
file:
Shadows are only cast as long as a mesh is visible.
A global shadow will not render small details — these will only be included if the castLocalShadows property is active.
Published: Mar 02 2024 by Last documented update: Mar 02 2024 by
This page explains how the level of detail is handled in environment files.
To learn how components (equipment) casts shadows, check Meshes, Shadows, and Shadow Meshes
Many world objects are automatically hidden if you move away from them, freeing the engine resources that had been used to render them.
Hiding an object will also hide its shadows and occluders.
The distance at which this happens is controlled by the forceAutoHideDistance
property of each world node:
This probably corresponds directly to the nearAutoHideDistance
in the proxy mesh ndoes below?
There is a specialized node type for world proxy meshes: the worldGenericProxyMeshNode
. (Which raises the question if there are specialized / extended worldXXProxyMeshNodes?)