scenario_structure_bsp

The scenario structure BSP tag, commonly just called the BSP, contains level geometry, weather data, material assignments, AI pathfinding information, lightmaps, and other data structures. You can think of the BSP as the "stage" where the game takes place objects are placed within it. Aside from sounds and bitmaps, the BSP tends to be one of the largest tags in a map. Singleplayer scenarios often use multiple BSPs which are switched between at loading zones.

The term "BSP" stands for Binary Space Partitioning, a technique where space within a sealed static mesh is recursively subdivided by planes into convex leaf nodes. The resulting BSP tree can be used to efficiently answer geometric queries, such as which surfaces should be collision-tested for physics objects.

Compilation

After level geometry is exported to JMS format from your 3D software of choice, it can be compiled into a BSP tag using Tool's structure verb.

BSP transitions

While a scenario can reference multiple BSPs, Halo can only have a single BSP loaded at a time. Transitions between BSPs can be scripted (switch_bsp), e.g. using trigger volumes. Objects in unloaded BSPs are not simulated.

Although multiple BSPs are intended for singleplayer maps and do not synchronize, some custom multiplayer maps have used nearly identical BSPs which only differ in lighting to add a day/night switch scripted by a button in the escape menu.

Shaders

The most commonly used shader type for BSPs is shader_environment, suitable for most opaque surfaces and alpha-tested grates or billboard trees (as in Timberland). This shader type supports the blending between multiple detail maps, often used for ground maps with dirt and grass areas.

Transparent shaders can also be used, for example:

The shader_model type will not be rendered by the game since it is intended for use with object models.

Portals

If you think of the BSP as a sealed volume, portals are like doorways that split that space into distinct sealed "rooms" called clusters. This lets the game know what areas of a level can be culled to maintain good framerates, and the resulting clusters can each have unique properties assigned like background sound.

You can create portals in a few ways:

  1. Surfaces which cut through the level and use the material name +portal. The portal planes can be connected along edges and even form box shapes but cannot have "leaky" gaps between the spaces they are meant to seal. Portal planes must not intersect each other.
  2. Surfaces which exactly match at their boundary with the vertices and eges in level geometry (e.g. around a doorway) and use the material name +exactportal. The faces of the exact portal do not need to be coplanar.
  3. Existing materials in the level that happen to consistently seal off closed volumes wherever they're used as if they were exact portals, like floor grates or glass windows covering a pit. Add the . material symbol, e.g. floor_grate.%.

It may be necessary to add multiple portals to close a space. For example, if a building had multiple doors and windows you would need to add portals to each of them for the inside of the building to be considered sealed off from the outside. For help troubleshooting portals, see here.

Clusters and cluster data

Clusters are sealed volumes of a BSP separated by portal planes. They are used both as a rendering optimization and artistically; map authors can assign weather_particle_system, wind, sound_environment, and sound_looping tags to define local atmospheric and ambience qualities for each section of the map. Different clusters can even reference different skies. The level will contain a single large cluster if no portals were created.

Note that it may still be desirable to reference weather for indoor clusters if there are outdoor areas visible from them, otherwise snow and rain particles will abruptly disappear. To mask weather in such clusters, use weather polyhedra.

Indoor vs. outdoor clusters

Clusters are either outdoor/exterior or indoor/interior. When a cluster contains +sky faces it is an outdoor cluster and has a sky index of 0 or greater. Furthermore, any cluster from which an outdoor cluster is potentially visible will also be an outdoor cluster. The material +seamsealer does not contribute to a cluster becoming outdoor so is typically used in BSP transition areas.

An indoor cluster is one where none of its potentially visible clusters are outdoor. These clusters have a sky index of -1 instead and use the indoor parameters of sky index 0 (the first sky), which always has the special role of doubling as the "indoor sky". For example, indoor clusters will use use its indoor fog color rather than its outdoor fog color.

When the game transitions between indoor and outdoor clusters the fog colour fades based on cumulative camera movement, not time. This effect can be seen easily in Danger Canyon: load it in Sapien and fly the camera through the hallways while debug_pvs 1 and rasterizer_wireframe 1 are enabled.

Potentially visible set

