The DS3 Codex
Chapter II

Levels

A DS3 map isn't one mesh. It's a layout file that places three separate kinds of geometry, plus the enemies, objects, regions and baked light that bring it to life. The example throughout is m40_00_00_00: the Cemetery of Ash, Iudex Gundyr's courtyard and Firelink Shrine.

The Cemetery of Ash, where the unkindled rise from their graves. Beyond it, the shrine where the fire still lingers, held together by four thousand placed stones.Codex · m40_00_00_00

In short
  • The MSB is the scene graph: model definitions, parts (instances), regions (points and volumes) and events.
  • What you see (FLVER map pieces) and what you stand on (Havok collision meshes) are made separately and never derived from each other. A third layer, the navmesh, is for AI.
  • Collision parts are rooms. Standing on one decides what's drawn (draw groups), the reverb, the area name and which neighbouring map stays loaded.
  • Every collision triangle has a material: stone, wood, water ankle-deep or poison swamp. It drives footsteps, splashes, sliding and wading.
Firelink Shrine on its cliff above the sea of clouds
Firelink Shrine on its cliff, rendered by our engine from the imported m40 (fly camera). The tower, the cliff, the cloud sea and the mountains are all map pieces placed by the MSB; the mountains are an unlit band ~9 km out.

1Three layers and a layout

LayerFileFormatUsed forPlaced by
Layoutmap/mapstudio/m40_00_00_00.msbMSBPlaces everything—
Visualm40_00_00_00_NNNNNN.mapbnd (337 in m40)BND4 → FLVER2Rendering onlyEach part's position, rotation, scale
Collision, highh40_00_00_00.hkxbhd/.hkxbdtBXF4 → Havok 2014 packfilesCharactersIdentity: baked into map space
Collision, lowl40_00_00_00.hkxbhdthe same, ~1/7 the trianglesOther queries guessIdentity
Navmeshm40_00_00_00.nvmhktbnd + .nvahkaiNavMeshAI pathfinding—

The scale of it, for High Wall of Lothric: the high collision has 1,363,951 triangles, the low 184,265, and the visual pieces are thousands of separate models data. Firelink's m40 places 4,057 map pieces of 394 models, with 1.26 M collision triangles.

2The MSB: the level's scene graph

An MSB (v1, 64-bit offsets, UTF-16 names) is a chain of tables data:

TableHolds
MODEL_PARAM_STModel definitions: map pieces, objects, collisions, enemies, the player
EVENT_PARAM_STTreasure, generators, object actions (doors, levers), platoons
POINT_PARAM_STRegions: spawn points, sound regions, effect regions, wind, light-probe points and boxes, trigger areas
PARTS_PARAM_STInstances: map pieces, objects, enemies, collisions, connect-collisions, player starts

Every part shares one header:

name, type, modelIndex, sibPath,
position (3f), rotation (3f, Euler degrees), scale (3f),
mapStudioLayer, drawGroups[8], dispGroups[8], backreadGroups[8],
entityData (EntityID, …, 8 render flags), typeData (per part type), [gparam], [sceneGparam]

The gparam block names the part's light set, fog, light scattering and env map (Renderer). The entity data ends in eight render flags: casts the sun's shadow, receives it, drawn by the reflection camera, drawn only by it, and more.

Draw groups: occlusion by where you stand

DS3 doesn't find what's visible with a general visibility system. The level designers wrote it down. Draw, display and back-read groups are 256-bit masks: standing on a collision part makes its display groups active, and a part is drawn when its draw groups share a bit with them. Back-read groups keep things loaded data. In m40, depending on the floor, between 3 M and 15 M of the 24.7 M placed LOD0 triangles are visible.

Groups

17 of m40's 45 collision parts show no groups (special-purpose ones). 7 map pieces have no draw groups, among them the great dead tree right of the starting grave. A game screenshot shows DS3 draws them anyway.

Groups

The same masks, in the .col (show groups per body) and .scene (groups per instance). Standing on a part with no groups keeps what was shown guess; ungrouped pieces always draw. At the spawn the groups took the frame from 8.2 to 6.3 M triangles.

Faithful

DS3's own groups from the MSB, with the rule as the map editors (Smithbox) describe it, checked against a game screenshot.

3Collision parts are rooms

A collision part carries gameplay data about a region of floor, not just a mesh reference:

