49 lines
3.3 KiB
Markdown
49 lines
3.3 KiB
Markdown
# `.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`.
|