The potentially visible set (PVS) data is precomputed when a BSP is compiled and helps the engine determine which clusters are likely visible from each other. A cluster can "see" any other cluster behind portals visible from itself plus one level of clusters further. Any clusters beyond that will not be rendered.

Tool also takes into account the indoor sky's indoor fog opaque distance and indoor fog maximum density when computing the PVS. If the density is 1.0 (fully opaque) then Tool knows that indoor clusters cannot see beyond the opaque distance even if there are clusters within a line of sight. Tool logs the indoor maximum world units when the BSP is imported (if there a sky referenced).

In addition to using the static PVS, the game may dynamically cull objects and parts of clusters using portal frustums.

Potentially audible set

Like the PVS, the potentially audible set (PAS) data encodes which clusters can hear sounds from other clusters. This allows the engine to cull sounds without having to perform a costlier obstruction check. It is unknown what criteria make clusters potentially audible.

Fog planes

Areas of a map which need a fog layer can be marked using fog planes. These are 2D surfaces which reference fog tags, not to be confused with atmospheric fog which is part of the sky tag.

Create a fog plane by modeling a flat plane and giving its faces the material symbol $, either on an existing material (like my_level_ocean!$) or as +unused$ if the fog exists in isolation. The fog will exist behind/antinormal to the plane. It must be completely flat and it is invalid for any cluster to be able to see multiple fog planes (more details), so adjust the size and shape of the plane accordingly. It's also important to know that the fog plane is rendered as if it's infinite, no matter of how the fog plane is actually shaped when you model it. The shape of the plane in your model is used to know which clusters intersect it.

You can even create slanted or upside-down fog planes as long as they follow the rules. Once your BSP is imported, you'll be able to assign fog to your plane(s) in Sapien.

Weather polyhedra

Weather polys extracted from Assault on the Control Room.

Weather polyhedra are artist-defined volumes where weather particle systems will not render, such as under overhangs where you would not expect to see rain.

To create them, simply model outwardly facing convex volumes where all faces use the +weatherpoly material name and Tool will generate the weather polyhedra when importing your BSP. The volumes can overlap and up to 8 can be visible at any time before the game starts ignoring some (Sapien will print warnings when this happens).

It is important that you still create weather polyhedra even if you have portals separating inside and outside spaces. Simply not assigning weather to the clusters which are under cover is not enough to prevent rain from appearing there. This is because the game renders weather based on the camera's current cluster, so if the player is outside a building looking in through a doorway they will still see rain indoors because the camera is currently located outside. Conversely, if the cluster within the building has no weather assigned then players will not see rain outdoors when looking out the doorway from inside. The solution is still assigning weather to covered clusters, then masking those areas with large weather polyhedra. This can be seen in the example figure.

Within the tag, the polyhedra are represented as a center point, bounding radius, and up to 16 planes which enclose a volume. Therefore your polyhedra technically don't need to be sealed volumes because they are limited to their bounding radius. A polyhedron can be created with as little as 1 plane.

Lightmaps

Lightmaps are the visual representation of the BSP, and are stored in a separate representation from its collision data. The lightmaps data includes the renderable triangles and a precalculated radiosity bitmap.

See main page: Lightmaps.

Lens flare markers

In a10, lens flare markers were generated for fluorescent lights

When a shader_environment references a lens_flare, lens flare markers are automatically created and stored in the BSP tag during initial structure import or updated with structure-lens-flares. These are used to give lights a "glowy" appearance. If the shader has a lens flare spacing of 0, a single lens flare is placed on the surface. Otherwise, the lens flares are evenly spaced within the surface according to the spacing value (world units).

A BSP can contain up to 65535 lens flare markers, and up to 256 types of lens flares. However, there is a much lower limit to how many the game will draw at a given time, exactly how many is unknown.

Collision artifacts

Phantom BSP

Danger Canyon contains at least two prevalent cases of phantom BSP. The Warthog and bullets are both colliding with invisible extensions of nearby surfaces.

The phantom BSP here is caused by nearly coplanar faces on the nearby ramp.

Phantom BSP is a collision artifact sometimes produced when compiling BSPs. It manifests itself as invisible surfaces which projectiles and vehicles collide with (but not players), and mostly appears around sharp corners near cases of "nearly coplanar faces" warnings in your WRL file. It can also cause objects above the problem area to fall back to BSP default lighting.