FieldMeaning
HitFilterIDWhich collision layer: 8 standard, 9 camera only, 11 enemies only, 13 fall-death camera, 15 kill, 22 a hull in front of the walls
SoundSpaceTypeThe reverb while you stand here
ReflectPlaneHeightThe water's reflection plane (−62.254 m on the stream)
MapNameIDThe area name shown on screen
DisableBonfireEntityIDA bonfire you can't use while here (boss arenas)
PlayRegionID, LockCamID1/2, MapVisTypeMultiplayer region, camera override, darkness
draw / disp groupsWhat becomes visible when you stand on it

ConnectCollision parts say that standing on this piece keeps a neighbouring map loaded and drawn. Maps stitch together through these, not through one world mesh. Enemies and objects record which collision part they belong to, so whole rooms of characters switch on and off with the floor.

Special layers

h900400 is a near-flat sheet just below Firelink's cliff edges (filter 13): crossing it means you fell to your death, and the camera stops following. h900000 (15) kills. h900100 (22) is a hull just in front of the visible walls, with material 29 "Without".

Special layers

9 stops only the camera ray, 11 only enemies, 13 and 15 are triggers that kill the player on crossing, and 22 stops characters and the camera but not swords, so swings can clang off the real wall behind it. Before this, corpses lay on the fall-death sheet in mid-air.

4Collision: Havok's compressed meshes

Each collision model is a Havok 2014.1 packfile using the newer hknp physics data:

hkRootLevelContainer
 └─ hknpPhysicsSceneData → hknpPhysicsSystemData
     ├─ materials[]     generic hknpMaterial (friction 1.75 …)
     └─ bodyCinfos[]    one static body, identity transform
          └─ fsnpCustomParamCompressedMeshShape      ← FromSoft's subclass
               ├─ hknpCompressedMeshShapeData         BVH + quantised vertices
               ├─ fsnpCustomMeshParameter              ← per-triangle game data
               └─ triangleIndexToShapeKey[]

Every triangle knows what it's made of

materialNameData = slideClass × 100 + HitMtrlParam row

The material (row)

1 cobblestone, 2 stone (73 % of High Wall), 3 soil, 4 wood, 5 grass, 8 trees, 9 swamp, 11 iron, 13 sand, 16 roof tiles, 17 cloth, 18 fallen leaves, 20 water (puddle), 21 ankle, 22 knee, 27 waist, 23-26 poison swamp, 28 carpet … It picks footsteps, foot effects, wading and status effects.

The slide class (hundreds)

0 normal (slope rules apply), 1 never slide, 2 mostly vertical (unclear), 4 always slide regardless of angle ER.

