---
name: kicad-3d-models
description: Add standard KiCad 3D models to a .kicad_pcb file (especially tscircuit exports that ship without model references), open the 3D viewer in KiCad on the user's desktop, and screenshot to verify. Trigger words — 3d models in kicad, kicad 3d viewer, alt+3, no 3d chips, missing 3d models, add 3d models, kicad 3d screenshot, show me 3d in kicad, prove 3d works, kicad models missing.
---

# kicad-3d-models

**Prerequisite: Read `kicad-interaction` skill first.** It covers state checks, window management, and dialog handling that this skill depends on.

Add KiCad-standard 3D model references to `.kicad_pcb` files, push to the user's desktop, open the 3D viewer, and screenshot to prove it worked.

## When to use

- User opens a tscircuit-exported `.kicad_pcb` in KiCad and sees no 3D models (Alt+3 shows bare board)
- User asks to add 3D models to a KiCad PCB
- User wants to see/screenshot the KiCad 3D viewer

## Why tscircuit exports lack 3D models

`tsci export -f kicad_pcb` produces valid PCB layouts (pads, traces, silkscreen, board outline) but does **not** inject `(model ...)` entries into footprint blocks. KiCad's 3D viewer needs those entries to know which `.step`/`.wrl` file to render for each component.

## Step 1 — Discover the KiCad 3D model environment variable

KiCad versions use version-specific env vars. Query `service-kicad` to get the correct one:

```bash
service-kicad fp fetch Resistor_SMD R_0402_1005Metric 2>&1 | grep "model"
```

This returns something like:
```
(model "${KICAD10_3DMODEL_DIR}/Resistor_SMD.3dshapes/R_0402_1005Metric.step"
```

The `${KICAD10_3DMODEL_DIR}` prefix is what you need. Adjust for the KiCad version on the user's desktop.

## Step 2 — Map footprint types to 3D models

List the unique footprint types in the `.kicad_pcb`:

```bash
grep -A1 "(footprint" board.kicad_pcb | grep '"tscircuit:' | sort -u
```

Then build a mapping. Common tscircuit footprint → KiCad 3D model mappings:

| tscircuit footprint | KiCad 3D model path |
|---|---|
| `tscircuit:resistor_0402` | `Resistor_SMD.3dshapes/R_0402_1005Metric.step` |
| `tscircuit:resistor_0603` | `Resistor_SMD.3dshapes/R_0603_1608Metric.step` |
| `tscircuit:resistor_0805` | `Resistor_SMD.3dshapes/R_0805_2012Metric.step` |
| `tscircuit:capacitor_0402` | `Capacitor_SMD.3dshapes/C_0402_1005Metric.step` |
| `tscircuit:capacitor_0603` | `Capacitor_SMD.3dshapes/C_0603_1608Metric.step` |
| `tscircuit:capacitor_0805` | `Capacitor_SMD.3dshapes/C_0805_2012Metric.step` |
| `tscircuit:led_0603_color(...)` | `LED_SMD.3dshapes/LED_0603_1608Metric.step` |
| `tscircuit:TYPE-C-31-M-12` | `Connector_USB.3dshapes/USB_C_Receptacle_HRO_TYPE-C-31-M-12.step` |
| `tscircuit:chip` (machine pins) | Skip or use a small placeholder |

For non-standard footprints, use `service-kicad fp fetch <library> <name>` to look up the model path. Common KiCad 3D model libraries:

- `Resistor_SMD`, `Resistor_THT`
- `Capacitor_SMD`, `Capacitor_THT`
- `LED_SMD`, `LED_THT`
- `Connector_USB`, `Connector_PinHeader`
- `Package_SO`, `Package_QFP`, `Package_BGA`, `Package_DFN_QFN`

## Step 3 — Inject model entries

Write a Python script that:
1. Reads the `.kicad_pcb` file
2. For each `(footprint ...)` block, identifies the footprint type from the line after `(footprint`
3. Finds the closing `)` of the footprint block (by tracking paren depth)
4. Inserts a `(model ...)` block before the closing paren

Model block format:
```
    (model "${KICAD10_3DMODEL_DIR}/Library.3dshapes/ModelName.step"
      (offset (xyz 0 0 0))
      (scale (xyz 1 1 1))
      (rotate (xyz 0 0 0))
    )
```

Verify the result:
```bash
grep "KICAD.*3DMODEL" board.kicad_pcb | sort | uniq -c
```

## Step 4 — Send to desktop and open in KiCad

**CRITICAL: Close all existing KiCad windows BEFORE opening the updated file.** Opening the same file twice creates "File Open Warning" dialogs and leaves stale editors around.

```bash
# Always close first
adom-desktop kicad_close '{}'

# Send files (send PCB and any sidecar STEP/WRL separately if >1MB total to avoid 413)
adom-desktop send_files '{"filePaths":["/path/to/board.kicad_pcb"],"category":"kicad"}'
adom-desktop send_files '{"filePaths":["/path/to/USB_C_model.wrl"],"category":"kicad"}'

# Open fresh
adom-desktop kicad_open_board '{"filePath":"C:/Users/john/Downloads/board.kicad_pcb"}'
```