Bungie was aware of this artifact and implemented a feature to help spot it (collision_debug_phantom_bsp 1 in Sapien or Standalone). If you find phantom BSP in your map, there are a few steps you can take to resolve it:

  1. Preemptively, keep your geometry simple and robust without an abundance of dense, complex, or spiky shapes. Flat surfaces like floors and walls should be kept as flat as possible by using alignment tools when modeling rather than "eyeballing it".
  2. Fix any "nearly coplanar" warnings in your source model by scaling affected faces to 0 along their normal axis or using alignment. Since Tool slightly rounds vertex coordinates when compiling BSPs, sometimes this warning cannot be resolved for surfaces which are not axis-aligned.
  3. There is an element of chance to phantom BSP appearing which depends on how your geometry is recursively subdivided to form a BSP tree. Modifying unrelated parts of your level like adding portals or moving vertices can sometimes affect how the level is subdivided and make phantom BSP disappear or appear in new places.
  4. Using H1A Tool's fix-phantom-bsp option to compile your BSP will prevent most phantom BSP at the cost of slightly increasing the tag size. There have been reports that this may not resolve all phantom BSP.
  5. If you do not have access to source JMS, and are trying to fix a BSP tag, you will need to import the tag into Blender and rework the model to make it export-ready and free of nearly coplanar surfaces.

On a technical level, cases of phantom BSP are dividing planes where a child index is -1, but the space on that side of the plane is not actually completely outside the level. The artifact is bounded by all parent dividing planes.

BSP holes

This location in Derelict has a small collision hole where items can fall through the map.

BSP holes or leaks are another type of collision artifact where items or players can fall through the map. It is not known what causes this, but it can be resolved by altering triangulation around the affected area (rotating edges). Compiling the BSP with H1A Tool's fix-phantom-bsp option also prevents this.

Pathfinding data

The BSP contains data on traversable surfaces which aid AI in pathfinding (walking to a destination). This data is generated automatically during BSP compilation and is retained even in when the BSP is compiled into multiplayer maps.

See more about the pathfinding system.

Related HaloScript

The following are related functions that you can use in your scenario scripts and/or debug globals that you can enter into the developer console for troubleshooting.

Function/global

Type

(collision_debug [boolean])

If enabled, a ray is continually shot from the camera (by default) to troubleshoot ray-object and ray-BSP collisions. A red normal-aligned marker will be shown where the ray collides with a surface. The collision surface itself, whether BSP or model, will be outline in red. Information about the collision surface will be shown in the top left corner of the screen, including plane and surface indices from the BSP structure, material type, and how many degrees the surface's normal differs from vertical.

The types of surfaces which the test ray hits or ignores can be toggled with the collision_debug_flag_* commands. The maximum range of the ray is controlled with collision_debug_length.

This feature can be frozen in place with collision_debug_repeat and also moved directly with the collision_debug_point_* and collision_debug_vector_* globals. A red ray is visible in these situations when the ray is no longer shot from the camera's point of view.

Global
(collision_debug_features [boolean])

Toggles the display of collision features near the camera, which can be spheres (red), cylinders (blue), or prisms (green). Collision size can be adjusted with collision_debug_width and collision_debug_height. The test point can be frozen in place using collision_debug_repeat.

Global
(collision_debug_flag_structure [boolean])

Toggles if collision debug rays collide with the structure BSP. Collisions with model_collision_geometry BSPs are unaffected.

Global
(collision_debug_phantom_bsp [boolean])

Causes a floating pink cube and label "phantom bsp" to appear whenever a test ray from the center of the screen intersects with phantom BSP. It can be helpful to pair this with collision_debug_spray.

Global
(collision_debug_spray [boolean])

Setting this to true will cause collision ray tests to be performed in a dense grid from the viewport. This operates independently of the collision_debug setting, and only the destination hit markers are shown. Can be affected by collision flags, length, and frozen with collision_debug_repeat.

Global
(debug_bsp [boolean])

Toggles the display of structure BSP node traversal for the camera location. At each level, the node and plane indices are shown as well as a + or - symbol indicating if the camera was on the front or back side of the plane.

