Install this skill

Paste this into Claude Code (VS Code panel, Adom editor, or terminal) to install:

Search the Adom Wiki for the skill "3D Component Creator" (slug: 3d-component-creator) at https://wiki-ufypy5dpx93o.adom.cloud/wiki/skills/3d-component-creator and install it into my local ~/.claude/skills/3d-component-creator/ directory. Fetch the skill_source content from the wiki page and save it as SKILL.md. Then confirm it's installed by showing the first 5 lines.
?
What is a skill? Skills are instructions that teach AI assistants like Claude Code how to perform specific tasks. The description below is loaded into the AI as context when you invoke this skill. Well-written skills make the AI significantly more effective. Like Wikipedia, anyone can improve a skill by clicking Edit AI Skill — or have your AI submit an edit on your behalf.

Description

Edit AI Skill

name: 3d-component-creator description: Use when the user asks to "generate a 3D model", "create a 3D view", "show the 3D model", "visualize in 3D", "create a STEP file", "make a GLB", "add chip markings", "laser etch", "3D preview", or wants to see an interactive 3dView of a PCB component in the Gallia Viewer. Covers STEP/GLB generation, 3D model resolution from KiCad packages3D, laser etch chip markings, and interactive Babylon.js 3dView.

3D Component Creator

Generate interactive 3D models of PCB components with IC body, laser-etched chip markings, and pad geometry. Preview in 3dView (the Gallia Viewer's built-in Babylon.js 3D viewer), iterate with the user, and deliver STEP files for KiCad or Fusion 360.

Input: An existing .kicad_mod footprint + metadata JSON (from the footprint-creator skill) Output: GLB for 3dView preview, STEP for CAD delivery, standalone HTML for sharing

KiCad Service

All kicad-cli operations (STEP/GLB export, 3D model resolution) use the remote KiCad CLI service — KiCad is NOT installed locally in user containers. The service URL is configured via the KICAD_API environment variable (default: http://127.0.0.1:8780).

API client: /home/adom/gallia/viewer/kicad-api-client.js Wrapper module: /home/adom/gallia/viewer/generate-step.js (delegates to API client)

Library Lookup — Query Existing 3D Models and Footprints

The KiCad service has 14,000+ 3D models (STEP/WRL) and 7,000+ footprints installed. Before creating anything from scratch, query the library:

import { models3dList, models3dDownload, fpList, fpDownload, fpExportGlbByName, fpExportStepByName } from '/home/adom/gallia/viewer/kicad-api-client.js';

// ── Fastest: Export GLB or STEP directly by library + name (single call) ──
// No file download needed — the service looks up the footprint, resolves the 3D model, and returns the export.
const glb = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23.glb');
// → { outputPath: '/tmp/SOT-23.glb', hasModel: true, modelSource: 'cache', modelName: 'SOT-23' }
// Export GLB without the FR4 board (component + pads only — ideal for web 3D viewers)
const glbNoBoard = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23-noboard.glb', { noBoard: true });
// Export "enhanced" GLB — viewer-ready with organized scene graph and PBR materials
const glbEnhanced = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23-enhanced.glb', { enhanced: true });
// Export enhanced + mm-scaled GLB — pre-scaled 1000x for Babylon.js shadows (check asset.extras.adom.mmScale to avoid double-scaling)
const glbMM = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23-mm.glb', { enhanced: true, mmScale: true });
const step = await fpExportStepByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23.step');

// ── Search for models/footprints when you don't know the exact library ──
const models = await models3dList({ q: 'sot-23', format: 'step' });
// → { total: 12, models: [{ library: "Package_TO_SOT_SMD", name: "SOT-23-5", ... }] }
await models3dDownload('Package_TO_SOT_SMD.3dshapes/SOT-23-5.step', '/tmp/SOT-23-5.step');

const fps = await fpList({ q: 'sot-23-5' });
await fpDownload('Package_TO_SOT_SMD.pretty/SOT-23-5.kicad_mod', '/tmp/SOT-23-5.kicad_mod');

When to use which approach:

  • fpExportGlbByName / fpExportStepByName — Best for web preview (GLB) or CAD delivery (STEP) of standard library components. Single call, no intermediate files. Use fpList first to find the library + name if you don't know them.
  • Direct download (models3dDownload) — When you just need the raw STEP/WRL file itself (e.g., importing into Fusion 360 as a reference model). Faster, no footprint context.
  • Export pipeline (pcbExportGlb / pcbExportStep) — When you have a custom .kicad_mod (not from the standard library) and need it rendered with pads + IC body on substrate.

Workflow Overview

1. Locate footprint  →  2. Resolve 3D model  →  3. Generate GLB  →  4. Preview in Gallia Viewer  →  5. Iterate  →  6. Deliver STEP

Step 1: Locate the Footprint

The 3D pipeline requires:

  • A .kicad_mod file (from footprint-creator or the KiCad library via fpList / fpDownload)
  • Metadata JSON with key fields: bodySize, padCount, packageType, partName, manufacturer
/home/adom/project/project-content/schematics/footprints/
  PART_NAME/
    PART_NAME.kicad_mod             # Input footprint
    PART_NAME-fp-metadata.json      # Metadata with bodySize, manufacturer, etc.
    PART_NAME.step                  # Output STEP file
    PART_NAME-3d-viewer.html        # 3dView — standalone 3D viewer HTML

Step 2: 3D Model Resolution (3-Tier Fallback)

The pipeline automatically resolves IC body STEP models:

TierSourceWhen
1KiCad packages3D (GitLab)Standard packages (TSSOP, QFN, SOT, BGA, etc.) — auto-downloaded and cached
2Custom STEPManufacturer-provided or user-supplied STEP file
3Pads onlyNo model available — exports pad geometry on PCB substrate

Resolution Algorithm

  1. Read .kicad_mod, extract (model "${KICAD7_3DMODEL_DIR}/Category.3dshapes/Name.wrl") reference
  2. Convert .wrl to .step in the relative path
  3. Check local cache — if hit, return immediately
  4. HTTP GET from GitLab raw URL (15s timeout)
  5. On success: save to cache, return path
  6. On failure: return null (falls back to pads-only)

GitLab URL format (~7,200 STEP files):

https://gitlab.com/kicad/libraries/kicad-packages3D/-/raw/master/{category}.3dshapes/{name}.step

Cache

Downloaded models are cached at /home/adom/.cache/kicad-3d-models/ mirroring the KiCad repo structure:

/home/adom/.cache/kicad-3d-models/
  Package_SO.3dshapes/TSSOP-14_4.4x5mm_P0.65mm.step
  Package_LGA.3dshapes/Bosch_LGA-14_3x2.5mm_P0.5mm.step

Cache persists across sessions — models are only downloaded once.

3D Model Variable Resolution (handled by remote service)

KiCad footprints reference 3D models via ${KICAD7_3DMODEL_DIR}/...wrl. The remote KiCad service automatically resolves these variable paths to cached .step files server-side. You do NOT need to handle this manually — just call generateGLB() or generateSTEP() from generate-step.js and the service handles model resolution, downloading, and caching.

Code: /home/adom/gallia/viewer/kicad-3d-model-resolver.js

Key exports:

  • resolveModel(kicadModPath) — full resolution pipeline (cache check → download → fallback)
  • resolveCustomModel(stepPath) — for manufacturer-provided STEP files
  • rewriteModelPaths(content) — replaces ${KICAD7_3DMODEL_DIR}/...wrl with absolute cache paths
  • injectModelReference(content, modelPath) — adds (model ...) to footprints that lack one

Step 3: Generate GLB / STEP

import { generateGLB, generateSTEP } from '/home/adom/gallia/viewer/generate-step.js';

// GLB for 3D viewer (with IC body auto-resolved)
const glbResult = await generateGLB(kicadModPath, fpName, outputPath, options);

// STEP for CAD delivery
const stepResult = await generateSTEP(kicadModPath, fpName, outputPath, options);

// Result: { outputPath, hasModel, modelSource, modelName }
// modelSource: 'cache' | 'download' | 'custom' | 'none'

Options

  • Default: auto-resolves 3D model (downloads from GitLab if needed)
  • { skipModelResolution: true }: pads-only export
  • { customStepPath: '/path/to/model.step' }: use a specific STEP file
  • { enhanced: true }: viewer-ready GLB with organized scene graph and PBR materials (see Enhanced GLB section below)
  • { mmScale: true }: scale 1000x (meters → millimeters) for Babylon.js shadow/lighting compatibility
  • { enhanced: true, mmScale: true }: combined — best for Babylon.js 3D viewers

Export Flags (used by remote service internally)

The remote service sets these flags automatically — listed here for reference only:

ScenarioFlags
With IC body model--include-pads --subst-models -f -o output
Pads only (no model)--include-pads --no-components -f -o output

You do NOT need to set these flags — generateGLB() and generateSTEP() handle them.

Enhanced GLB Mode

Pass { enhanced: true } to any GLB export function to get a viewer-ready GLB with:

  1. Organized scene graph — named parent nodes: component, board_pads, board_fr4
  2. PBR materials — IC body (dark mold), leads (metallic silver), copper pads (gold), FR4 (green)
  3. Body/lead classification — dual-criteria algorithm (inner 70% center test + upper 50% height test) correctly classifies primitives across SOT, SOIC, QFP, and other IC packages

Optionally add { mmScale: true } to pre-scale 1000x (meters → millimeters) for Babylon.js shadow/lighting compatibility.

// Enhanced GLB for 3dView (recommended for web viewers)
const glb = await fpExportGlbByName('Package_SO', 'SOIC-8_3.9x4.9mm_P1.27mm', '/tmp/soic8.glb', { enhanced: true });
// Enhanced + mm-scaled for Babylon.js (shadows work correctly at this scale)
const glb = await fpExportGlbByName('Package_SO', 'SOIC-8_3.9x4.9mm_P1.27mm', '/tmp/soic8.glb', { enhanced: true, mmScale: true });
// Or with POST endpoint:
const glb = await pcbExportGlb(kicadModPath, fpName, outputPath, { enhanced: true, mmScale: true });

The enhanced GLB scene graph:

root
  └─ component        ← REF** node (IC body + leads with distinct materials)
  └─ board_pads       ← copper pad meshes
  └─ board_fr4        ← FR4 substrate mesh (omitted if noBoard: true)

GLB Metadata (asset.extras.adom)

Every post-processed GLB embeds metadata in gltf.asset.extras.adom:

{ "zUp": true, "enhanced": true, "mmScale": true, "boardStripped": false }

Babylon.js viewers should check asset.extras.adom.mmScale before applying their own 1000x scale to avoid double-scaling.

When to use enhanced mode:

  • Displaying in 3dView / Babylon.js — enhanced GLBs render with correct materials out of the box
  • Client-side material fixup is no longer needed — the GLB itself contains proper PBR materials

When to add mmScale:

  • Babylon.js viewers that need correct shadow maps and lighting at component scale
  • Only if the viewer doesn't already apply its own 1000x scale (check asset.extras.adom.mmScale)

When NOT to use enhanced mode:

  • Exporting for CAD tools (use STEP instead)
  • When you need the raw KiCad output without modifications

GLB Coordinate System (critical)

Standard GLB output is in meters (glTF spec default), NOT millimeters. mmScale GLB output is in millimeters (1000x scaled).

Standard mode:          1 mm = 0.001 scene units (use const MM = 0.001)
mmScale mode:           1 mm = 1 scene unit (no conversion needed)

Use const MM = 0.001 for standard mode. Enhanced mode uses millimeter-scale coordinates directly. Y-axis is up.

Step 4: Preview in 3dView

Option A: Built-in 3dView (preferred)

3dView is the Gallia Viewer's built-in Babylon.js 3D viewer. Use the gv_3d_display MCP tool:

gv_3d_display
  glb_path: "/path/to/PART_NAME-3d.glb"
  body_size: { x: 4.4, y: 5.0, z: 1.2 }
  part_name: "CDCEL913"
  manufacturer: "Texas Instruments"
  package_type: "TSSOP-14"
  pad_count: 14
  title: "CDCEL913 3D Model"

This copies the GLB to the Gallia Viewer server's serving directory and switches to 3dView with the model loaded.

Option B: Standalone HTML (for export/sharing)

Generate self-contained HTML with embedded Babylon.js:

import { generate3DViewerHTML } from '/home/adom/gallia/viewer/kicad-3d-viewer.js';
import { writeFileSync } from 'fs';

const html = await generate3DViewerHTML(glbPath, {
  bodySize: metadata.bodySize,
  padCount: metadata.padCount,
  packageType: metadata.packageType,
  partName: metadata.partName,
  manufacturer: metadata.manufacturer,
});
writeFileSync(`${outputDir}/PART_NAME-3d-viewer.html`, html);

Display via gv_display_file MCP tool.

3dView Features

3dView provides:

  • Arc-rotate camera (orbit, pan, zoom, WASD movement)
  • Skybox and studio lighting
  • View cube for orientation
  • Ground plane toggle
  • Shadow casting
  • IC body with laser-etched chip markings
  • Info bar showing package details
  • Bottom light ON by default (illuminates underside/pad surfaces)
  • XYZ axes OFF by default
  • Pad highlighting with cross-pane sync (LibView)

WebSocket Messages

MessageDirectionPurpose
show_3dserver → viewerSwitch to 3dView and load model
clear_3dserver → viewerClear the 3D scene
model_3d_optionsserver → viewerUpdate laser etch / info bar
stop_tourrelay → viewerStop cinematic tour, freeze camera
start_tourrelay → viewerStart/restart cinematic tour
set_camerarelay → viewerSet camera alpha/beta/radius (beauty angle)
set_fr4relay → viewerExplicitly show/hide FR4 board ({ visible: true })
toggle_fr4relay → viewerToggle FR4 board visibility
show_originrelay → viewerToggle XYZ axis lines at world origin ({ visible: true/false })
set_viewrelay → viewerSet camera to named preset: front, back, left, right, top, bottom, isometric

The relay → viewer messages are sent via the management relay's broadcast action:

curl -X POST http://127.0.0.1:8772/command -H 'Content-Type: application/json' \
  -d '{"action":"broadcast","message":{"type":"stop_tour"}}'

Step 5: Laser Etch Chip Markings

3dView renders laser-etched text on the IC body top surface — manufacturer name, part number, date code, and pin 1 dot. This is automatic when manufacturer and partName are provided and the model has an IC body.

How It Works

  • Uses known body dimensions from metadata (NOT bounding box detection or raycasting)
  • Multiplies mm dimensions by 0.001 to convert to scene units (meters)
  • IC body top surface = highest point of centered model bounding box (Y-up)
  • Plane at 85% of body dimensions (realistic edge margin)
  • DynamicTexture with monospace font, silver/white text color
  • Pin 1 dot in top-left corner

Why This Approach Works

All IC packages have a flat top surface for pick-and-place during reflow soldering. The top surface position is deterministic from known body dimensions — no mesh analysis or raycasting needed.

Babylon.js DynamicTexture Implementation

const MM = 0.001;
const topY = boundingBox.max.y;  // IC body top (Y-up)
const bw = bodySize.x * MM;     // body width in scene units
const bd = bodySize.y * MM;     // body depth in scene units
const longSide = Math.max(bw, bd);
const shortSide = Math.min(bw, bd);

// Canvas texture
const dtex = new BABYLON.DynamicTexture('laserEtch', 1024, scene, true);
const ctx = dtex.getContext();
ctx.fillStyle = 'rgba(210, 212, 215, 0.8)';
ctx.textAlign = 'center';
ctx.font = fontSize + 'px monospace';
// ... draw text lines, pin 1 dot ...
dtex.update();

// Material
const mat = new BABYLON.StandardMaterial('etchMat', scene);
mat.diffuseTexture = dtex;
mat.diffuseTexture.hasAlpha = true;
mat.useAlphaFromDiffuseTexture = true;
mat.emissiveColor = new BABYLON.Color3(0.82, 0.83, 0.84);
mat.backFaceCulling = false;
mat.zOffset = -1;

// Plane on IC body top
const plane = BABYLON.MeshBuilder.CreatePlane('etch', {
  width: longSide * 0.85,
  height: shortSide * 0.85,
}, scene);
plane.rotation.x = Math.PI / 2;        // lay flat (Babylon planes face +Z)
plane.position.y = topY + 0.01 * MM;   // 0.01mm above surface
plane.material = mat;

Marking Lines Logic

Default marking lines (when manufacturer and partName are provided):

  1. manufacturer.toUpperCase() (e.g., "TEXAS INSTRUMENTS")
  2. partName.toUpperCase() (e.g., "CDCEL913")
  3. Date code (YYMM format)

Override with custom markingLines array for non-standard markings.

Step 6: Deliver

6a: STEP file for KiCad Desktop

const stepResult = await generateSTEP(kicadModPath, fpName, outputPath);

Then send via MCP tools:

1. send_files
     filePaths: ["/path/to/PART_NAME.step"]
     targetApp: "kicad"
     destinationFolder: "3d-models"

6b: STEP to Fusion 360

1. send_files
     filePaths: ["/path/to/PART_NAME.step"]
     targetApp: "fusion360"
     destinationFolder: "fusion"
2. fusion_import_step
     filePath: "<destinationPath from step 1>"

Code Reference

FilePurpose
/home/adom/gallia/viewer/generate-step.jsSTEP/GLB export via remote KiCad service + model resolution
/home/adom/gallia/viewer/kicad-3d-model-resolver.jsModel download, caching, path rewriting
/home/adom/gallia/viewer/kicad-3d-viewer.js3dView integration (serve3DModel) + standalone HTML (generate3DViewerHTML)

Common Pitfalls

  • GLB is in meters, not mm — GLB output uses meters per glTF spec. 1mm = 0.001 scene units. Passing raw mm values produces geometry 1000x too large
  • Model path resolution${KICAD7_3DMODEL_DIR} variables in .kicad_mod are resolved automatically by the remote KiCad service. You do NOT need to rewrite paths manually
  • .wrl.step substitution — handled automatically by the remote service (--subst-models flag applied server-side)
  • DynamicTexture size — Use power-of-2 dimensions (1024, 512) for optimal GPU performance
  • Babylon.js plane orientation — Planes face +Z by default; use rotation.x = Math.PI/2 to lay flat on XZ plane facing Y-up
  • Gallia Viewer restart required for 3d.html — After editing viewer/3d.html, restart the Gallia Viewer server (3d.html is cached with readFileSync at startup). index.html is read fresh on each request and does NOT require a restart

Skill Source

Edit AI Skill
---
name: 3d-component-creator
description: Use when the user asks to "generate a 3D model", "create a 3D view", "show the 3D model", "visualize in 3D", "create a STEP file", "make a GLB", "add chip markings", "laser etch", "3D preview", or wants to see an interactive 3dView of a PCB component in the Gallia Viewer. Covers STEP/GLB generation, 3D model resolution from KiCad packages3D, laser etch chip markings, and interactive Babylon.js 3dView.
---

# 3D Component Creator

Generate interactive 3D models of PCB components with IC body, laser-etched chip markings, and pad geometry. Preview in 3dView (the Gallia Viewer's built-in Babylon.js 3D viewer), iterate with the user, and deliver STEP files for KiCad or Fusion 360.

**Input**: An existing `.kicad_mod` footprint + metadata JSON (from the **footprint-creator** skill)
**Output**: GLB for 3dView preview, STEP for CAD delivery, standalone HTML for sharing

## KiCad Service

All kicad-cli operations (STEP/GLB export, 3D model resolution) use the remote KiCad CLI service — KiCad is NOT installed locally in user containers. The service URL is configured via the `KICAD_API` environment variable (default: `http://127.0.0.1:8780`).

API client: `/home/adom/gallia/viewer/kicad-api-client.js`
Wrapper module: `/home/adom/gallia/viewer/generate-step.js` (delegates to API client)

### Library Lookup — Query Existing 3D Models and Footprints

The KiCad service has **14,000+ 3D models** (STEP/WRL) and **7,000+ footprints** installed. Before creating anything from scratch, query the library:

```javascript
import { models3dList, models3dDownload, fpList, fpDownload, fpExportGlbByName, fpExportStepByName } from '/home/adom/gallia/viewer/kicad-api-client.js';

// ── Fastest: Export GLB or STEP directly by library + name (single call) ──
// No file download needed — the service looks up the footprint, resolves the 3D model, and returns the export.
const glb = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23.glb');
// → { outputPath: '/tmp/SOT-23.glb', hasModel: true, modelSource: 'cache', modelName: 'SOT-23' }
// Export GLB without the FR4 board (component + pads only — ideal for web 3D viewers)
const glbNoBoard = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23-noboard.glb', { noBoard: true });
// Export "enhanced" GLB — viewer-ready with organized scene graph and PBR materials
const glbEnhanced = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23-enhanced.glb', { enhanced: true });
// Export enhanced + mm-scaled GLB — pre-scaled 1000x for Babylon.js shadows (check asset.extras.adom.mmScale to avoid double-scaling)
const glbMM = await fpExportGlbByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23-mm.glb', { enhanced: true, mmScale: true });
const step = await fpExportStepByName('Package_TO_SOT_SMD', 'SOT-23', '/tmp/SOT-23.step');

// ── Search for models/footprints when you don't know the exact library ──
const models = await models3dList({ q: 'sot-23', format: 'step' });
// → { total: 12, models: [{ library: "Package_TO_SOT_SMD", name: "SOT-23-5", ... }] }
await models3dDownload('Package_TO_SOT_SMD.3dshapes/SOT-23-5.step', '/tmp/SOT-23-5.step');

const fps = await fpList({ q: 'sot-23-5' });
await fpDownload('Package_TO_SOT_SMD.pretty/SOT-23-5.kicad_mod', '/tmp/SOT-23-5.kicad_mod');
```

**When to use which approach:**
- **`fpExportGlbByName` / `fpExportStepByName`** — Best for web preview (GLB) or CAD delivery (STEP) of standard library components. Single call, no intermediate files. Use `fpList` first to find the library + name if you don't know them.
- **Direct download** (`models3dDownload`) — When you just need the raw STEP/WRL file itself (e.g., importing into Fusion 360 as a reference model). Faster, no footprint context.
- **Export pipeline** (`pcbExportGlb` / `pcbExportStep`) — When you have a custom `.kicad_mod` (not from the standard library) and need it rendered with pads + IC body on substrate.

## Workflow Overview

```
1. Locate footprint  →  2. Resolve 3D model  →  3. Generate GLB  →  4. Preview in Gallia Viewer  →  5. Iterate  →  6. Deliver STEP
```

## Step 1: Locate the Footprint

The 3D pipeline requires:
- A `.kicad_mod` file (from footprint-creator or the KiCad library via `fpList` / `fpDownload`)
- Metadata JSON with key fields: `bodySize`, `padCount`, `packageType`, `partName`, `manufacturer`

```
/home/adom/project/project-content/schematics/footprints/
  PART_NAME/
    PART_NAME.kicad_mod             # Input footprint
    PART_NAME-fp-metadata.json      # Metadata with bodySize, manufacturer, etc.
    PART_NAME.step                  # Output STEP file
    PART_NAME-3d-viewer.html        # 3dView — standalone 3D viewer HTML
```

## Step 2: 3D Model Resolution (3-Tier Fallback)

The pipeline automatically resolves IC body STEP models:

| Tier | Source | When |
|------|--------|------|
| 1 | **KiCad packages3D** (GitLab) | Standard packages (TSSOP, QFN, SOT, BGA, etc.) — auto-downloaded and cached |
| 2 | **Custom STEP** | Manufacturer-provided or user-supplied STEP file |
| 3 | **Pads only** | No model available — exports pad geometry on PCB substrate |

### Resolution Algorithm

1. Read `.kicad_mod`, extract `(model "${KICAD7_3DMODEL_DIR}/Category.3dshapes/Name.wrl")` reference
2. Convert `.wrl` to `.step` in the relative path
3. Check local cache — if hit, return immediately
4. HTTP GET from GitLab raw URL (15s timeout)
5. On success: save to cache, return path
6. On failure: return null (falls back to pads-only)

**GitLab URL format** (~7,200 STEP files):
```
https://gitlab.com/kicad/libraries/kicad-packages3D/-/raw/master/{category}.3dshapes/{name}.step
```

### Cache

Downloaded models are cached at `/home/adom/.cache/kicad-3d-models/` mirroring the KiCad repo structure:
```
/home/adom/.cache/kicad-3d-models/
  Package_SO.3dshapes/TSSOP-14_4.4x5mm_P0.65mm.step
  Package_LGA.3dshapes/Bosch_LGA-14_3x2.5mm_P0.5mm.step
```
Cache persists across sessions — models are only downloaded once.

### 3D Model Variable Resolution (handled by remote service)

KiCad footprints reference 3D models via `${KICAD7_3DMODEL_DIR}/...wrl`. The remote KiCad service automatically resolves these variable paths to cached `.step` files server-side. You do NOT need to handle this manually — just call `generateGLB()` or `generateSTEP()` from `generate-step.js` and the service handles model resolution, downloading, and caching.

**Code**: `/home/adom/gallia/viewer/kicad-3d-model-resolver.js`

Key exports:
- `resolveModel(kicadModPath)` — full resolution pipeline (cache check → download → fallback)
- `resolveCustomModel(stepPath)` — for manufacturer-provided STEP files
- `rewriteModelPaths(content)` — replaces `${KICAD7_3DMODEL_DIR}/...wrl` with absolute cache paths
- `injectModelReference(content, modelPath)` — adds `(model ...)` to footprints that lack one

## Step 3: Generate GLB / STEP

```javascript
import { generateGLB, generateSTEP } from '/home/adom/gallia/viewer/generate-step.js';

// GLB for 3D viewer (with IC body auto-resolved)
const glbResult = await generateGLB(kicadModPath, fpName, outputPath, options);

// STEP for CAD delivery
const stepResult = await generateSTEP(kicadModPath, fpName, outputPath, options);

// Result: { outputPath, hasModel, modelSource, modelName }
// modelSource: 'cache' | 'download' | 'custom' | 'none'
```

### Options

- Default: auto-resolves 3D model (downloads from GitLab if needed)
- `{ skipModelResolution: true }`: pads-only export
- `{ customStepPath: '/path/to/model.step' }`: use a specific STEP file
- `{ enhanced: true }`: viewer-ready GLB with organized scene graph and PBR materials (see Enhanced GLB section below)
- `{ mmScale: true }`: scale 1000x (meters → millimeters) for Babylon.js shadow/lighting compatibility
- `{ enhanced: true, mmScale: true }`: combined — best for Babylon.js 3D viewers

### Export Flags (used by remote service internally)

The remote service sets these flags automatically — listed here for reference only:

| Scenario | Flags |
|----------|-------|
| **With IC body model** | `--include-pads --subst-models -f -o output` |
| **Pads only (no model)** | `--include-pads --no-components -f -o output` |

You do NOT need to set these flags — `generateGLB()` and `generateSTEP()` handle them.

### Enhanced GLB Mode

Pass `{ enhanced: true }` to any GLB export function to get a **viewer-ready** GLB with:

1. **Organized scene graph** — named parent nodes: `component`, `board_pads`, `board_fr4`
2. **PBR materials** — IC body (dark mold), leads (metallic silver), copper pads (gold), FR4 (green)
3. **Body/lead classification** — dual-criteria algorithm (inner 70% center test + upper 50% height test) correctly classifies primitives across SOT, SOIC, QFP, and other IC packages

Optionally add `{ mmScale: true }` to pre-scale 1000x (meters → millimeters) for Babylon.js shadow/lighting compatibility.

```javascript
// Enhanced GLB for 3dView (recommended for web viewers)
const glb = await fpExportGlbByName('Package_SO', 'SOIC-8_3.9x4.9mm_P1.27mm', '/tmp/soic8.glb', { enhanced: true });
// Enhanced + mm-scaled for Babylon.js (shadows work correctly at this scale)
const glb = await fpExportGlbByName('Package_SO', 'SOIC-8_3.9x4.9mm_P1.27mm', '/tmp/soic8.glb', { enhanced: true, mmScale: true });
// Or with POST endpoint:
const glb = await pcbExportGlb(kicadModPath, fpName, outputPath, { enhanced: true, mmScale: true });
```

The enhanced GLB scene graph:
```
root
  └─ component        ← REF** node (IC body + leads with distinct materials)
  └─ board_pads       ← copper pad meshes
  └─ board_fr4        ← FR4 substrate mesh (omitted if noBoard: true)
```

### GLB Metadata (`asset.extras.adom`)

Every post-processed GLB embeds metadata in `gltf.asset.extras.adom`:
```json
{ "zUp": true, "enhanced": true, "mmScale": true, "boardStripped": false }
```

Babylon.js viewers should check `asset.extras.adom.mmScale` before applying their own 1000x scale to avoid double-scaling.

**When to use enhanced mode:**
- Displaying in 3dView / Babylon.js — enhanced GLBs render with correct materials out of the box
- Client-side material fixup is no longer needed — the GLB itself contains proper PBR materials

**When to add mmScale:**
- Babylon.js viewers that need correct shadow maps and lighting at component scale
- Only if the viewer doesn't already apply its own 1000x scale (check `asset.extras.adom.mmScale`)

**When NOT to use enhanced mode:**
- Exporting for CAD tools (use STEP instead)
- When you need the raw KiCad output without modifications

### GLB Coordinate System (critical)

**Standard GLB output is in meters** (glTF spec default), NOT millimeters. **mmScale GLB output is in millimeters** (1000x scaled).

```
Standard mode:          1 mm = 0.001 scene units (use const MM = 0.001)
mmScale mode:           1 mm = 1 scene unit (no conversion needed)
```

Use `const MM = 0.001` for standard mode. Enhanced mode uses millimeter-scale coordinates directly. Y-axis is up.

## Step 4: Preview in 3dView

### Option A: Built-in 3dView (preferred)

**3dView** is the Gallia Viewer's built-in Babylon.js 3D viewer. Use the `gv_3d_display` MCP tool:

```
gv_3d_display
  glb_path: "/path/to/PART_NAME-3d.glb"
  body_size: { x: 4.4, y: 5.0, z: 1.2 }
  part_name: "CDCEL913"
  manufacturer: "Texas Instruments"
  package_type: "TSSOP-14"
  pad_count: 14
  title: "CDCEL913 3D Model"
```

This copies the GLB to the Gallia Viewer server's serving directory and switches to 3dView with the model loaded.

### Option B: Standalone HTML (for export/sharing)

Generate self-contained HTML with embedded Babylon.js:

```javascript
import { generate3DViewerHTML } from '/home/adom/gallia/viewer/kicad-3d-viewer.js';
import { writeFileSync } from 'fs';

const html = await generate3DViewerHTML(glbPath, {
  bodySize: metadata.bodySize,
  padCount: metadata.padCount,
  packageType: metadata.packageType,
  partName: metadata.partName,
  manufacturer: metadata.manufacturer,
});
writeFileSync(`${outputDir}/PART_NAME-3d-viewer.html`, html);
```

Display via `gv_display_file` MCP tool.

### 3dView Features

**3dView** provides:
- Arc-rotate camera (orbit, pan, zoom, WASD movement)
- Skybox and studio lighting
- View cube for orientation
- Ground plane toggle
- Shadow casting
- IC body with laser-etched chip markings
- Info bar showing package details
- Bottom light ON by default (illuminates underside/pad surfaces)
- XYZ axes OFF by default
- Pad highlighting with cross-pane sync (LibView)

### WebSocket Messages

| Message | Direction | Purpose |
|---------|-----------|---------|
| `show_3d` | server → viewer | Switch to 3dView and load model |
| `clear_3d` | server → viewer | Clear the 3D scene |
| `model_3d_options` | server → viewer | Update laser etch / info bar |
| `stop_tour` | relay → viewer | Stop cinematic tour, freeze camera |
| `start_tour` | relay → viewer | Start/restart cinematic tour |
| `set_camera` | relay → viewer | Set camera alpha/beta/radius (beauty angle) |
| `set_fr4` | relay → viewer | Explicitly show/hide FR4 board (`{ visible: true }`) |
| `toggle_fr4` | relay → viewer | Toggle FR4 board visibility |
| `show_origin` | relay → viewer | Toggle XYZ axis lines at world origin (`{ visible: true/false }`) |
| `set_view` | relay → viewer | Set camera to named preset: `front`, `back`, `left`, `right`, `top`, `bottom`, `isometric` |

The `relay → viewer` messages are sent via the management relay's `broadcast` action:
```bash
curl -X POST http://127.0.0.1:8772/command -H 'Content-Type: application/json' \
  -d '{"action":"broadcast","message":{"type":"stop_tour"}}'
```

## Step 5: Laser Etch Chip Markings

3dView renders laser-etched text on the IC body top surface — manufacturer name, part number, date code, and pin 1 dot. This is automatic when `manufacturer` and `partName` are provided and the model has an IC body.

### How It Works

- Uses known body dimensions from metadata (NOT bounding box detection or raycasting)
- Multiplies mm dimensions by `0.001` to convert to scene units (meters)
- IC body top surface = highest point of centered model bounding box (Y-up)
- Plane at 85% of body dimensions (realistic edge margin)
- DynamicTexture with monospace font, silver/white text color
- Pin 1 dot in top-left corner

### Why This Approach Works

All IC packages have a flat top surface for pick-and-place during reflow soldering. The top surface position is deterministic from known body dimensions — no mesh analysis or raycasting needed.

### Babylon.js DynamicTexture Implementation

```javascript
const MM = 0.001;
const topY = boundingBox.max.y;  // IC body top (Y-up)
const bw = bodySize.x * MM;     // body width in scene units
const bd = bodySize.y * MM;     // body depth in scene units
const longSide = Math.max(bw, bd);
const shortSide = Math.min(bw, bd);

// Canvas texture
const dtex = new BABYLON.DynamicTexture('laserEtch', 1024, scene, true);
const ctx = dtex.getContext();
ctx.fillStyle = 'rgba(210, 212, 215, 0.8)';
ctx.textAlign = 'center';
ctx.font = fontSize + 'px monospace';
// ... draw text lines, pin 1 dot ...
dtex.update();

// Material
const mat = new BABYLON.StandardMaterial('etchMat', scene);
mat.diffuseTexture = dtex;
mat.diffuseTexture.hasAlpha = true;
mat.useAlphaFromDiffuseTexture = true;
mat.emissiveColor = new BABYLON.Color3(0.82, 0.83, 0.84);
mat.backFaceCulling = false;
mat.zOffset = -1;

// Plane on IC body top
const plane = BABYLON.MeshBuilder.CreatePlane('etch', {
  width: longSide * 0.85,
  height: shortSide * 0.85,
}, scene);
plane.rotation.x = Math.PI / 2;        // lay flat (Babylon planes face +Z)
plane.position.y = topY + 0.01 * MM;   // 0.01mm above surface
plane.material = mat;
```

### Marking Lines Logic

Default marking lines (when `manufacturer` and `partName` are provided):
1. `manufacturer.toUpperCase()` (e.g., "TEXAS INSTRUMENTS")
2. `partName.toUpperCase()` (e.g., "CDCEL913")
3. Date code (YYMM format)

Override with custom `markingLines` array for non-standard markings.

## Step 6: Deliver

### 6a: STEP file for KiCad Desktop

```javascript
const stepResult = await generateSTEP(kicadModPath, fpName, outputPath);
```

Then send via MCP tools:
```
1. send_files
     filePaths: ["/path/to/PART_NAME.step"]
     targetApp: "kicad"
     destinationFolder: "3d-models"
```

### 6b: STEP to Fusion 360

```
1. send_files
     filePaths: ["/path/to/PART_NAME.step"]
     targetApp: "fusion360"
     destinationFolder: "fusion"
2. fusion_import_step
     filePath: "<destinationPath from step 1>"
```

## Code Reference

| File | Purpose |
|------|---------|
| `/home/adom/gallia/viewer/generate-step.js` | STEP/GLB export via remote KiCad service + model resolution |
| `/home/adom/gallia/viewer/kicad-3d-model-resolver.js` | Model download, caching, path rewriting |
| `/home/adom/gallia/viewer/kicad-3d-viewer.js` | 3dView integration (`serve3DModel`) + standalone HTML (`generate3DViewerHTML`) |

## Common Pitfalls

- **GLB is in meters, not mm** — GLB output uses meters per glTF spec. `1mm = 0.001 scene units`. Passing raw mm values produces geometry 1000x too large
- **Model path resolution** — `${KICAD7_3DMODEL_DIR}` variables in `.kicad_mod` are resolved automatically by the remote KiCad service. You do NOT need to rewrite paths manually
- **`.wrl` → `.step` substitution** — handled automatically by the remote service (`--subst-models` flag applied server-side)
- **DynamicTexture size** — Use power-of-2 dimensions (1024, 512) for optimal GPU performance
- **Babylon.js plane orientation** — Planes face +Z by default; use `rotation.x = Math.PI/2` to lay flat on XZ plane facing Y-up
- **Gallia Viewer restart required for 3d.html** — After editing `viewer/3d.html`, restart the Gallia Viewer server (`3d.html` is cached with `readFileSync` at startup). `index.html` is read fresh on each request and does NOT require a restart

Sub-Skills
?
What are Sub-Skills?

Sub-skills are community-contributed AI skill extensions for this component. They teach AI assistants about specific tools, configurators, or workflows.

Examples:

  • A manufacturer’s configuration tool for a motor controller
  • A community-written design guide for an amplifier circuit
  • An automated test/validation script for a sensor module

How to add one: Click Add Sub-Skill, provide the URL to your skill and a brief description. Submissions are reviewed by the Adom team before going live.

No sub-skills yet. Be the first to contribute one!

0 revisions · Updated 2026-03-02 17:31:35