# Model components, landing structures and pointer sharing

Update: [animated geometry and linked model states](model-animation-and-states.md)
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. Reproduce with `python3 tools/inspect_model_components.py`.
The machine-readable inventory is [components.json](../analysis/models/components.json).
Addresses below refer to the unrelocated executable image or explicitly to a bank's geometry pool.

## Screen-space discs

The command is `22 colour:u8 radius_numerator:u16le`. In the ordinary projection
path, `FC22` dispatches to `314D`: it subtracts four from the projected-vertex
cursor, takes that vertex's screen coordinates, resolves the colour via `3383`,
and calls `A5C0` with the numerator in AX and projected depth in CX.
Thus it **consumes the last vertex slot**, which subsequent vertices may reuse.
The native A,B,C coordinates below use the model export's convention `(A,-C,B)`
for Y-up coordinates.

At `A5C0`, unsigned integer division gives `radius_numerator / projected_depth`.
The result is capped at 256 screen units; zero takes the single-pixel path.
The following routine constructs clipped horizontal circle spans. There is no
bitmap, UV map or spherical polygon mesh in this command. The near/clipped
handler `3161` rejects depths below 32 before projection. Colours go through the
same distance/runtime shade machinery as flat faces, then the display lookup.
These are internal units, not established metres or an independent world radius.

| Bank / entry | Centre A,B,C | Colour byte | Numerator |
| --- | --- | --- | ---: |
| 30 / city 1, energy beacon | (0,0,-2400) | `0D` | 30720 (`7800`) |
| 30 / city 11 | (0,0,-720) | `1C` | 6144 (`1800`) |
| 30 / city 101 | (198,-198,-160), (198,-198,-608) | `93`, `1B` | 19712 each (`4D00`) |
| 30 / city 102 | (198,198,-160), (198,198,-608) | `93`, `1B` | 19712 each |
| 30 / city 103 | (-198,198,-160), (-198,198,-608) | `93`, `1B` | 19712 each |
| 30 / city 104 | (-198,-198,-160), (-198,-198,-608) | `93`, `1B` | 19712 each |
| 30 and 32 / special 0 | (64,0,0) | `1C` | 2176 (`0880`) |

The tower disc therefore sits at Y=2400 in the exported coordinate system.
For illustration, projected depths 1024 and 2048 yield integer screen radii 30
and 15. `1C` is a dynamic shade token, not simply a fixed palette index 28.
The two discs of types 101–104 are real separate drawing commands.

