# Part D2 Report: Finish the Vehicle Rigs (Round 2)

Completed at: 2026-10-18 18:09 CET (Europe/Paris)
Worktree: `/home/claude-agent/forge-build/forge-assets/.forge-worktrees/1792337826-d5f9da`
Branch: `task/1792337826-d5f9da`

0. **Blocked on you**: nothing.
1. **What changed**: We rigged 11 additional land vehicles (`Body`, `Wheel_FL/FR/RL/RR`, centered hub origins, `steer` list, and measured `wheelbase`/`track`) and 3 boats (`Hull`, `Rudder`, and `Propeller` or `Oar_L`/`Oar_R` with `gameplay.drive = "boat"`, `length`, and `beam`), exported 14 new GLBs to `store/blobs/`, wrote 14 new `vN+1` cards via `lib/card.py`, rendered before/after PNGs into `rigs-evidence/`, and rebuilt `index.json`.
2. **How we know it works**: `python3 tools/build_index.py` passed all 484 active cards (`index.json: 484 assets, 1133 KB`), all 14 exported GLBs verified with identity rest rotations (`rest_rot: None`) and Three.js axes (`+Y` up, `+Z` forward), and all 14 side-by-side Cycles renders in `rigs-evidence/` (61 KB to 84 KB each, under the 300 KB cap) visually confirm clean wheel steering (+25 deg), rudder hinging (+35 deg), propeller rotation (+45 deg), and oar sweep (+-35 deg).
3. **What would break if we were wrong**: Existing games pinning earlier card versions (`v1`/`v2`) are unaffected because cards and blobs are immutable; when merging this branch, copy the 14 new `.glb` files from `store/blobs/` into the shared blob store. To undo any update, remove the new `vN+1.json` card file and re-run `python3 tools/build_index.py`.
4. **What we did NOT do**: Skipped `vehicle.sea.rowboat/harbour-rowboat-flipped` (static overturned wreck prop) and skipped cutting doors on the 8 higher-detail road cars (`classic-saloon`, `euro-hatchback`, `family-sedan`, `sports-coupe` and their `-g4` twins) because their outer `Paint` shells are continuous single-walled monocoque hulls without internal door jambs or clean vertical seam cuts.

---

## Boat Rig Convention (`vehicle.sea.*`)

All steerable/propelled watercraft in `vehicle.sea.*` follow this card and GLB node convention in Three.js space (`+Y` up, `+Z` bow/forward, `+X` port/left):

```json
"nodes": {
  "hull": ["Hull"],
  "rudder": ["Rudder"],
  "propeller": ["Propeller"],
  "oars": ["Oar_L", "Oar_R"],
  "moving": {
    "Rudder": {
      "kind": "hinge",
      "axis": [0.0, 1.0, 0.0],
      "limit": [-0.6109, 0.6109],
      "axis_source": "measured"
    },
    "Propeller": {
      "kind": "spin",
      "axis": [0.0, 0.0, 1.0],
      "axis_source": "measured"
    }
  },
  "axes": {
    "Rudder": [0.0, 1.0, 0.0],
    "Propeller": [0.0, 0.0, 1.0]
  },
  "limits": {
    "Rudder": [-0.6109, 0.6109]
  }
},
"gameplay": {
  "drive": "boat",
  "length": 18.083,
  "beam": 6.13,
  "source": "measured"
}
```

- `nodes.hull`: static vessel hull and superstructure mesh nodes (primary mesh named `Hull`).
- `nodes.rudder`: `["Rudder"]` with node origin placed on the leading-edge rudder post hinge line, vertical hinge axis `[0, 1, 0]`, and limit `[-0.6109, 0.6109]` radians (+-35 deg).
- `nodes.propeller` (powered vessels): `["Propeller"]` with node origin at the screw hub center and spin axis along the boat's length (`[0, 0, 1]` for `+Z`-bow vessels; `[1, 0, 0]` when hull length is along `X`).
- `nodes.oars` (rowed vessels): `["Oar_L", "Oar_R"]` with node origins at the port (`+X`) and starboard (`-X`) rowlock pivots, sweep axis `[0, 1, 0]`, and limit `[-0.6109, 0.6109]` radians (+-35 deg).
- `gameplay`: `{"drive": "boat", "length": <m>, "beam": <m>, "source": "measured"}`.

---

## 1. Land Vehicles Fixed (11 Assets, `v1` -> `v2`)