Global
(debug_camera [boolean])

Shows debug information about the camera in the top left corner, including:

  • 3D coordinates in world units
  • BSP leaf and cluster indices
  • Ground point coordinates and BSP surface index (point directly below the camera on the ground)
  • Facing direction (yaw angle)
  • Tag path of the shader pointed at by the camera. The surface must be collideable and this feature cannot be configured with collision debug flags.
Global
(debug_no_frustum_clip [boolean])

Disables portal-based occlusion culling for objects and parts of the BSP (use rasterizer_wireframe 1 to see this). This isn't about clipping to the whole view frustum but rather portal diminished frustrums; in addition to the PVS, the game seems to cull objects and the BSP by whether object bounding spheres and possibly subclusters are visible through portals. The use of debug_pvs at the same time makes the behaviour of this unpredictable.

Global
(debug_portals [boolean])

Draws structure BSP portals as red outlines.

Global
(debug_sound_environment [boolean])

If enabled, shows the tag path of the cluster's current sound_environment.

Global
(debug_structure [boolean])

When enabled, all scenario_structure_bsp collision surfaces will be rendered with green outlines. A red bounding box surrounds renderable surfaces.

Global
(debug_structure_automatic [boolean])

Like debug_structure, but only appears when the camera is in "solid" space (outside the BSP). This can be handy for finding your way back to the level when switching BSPs. It is set to true by default in Sapien. This is a backported feature from later Halos and only available in the newer MCC tools.

Global

Structure and fields

FieldTypeComments
lightmap bitmapsTagDependency: bitmap

A reference to the bitmap storing the level's baked lightmaps. The level will not be visible without this.

vehicle floorfloat
  • Unit: world units

The lowest Z-axis height (absolute, not frame relative) that vehicles can reach while occupied. Does not affect other object types. An unoccupied vehicle will still drop below this soft barrier, but when it becomes occupied again it will quickly shoot back up into the play area. An example of a map using a vehicle floor is Gephyrophobia to prevent players from flying too far below the bridge.

vehicle ceilingfloat
  • Unit: world units

The maximum Z-axis height (absolute, not frame relative) that vehicles can reach while occupied. Does not affect other objects types. Above this point, occupied vehicles will elastically move back below the ceiling. This can be used to limit the height that players in flying vehicles like Banshees can reach for gameplay reasons. An example of this can be seen in Blood Gulch.

default ambient colorColorRGB

If the red component of this colour is non-zero, this and all following default lighting fields will be used to light objects when a ground point cannot be found within 10wu below them, including when objects are outside the BSP or above phantom BSP. Otherwise, objects sample lightmaps data at their ground point.

default distant light 0 colorColorRGB

Primary distant light colour when default lighting applies.

default distant light 0 directionVector3D

Primary distant light direction when default lighting applies.

FieldTypeComments
ifloat
jfloat
kfloat
default distant light 1 colorColorRGB

Secondary distant light colour when default lighting applies.

default distant light 1 directionVector3D?

Secondary distant light direction when default lighting applies.

default reflection tintColorARGB

Reflection tint when default lighting applies.

FieldTypeComments
alphafloat
redfloat
greenfloat
bluefloat
default shadow vectorVector3D?

Shadow vector when default lighting applies.

default shadow colorColorRGB

Shadow colour when default lighting applies.

collision materialsBlock
  • HEK max count: 512
  • Read-only
  • Processed during compile
FieldTypeComments
shaderTagDependency: shader
  • Non-null
materialenum
  • Cache only
OptionValueComments
dirt0x0
sand0x1
stone0x2
snow0x3
wood0x4
metal hollow0x5
metal thin0x6
metal thick0x7
rubber0x8
glass0x9
force field0xA
grunt0xB
hunter armor0xC
hunter skin0xD
elite0xE
jackal0xF
jackal energy shield0x10
engineer skin0x11
engineer force field0x12
flood combat form0x13
flood carrier form0x14
cyborg armor0x15
cyborg energy shield0x16
human armor0x17
human skin0x18
sentinel0x19
monitor0x1A
plastic0x1B
water0x1C
leaves0x1D
elite energy shield0x1E
ice0x1F
hunter shield0x20
collision bspBlock
  • HEK max count: 1
  • Read-only
  • Processed during compile
