DARKER Research archive

First static city-model extraction

Update: animated geometry and linked model states now implements gate/fountain transforms, exports 27 additional linked definitions, and adds state/animation controls. Earlier references below to those models being unresolved describe the previous extraction pass.

2026-09-18. Open the interactive type atlas. Each selectable type has a rotatable preview and an OBJ download. Pair bank 30 with map 68, bank 31 with map 69, and bank 32 with maps 70–77. This supplies a visual key; most semantic building names have not been assigned. Bank 31 type 3 is identified by the automatic supply-pad landing code.

362 of 391 city type records, plus 99 special-object entries, export successfully. These are type records, not 461 distinct meshes: aliases and variants share geometry. Eight city records encounter unsupported commands, and 21 city records produce no polygon faces on this path. The manifest records every success/failure without substituting guessed shapes.

The first bank includes recognizable wall sections/junctions, towers and other city structures. The provided city-reference map can help label landmarks, but its labels have not yet been registered to grid coordinates.

Bytecode, not a conventional vertex array

Rendering starts at model-header offset +11 (2CB6–2D11). A byte chooses an entry inside the executable's FCxx dispatch page; FExx supplies the clipping path. Some byte values enter the middle of a shared instruction sequence.

Examples from the far/render path:

Opcode Static interpretation
0D Return from drawing subroutine
3E Call relative subroutine; signed displacement relative to its own word
4D Relative jump
25, 2C Set/reset output vertex cursor
8E Read signed 16-bit C, A, B components; emit vertex
91 Replace A and B, retain C; emit
7F, 88, 94 Replace C, A or B respectively; emit
73, 76, 79 Negate C, A or B respectively; emit
5E, 61, 64 Zero C, A or B respectively; emit
52/56/5A, 67/6B/6F Zero/negate a component, then continue without emitting
02, 03 Triangle/quad, colour byte followed by vertex indices
01 Explicit count-minus-one, colour and indices
0E, 0F, 11 Gouraud triangle/quad/explicit count; each vertex index has a shade byte
16 Camera-facing test and conditional skip
1A Distance test and conditional skip
1E Coloured line between two indexed vertices

Thus a symmetric shape can generate mirrored points with one-byte instructions instead of repeating all three coordinates. Signed relative calls also let multiple model records share a vertex-generation subroutine.

Export semantics and limits

The exporter executes the supported static command stream, retaining coordinate state across calls. Faces snapshot the vertices they reference, so later cursor reuse does not overwrite previously recorded geometry. It chooses fall-through for visibility/distance tests, intentionally retaining surfaces normally culled. This can combine mutually exclusive details and does not reconstruct painter ordering. It stops on unknown instructions and does not emit a partial mesh under a successful filename.

Coordinates remain in native integer units; OBJ axes are (A, -C, B) with Y up. The viewer now uses recovered palettes and face/vertex shades; neutral artificial shading remains an optional inspection mode. OBJ/MTL exports preserve the flat fallback colours. See colours and damage states. Game fades, exact rasterization, animated/procedural commands and special-object naming remains incomplete. Links to exported damage models are navigable; some variants have no separate directory type and are not yet exported.

Verification

tools/verify_model_vertices.py runs the original x86 coordinate interpreter in Unicorn for every successfully exported type. It extracts the vertex-command sequence, omitting drawing and control flow, and configures two independent axis projections. All 32,900 vertex events, across 461 exported entries, agree with the decoded coordinates in both projections.

This checks the coordinate-command semantics, including component reuse and negation. It does not independently validate polygon topology, shade interpolation, visibility branching or axis orientation against gameplay. The standalone viewer also loads without browser JavaScript errors; thumbnails and the large preview were visually inspected.

python3 tools/extract_models.py
PYTHONPATH=/path/to/unicorn python3 tools/verify_model_vertices.py
python3 tools/model_viewer.py

Outputs: analysis/models/manifest.json, vertex-verification.json, bankNN_typeNNN.obj, and standalone index.html.

Ships and the light tower

The special-object directory is now exported as a separate atlas category. Bank 30 special object 25 is the Caero player model; bank 31 special objects 26/27 supply ordinary/upgraded Skimma slots and share geometry. See ships and towers for pointers and aliasing limits.

Opcode 22 is now decoded as a camera-facing screen-space disc at the most recent output vertex. It decrements the output cursor, reads a colour and an unsigned radius numerator, and passes numerator / projected depth to the disc rasterizer. Its metadata is preserved, but it is omitted from OBJ polygon surfaces and the viewer. This unlocks the polygon bodies of six city types, including bank 30 type 1, the energy tower. Unsupported transform commands 32 and 3B remain explicit export failures.