| Asset ID | Version | New GLB SHA256 (prefix) | Wheelbase (m) | Track (m) | Nodes & Notes |
| --- | --- | --- | --- | --- | --- |
| `vehicle.car.suv/kenney-suv-luxury` | v2 | `28d51d4d4f35` | 1.520 | 0.850 | `Body`, `Wheel_FL/FR/RL/RR`, `steer: [Wheel_FL, Wheel_FR]`, centered hub origins |
| `vehicle.kart.kart/kenney-kart-oobi` | v2 | `f902383c1f1f` | 0.685 | 0.764 | Unparented wheels from `kart-oobi` (`Body`), centered hub origins |
| `vehicle.kart.kart/kenney-kart-oodi` | v2 | `b3ec6641a31a` | 0.685 | 0.764 | Unparented wheels from `kart-oodi` (`Body`), centered hub origins |
| `vehicle.kart.kart/kenney-kart-ooli` | v2 | `5eb0e8f914a0` | 0.685 | 0.764 | Unparented wheels from `kart-ooli` (`Body`), centered hub origins |
| `vehicle.kart.kart/kenney-kart-oopi` | v2 | `ffa4aef889b4` | 0.685 | 0.764 | Unparented wheels from `kart-oopi` (`Body`), centered hub origins |
| `vehicle.kart.kart/kenney-kart-oozi` | v2 | `da31070410cc` | 0.685 | 0.764 | Unparented wheels from `kart-oozi` (`Body`), centered hub origins |
| `vehicle.utility.delivery-truck/kenney-delivery-flat` | v2 | `913e0a741c45` | 1.620 | 0.975 | `Body`, `Wheel_FL/FR/RL/RR`, centered hub origins |
| `vehicle.utility.pickup/kenney-truck-flat` | v2 | `394ab5cf7b0a` | 1.420 | 0.900 | `Body`, `Wheel_FL/FR/RL/RR`, centered hub origins |
| `vehicle.utility.tractor/kenney-tractor` | v2 | `afc1d5b10a6c` | 1.306 | 0.880 | `Body`, `Wheel_FL/FR/RL/RR`, centered hub origins |
| `vehicle.utility.tractor/kenney-tractor-police` | v2 | `1f1181b2a8fb` | 1.306 | 0.880 | `body: [Body, grill]`, `Wheel_FL/FR/RL/RR`, centered hub origins |
| `vehicle.utility.van/kenney-van` | v2 | `d16d83f47f14` | 1.520 | 0.850 | `Body`, `Wheel_FL/FR/RL/RR`, centered hub origins |

---

## 2. Boats Fixed (3 Assets)

| Asset ID | Version | New GLB SHA256 (prefix) | Length (m) | Beam (m) | Moving Nodes & Axes (Three.js) |
| --- | --- | --- | --- | --- | --- |
| `vehicle.sea.rowboat/harbour-rowboat` | v2 -> v3 | `c3906b9bb1ad` | 3.690 | 1.520 | `Hull`, `Rudder` (`[0,1,0]`, +-35 deg), `Oar_L` (`[0,1,0]`, +-35 deg at port rowlock), `Oar_R` (`[0,1,0]`, +-35 deg at starboard rowlock) |
| `vehicle.sea.trawler/harbour-trawler` | v1 -> v2 | `33e7ee048d99` | 14.608 | 5.529 | `Hull` (from `lm_trawler`), `Rudder` (`[0,1,0]`, +-35 deg at sternpost), `Propeller` (`[1,0,0]` spin along X hull length) |
| `vehicle.sea.trawler/harbour-trawler-real` | v2 -> v4 | `551b3b9e1223` | 18.083 | 6.130 | Separated underwater `Rudder` (156 faces, `[0,1,0]`, +-35 deg) and 4-blade `Propeller` (1,390 faces, `[0,0,1]` spin) from `Hull`; preserved Draco + 32 KTX2 texture slots |

---

## 3. Skipped Assets and Why (9 Assets)

1. `vehicle.sea.rowboat/harbour-rowboat-flipped`: Static overturned wreck prop resting on a beach/quayside; not a steerable watercraft.
2. `vehicle.car.classic/classic-saloon`: Outer `Paint` shell (`Pressed quarter and door skins`, island `[0]`, 6,776 faces, `z = [-1.953, 1.919]`) has 39 triangles crossing the vertical door seam plane and no internal door jambs or sill caps.
3. `vehicle.car.classic/classic-saloon-g4`: Outer `Paint` shell (`Continuous pressed monocoque`, island `[0]`, 9,260 faces, `z = [-1.914, 1.914]`) is a single closed monocoque tube with continuous A-to-C pillar `Chrome` window arches (`z = [-0.985, 0.555]`) and no internal door jambs.
4. `vehicle.car.hatchback/euro-hatchback`: Outer `Paint` shell (island `[0]`, 9,996 faces, `z = [-1.945, 2.128]`) has 25 triangles crossing the vertical door seam plane and continuous roof/pillar arches (`z = [-1.846, 1.033]`).
5. `vehicle.car.hatchback/euro-hatchback-g4`: Outer `Paint` shell (island `[0]`, 9,546 faces, `z = [-2.004, 2.016]`) is a single-walled monocoque hull with continuous `Chrome`/`Paint` window arches (`z = [-1.357, 0.748]`) and no internal door jambs.
6. `vehicle.car.sedan/family-sedan`: Outer `Paint`/`Plastic` hull (island `[0]`, 30,944 faces, `z = [-2.390, 2.378]`) is a single-walled monocoque shell with continuous `Metal` window arches (`z = [-1.402, 0.902]`) and no internal door jambs.
7. `vehicle.car.sedan/family-sedan-g4`: Outer `Paint` hull (`Unified compound hull`, island `[0]`, 32,824 faces, `z = [-2.351, 2.427]`) is a single-walled monocoque shell with continuous `Paint` window arches (`z = [-1.680, 0.920]`) and no internal door jambs.
8. `vehicle.car.sports/sports-coupe`: Outer `Paint` side shells (islands `[0]` and `[1]`, 3,183 faces each, `z = [-2.137, 2.117]`) have 13 triangles crossing the door seam plane and a single 1,004-face one-piece `Glass` canopy dome (`z = [-1.000, 0.840]`).
9. `vehicle.car.sports/sports-coupe-g4`: `Body_Mesh` (island `[0]`, 26,520 faces, `z = [-2.199, 2.099]`) fuses `Glass`, `Paint`, and `Trim` into a single welded monocoque island with no internal door jambs.