This inventory covers the statically traversed base definitions, not every
alternate/damage model or unresolved animation branch. No discs were encountered
in bank 31's traversed definitions. Discs are now shown as camera-facing circles in the model preview (and city
viewer), but remain omitted from OBJ surfaces. Their exact definitions and pool
offsets are retained in the JSON; see [preview scaling](model-colours.md#tile-placement-and-screen-space-discs).

Other non-polygon components encountered are opcode `1E` coloured lines:
city types 65 and 67 in bank 30, type 41 in bank 31, and 111 and 112 in bank 32.
There are also model-header distant-point representations and conditional
visibility branches; these are not additional mesh faces. The static inventory
is deliberately not a claim to have exhausted runtime effects or particles.

## Fountain reflections: authored geometry, not transparent water

[Open the assembled animated fountain](../analysis/models/fountain.html).
Its twelve components are placed from map 68: two instances each of types
101–104 and one each of 120–123. The authored face/disc order reproduces the
tinted reflection over the opaque water; switching off **Authored drawing order**
shows why ordinary polygon depth sorting breaks this effect. Animation starts
automatically and the clock can be paused/scrubbed. The view remains orthographic,
with approximate between-component sorting and disc sizing.

The facing-test sign was checked against 256 controlled executions of native
`2F65`: it skips when the screen-space cross product is negative, as used by
the viewer. The earlier reflection failure was caused by inspection depth
sorting, not an inverted conditional branch. This check covers the sign for
supplied projected vertices, not equivalence of every perspective camera pose.

Visual inspection identified the lower discs and pedestal shapes as reflections.
The bank-30 type-101 data confirms reflection about the water plane at exported
Y=384: the upper light is at Y=608, the lower copy at Y=160, exactly 224 units
on either side. Types 102–104 repeat the arrangement in the other quadrants.
Both discs use radius numerator 19712, but the upper colour is `1B` and the
reflected colour is `93`, giving the reflection its authored tint.

The lower pedestal includes exact mirrored polygon counterparts: replacing
native C with `-768-C` maps upper faces to lower ones, with different face
colours (for example `47` becomes `86`, and `70` becomes `8F`). These are stored
vertices and palette choices, not a runtime reflection transform.

There is **no transparent-water material** in this model. The ordinary opaque
water faces (colour `8B`, Y=384) are issued first, followed by the reflected
disc and tinted pedestal geometry, then the remaining fountain structure.
For type 101, water commands start at pool `9D19`, the reflection disc is at
`9D45`, and reflected pedestal faces follow at `9D49` onward. Stored command
order matters more than increasing byte offsets because the model calls shared
subroutines. The software renderer has no depth buffer to reject those later
reflection pixels for lying below the water plane.

Thus the appearance of seeing through water is an authored reflection illusion:
opaque colours and drawing order, not alpha blending. No semitransparent model
material has been identified in the decoded retail model command set. Collision
overlay translucency belongs solely to the inspection UI. This does not claim
that every other screen effect or sprite in the game uses the same mechanism.

## Landing gates and animated geometry

HQ gates were proposed for 17,19,21,23 and approach stripes for 18,20,22,24.
The executable strongly supports the **landing gate/approach** identification:

- `C598–C5A5` fetches the cell beneath the Caero. During the automated landing
  state, `C5F9` subtracts 17, requires a result below 8, then shifts right.
  The odd/even branch therefore distinguishes the gate/stripe pairs explicitly.
- `C656–C668` advances an animation value by `CS:[7A2B] << 6`, capped at `E800`.
  `C631` stores that value at `SS:7935`. Another branch computes it from approach
  distance; landing/takeoff state also affects it (`C5A7–C5BD`).
- Each of gate types 17,19,21,23 contains `32 00`, at pool offsets `1408`, `168E`,
  `1731`, `1D11`, respectively. Handler `319E` reads the low five cell-state bits,
  adds the command's parameter byte and indexes words at `SS:7935`.
  The `32/35/38` handlers then alter projected geometry using that parameter.
- `C670–C6EF` checks the landing approach, installs the automated movement
  callback and toggles state bit `80` on the selected cell and its two neighbours.
  That bit also selects alternate model definitions, as documented previously.

This is stronger evidence than spatial proximity alone. It does not establish
that *every occurrence* belongs to one of the four specifically named HQs on the
paper map. The main-city resource contains nine type-17 and eight type-21 cells,
with the corresponding stripe immediately in front of each. Types 19/20 and
23/24 do not occur in that resource. Other mission state may alter the map.

Types **14 and 16 are empty base drawing definitions**, not currently undisplayed
HQ meshes: both point at pool `0FC9`, whose body `5A 52 61 0D` emits an origin
vertex and returns without polygons. Types 78 and 125 share it. Neither 14 nor
16 occurs in the supplied main-city map. An additional role elsewhere is not excluded.

Types **120–123** use another dynamic transform, `3B 09`, at pool offsets
`BDBB`, `C037`, `C197`, `C2F7`. Handler `334A` reads the same parameter table and
scales projection coefficients. `DB1A–DB69` updates six parameters beginning at
`SS:7947` (index 9), driven by the clock and a sine lookup. This identifies a
procedural animation dependency. Subsequent visual inspection identified these as
probable fountain effects: their four map cells form the central 2×2 of the
fountain, surrounded by pool types 101–104. See the
[assembled scene and coordinates](city-scene.md#fountain-identification).
The eight animated definitions still need correct transform emulation before
claiming complete exports.

## Shared pointers versus copies

Within a bank, the repeated entries overwhelmingly are **aliases to the exact
same drawing stream**, not duplicated meshes. The 33 special records use only
23 distinct pointers in bank 30, 9 in bank 31, and 19 in bank 32.

| Bank | Special slots | Shared pool offset |
| --- | --- | --- |
| 30 | 19,24,26,27 — Skimma geometry | `D25F` |
| 30 | 25,28,29 — Caero geometry | `C9E0` |
| 30 | 11,13,16 | `C819` |
| 30 | 12,17 | `C8C0` |
| 31 | 19,24,25,26,27,28,29,30,31,32 — Skimma geometry | `4E53` |
| 31 | 0–10,15 | `4C37` |
| 31 | 11,13,16,18 | `4C8C` |
| 32 | 19,22,23,24,26,27 | `C1AF` |
| 32 | 25,28 — Caero geometry | `B930` |
| 32 | 29,30,31,32 | `C5A4` |

Every group, including the remaining pairs and singletons, is in `components.json`.
The early special objects were identified as ammunition through gameplay; individual weapon names
have not yet been assigned from their behaviour records. The apparent repeated
tanks can be checked by their numeric slots in this table/inventory without
assuming a tank name from silhouette alone.

The executable retains separate **24-byte object definitions at `1926+24*i`**;
the bank loader fills their model pointers. Equal geometry therefore does not
imply equal behaviour, weapons, damage or player controls.

Across banks, data lives in separate resource pools: there is no cross-bank
pointer sharing in this directory format. Shape comparison, ignoring colour
and shade attributes, finds bank-30/31 Skimma geometry identical, bank-30/32 Caero
geometry identical, and specials 20 and 21 geometrically identical across all
three banks. Their complete colour-bearing exports are not all identical.
The underground Skimma has 97 polygon commands versus 99 in banks 30/31.
Drawing streams also use relative subroutine calls; their actual targets are
listed, rather than treating each directory slot as a self-contained mesh blob.

## Follow-up audit: empty Delphi types 14, 16, 78 and 125

These four IDs have **identical eight-byte directory records**, `c9 0f 80 80 00 00 00 00`, not merely similar-looking previews. All select pool offset `0FC9` (resource offset `1457`). None occurs in the supplied Delphi map, resource 00/68.

The 11-byte header is `00 00 00 00 f0 86 ff 00 00 00 00`: both damage and alternate links are zero. The complete drawing body is `5A 52 61 0D`, which produces one origin vertex and returns. There are **no polygon faces, lines, light discs, transforms or subroutine calls**. All 256 possible state-byte values retain that same empty result.

An audit of all 154 unique city headers reachable through the bank's type directory and state links found no incoming damage/alternate links to this definition. None of the 33 special-object pointers aliases it. Decoding city/state and special drawing streams found no calls into it; a broader raw scan also found no relative call/jump candidates into its 15-byte header/body range.

The evidence therefore supports **unused empty directory slots in the shipped Delphi base assets**, plausibly placeholders left by content development. Their position next to particular building IDs does not establish a hangar, collision trigger or other identity. There is no concealed model or non-polygon visual component to recover from these bytes.

This is not proof that the IDs can never be introduced dynamically: the audit does not yet cover every executable path and mission-script operation. No specific gameplay purpose has been established for them. Likewise, this finding applies to bank 30; the same numeric IDs in other banks are different definitions.

Reproduce with `python3 tools/inspect_empty_models.py`; results are in [empty-model-audit.json](../analysis/models/empty-model-audit.json).