FieldTypeComments
bsp3d nodesBlock
  • HEK max count: 131072

A block of nodes used to efficiently find collideable surfaces. Each node divides space with an infinite plane and references two child nodes by index into this block. The first element in the block is the root node. A test point can be recursively tested against planes to find a leaf of potentially colliding surfaces.

  • Read-only
FieldTypeComments
planeuint32

An index into the planes block. The plane divides 3D space into front and back half-spaces. A point can be tested against this plane to determine which half-space to descend into.

Tool will create new planes or use existing surface planes when generating the BSP tree based on a heuristic of how many surfaces remain in the node. For 1024 and above, axis-aligned planes based on vertex bounds are used. Otherwise, the surface within the remaining set whose plane minimizes the difference between front and back surface counts is used. In the case of levels, only collideable surfaces and portal planes are candidates.

back childuint32
  • Flagged

Index of the BSP3D node representing space to the back of the dividing plane. A value of 0xFFFFFFFF is considered null and the space behind the plane is considered outside the sealed BSP (Sapien labels this as "solid" space when using debug_structure). Otherwise, if the highest order bit is set (0x80000000), the remaining 31 bits (mask 0x7FFFFFFF) represent an index into the leaves block.

front childuint32
  • Flagged

Similar to the back child field.

planesBlock
  • HEK max count: 65536

Planes are infinite 2D surfaces in 3D space. They are used both to subdivide the BSP in each bsp3d node and to define collideable surfaces. The first 8 planes in the block seem to serve a special purpose -- the first 4 define the XY bounding box, with the next 4 axis aligned planes unkown. Furthermore, the last 8 planes in the block are used for the first few levels of bsp3d nodes. A single plane may be referenced from multiple bsp3d nodes since the surfaces that planes derive from can also be found in multiple leaves.

  • Read-only
FieldTypeComments
planePlane3D

A plane which divides 3D space in two.

FieldTypeComments
vectorVector3D?

A 3-float normal vector.

wfloat

Distance from origin (along normal).

leavesBlock
  • HEK max count: 65536

Leaves mark the transition between the 3D BSP and a collection of convex collideable surfaces in the same localized area. Each set of co-planar surfaces within this leaf is stored within a child 2D BSP.

Note that surfaces may be found under multiple leaves, since any surface which is not completely on either side of a 3D plane will need to belong to both child 3D BSP nodes.

  • Read-only
FieldTypeComments
flagsbitfield

Flags used to optimize collision checks at runtime.

FlagMaskComments
contains double sided surfaces0x1

Indicates if any surface in any of this leaf's 2D BSPs is double-sided (e.g. glass).

bsp2d reference countuint16

Determines how many contiguous 2D BSP references belong to this leaf.

first bsp2d referenceuint32

Index of the first 2D BSP reference associated with this leaf. It will be followed by a number of other 2D BSP references according to the above count.

bsp2d referencesBlock
  • HEK max count: 131072

Represents either a 2D BSP of surfaces, or a singular surface if the node index is flagged. In either case, test points or 3D line traces should be projected onto the basis plane in order to continue.

  • Read-only
FieldTypeComments
planeuint32
  • Flagged

Index for the plane used to decide what basis plane is best to project on (X,Y), (Y,Z) or (X,Z). The basis plane is chosen by the referenced plane's most significant normal component. A flagged plane index means its normal vector is opposite.

bsp2d nodeuint32
  • Flagged

The starting node for the 2D BSP on this plane. If flagged, then the index (masked to 0x7FFFFFFF) refers to a surface instead. Null if 0xFFFFFFFF.

bsp2d nodesBlock
  • HEK max count: 65535
  • Read-only
FieldTypeComments
planePlane2D

A 2D plane (line) subdividing left and right child surfaces. This line is in the space of the 2D BSP reference's basis plane.

FieldTypeComments
vectorVector2D
FieldTypeComments
ifloat
jfloat
wfloat
left childuint32
  • Flagged

refers to a BSP2D node if not signed and a surface if sign bit is set; null if 0xFFFFFFFF.