Wait for the PCB editor to load, then check for dialogs:
```bash
adom-desktop kicad_window_info '{}'
```

## Step 5 — Open the 3D viewer and screenshot

```bash
# Open the 3D viewer (equivalent to Alt+3)
adom-desktop kicad_open_3d_viewer '{"hwnd": <pcb_editor_hwnd>}'

# Wait for rendering, then check the viewer window appeared
sleep 5
adom-desktop kicad_window_info '{}'

# Screenshot the 3D viewer window
adom-desktop desktop_screenshot_window '{"hwnd": <3d_viewer_hwnd>}'
```

The screenshot saves to `/tmp/adom-desktop-screenshots/`. The file is already on Docker — read it with the Read tool to verify the 3D models rendered correctly.

## Handling missing models (e.g. manufacturer-specific USB-C connectors)

Some footprints reference 3D models that aren't in KiCad's standard `packages3D` distribution. Common case: `USB_C_Receptacle_HRO_TYPE-C-31-M-12` ships in the KiCad footprint library but has no matching STEP in packages3D.

**Fix: relative path + ship the STEP alongside the PCB.**

1. Find a visually similar model that IS available:
   ```bash
   service-kicad model fetch "Connector_USB.3dshapes/USB_C_Receptacle_GCT_USB4105-xx-A_16P_TopMnt_Horizontal.step" --out /tmp/usbc-standin.step
   ```
2. Rename it to match the expected filename and place it next to the `.kicad_pcb`
3. Change the model reference in the PCB to a relative path:
   ```
   (model "./USB_C_Receptacle_HRO_TYPE-C-31-M-12.step" ...)
   ```
4. Send BOTH the `.kicad_pcb` and the `.step` file to the desktop (send separately if the STEP triggers a 413 payload-too-large error)

Test with `service-kicad model fetch` first — if it 404s, the model isn't in packages3D and needs this workaround.

**CRITICAL: WRL unit conversion.** KiCad interprets WRL/VRML coordinates as **inches**. If you convert an OBJ/GLB/STL (which are typically in mm) to WRL, you MUST divide all vertex coordinates by 25.4. Without this, the model renders 25.4x too large. STEP files don't have this problem — KiCad reads STEP as mm natively.

## Up-axis rotation convention

STEP files are authored with different up-axis conventions. KiCad uses Z-up. When injecting `(model ...)` references, read `chosen_up_axis` from chip-fetcher's `info.json` to determine the correct rotation:

| `chosen_up_axis` | Source convention | KiCad rotation needed |
|---|---|---|
| `z` | Z-up (KiCad native) | `(rotate (xyz 0 0 0))` |
| `y` | Y-up (Fusion 360, many vendors) | `(rotate (xyz -90 0 0))` |

If `info.json` says `chosen_up_axis: "y"` and you use `(rotate (xyz 0 0 0))`, the model will render sideways or upside-down in KiCad. chip-fetcher's dashboard shows the orientation visually (Z-up/Y-up thumbnail icons) -- check it BEFORE writing the model reference.

See the [`board-building-pipeline`](../board-building-pipeline/SKILL.md) skill for the full up-axis table and how `chosen_up_axis` flows through the entire pipeline (chip-fetcher -> step2glb -> chiplinter -> chipfit -> KiCad footprint writer).

## Footprint source matters (CRITICAL)

When injecting a model reference for a non-standard component, the STEP model and the footprint MUST come from the same source:

- The STEP model from chip-fetcher was designed to match the chip-fetcher footprint (.kicad_mod), **NOT** the tscircuit-exported footprint
- If you source a STEP from SnapMagic/Mouser/manufacturer, you must ALSO use the footprint from that same source
- Replace the entire footprint block in the .kicad_pcb, preserving only the board placement coordinates `(at X Y rot)`

**Why this matters:** A tscircuit footprint and a SnapMagic footprint for the same component (e.g. USB-C HRO TYPE-C-31-M-12) have completely different pad positions and mounting hole locations. Bolting the SnapMagic STEP onto the tscircuit footprint causes pads to be misaligned even though the STEP renders in the 3D viewer -- the model just floats above the wrong pad locations.

**How to replace a footprint block:**
1. Open the chip-fetcher .kicad_mod file for the component
2. Find the matching `(footprint ...)` block in the .kicad_pcb
3. Note the `(at X Y rot)` line from the original block (board placement)
4. Replace the entire footprint block with the chip-fetcher version
5. Update the `(at ...)` line to use the original board placement coordinates
6. Ensure the `(model ...)` reference points to the STEP from the same source

## Checklist before reporting success

- [ ] Model count matches expected component count
- [ ] Screenshot shows 3D component packages (not a bare green board)
- [ ] USB-C connector visible if present
- [ ] LED packages visible and arranged in expected pattern
- [ ] No error dialogs in KiCad
