Files
firestorm/MFD-RADAR-MAPPINGS.md
T
dicion 9755803949 Fix external MFD and radar damage mappings
Install the validated J&J coordinates for 19 external display sets across 13 chassis. Update all affected texuv2/offset2 and texuv3/offset3 rows while preserving the 65-mech positional layout and coord.cpp CRLF encoding.

Add a reusable Pillow comparison generator, 38 red/green review maps, and a generated summary. The final audit reports 19 exact mappings, zero differences, zero input warnings, and seven display sets without complete supplied inputs.

Normalize four unambiguous measurement transcription issues: Behemoth MFD LT 1742 to 174, Behemoth Radar CT punctuation and blank S2, and Fafnir MFD LL punctuation.

Document the complete authoring and runtime workflow, including legacy tuple normalization, MFD/Radar scaling, odd-coordinate handling, canonical runtime names, pixel-versus-byte BMP comparison, validation checks, rebuild requirements, and the final installed-set inventory.

Compare every supplied exploded runtime BMP against Gameleap/mw4/hsh. All 19 are already pixel-identical, so no runtime art files are replaced.
2026-08-07 20:46:32 -05:00

452 lines
21 KiB
Markdown

# MFD and Radar Damage Mapping Guide
This document explains how the external MFD and Radar damage-paper-doll mappings are authored,
stored, rendered, checked, and added for a new 'Mech. It records the workflow established while
reviewing and installing all usable art under `Finished HUDS from J&J/` on 2026-08-07.
## Scope and terminology
There are three related damage displays. Do not mix their mapping tables:
| Display | Mapping source | Art path |
|---|---|---|
| Normal in-cockpit HUD | `Gameleap/code/mw4/Code/MW4/huddamage.cpp`: `texuv` and `offset` | Content texture `hud\<name>` |
| External MFD | `Gameleap/code/CoreTech/Libraries/GameOS/coord.cpp`: `texuv2` and `offset2` | Loose `Gameleap/mw4/hsh/hud/<name>.bmp` |
| Radar secondary damage display | `coord.cpp`: `texuv3` and `offset3` | Loose `Gameleap/mw4/hsh/radar/hud/<name>.bmp` |
This guide is about the last two displays, both controlled by `coord.cpp`. The nearby `// MFD`
comments refer to the external display. The normal cockpit HUD has different coordinates in
`huddamage.cpp`.
The small images in `hsh/MFD/` are also a different asset set. `render.cpp` tiles those images
into a mech texture atlas. They are not the 512x512 damage-mask images mapped by `coord.cpp`.
## Controlling code
`coord.cpp` defines four positional arrays:
```cpp
float texuv2[65][11][4]; // MFD source rectangles
int offset2[65][11][2]; // MFD exploded destination positions
float texuv3[65][11][4]; // Radar source rectangles
int offset3[65][11][2]; // Radar exploded destination positions
```
The first index is the numeric Mech ID. It must match the positional ID sequence in
`Gameleap/code/mw4/Code/MW4/MechLabHeaders.h`. At present there are 65 active IDs, 0 through
64. Each array therefore has exactly 65 active rows in the same order, from Annihilator through
Zeus. Dasher rows exist but are commented out because Dasher is not active in the roster.
The second index always uses this eleven-zone order:
```text
0 LL left leg
1 RL right leg
2 LA left arm
3 RA right arm
4 RT right torso
5 LT left torso
6 CT center torso
7 CTR center torso rear
8 HD head
9 S1 special 1
10 S2 special 2
```
Use `{0,0,0,0}` and `{0,0}` for a zone that has no art. The comment above `offset2` currently
says `S2 S1`; the array consumers and the other tables use index 9 as S1 and index 10 as S2.
Treat the header comment as stale and preserve the index order above.
## What the values mean
An unexploded rectangle is recorded as:
```text
(x0, y0, x1, y1)
```
`(x0,y0)` is the upper-left pixel and `(x1,y1)` is the lower-right pixel of that component in
the prepared unexploded image. In code this becomes one `texuv2` or `texuv3` entry.
An exploded position is recorded as:
```text
(x2, y2)
```
It is the upper-left destination position of that component in the exploded layout. In code
this becomes one `offset2` or `offset3` entry. The component keeps the width and height implied
by its unexploded rectangle:
```text
width = x1 - x0
height = y1 - y0
exploded bounds = (x2, y2, x2 + width, y2 + height)
```
Coordinates are normally even because both authoring pipelines were designed around even pixel
boundaries. Do not silently round a visually validated odd value: several supplied sets contain
intentional odd coordinates, including Assassin II Radar S1 `y=3`, Behemoth MFD RA `x=281`, and
Black Hawk Radar RT `x=85`. Radar's integer `/2` truncates odd values at runtime, so changing an
odd authored value can move an edge or origin in the rendered 256x256 view. Work from the
upper-left to lower-right of each component, and do not overlap source rectangles.
## MFD authoring pipeline
Start with the finished 1024x1024 full-color 'Mech image.
1. Reduce the image to 320x320.
2. Expand the canvas to 340x340, centered. This adds 10 pixels on every side.
3. Separate adjacent body sections with a 4x4-pixel black line so source rectangles do not
overlap or bleed into one another.
4. Save this as the unexploded working view.
5. For every zone, record its unexploded `(x0,y0,x1,y1)` rectangle, preferring even values.
6. Starting from the unexploded view in the upper-left, place copies of the separated pieces in
their exploded positions.
7. Record each exploded piece's upper-left `(x2,y2)` coordinate, preferring even values.
8. Expand the canvas to 512x512, anchored at the upper-left. Added area is black.
9. Convert the exploded view to indexed color mode and save it as the runtime BMP.
Enter the rectangle values in `texuv2` and the exploded upper-left values in `offset2`.
At runtime, `huddamage.cpp` draws an MFD component as:
```cpp
mfd_device.DrawTexture(
offset2[mech][zone][0] + 100,
offset2[mech][zone][1] + 40,
color,
texuv2[mech][zone][0],
texuv2[mech][zone][1],
texuv2[mech][zone][2],
texuv2[mech][zone][3]);
```
The `+100,+40` values position the complete paper doll in the MFD UI. They are not part of the
authored coordinates and must not be added to `coord.cpp`.
`CMFD_Device::LoadDamageTexture()` receives the logical name `hud\<name>` and loads the loose
bitmap at `hsh\hud\<name>.bmp`.
## Radar authoring pipeline
Start with the same finished 1024x1024 full-color image.
1. Reduce the image to 400x400.
2. Expand the canvas to 410x410, centered. This adds 5 pixels on every side.
3. Expand the canvas to 512x512, anchored at the upper-left. Added area is black.
4. Save this as the unexploded working view.
5. Split the body into non-overlapping components from each component's upper-left to
lower-right.
6. Outline components with a 2x2-pixel white line.
7. Record each unexploded `(x0,y0,x1,y1)` rectangle, preferring even values.
8. Starting from the unexploded view in the upper-left, place copies of the pieces in their
exploded positions.
9. Record each exploded piece's upper-left `(x2,y2)` coordinate, preferring even values.
10. Convert the exploded view to indexed color mode and save it as the runtime BMP.
Enter the rectangle values in `texuv3` and the exploded upper-left values in `offset3`.
Radar coordinates are authored in the full 512x512 coordinate space. The runtime deliberately
divides every rectangle and offset coordinate by two, switches the texture dimensions to
256x256 for the draw, and then adds the Radar UI origin:
```cpp
radar_device.DrawTexture(
(offset3[mech][zone][0] / 2) + 138,
(offset3[mech][zone][1] / 2) + 406,
color,
texuv3[mech][zone][0] / 2,
texuv3[mech][zone][1] / 2,
texuv3[mech][zone][2] / 2,
texuv3[mech][zone][3] / 2);
```
Do not pre-divide values when editing `coord.cpp`. Author and store the full-size values;
preserve visually validated odd coordinates because runtime integer division determines their
final half-resolution placement.
The runtime loads `hsh\radar\hud\<name>.bmp` through
`CRadar_Device::LoadRadarDamageTexture()`.
## Reading an existing mapping
1. Find the Mech ID in `MechLabHeaders.h`.
2. Confirm that the same row number is used in all four `coord.cpp` arrays. Comments are useful
labels but do not control the mapping; array position does.
3. Read the eleven `texuv` rectangles in the fixed zone order above.
4. Read the eleven matching `offset` points in the same order.
5. Ignore spelling differences in end-of-row comments unless they indicate an actual row-order
error. Examples in the current file include `M_HollnaderII` and capitalization differences.
6. Check that every nonzero rectangle has a corresponding nonzero offset, except where a design
intentionally draws a component at `(0,0)`.
7. Check rectangle bounds, non-overlap, and whether any odd coordinate is intentional and
visually aligned. MFD source geometry is normally inside the 340x340 working area. Radar's
nominal art area is 410x410, but validate imported legacy Radar rectangles against the full
512x512 prepared canvas: accepted Black Hawk geometry extends beyond 410 and aligns visually.
## Making or fixing a mapping
1. Preserve the Mech's row position. Never sort one array independently.
2. Prepare separate MFD and Radar unexploded images using the pipelines above.
3. Measure source rectangles in zone order and record them in a text file before touching code.
4. Prepare exploded layouts and record each upper-left destination point.
5. Verify visually with overlays.
6. Replace only that Mech's row in `texuv2`, `offset2`, `texuv3`, and `offset3` as needed.
7. Preserve `coord.cpp` CRLF line endings and avoid reformatting unrelated rows.
8. Inspect the diff. A one-Mech repair should change at most four rows unless art or roster
registration is also being changed.
9. Rebuild the game because `DXRasterizer.cpp` includes `coord.cpp` directly. Rebuild the needed
Release/Profile targets and redeploy the resulting executable.
10. Compare the supplied exploded BMP to the existing canonical runtime BMP by decoded pixels,
not only by file hash. Replace it only when the pixels differ. See "Runtime art comparison"
below.
11. Install a changed runtime BMP under the matching `Gameleap/mw4/hsh` path. Loose `hsh` art
does not require a resource-package rebuild.
12. Test both intact and damaged states on the real external MFD and Radar displays.
When adding a new chassis rather than repairing an existing row, also increase the first
dimension of all four arrays and add one correctly positioned row to every array. This is part
of the larger positional-ID workflow documented in `ADDING-A-MECH.md`.
## Overlay validation method
Visual overlays are the fastest way to distinguish a coordinate problem from an art problem.
Generate every available J&J comparison and the status report with:
```bash
python3 "Finished HUDS from J&J/generate_comparison_maps.py"
```
This requires Pillow. It writes two PNGs per available display and updates
`Finished HUDS from J&J/COMPARISON-SUMMARY.md`. The summary reports exact numeric matches,
different zones, missing display inputs, and malformed source measurements.
Two J&J measurement formats exist:
- Modern: the Unexploded file contains four-value source rectangles and the Exploded file
contains two-value destination origins.
- Legacy: the Unexploded file contains only each source rectangle's upper-left point, while the
Exploded file contains the complete destination bounding box. For legacy data, derive the
source width and height from the exploded box, apply those dimensions at the unexploded point,
and use the exploded box's upper-left as the destination offset.
Some combined `Coords.txt` files contain both sections. Trust the section heading and tuple
shape, not the filename alone. The generator handles both formats and indexes current mappings
by numeric Mech ID rather than end-of-row comment spelling.
All-zero absent-zone tuples are found in both two-value and four-value forms in legacy files.
Normalize an all-zero tuple to the width expected by its section; it still means no art. For
nonzero tuples, a wrong tuple width is an input error and must not be guessed.
For an unexploded comparison:
- Use the correctly transformed unexploded canvas, not the exploded-pieces BMP.
- Draw the current `texuv` rectangles in red.
- Draw proposed rectangles in green.
- Label each rectangle with its zone.
- A good rectangle encloses one component, follows its outer extent, and does not include pixels
from another component.
For an exploded comparison:
- Use the exploded-pieces BMP.
- Mark each `offset` as an upper-left cross.
- Draw a box from that point using the matching unexploded rectangle's width and height.
- Draw current mappings in red and proposed mappings in green.
- A good exploded mapping places the box exactly over the intended exploded piece.
Do not draw `texuv2` rectangles directly on the raw 1024x1024 source. For MFD, first apply
1024 -> 320, center on 340, then place at the upper-left of 512. For Radar, apply
1024 -> 400, center on 410, then place at the upper-left of 512. When a supplied legacy
unexploded image is already 512x512, use it directly rather than reconstructing and cropping it;
the supplied coordinates may intentionally reference pixels outside the nominal 410 area.
## Runtime art comparison
The exploded 512x512 BMP is the runtime asset. The unexploded BMP and full-color source image
are authoring and measurement references only; do not copy those into `hsh` as the runtime art.
Resolve the destination by the canonical runtime texture stem for the numeric Mech ID, not by
blindly copying the supplied filename. Current destinations are:
```text
MFD: Gameleap/mw4/hsh/hud/<stem>.bmp
Radar: Gameleap/mw4/hsh/radar/hud/<stem>.bmp
```
Known naming traps from this review:
- Assassin II supplied files are misspelled `assian2_*`, but the canonical runtime stem is
`assassin2`; do not overwrite the separate historical `assassinii.bmp` by accident.
- The existing Fafnir runtime file is `Fafnir.bmp` with an uppercase `F` on this case-sensitive
checkout. Resolve destination names case-insensitively, then preserve the existing spelling.
- Folder names, comments, and supplied filenames are labels. Numeric Mech ID and the runtime
texture-name table remain authoritative.
BMP byte equality is stricter than visual/runtime equality. A supplied file may be indexed `P`,
grayscale `L`, or `RGB`, and may carry a different palette, header, row padding, or metadata while
decoding to exactly the same pixels. Compare in this order before replacing anything:
1. Confirm both images are 512x512.
2. If file bytes match, the destination is byte-exact.
3. Otherwise decode both images, convert both to a common mode such as RGBA, and compare every
pixel.
4. If decoded pixels match, the destination is already correct. Keep the existing runtime file;
replacing it creates binary churn and may exchange a compact indexed/grayscale BMP for RGB.
5. Copy the supplied exploded BMP only if decoded pixels differ, then repeat the pixel comparison
against the installed destination.
Loose `hsh` art is not packed into a `.mw4` resource for this path, so an actual art replacement
does not require `build-resources.ps1`. Coordinate changes still require rebuilding the game
executable because `DXRasterizer.cpp` includes `coord.cpp` directly.
## Reproducible validation
After editing coordinates, rerun:
```bash
python3 "Finished HUDS from J&J/generate_comparison_maps.py"
```
For the 2026-08-07 data set, the expected terminal result is:
```text
Generated 38 maps for 19 display sets: 19 exact, 0 different
Warnings: 0; missing display sets: 7
```
Also verify all of the following before considering the source complete:
- `COMPARISON-SUMMARY.md` contains 19 `EXACT` rows and no `DIFFERENT` rows.
- Each of `texuv2`, `offset2`, `texuv3`, and `offset3` still has 65 active rows.
- Every active row still has exactly 11 zones.
- The edited `coord.cpp` remains CRLF-only; the repository deliberately uses byte-exact files.
- The diff changes only intended Mech rows. This installation changes 38 rows total: two rows
for each of 19 display sets.
- `git status --short -- Gameleap/mw4/hsh` is empty when every runtime image was already correct.
- A Windows VC6 Release/Profile rebuild and physical MFD/Radar test remain required; the Linux
comparison workflow validates data and geometry but cannot replace that runtime test.
## J&J mapping status as of 2026-08-07
All 19 complete display sets under `Finished HUDS from J&J/` are installed in `coord.cpp` and
report `EXACT` in `COMPARISON-SUMMARY.md`: 12 MFD sets and 7 Radar sets across 13 chassis.
Seven opposite-display sets have no complete supplied mapping/image inputs and were not changed.
Installed sets:
| Mech ID | Chassis | MFD | Radar |
|---:|---|:---:|:---:|
| 0 | Annihilator | installed | installed |
| 4 | Argus | installed | no supplied set |
| 5 | Assassin II | installed | installed |
| 7 | Avatar | installed | installed |
| 11 | Behemoth | installed | installed |
| 13 | Black Hawk | installed | installed |
| 27 | Fafnir | installed | no supplied set |
| 28 | Flea | installed | no supplied set |
| 29 | Gladiator | installed | no supplied set |
| 33 | Hellspawn | no supplied set | installed |
| 37 | Kodiak | installed | no supplied set |
| 39 | Longbow | installed | no supplied set |
| 62 | Warhammer | installed | installed |
The supplied exploded BMP for every installed display set was also compared to its canonical
runtime destination under `Gameleap/mw4/hsh/hud/` or `Gameleap/mw4/hsh/radar/hud/`. All 19 are
pixel-identical at 512x512. Their file hashes differ because the supplied files use different BMP
encodings, palettes, or headers; the runtime copies are already correct, so none were replaced.
Pixel-identical runtime files:
```text
MFD: annihilator, argus, assassin2, avatar, behemoth, blackhawk, Fafnir,
flea, gladiator, kodiak, longbow, warhammer
Radar: annihilator, assassin2, avatar, behemoth, blackhawk, hellspawn, warhammer
```
Three obvious measurement-file transcription errors were normalized without changing the
validated geometry: Behemoth MFD LT `1742` to `174`, Behemoth Radar CT `202.242` to `202,242`,
and Fafnir MFD LL `162.326` to `162,326`. Behemoth Radar's blank S2 was made explicit as `0,0`.
### Assassin II details
Reference art and measurements are under:
```text
Finished HUDS from J&J/AssassinII/
```
The supplied files are:
```text
HUD/assassinII_HUD_unexploded.txt
HUD/assassinII_hud_exploded.txt
Radar/assassinII_radar_unexploded.txt
Radar/assassinII_radar_exploded.txt
```
The supplied Assassin II values were checked against the correctly transformed source art.
Both corrected MFD and Radar mappings align. All four rows were installed in `coord.cpp` on
2026-08-07 and are pending an executable rebuild and hardware test.
Generated comparison images from the review are:
```text
Finished HUDS from J&J/AssassinII/HUD/assian2_mfd_unexploded_coords_comparison.png
Finished HUDS from J&J/AssassinII/HUD/assian2_mfd_exploded_coords_comparison.png
Finished HUDS from J&J/AssassinII/Radar/assian2_radar_unexploded_coords_comparison.png
Finished HUDS from J&J/AssassinII/Radar/assian2_radar_exploded_coords_comparison.png
```
The generator now draws the installed `coord.cpp` mapping in red and the supplied J&J mapping in
green, so the colors overlap exactly. These PNGs are review art only and are not loaded by the
game.
### Implemented Assassin II rows
MFD `texuv2`:
```cpp
{{ 98,142,148,328},{190,142,240,328},{ 56, 48,124,154},{216, 48,282,154},{180, 22,220,120},{120, 22,158,120},{144, 36,194,186},{ 0, 0, 0, 0},{158, 70,180, 86},{148, 10,190, 48},{ 0, 0, 0, 0}}
```
MFD `offset2`:
```cpp
{{ 6,148},{282,148},{ 6, 10},{266, 10},{206, 22},{ 98, 22},{144,180},{ 0, 0},{158,140},{148, 10},{ 0, 0}}
```
Radar `texuv3`:
```cpp
{{114,170,178,406},{232,170,294,406},{ 60, 48,144,184},{264, 48,348,184},{218, 18,268,140},{142, 18,190,140},{174, 36,234,140},{ 0, 0, 0, 0},{192, 78,218, 98},{178, 2,230, 48},{ 0, 0, 0, 0}}
```
Radar `offset3`:
```cpp
{{ 8,220},{336,220},{ 8, 48},{314, 48},{244, 48},{120, 48},{178,206},{ 0, 0},{192, 78},{178, 3},{ 0, 0}}
```
One supplied Radar S1 offset is odd (`y=3`) despite the general even-coordinate rule. Preserve
the measured value until it is deliberately reviewed against the source pixels; do not silently
round it while transcribing the row.
## Common mistakes
- Drawing MFD boxes on the raw 1024 image instead of the transformed 340 working canvas.
- Drawing unexploded rectangles on the exploded-pieces BMP.
- Using `texuv`/`offset` from `huddamage.cpp` when the task is external MFD/Radar mapping.
- Using the small `hsh/MFD` atlas image as the external damage-mask source.
- Pre-dividing Radar coordinates by two before putting them in `coord.cpp`.
- Adding the MFD `+100,+40` or Radar `+138,+406` UI origins to authored offsets.
- Treating `(x2,y2)` as a second rectangle instead of an upper-left destination point.
- Measuring overlapping component rectangles or omitting separator/outline pixels.
- Swapping S1 and S2 because of the stale `offset2` header comment.
- Inserting a row into only one parallel array and shifting every later Mech out of alignment.
- Re-encoding or normalizing the entire legacy source file while changing four rows.
- Treating a different BMP hash as different art without decoding and comparing pixels.
- Copying an unexploded/reference BMP instead of the exploded 512x512 runtime BMP.
- Using the supplied `assian2` typo as the destination instead of canonical `assassin2.bmp`.
- Rounding odd coordinates despite a visually aligned overlay.