Files
2026-10-25 03:29:04 -03:00

3.3 KiB

.hmap file format (version 1)

Chunked, little-endian binary. Readers must skip chunks they do not know, so new chunks can be added without breaking old readers. A file may carry source data (editable), compiled data (runtime), or both.

header   "HMAP" (4 bytes) | u32 version (=1) | u32 flags (bit0 = source chunks, bit1 = compiled chunk)
chunk*   tag[4] | u32 payloadSize | payload

Primitive types: u8 u32 i32 f32 (LE) - str = u32 byteLength + UTF-8 bytes (no terminator) - vec2/vec3 = 2/3 f32 - plane = vec3 normal + f32 d (points p on the plane satisfy dot(n,p) == d, solid is on the back side dot(n,p) < d) - aabb = vec3 min + vec3 max - uv = vec2 offset, vec2 scale, f32 rotation(deg).

Tag Content
META str name, vec3 ambient, f32 gridSize, f32 unitsPerMeter, u32 nextId
MATS u32 n, then n x { str name, str texture, vec2 tileSize, vec3 color, u32 flags }
BRSH u32 n, then n x { u32 id, u32 owner, u32 contents, u32 flags, u32 faceCount, faces: { plane, i32 material, uv, u32 flags } }
MESH u32 n, then n x { u32 id, u32 owner, u32 flags, str name, u32 vertCount, vec3[], u32 polyCount, polys: { i32 material, u32 smooth, uv proj, u32 idxCount, u32[], u32 uvCount, vec2[] } }
ENTS u32 n, then n x { u32 id, u32 flags, str classname, vec3 origin, vec3 angles, vec3 scale, u32 kvCount, (str key, str value)[] }
CMPL compiled map, see below
  • owner = id of the brush entity that owns the object (0 = world). contents bits: 1 solid, 2 water, 4 clip, 8 trigger, 16 sky.
  • Mesh polygons are convex, indices are CCW seen from the front. uvCount is either 0 (auto-projected with proj) or idxCount.
  • Angles are (pitch, yaw, roll) degrees, applied as R = Ry(yaw) * Rx(pitch) * Rz(roll); forward = -Z at zero angles.

CMPL (compiled)

vec3 ambient | f32 unitsPerMeter | u8 baked | aabb bounds
u32 batchCount, then { i32 material, u8 source (0 brush/BSP, 1 mesh), i32 model,
                       u32 vertexCount, vertex[] { vec3 pos, vec3 normal, vec2 uv, u32 colorRGBA },
                       u32 indexCount, u32[] }               // triangle lists, CCW front
u32 modelCount, then { u32 entityId, aabb, u32 brushCount, collisionBrush[] }   // model 0 = world
BSP: i32 root | u32 depth | aabb | u32 planeCount, plane[] | u32 nodeCount, { i32 plane, i32 front, i32 back }[]
     | u32 leafCount, { u32 firstBrush, u32 brushCount }[] | u32 leafBrushCount, u32[] | u32 brushCount, collisionBrush[]
u32 meshCollCount, then { u32 objectId, aabb, u32 vertexCount, vec3[] }          // 3 vertices per triangle
u32 lightCount, then { u8 type (0 point, 1 spot, 2 sun), vec3 pos, vec3 dir, vec3 color,
                       f32 intensity, f32 radius, f32 innerDeg, f32 outerDeg, u32 entityId }
collisionBrush = u32 contents, u32 sourceId, aabb, u32 planeCount, plane[]

BSP child links: >= 0 is a node index, < 0 is leaf ~value. Every node splits on planes[node.plane] (front = dot(n,p) - d >= 0). Leaves list collision brushes; a point is solid when it lies behind all planes of a brush. Colour is little-endian RGBA bytes (0xAABBGGRR); it holds the baked vertex lighting when baked = 1, otherwise white.

Entities and materials in a compiled-only file come from the ENTS / MATS chunks that precede CMPL.