Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
5.8 KiB
Markdown
84 lines
5.8 KiB
Markdown
# Asset Pipeline — Textures, Materials & Gauges
|
||
|
||
Companion to `BGF_FORMAT.md`. Covers how a model's surface resolves to pixels, and the cockpit
|
||
gauge/HUD formats. Verified by hexdump against real content; header cites are `file:line` under
|
||
`…/CODE/RP/MUNGA_L4/libDPL/`.
|
||
|
||
## Format inventory (CONTENT/BT)
|
||
| Ext | Count | Kind | Role |
|
||
|---|---|---|---|
|
||
| `.bgf` | 1275 | `DIV-BIZ2` FILETYPE=0 | 3D geometry (see BGF_FORMAT.md) |
|
||
| `.bmf` | 2222 | `DIV-BIZ2` FILETYPE=1 | material + texture **libraries** (no pixels) |
|
||
| `.vtx` | 5 | `DIV-VTX2` | raw RGB texel image |
|
||
| `.tga` | 8 | Truevision TGA2 type-2 | 24-bit truecolor texel image |
|
||
| `.sgi` | 0 | (loader exists) | RGBA texel image (none shipped) |
|
||
| `.bsl` | 3 | `DIV-BSL2` | bit-slice / multi-image + mip container |
|
||
| `.pcc` | 434 | ZSoft PCX, 8-bit RLE | cockpit HUD/gauge **raster** bitmaps |
|
||
| `.gim` | 311 | ASCII INI | MFD/radar **vector** line-gauges (NOT raster) |
|
||
| `.gat` | 3 | ASCII INI | gauge color/attribute table |
|
||
|
||
Layout: `VIDEO/GEO/*.bgf`, `VIDEO/MAT/*.bmf`, `VIDEO/TEX/*.vtx,*.bsl` (+ `TEX/BUILD/*.tga`),
|
||
`VIDEO/BUILD/*` staging, `GAUGE/*.gim,*.gat,*.pcc`, `MODELS/*.mod` (INI tying bgf+gim+sld).
|
||
|
||
## Name-resolution chain (verified)
|
||
```
|
||
BGF geogroup SV_F_MATERIAL "library:material"
|
||
→ open library.bmf
|
||
→ MATERIAL chunk whose NAME == "material"
|
||
→ its MATERIAL_TEXTURE (0x0021): [u8 type=2 NAMED][texture-name string]
|
||
→ TEXTURE chunk whose NAME == that texture-name
|
||
→ its TEXTURE_MAP (0x0011): image basename (no ext)
|
||
→ loader appends ext, searches texmap path → .vtx/.tga/.sgi/.bsl
|
||
```
|
||
`dpfCreateMaterialName(extPath,library,mname)` `PFILE.H:821`.
|
||
|
||
## .BMF chunk layout (same TLV grammar as BGF; tags in `PFBIZTAG.H:43-62`)
|
||
**TEXTURE `0x0010`** (`dpfTEXTURE`, `__PFILE.H:120-137`): NAME `0x2008` (string); TEXTURE_MAP `0x0011`
|
||
(basename string); MINIFY `0x0012`/MAGNIFY `0x0013` (mip/filter enums, `PFILE.H:155-162`); ALPHA `0x0014`
|
||
{blend/cut/blendcut}; WRAP_U/V `0x0015/16` {repeat/clamp/select}; DETAIL `0x0017`; BITSLICE `0x0018` (u8 → .bsl slice).
|
||
**MATERIAL `0x0020`** (`dpfMATERIAL`, `__PFILE.H:152-169`): NAME `0x2008`; MATERIAL_TEXTURE `0x0021`
|
||
[`u8 type`(2=named)+name]; AMBIENT `0x0023`/DIFFUSE `0x0024` = 3×f32 RGB (verified); SPECULAR `0x0025`/
|
||
EMISSIVE `0x0026`/OPACITY `0x0027` (optional, inferred 3-4 f32); RAMP `0x0028` (ramp-name string).
|
||
**RAMP `0x0030`**: NAME + RAMP_DATA `0x0031` (float color data).
|
||
|
||
## Pixel formats (normalize all to 32-bit RGBA at load)
|
||
- **.VTX (`DIV-VTX2`):** TLV; size tag `0x2062` = two int32 (w,h, e.g. 128×128); body = **uncompressed RGB
|
||
3 B/px**, top-to-bottom. Type enum `dpiBSLTYPE` (`PIMAGE.H:104-116`): MONO0-5/BILINEAR/RGB/RGBA.
|
||
- **.TGA:** standard type-2, 24-bit BGR, TGA2 footer. `dpl_tgaRead`.
|
||
- **.SGI:** RGBA (`dpl_sgiRead`; none shipped).
|
||
- **.BSL (`DIV-BSL2`): DECODED — a BIT-SLICED multi-image container** (corpus-verified on all 66 archive
|
||
BSLs; reference = the shipping WinTesla loader `DivLoader/VGCDivLoader.cpp:323-410`
|
||
`LoadBSLFile`/`getBSLData` + Division `dsys/PIMAGE.H` `dpiBSLTYPE` + the content build scripts
|
||
`img2vtx.exe -b -mN <tga>`). Header (int32 LE): w `@0x08`, h `@0x0C`, depth `@0x10` (bits per slice,
|
||
always 4), tableBytes `@0x14` (directory length counted from the nEntries field; imageDataOffset =
|
||
`0x18 + tableBytes`), nEntries `@0x18`; directory entries `{int32 recLen; int32 sliceType; char
|
||
name[recLen-4]}` — **entry names are authoring records only, read-and-discarded at runtime**; then
|
||
`w*h` little-endian 32-bit texel WORDS. Byte 0 of each word = pad (structurally unused, 0 in all 66
|
||
files); the other 3 bytes = SIX independent **4-bit GRAYSCALE slices**, nibble PAIR-SWAPPED (even slice
|
||
= HIGH nibble): slice0=bits12-15, 1=8-11, 2=20-23, 3=16-19, 4=28-31, 5=24-27 (`shift=(c+((c+1)%2)*2)*4`,
|
||
value returned `<<4` = 0x00..0xF0). sliceType 7 = **RGB444** (r=s5,g=s4,b=s3), 8 = **RGBA4444** (+a=s2);
|
||
both coexist with mono slices in one file (BDAM/BEXP/BDET). Slice SELECTION = the BMF TEXTURE record's
|
||
`BITSLICE` tag `0x18` (u8; **absent = slice 0**) — e.g. BLHSKIN.BMF blkhwk1_tex=absent(0),
|
||
blkhwk2/3/4_tex=01/02/03, all mapping image basename `blkhwk` → BLKHWK.BSL. Mono slices are colorized
|
||
downstream by material RAMP (+ diffuse tint on neutral ramps) — all mech skins, vehicle atlases
|
||
(BASEV = 6 gray sheets, NOT truecolor), logos, effects grit. ⚠ The previous "base image = trailing
|
||
w*h*bpp, pixel order [pad,R,G,B]" claim was WRONG — it overlaid 2-3 different gray slices as RGB
|
||
channels (the rainbow "graffiti" mech-skin bug); it survives as the `BT_BSL=0` diagnostic fallback in
|
||
`port/src/image.cpp decodeBSL`.
|
||
|
||
## Cockpit gauges
|
||
- **.GIM** = ASCII INI, **vector** top-down line drawing for MFD/radar: `[lods] level0=3000…`,
|
||
`[vertices] v0=x,y,z`, `[level0] linelist=<group> v0 v1 v2…`. First token = color/attr group resolved
|
||
via `.gat`. Feeds the `dpl2d_*` 2D line path. Verified `AB01_GA.GIM`.
|
||
- **.GAT** = ASCII INI: `[colors]`,`[groups] a_bld=amber1…` → group→palette-index. Verified `GAUGE.GAT`.
|
||
- **.PCC** = ZSoft **PCX**, 8-bit RLE, 256-color palette (palette after `0x0C` EOF marker); 640×480-class.
|
||
The actual HUD raster bitmaps → need 8-bit-palette → RGBA expansion. Verified `ADPAL.PCC`.
|
||
|
||
## ⚠ Dependencies / open items
|
||
1. **The retail texel raster set is NOT in this archive** — only the BUILD/logo subset of `.vtx/.tga/.bsl`
|
||
ships. The 2222 `.bmf` reference maps that aren't here. **Need the full texture content from Nick**, or
|
||
the renderer must tolerate missing maps and fall back to material DIFFUSE color.
|
||
2. **No source for the `.gim/.gat/.pcc` loaders** — inside the closed `LIBDPL.LIB`. `.pcc`(PCX) and
|
||
`.gim/.gat`(INI) are standard/simple. `.bsl` and `.vtx` are now FULLY decoded (see Pixel formats above;
|
||
the shipping `DivLoader/VGCDivLoader.cpp` turned out to carry the reference reader for both).
|