Collision view coloured by material
Iudex Gundyr's courtyard as collision, each triangle coloured by its material: stone grey, soil tan, the pool's water blue, walls red. The orange capsule on the dais is Gundyr's character proxy. It's authored by hand and far coarser than the art (compare the courtyard's picture on the Renderer page).

Collision

Havok's compressed mesh with a BVH per section and FromSoft's per-triangle parameter block.

Collision

.col: triangles with {surface, slide class}. The engine makes them 363 Jolt MeshShape bodies in 0.5 s (43 MB of Jolt heap), with the material in each triangle's user data, read back from the ground contact's sub-shape. Jolt builds its own BVH.

Faithful

DS3's triangles, materials and filter layers. Only the physics library differs (Jolt instead of Havok).

How it was fitted to the art: each Havok body is named after its source, an art model's hit mesh (m005590_hh) or a placed instance (m002040_1004), and a measurement shows three quarters of collision vertices lie within 5 cm of the art but only a third on its vertices. What that suggests about how FromSoftware built it is on How it was probably made.

5Water in the level

Water is three separate things in DS3's data, none of them a "water volume":

  1. The surface you wade in: collision part h900300 is nothing but flat water_ankle sheets at each pond's and stream's level (y −62.25, −59.77, −69.02), 0.1-0.45 m above the bed. The bed beneath is ordinary solid collision. The wading materials are the HitMtrlParam rows with HitMtrlType1 = 2.
  2. What you see: the water meshes m000010/11/12 with a water material (Renderer).
  3. What it reflects: the collision parts' ReflectPlaneHeight, and a few parts marked DrawOnlyReflectCam, bands of unlit cards with 128×64 panoramas of the surroundings that stand in for the terrain in the mirror.
The stream at the Cemetery of Ash
The cemetery's stream. The bed is solid collision; an ankle-deep sheet floats at the surface. Our engine puts nothing on that layer, then casts a ray down onto it to find the depth for the splashes.

6Objects: doors, candles, corpses

Objects are parts with an objbnd, which can hold a model, its own collision, a skeleton and clips. Three m40 examples:

The posed corpses

The item corpse o000250 is placed 29 times. Each part's AnimIDs[0] names a one-frame clip in its objbnd: slumped against a wall, face down, on its back in a grave. Without the pose they stand upright, half in the ground. Ours: the importer bakes each pose into a mesh stand-in

Candles and torches

Effects come from ModelSfxParam row = model number × 1000 + the part's relative id: 210 lit candles get a flame per wick at dummy poly 200, and 10 wall torches get fire and a point light (Effects).

Gundyr's door

A FLVER with rigid leaves, a 4-bone skeleton, a 10 s opening clip and three keyframed Havok bodies. None of it is in the map's collision. An ObjAct event and event script 14000816 open it once Gundyr dies (flag 14000800).

The door behind Iudex Gundyr
The door behind Iudex Gundyr. In our engine its bodies are Jolt kinematic bodies posed from the door's bones every tick. While the player plays the push clip a000_060170 (TAE flag 19: no map collision), they pass through and end 1.15 m beyond.

7Bonfires

A bonfire is surprisingly many things data:

Object parto000100 (Firelink's o400100), named by a BonfireWarpParam row: menu text, lit flag
Event scriptRegisterBonfire(lit flag, entity, 5 m, 180°) in m40's event 0; a boss's bonfire waits in common event 20005500 for the defeat flag, then plays effect 1060 and appears
An invisible charactera c1000 enemy part (no mesh) sits on it with a talk script (an ESD state machine): "Light bonfire" (button 6100), then "Rest at bonfire" (6101), then the menu
The player's clipslight a000_068000, sit 068010 → loop 068011, stand 068012; warp out 068100, arrive 060260
Where you standa SpawnPoint region with the bonfire's entity id + 1000, 1.3-1.7 m away and facing it
Close

Lighting, resting (HP, FP, stamina and flasks refilled, enemies back, the death return point) and Gundyr's bonfire appearing, all from the event and talk scripts. The fire's effect (1011) is a guess, and there's no travel, attune or storage menu.

8Enemies in the map

Enemy parts carry a model, an NpcParam row (its body and stats), an NpcThinkParam row (its AI), a talk id, a platoon, a walk route and an entity id for event scripts. m40 has 100 enemy parts and 43 DummyEnemy parts (cutscene stand-ins). One map holds two areas: the Cemetery of Ash and, at the same places, the late-game Untended Graves. The hollows exist in both sets, sometimes at identical positions; event scripts pick the set guess.

Our importer drops the parts whose rows are for Untended Graves (by Paramdex's names or the level-scaling SpEffect 7130/7135), 37 of them, Champion Gundyr among them. The engine places 25 Cemetery of Ash hollows of four kinds and Iudex Gundyr (Enemies).

9Regions

Region typeIn m40Engine
SpawnPointNew-game start, one per bonfireYes: the engine can start at any bonfire
EnvironmentMapPoint / EffectBox14 probe cubemaps, 31 boxes that blend them per pixelYes (Renderer)
SFX18: ground mist, distant fog banks, the shrine's dust motes, candle lightsYes (Effects)
Sound19: boss music, hub music, the Fire Keeper's chant, windYes (Sound)
Event areaGundyr's fight-start box 4002800 in the courtyard's gatewayCopied into code stand-in
WindSFX, walk routes6 wind, hollow patrolsnot done

m40_00_00_00.nvmhktbnd holds one hkaiNavMesh per navmesh part (an AABB tree and a cluster graph). The .nva links navmeshes and portals. Only the AI uses it.

Not done

Read but not imported: our enemies walk straight at you, as the hollows on open ground mostly do.

11What our .scene carries

The importer merges the MSB, the area's gparam, its lights (.btl), lightmap atlas table (.btab) and probe grids (.btpb) into one .scene (v16): meshes and instances (with groups, light set, fog, LOD distances), spawns, probe cubemaps and boxes, SH probe grids, light sets, fogs, enemies, effects, lightmaps, point lights, bonfires, sound regions and animated objects. Collision goes to .col, an animated object's bodies to .objcol. The engine loads the area into a level arena and frees it by resetting the arena.