The first-person cockpit now shows by default and matches gameplay footage (verified on the Madcat). Two decomp-verified reconstructions: EYEPOINT (FUN_004579a8 + caller part_014.c:5525, FUN_004c22c4): - eye offset = siteeyepoint GetBaseOffset (not GetSegmentToEntity, not the upright hack), parented on the PARENT segment DCS (btl4vid.cpp) - live view = affine INVERSE of the eye world matrix (L4VIDRND Execute) -- replaces the hand-rolled LookAtRH whose +Z-forward/+Y-up row guess aimed some mechs into the canopy; combine order fixed to baseOffset * R - chase camera basis + aim boresight re-expressed for the inverse convention (row2 = back); boresight now derives from the view matrix (the stale rows aimed the pick ray at the sky = no target, no discharge) CANOPY (*_COP.BGF, dpl_Punchize geogroups): - every shell is an open strut lattice (38-59% boundary edges, all 12 mechs); rendered single-sided with per-face INWARD winding (l/r torso patches are mirrored, so no global winding works) the struts are the dark frame and the openings show the world - frame colour = the 'softer' ramp near its dark end (texture-less unlit ramp; BLXSKIN.BMF has no texture -- the old "punch texels" theory was wrong) - scoped by filename (meshIsCop); wrong drop-punch + view-cut paths removed - BT_FORCE_MODEL=<name> forces the player mech for per-mech bring-up KB: new context/cockpit-view.md topic; punch split corrected in bgf-format; gotchas 14 (LookAt axis guess) + 15 (per-patch edge namespaces); env gates + eye/punch addresses in decomp-reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.8 KiB
id, title, status, source_sections, related_topics, key_terms, open_questions
| id | title | status | source_sections | related_topics | key_terms | open_questions | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| bgf-format | BGF Model Format (DIV-BIZ2 geometry) + the render gotchas | established | docs/BGF_FORMAT.md (full spec); CLAUDE.md §5, §10 render-fidelity notes |
|
|
|
BGF Model Format
.BGF = DIV-BIZ2 little-endian nested Tag-Length-Value geometry. Full byte-level parsing
spec (verified against all 1275 content .BGF files) lives in docs/BGF_FORMAT.md — this
topic captures the structure + the hard-won gotchas that cost real debugging. Loader of record:
engine/MUNGA_L4/bgfload.cpp (game-callable via LoadBgfFile); standalone reference reader
DivLoader/VGCDivLoader.cpp. Tag names: dsys/PFBIZTAG.H.
Structure
OBJECT → LOD → PATCH (geogroup) → PMESH. Interleaved float32 verts; QUAD faces (must
triangulate [a b c][a c d]); materials referenced by library:material name (resolved
against the map's .BMF libs). Damage/articulation are name-driven via SV_SPECIAL (0x2037)
dz_* tokens, not stored geometry. [T1]
Indices — the CONN/PCONN story (two corrections)
A PMESH carries faces in CONNECTION_LIST (0x0047) and/or PCONN_LIST:
CONNECTION_LISTis a FLAT TRIANGLE LIST (3 indices/face) — NOT "one polygon, fan-triangulate" (the original doc was wrong; fan-triangulating turned buttec's 115 tris into 341 fan-garbage). Corpus-verified: ALL 3488 CONN chunks have n%3==0. [T2]- CONN and PCONN COEXIST — a pmesh can carry quads/hexes in PCONN PLUS loose triangles in
CONN. 370 of 841 GEO models had mixed pmeshes silently dropping their CONN triangles until
both were processed always (the "turret base missing panels" bug).
ppf=6hexagons exist. [T2]
LODs — the √3 decode
Each LOD chunk carries a 0x2046 header = [near..far) viewing band. The board selected the
LOD whose band contains the camera distance.
- √3 range decode: every stored band value is the authored euclidean distance ÷ √3 (92% of
the corpus is exactly nice-number/√3; hand-conversion typos prove it). Multiply parsed bands by
√3(kLodRangeScale=1.7320508). [T2] - Metric = the instance-transformed OBJECT ORIGIN distance, ONE shared
dfor all LODs of an object (a bounding-sphere-SURFACE metric desyncs sibling bands — wrong). [T2] - ADDITIVE_LODS (object-level
SV_SPECIALtoken, 135 pod BGFs — all arena structures): at eye distance d, EVERY LOD withd < OutDistdraws (near detail ADDS onto coarser massing). Non-additive keep first-LOD-only (the 2007 engine's own behavior). [T2] - PUNCH (patch-level
SV_SPECIAL,dpl_Punchize= FUN_00490308 → VPX board cmd0x20, tokens{0x80000003,0x80000017,0x80000004}— a CONSTANT geogroup-modifier opcode, no color key; the siblingdpl_DamagizeFUN_004902b0 uses the same cmd with a different triple). Two cases: [T1]- Textured punch (arena walls/catwalks): black texels = HOLES (alpha-test in the opaque pass). Coplanar shells inside one LOD z-fight → depth-bias by submission ordinal. [T2]
- Texture-less punch (the
*_copcockpit canopies: blakskn +softerramp, NO texture tag in the BMF — byte-verified, 238-byte BLXSKIN.BMF): the frame/window split is the GEOMETRY — every_copis an open strut lattice (38-59% boundary edges, all 12 mechs); rendered single-sided with per-face INWARD winding orientation (the l/r torso patches are MIRRORED, so no single global winding works) the struts are the dark frame and the openings show the world. The old "canopy windows are punch texels" claim was WRONG — there is no texel to key. See cockpit-view. [T2 render-verified in-game on Madcat vs gameplay footage]
Shading model (from libDPL DPLTYPES.H)
Shading is selected PER-GEOMETRY by vertex type:
- No-normal geometry (XYZ_UV / XYZ_RGBA_UV: mesas/ground/sky/buildings/mech, WHITE baked verts) is UNLIT, colorized by the material's 2-endpoint RAMP (see material-ramp).
- Normal-bearing geometry (XYZ_N_UV: ~150 vehicle/missile files) is LIT by the map light.
- Applying the ramp to lit geometry = the "dusty white blobs" bug — gate on
hasNormals. [T2]
Skeleton assembly
A mech is not one BGF — it's a .SKL skeleton (a DCS tree of joints) whose each joint's
Object=part.bgf is loaded + posed, then animated by .ANI clips. See asset-formats /
locomotion for skeleton + animation. [T1]
Key Relationships
- Detailed spec:
docs/BGF_FORMAT.md. - Consumed by: rendering (the D3D9 draw path), locomotion (skeleton parts).
- Related: asset-formats (BMF/BSL/SKL/ANI), decomp-reference.