right childuint32
  • Flagged

refers to a BSP2D node if not signed and a surface if sign bit is set; null if 0xFFFFFFFF.

surfacesBlock
  • HEK max count: 131072

Surfaces are planar collideable polygons. They are not necessarily triangular and may have up to 8 edges. BSP collision surfaces can be visualized in Sapien using debug_structure 1. These surfaces are not used for the rendered geometry.

  • Read-only
FieldTypeComments
planeuint32
  • Flagged

Index into the planes block for this surface's plane. Note that multiple co-planar surfaces may reference the same plane since this plane index is a copy of the parent bsp2d reference's plane index, even when flagged.

first edgeuint32
flagsbitfield
FlagMaskComments
two sided0x1
invisible0x2
climbable0x4

Indicates if the surface is a climbable ladder.

breakable0x8

Indicates if the surface is breakable. The surface must also have a breakable surface index below.bit there cannot be more than 256 unique breakable surfaces in a BSP.

breakable surfaceint8

Index into this tag's breakable surfaces block. It is unknown if this is a signed or flagged field, but since it is 8-bit there cannot be more than 256 unique breakable surfaces in a BSP.

materialuint16
edgesBlock
  • HEK max count: 262144

Edges, surfaces, and vertices form a data structure called a doubly connected edge list (DCEL), also known as a half-edge data structure. The edges and their vertices define the boundaries of the collideable surfaces within a leaf.

  • Read-only
FieldTypeComments
start vertexuint32
end vertexuint32
forward edgeuint32
reverse edgeuint32
left surfaceuint32
right surfaceuint32
verticesBlock
  • HEK max count: 131072

The 3D coordinates used for edge starting and ending locations.

  • Read-only
FieldTypeComments
pointPoint3D
FieldTypeComments
xfloat
yfloat
zfloat
first edgeuint32
nodesBlock
  • HEK max count: 131072
  • Read-only
FieldTypeComments
node stuffuint16[3]
world bounds xBounds
FieldTypeComments
minfloat
maxfloat
world bounds yBounds?
world bounds zBounds?
leavesBlock
  • HEK max count: 65536
  • Read-only
FieldTypeComments
verticesuint16[3]
clusteruint16
surface reference countuint16
surface referencesint32
leaf surfacesBlock
  • HEK max count: 262144
  • Read-only
FieldTypeComments
surfaceint32
nodeint32
surfacesBlock
  • HEK max count: 131072
  • Read-only
FieldTypeComments
vertex0 indexuint16
vertex1 indexuint16
vertex2 indexuint16
lightmapsBlock
  • HEK max count: 128
  • Read-only
FieldTypeComments
bitmapuint16

Indicates which index of bitmap data within the lightmap bitmaps is used for the faces of these materials. In other words, the baked lightmaps can be split across multiple bitmap "pages" and this index tells the game what page the faces of these materials use. An index of -1 means these faces are not lightmapped, like if radiosity has not been run yet or, once it has, for additive blended transparent shaders like lights (which Tool groups in the last item of this block).

materialsBlock
  • HEK max count: 2048
  • Read-only
  • Processed during compile
FieldTypeComments
shaderTagDependency: shader
  • Non-null
shader permutationuint16
flagsbitfield
FlagMaskComments
coplanar0x1
fog plane0x2
surfacesint32
surface countint32
centroidPoint3D?
ambient colorColorRGB
distant light countuint16
distant light 0 colorColorRGB
distant light 0 directionVector3D?
distant light 1 colorColorRGB
distant light 1 directionVector3D?
reflection tintColorARGB?
shadow vectorVector3D?
shadow colorColorRGB
planePlane3D?
breakable surfaceuint16
rendered vertices typeenum
  • Cache only

Indicates how rendered vertices are stored.

OptionValueComments
structure bsp uncompressed rendered vertices0x0
structure bsp compressed rendered vertices0x1
structure bsp uncompressed lightmap vertices0x2
structure bsp compressed lightmap vertices0x3
model uncompressed0x4
model compressed0x5
rendered vertices countuint32

The number of rendered vertices for this material, found at rendered vertices offset. This count is always equal to lightmap vertices count when the bitmap index is not -1.

rendered vertices offsetuint32

An offset to an array of rendered vertices, each 56 bytes:

  • Position
  • Normal
  • Binormal
  • Tangent
  • UV
unknown pointerptr32

on PC, this gets changed to a pointer to something when the BSP is loaded in-game

rendered vertices index pointerptr32
  • H1X only
  • Cache only

(Xbox only) an indirect pointer to the rendered vertices

lightmap vertices typeenum?
  • Cache only

Indicates how lightmap vertices are stored.

lightmap vertices countuint32

The number of lightmap vertices for this material, found at lightmap vertices offset. This count is always equal to rendered vertices count when the bitmap index is not -1.

lightmap vertices offsetuint32

An offset to an array of lightmap vertices, each 20 bytes:

  • Incident radiosity vector, used for dynamic object shadows and environmental bump mapping)
  • UV coordinate in lightmap page indicated by bitmap index.
lightmap vertices index pointerptr32
  • H1X only
  • Cache only

(Xbox only) an indirect pointer to the lightmap vertices

uncompressed verticesTagDataOffset
FieldTypeComments
sizeuint32
externaluint32
file offsetuint32
pointerptr64
compressed verticesTagDataOffset?
lens flaresBlock
  • HEK max count: 256

Lists the available lens flares used by this BSP's lens flare markers.

  • Read-only
FieldTypeComments
lensTagDependency: lens_flare
lens flare markersBlock
  • HEK max count: 65536

Points within the BSP where a lens flare will be rendered.

  • Read-only
FieldTypeComments
positionPoint3D?

The 3D position of the lens flare.

direction i componentint8
direction j componentint8
direction k componentint8
lens flare indexint8

Determines which flare from the lens flares block is rendered at this position.

clustersBlock
  • HEK max count: 8192
  • Read-only
FieldTypeComments
skyuint16

Sets which sky is visible when within this cluster. Indexes the skies block of the scenario tag, so a value of 0 would mean the first sky, 1 the second, etc. A value of -1 marks this cluster as indoor/interior for the purposes of certain sky tag fields.

Tool appears to mark clusters as indoor when there are no outdoor clusters visible from it according to the PVS. Tool validates that all sky faces in a cluster are for the same sky. For example, a cluster cannot contain both +sky0 and +sky1 materials. When a player moves between clusters with different skies, fog colour will smoothly transition by cumulative camera movement over approximately 20 world units.

foguint16
background sounduint16
sound environmentuint16
weatheruint16
transition structure bspuint16
first decal indexuint16
  • Cache only
decal countuint16
  • Cache only
predicted resourcesBlock
  • HEK max count: 1024
  • Cache only
FieldTypeComments
typeenum
OptionValueComments
bitmap0x0
sound0x1
resource indexuint16
taguint32
subclustersBlock
  • HEK max count: 4096
  • Read-only
FieldTypeComments
world bounds xBounds?
world bounds yBounds?
world bounds zBounds?
surface indicesBlock
  • Read-only
FieldTypeComments
indexint32
first lens flare marker indexuint16
lens flare marker countuint16
surface indicesBlock
  • HEK max count: 32768
  • Read-only
FieldTypeComments
indexint32
mirrorsBlock
  • HEK max count: 16
  • Read-only
FieldTypeComments
planePlane3D?
shaderTagDependency: shader
verticesBlock
  • HEK max count: 512
  • Read-only
FieldTypeComments
pointPoint3D?
portalsBlock
  • HEK max count: 128
  • Read-only
FieldTypeComments
portaluint16
cluster dataTagDataOffset?
cluster portalsBlock
  • HEK max count: 512
  • Read-only
FieldTypeComments
front clusteruint16
back clusteruint16
plane indexint32
centroidPoint3D?
bounding radiusfloat
flagsbitfield
FlagMaskComments
ai can simply not hear through all this amazing stuff darn it0x1
verticesBlock
  • HEK max count: 128
  • Read-only
FieldTypeComments
pointPoint3D?
breakable surfacesBlock
  • HEK max count: 256
  • Read-only
FieldTypeComments
centroidPoint3D?
radiusfloat
collision surface indexint32
fog planesBlock
  • HEK max count: 32
  • Read-only
FieldTypeComments
front regionuint16
material typeenum?
  • Cache only
planePlane3D?
verticesBlock
  • HEK max count: 2048
  • Read-only
FieldTypeComments
pointPoint3D?
fog regionsBlock
  • HEK max count: 32
  • Read-only
FieldTypeComments
foguint16
weather paletteuint16
fog paletteBlock
  • HEK max count: 32
  • Max: 65534
FieldTypeComments
nameTagString
fogTagDependency: fog
fog scale functionTagString
weather paletteBlock
  • HEK max count: 32
FieldTypeComments
nameTagString
particle systemTagDependency: weather_particle_system
particle system scale functionTagString
windTagDependency: wind
wind directionVector3D?
wind magnitudefloat
wind scale functionTagString
weather polyhedraBlock
  • HEK max count: 32
  • Read-only
FieldTypeComments
bounding sphere centerPoint3D?
bounding sphere radiusfloat
planesBlock
  • HEK max count: 16
  • Read-only
FieldTypeComments
planePlane3D?
pathfinding surfacesBlock
  • HEK max count: 131072
  • Read-only
FieldTypeComments
dataint8
pathfinding edgesBlock
  • HEK max count: 262144
  • Read-only
FieldTypeComments
midpointint8
background sound paletteBlock
  • HEK max count: 64
FieldTypeComments
nameTagString
background soundTagDependency: sound_looping
scale functionTagString
sound environment paletteBlock
  • HEK max count: 64
FieldTypeComments
nameTagString
sound environmentTagDependency: sound_environment
sound pas dataTagDataOffset?

Contains the potentially audible set (PAS) data. Similar to the PVS with visibility, this encodes which clusters can potentially hear sounds from other clusters. Custom Edition doesn't make use of this data but H1A does.

markersBlock
  • HEK max count: 1024

Named points with orientation within the BSP. They are similar to gbxmodel markers but are not to be confused with scenario cutscene flags. These markers become visible in Sapien when enabling snap to markers in the tool window.

FieldTypeComments
nameTagString
rotationQuaternion
FieldTypeComments
ifloat
jfloat
kfloat
wfloat
positionPoint3D?
detail objectsBlock
  • HEK max count: 1
  • Processed during compile
FieldTypeComments
cellsBlock
  • HEK max count: 262144
FieldTypeComments
no nameint16
no name 1int16
no name 2int16
no name 3int16
no name 4int32
no name 5int32
no name 6int32
instancesBlock
  • HEK max count: 2097152
FieldTypeComments
no nameint8
no name 1int8
no name 2int8
no name 3int8
no name 4int16
countsBlock
  • HEK max count: 8388608
FieldTypeComments
no nameint16
z reference vectorsBlock
  • HEK max count: 262144
FieldTypeComments
no namefloat
no name 1float
no name 2float
no name 3float
bullshituint8
  • Cache only
runtime decalsBlock
  • HEK max count: 6144
  • Cache only
  • Read-only
FieldTypeComments
positionPoint3D?
decal typeuint16
yawint8
pitchint8
leaf map leavesBlock
  • HEK max count: 65536
  • Read-only
FieldTypeComments
facesBlock
  • HEK max count: 256
  • Read-only
FieldTypeComments
node indexint32
verticesBlock
  • HEK max count: 64
  • Read-only
FieldTypeComments
vertexPoint2D
FieldTypeComments
xfloat
yfloat
portal indicesBlock
  • HEK max count: 256
  • Read-only
FieldTypeComments
portal indexint32
leaf map portalsBlock
  • HEK max count: 524288
  • Read-only
FieldTypeComments
plane indexint32
back leaf indexint32
front leaf indexint32
verticesBlock
  • Read-only
FieldTypeComments
pointPoint3D?

Acknowledgements

Thanks to the following individuals for their research or contributions to this topic:

  • Conscars (Collision BSP structure and phantom BSP research)
  • Galap (Researching the effect of cluster sky index on lighting)
  • Hari (Collision BSP compilation reversing)
  • Ifafudafi (Discovering that PAS data is unused in H1CE)
  • Kavawuvi (Invader tag definitions)
  • MosesOfEgypt (Tag structure research)