@hypersoniclabs/helix-mcp 0.2.4 → 0.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +81 -11
  2. package/dist/continuumCanary.d.ts +17 -0
  3. package/dist/continuumCanary.js +17 -0
  4. package/dist/continuumCanary.js.map +1 -0
  5. package/dist/server.d.ts +14 -1
  6. package/dist/server.js +4058 -162
  7. package/dist/server.js.map +1 -1
  8. package/dist/tsconfig.build.tsbuildinfo +1 -1
  9. package/dist/vehicleTools.d.ts +86 -0
  10. package/dist/vehicleTools.js +229 -0
  11. package/dist/vehicleTools.js.map +1 -0
  12. package/docs/avatar-face.md +115 -0
  13. package/docs/bridge.md +98 -0
  14. package/docs/bring-your-world.md +117 -0
  15. package/docs/catalog.md +69 -1
  16. package/docs/character-animation.md +442 -0
  17. package/docs/character-attachments.md +166 -0
  18. package/docs/character-world.md +785 -130
  19. package/docs/continuum.md +153 -0
  20. package/docs/items.md +73 -0
  21. package/docs/lighting-world.md +667 -0
  22. package/docs/locomotion-clip-spec.md +294 -0
  23. package/docs/manifest.md +31 -6
  24. package/docs/multiplayer-logic.md +460 -17
  25. package/docs/multiplayer-templates/chrono-orchard.md +36 -22
  26. package/docs/multiplayer-templates/collect-a-thon.md +28 -38
  27. package/docs/multiplayer-templates/collections.md +24 -24
  28. package/docs/multiplayer-templates/hangout.md +124 -111
  29. package/docs/multiplayer-templates/npc-wave.md +310 -0
  30. package/docs/multiplayer-templates/obby.md +13 -16
  31. package/docs/multiplayer-templates/persistent-progress.md +218 -0
  32. package/docs/multiplayer-templates/physics-bumper.md +27 -9
  33. package/docs/multiplayer-templates/physics-football.md +22 -8
  34. package/docs/multiplayer-templates/relic-bearers.md +12 -15
  35. package/docs/multiplayer-templates/server-motion.md +16 -19
  36. package/docs/multiplayer-templates/shooter-range.md +275 -0
  37. package/docs/multiplayer-templates/team-control.md +28 -15
  38. package/docs/multiplayer-templates/turn-arena.md +31 -22
  39. package/docs/multiplayer-templates/voice-radio.md +166 -0
  40. package/docs/multiplayer-templates/wave-survival.md +7 -8
  41. package/docs/multiplayer-templates/world-shop.md +240 -0
  42. package/docs/multiplayer-world.md +222 -129
  43. package/docs/npc-world.md +623 -0
  44. package/docs/publishing.md +108 -28
  45. package/docs/purchases.md +223 -0
  46. package/docs/scene-performance.md +64 -0
  47. package/docs/screenshots.md +140 -0
  48. package/docs/sdk.md +324 -5
  49. package/docs/shooter-worlds.md +537 -0
  50. package/docs/terrain.md +173 -0
  51. package/docs/upgrades.md +324 -0
  52. package/docs/vehicles.md +727 -0
  53. package/docs/world-inspect.md +156 -0
  54. package/docs/world-look.md +241 -0
  55. package/docs/world-recipe.md +65 -6
  56. package/package.json +15 -4
  57. package/skills/README.md +91 -0
  58. package/skills/helix-assets/SKILL.md +491 -0
  59. package/skills/helix-assets/references/asset-sources.md +143 -0
  60. package/skills/helix-assets/references/vault-api.md +105 -0
  61. package/skills/helix-avatar-qa/SKILL.md +85 -0
  62. package/skills/helix-avatars/SKILL.md +206 -0
  63. package/skills/helix-avatars/references/contract.md +166 -0
  64. package/skills/helix-avatars/references/dynamics.md +367 -0
  65. package/skills/helix-avatars/references/face.md +50 -0
  66. package/skills/helix-avatars/references/publish.md +76 -0
  67. package/skills/helix-avatars/references/qa.md +251 -0
  68. package/skills/helix-avatars/references/rigging.md +88 -0
  69. package/skills/helix-avatars/references/source-generated.md +190 -0
  70. package/skills/helix-avatars/references/source-model.md +90 -0
  71. package/skills/helix-avatars/references/source-rigid.md +90 -0
  72. package/skills/helix-avatars/references/source-vrm.md +61 -0
  73. package/skills/helix-gauntlet/SKILL.md +128 -0
  74. package/skills/helix-multiplayer/SKILL.md +150 -0
  75. package/skills/helix-multiplayer/references/dsl-capability-map.md +107 -0
  76. package/skills/helix-vehicles/SKILL.md +218 -0
  77. package/skills/helix-vehicles/references/addons.md +212 -0
  78. package/skills/helix-vehicles/references/appearance.md +339 -0
  79. package/skills/helix-vehicles/references/audio-import.md +138 -0
  80. package/skills/helix-vehicles/references/audio.md +580 -0
  81. package/skills/helix-vehicles/references/cabin.md +225 -0
  82. package/skills/helix-vehicles/references/host-manifest.md +174 -0
  83. package/skills/helix-vehicles/references/physics-from-specs.md +463 -0
  84. package/skills/helix-vehicles/references/publish.md +214 -0
  85. package/skills/helix-vehicles/references/qa.md +177 -0
  86. package/skills/helix-vehicles/references/reference-package.json +3481 -0
  87. package/skills/helix-vehicles/references/reference-package.md +69 -0
  88. package/skills/helix-vehicles/references/source-beamng.md +167 -0
  89. package/skills/helix-vehicles/references/source-concept.md +38 -0
  90. package/skills/helix-vehicles/references/source-model.md +100 -0
  91. package/skills/helix-vehicles/references/source-scratch.md +60 -0
  92. package/skills/helix-world-build/SKILL.md +376 -0
  93. package/skills/helix-world-build/references/config-gates.md +104 -0
  94. package/skills/helix-world-director/SKILL.md +210 -0
  95. package/skills/helix-world-qa/SKILL.md +371 -0
  96. package/skills/helix-world-qa/references/perf-budgets.md +240 -0
  97. package/skills/helix-world-qa/references/perf-handle.md +125 -0
  98. package/skills/helix-world-qa/references/visual-scorecard.md +85 -0
@@ -0,0 +1,69 @@
1
+ # Reference package: the live Porsche 911 (993)
2
+
3
+ The reference is the complete `helix.vehicle-package/2` of a car that passes the platform's visual
4
+ QA gate and drives live: a two-door, rear-engined, naturally aspirated, rear-wheel-drive coupé.
5
+ It is about 72k characters of JSON, so it ships as a file, never as tool output. Get it with:
6
+
7
+ ```
8
+ get_vehicle_reference_package({ out: "/abs/car/vehicle-package.json" })
9
+ ```
10
+
11
+ That copies it to your path and prints a one-line summary of each block. Read the file block by
12
+ block. Then replace **every** number with your car's sourced values. Do not keep a 993 number
13
+ because it "looks plausible". Edit the citations first, then the values that cite them.
14
+
15
+ The copy is cleaned for reuse:
16
+
17
+ - every audio slot `note` and car-specific `reason` starts with `REWRITE`;
18
+ - the 993's own recordings (engine ladder, shifts, clutch, limiter, backfire) are reduced to the
19
+ authoring form, `name` + `file`, so they fail `check_vehicle_audio` until you supply your WAVs;
20
+ - the generic one-shots (`bg_*.wav`) keep their public URLs only as shape examples: they are
21
+ **another car's recordings, not your car's sound**. Replace each with a sound from your car's sim
22
+ mod or the BeamNG base game (`import_vehicle_audio`) with its provenance (every slot is sourced;
23
+ the only empty slot is `absent: "not-fitted"` for a component the real car lacks); do not ship a
24
+ `bg_*` row under your car.
25
+
26
+ **Rewrite every slot note for your car.** A note is provenance: it says what was recorded, from
27
+ which engine, and where it came from. Round 1's F40 shipped notes about a Subaru EJ257 because it
28
+ copied rows without reading them.
29
+
30
+ ## What to keep, change or delete
31
+
32
+ | Block | Do |
33
+ | --- | --- |
34
+ | `id`, `displayName`, `make`, `model`, `years`, `market` | Change. `id` is a stable slug, and it is locked after the first publish. |
35
+ | `provenance` | Change `note` to your source chain. `distributable` must be `true`. |
36
+ | `citations` | Replace them all. `kind` is one of `manufacturer`, `instrumented-test`, `reference-database`, `measured-fixture`, `engineering-literature`, or `reference`. |
37
+ | `mesh.classId` | One of `sedan`, `supercar`, `pickup`, `suv`, `van`, `truck`, `bus`, `cruiser`, `kart` (or `lightplane`). It sets the preset fallbacks and **how many seat rows the cabin derives**: `supercar` and `kart` have one row, `sedan` has two. An unknown id falls back to `sedan` with a warning. The 993's `coupe` is such a fallback, which is why it derives rear seats it does not declare. Choose deliberately. |
38
+ | `mesh.ref` | Leave `$self`. The platform binds it to your item id. |
39
+ | `mesh.wheelVisuals` | `author-static` when your GLB carries real wheels. |
40
+ | `mesh.parts` | List your node names. Wheels are never parts. |
41
+ | `mesh.visual` | Keep it whole. `format`, the five `tiers` and the `source` rendition are canonical, and the `target-web` derived rendition carries the `encodings`/`sourceRenditionId`/`textureMaxDimension` that `publish_vehicle` re-pins to the GLB it uploads (it updates `artifactRef`, `checksumSha256` and `byteLength` — the stale 993 pointer is never served). Update `triangleBudget` on both renditions to your GLB's triangle count. Deleting this block breaks the fresh mint: `validate_vehicle` accepts only a materialised `target-web`, and the publish path pushes one the sealed-root validator then rejects. |
42
+ | `mesh.articulation.panels` | Change the hinge pivots and axes to your car's, measured in your model frame. Keep one panel per opening, and match the signals to the specialty component actions. |
43
+ | `physics`, `simulation.land`, `targets` | Replace with your car's values (`physics-from-specs`), then run `simulate_vehicle` until every target is met. The 993 is ungoverned (`speedLimiterKph` is `null`, `topSpeedKph` equals `ungovernedTopSpeedKph`) and its curve sits on the maker's declared crank figures: keep both rules ("Top speed: governed vs ungoverned", "Torque: target the declared crank figures"). Its `simulation.land.driveline` is rear-drive only; an AWD car adds `front` and `centre` ("All-wheel drive"). |
44
+ | `audio` | Replace the whole block with your car's sound from the most accurate sim mod, extracted by `import_vehicle_audio` (never synthesised), and give every slot its `provenance` (`audio`). |
45
+ | `lights` | Shape only. Nothing at runtime reads `lights.lamps`, but keep it truthful: node names and colours. |
46
+ | `vfx` | Keep the declared-but-unconsumed slots, with their reasons. |
47
+ | `specialty` | Keep `module` (the `helix-convertible` bundle id, version and checksum) and the action list. Rename `seatVars` keys to the seats you declare, the `componentBuild` row `slot`s to your lamp node names, and the `articulators` to your panel nodes. **Keep every signal and action.** An action your car lacks (fog, with no fog lamps) lights nothing, which is harmless. Removing one changes the compiled `componentActions`, which must exactly equal the protocol compiled from `componentBuild`. Recompute `componentBuild.digest` after any row edit. |
48
+
49
+ ## componentBuild digest (the backend refuses a mismatch)
50
+
51
+ `validate_vehicle` prints the computed digest (`COMPONENT_BUILD_DIGEST_MISMATCH`) and the compiled
52
+ `componentActions` (`COMPONENT_ACTIONS_MISMATCH`) whenever they disagree with what you declared:
53
+ paste them. For reference, this is a port of the backend's `digestOf`; it computes
54
+ `607b38b594eeccc9` for the 993.
55
+
56
+ ```js
57
+ function stable(v){if(v===null||typeof v!=='object')return JSON.stringify(v)??'null';if(Array.isArray(v))return `[${v.map(stable).join(',')}]`;return `{${Object.keys(v).sort().map(k=>`${JSON.stringify(k)}:${stable(v[k])}`).join(',')}}`;}
58
+ function fnv1a64(s){let a=0x811c9dc5,b=0x01000193;for(let i=0;i<s.length;i++){const c=s.charCodeAt(i);a=Math.imul(a^c,0x01000193);b=Math.imul(b^(c+i),0x85ebca6b);}return (a>>>0).toString(16).padStart(8,'0')+(b>>>0).toString(16).padStart(8,'0');}
59
+ function digestOf(cb){const parts=cb.signals.map(s=>`s:${stable(s)}`);const rows=[];
60
+ for(const f of ['articulators','interacts','lights','materials','attachments','vfx','patterns','carried','unsupported'])
61
+ for(const r of cb[f]??[]) rows.push(`r:${r.type}/${r.id}:${stable(r)}`);
62
+ parts.push(...rows.sort()); return fnv1a64(parts.join('\n'));}
63
+ ```
64
+
65
+ ## Audio URLs
66
+
67
+ A runtime audio row needs `url`, `checksumSha256` and `bytes`, and the URL must serve exactly
68
+ those bytes. **A row with only a `cid` and no `url` plays nothing.** Author your own rows as
69
+ `name` + `file`; `publish_vehicle` uploads each WAV and writes the rest. Seal your own WAVs the same way; never reuse a `bg_*` row's URL.
@@ -0,0 +1,167 @@
1
+ # Source bridge: BeamNG mods
2
+
3
+ The HELIX vehicle system is modelled on BeamNG, so most fields map directly. The trap is
4
+ **budget**. BeamNG cars are built for a desktop simulator, and a raw conversion runs to roughly
5
+ 200k triangles and 100–400 draws. The platform wall is 90k triangles and 24 draws, so the work is
6
+ mostly *restructuring*, not converting.
7
+
8
+ ## 1. Pick and inventory the mod
9
+
10
+ - Download it with a real browser. Cloudflare blocks headless downloads from several mod hosts.
11
+ Unpack it fully.
12
+ - **Measure from the extracted files, never from the mod's listing page.** Count the `.jbeam` part
13
+ definitions, `.pc` configurations, DAE objects and materials, and the `art/sound` WAVs.
14
+ - Score the candidates:
15
+ - cabin as separate objects;
16
+ - doors and lids as separate objects, with jbeam hinges;
17
+ - each lamp separable (its own `glowMap` materials);
18
+ - real part slots, meaning options that change both numbers *and* visuals;
19
+ - mesh and texture quality.
20
+
21
+ A feature the request needs but no source has decides the pick. The F355 was rejected because
22
+ no obtainable mod had a separable Spider roof.
23
+ - Expect large data. The 993 mod had a 559 MB DAE, 605 parts, 122 slot types and 9 configs, and
24
+ 76 of its 128 stock flexbody references had no geometry.
25
+
26
+ ## 2. Import with Bridge
27
+
28
+ `source` may be the mod's `.zip` exactly as downloaded: the CLI extracts it to a temp directory
29
+ (refusing path traversal, duplicate entries and zip bombs), and the plan key is the same as for the
30
+ unpacked folder.
31
+
32
+ ```
33
+ bridge_detect({ source: "/abs/mod.zip" })
34
+ bridge_plan({ source, plugins: ["hypersonic.beamng-vehicle"], pluginDirs: ["<cli>/plugins/beamng-vehicle"],
35
+ inputs: { palette: "/abs/helix-materials/palette.json", beamngCommon: "/abs/beamng-common.json" } })
36
+ bridge_import({ source, output: "/abs/new-dir", inputs: {…}, expectedPlanKey: "<deterministicKey>",
37
+ allowPermissions: ["filesystem.read-source","filesystem.write-output","spawn-process","blender","unrestricted-host-execution"] })
38
+ ```
39
+
40
+ - The plugin is external. It ships inside the CLI package under `plugins/beamng-vehicle`, needs
41
+ **Blender** on the host, and requires a reviewed plan key. Read
42
+ `read_doc({ name: "bridge" })` for the permission model.
43
+ - The **`palette` input is required**: `helix-palette/4` or `/3`.
44
+ - **`beamngCommon`** indexes the base game's `vehicles/common` (the shared tyres and textures). It
45
+ is built once with `node plugins/beamng-vehicle/bin/index-beamng-common.mjs <unpacked basegame>`.
46
+ Without it, base-game textures go missing and the parts ship flat grey.
47
+ - **Cylinder count** drives the engine sound's firing frequency. The importer takes the first of
48
+ these that the mod declares:
49
+ 1. the `beamngEngine` input (a JSON file such as `{ "cylinders": 6 }`);
50
+ 2. a numeric field in the engine jbeam (`cylinderCount`, `fundamentalFrequencyCylinderCount`,
51
+ `firingOrder`);
52
+ 3. a layout in the engine part's name (`I6`, `V8`, `Flat-6`);
53
+ 4. a layout in the sound names (`box4_2006`).
54
+
55
+ If none of these exist, it falls back to 4. **Check `functionalVehicle.engineCylinders` in
56
+ `beamng-import-report.json`.** Mods mislabel engines. The mmbng 993 calls its flat six a "3.2L
57
+ V8 Engine", so its import needs `inputs: { beamngEngine: "/abs/engine.json" }` with
58
+ `{ "cylinders": 6 }`.
59
+ - Jbeam that BeamNG itself accepts also loads here, including a stray `},}` after the root object
60
+ and runs of commas. The report counts what was ignored.
61
+ - The importer refuses a mod with several `.pc` configurations or duplicate jbeam part names. Make
62
+ a reduced copy that holds **one `.pc`** (usually `Stock.pc`), its jbeam closure, and only the DAE
63
+ nodes it uses. Extract the add-on parts separately from the full mod.
64
+ - It emits `<id>.glb`, `vehicle-package.json` (`helix.vehicle-package/2`), `vehicle-addons.json`,
65
+ `vehicle-build.json`, `vehicle-source.json` and `vehicle-lowering.json`, plus an import report.
66
+ - Its acceptance gate allows 25k–300k triangles. **That is not the publish budget.**
67
+
68
+ ## 3. Field mapping
69
+
70
+ | BeamNG | HELIX |
71
+ | --- | --- |
72
+ | DAE object per part (`inf993_door_FL`…) | GLB node; rename to contract names (`door_lf`, `bonnet`, `boot`, `rim_lf`, `steering`) |
73
+ | jbeam hinge and latch break groups on doors, hood and trunk | `mesh.articulation.panels[]`, with the hinge pivot and axis measured in the HELIX frame |
74
+ | Wheel nodes, hubRadius, tyre meshes in `vehicles/common` | `rim_*` nodes (rim + tyre + disc merged), `chassis.wheelRadius` |
75
+ | `.pc` paints[0] | Baked paint in the atlas, plus `helixVehicleRole: "paint"` |
76
+ | Material stages (`*.materials.json`, 4 stages) | Baked PBR. **Strip paint-slot tags (`name [SECONDARY]`) before lookup.** Watch for a later `null` stage factor overriding a real one. |
77
+ | `glowMap` materials (`*_lowhi`, `*_brak`, `*_revik`, `*_sigL`…) | **One node and material per lamp function** (see section 5) |
78
+ | `instanceDiffuse` body parts | Need the host paint carried as a solid colour (they are a neutral mask) |
79
+ | Engine torque curve, idle and limiter, gearbox, final drive, diffs | `physics.engine` / `gearbox`, and `simulation.land.driveline`. **Calibrate to the real car's published figures.** Mod engines are often renamed templates. |
80
+ | `pressureWheels` tyre and brake values | Starting points for `grip`, `tyreByAxle` and `brakes`. Tyre-shape coefficients are HELIX priors. |
81
+ | Coilover and strut spring/damper leaves | `suspension`. Torsion bars and swaybar construction beams do not map. |
82
+ | Part slots (wheels, wings, bumpers, ECUs, exhausts…) | Add-ons; derive op ratios from option vs stock jbeam values |
83
+ | `soundConfig.sampleName` → `art/sound/blends/<name>.sfxBlend2D.json` and its recordings | `audio.bank` ladders with the source's own rpm rungs; the WAVs are written beside the package (Bridge plugin ≥ 0.7.0). `import_vehicle_audio` does this with per-slot provenance and also resolves stock FMOD events |
84
+ | Electrics and lights | Specialty actions and `componentBuild` rows (see the `reference-package` reference) |
85
+
86
+ Frame: BeamNG axes are converted to HELIX +X left, +Y up, +Z forward. If a result comes out with
87
+ the rear at +Z, re-convert it. The importer mirrors such a result automatically.
88
+
89
+ ## 4. Restructure to the contract (Blender, headless)
90
+
91
+ 1. **Inspect every Bridge part for geometry stacked at the origin.** Unapplied flexbody transforms
92
+ leave calipers and similar parts there. Drop them or re-place them.
93
+ 2. **Fix stance against real dimensions.** The jbeam wheelbase can disagree with the mesh's own
94
+ arches (2.60 m against 2.28 m). Find each arch opening from the lowest body vertex per Z slice,
95
+ and put the wheels there. Check the body height against the real car: the 993 mod sat 6–8 cm
96
+ low.
97
+ 3. **Pivot every wheel at its hub** (translation only), with rim, tyre and disc merged into one
98
+ mesh per corner.
99
+ 4. **Merge everything static** into `bodyshell`: the shell, trim, interior, engine bay and static
100
+ gauges. Keep these separate: `glass`, the doors and door glasses, `bonnet`, `boot`, `steering`,
101
+ the four wheels, one node per lamp function, `exhaust`, and every add-on hide target.
102
+ 5. **Decimate to about 85k triangles with a normal-aware weld first**, so trim cannot tear into
103
+ spikes. The 993 went from 218,504 to 84,548.
104
+ 6. **Fix arch clearance.** If raising the body exposes the mod's inner arch liners, build a closed
105
+ matte-black liner tub per wheel and cut the body to it. Merely raising the body left torn shards
106
+ that failed `render.intact`.
107
+ 7. Drop transmission, clearcoat and similar extensions so each node is one render state.
108
+
109
+ ## 5. Lamps
110
+
111
+ A BeamNG lamp housing is one mesh whose functions are separate glow materials. Split each function
112
+ into its own node: classify each triangle by its source material, or by its centroid against the
113
+ source DAE. Then author the emissive as the contract describes (`emissiveFactor`, strength 0, a
114
+ white emissive map).
115
+
116
+ Ship only the lamps the mod has. The Subaru mod had no fog lamp, CHMSL or DRL, and BeamNG
117
+ generates its number plate at runtime.
118
+
119
+ ## 6. Atlas and textures
120
+
121
+ Give each node **one atlas material**. Size the atlas cells to the UV *repeat*, not to the
122
+ texture: tiling materials such as leather grain repeat tens of times. Bake the source's
123
+ **uncompressed** images, not a KTX2 round-trip, or the cells fall back to flat colours.
124
+
125
+ Encode KTX2 with mips, keep data maps linear, and declare `KHR_texture_basisu`. Expect more
126
+ materials out than in. That is fine: draws are counted per primitive.
127
+
128
+ The engine team's atlas tool (`tools/vehicle-pipeline` in the engine repository) does exactly
129
+ this and verifies the result. It is not published as a package. If you cannot run it, do the same
130
+ steps in Blender: bake each node to its own atlas page region, then encode with `toktx`.
131
+
132
+ ## 7. Physics, audio and add-ons
133
+
134
+ - **Physics.** Take the importer's `vehicle-package.json` as a draft. Re-cite and re-calibrate
135
+ every number to the real car (`physics-from-specs`), and author or keep `simulation.land`.
136
+ - **Audio.** This mod's sound is the first candidate for the car's audio, not automatically the
137
+ answer: the audio comes from the **most accurate simulator mod of this exact car**, chosen by
138
+ measuring against real recordings (`audio`, "Which mod is most accurate?"). Never synthesise.
139
+ The importer extracts the mod's own engine recordings as PCM WAV files and writes the
140
+ `audio.bank` ladders and the `engineLadder`/`exhaustLadder` slots, with rows that have no `url`
141
+ yet; `publish_vehicle` uploads them and fills the URLs. Check `sampleCylinderCount`: BeamNG does
142
+ not record it, so it defaults to 4 (the 993 is a flat-six). One-shots (starter, horn) and engines
143
+ that only name a stock BeamNG FMOD event ship no recording in the bridge output: run
144
+ `import_vehicle_audio({ source: <mod>, out: <car dir>, sim: "beamng", beamngRoot: <BeamNG.drive> })`
145
+ to resolve the stock blends and banks (`art_sound.zip`, `audio.zip`, `vehicles/common.zip`) with
146
+ provenance, and fill what is still missing in the `audio` reference's order.
147
+ - **Add-ons.** `vehicle-addons.json` records are **not publishable as emitted**: they have empty
148
+ `requires` and no `fits.only.items`. Build each add-on as the `addons` reference describes.
149
+ Derive the ops from the jbeam option values against stock. Wheel options that share
150
+ hubRadius, offset and width are geometry-only.
151
+
152
+ ## Known traps (each one cost a revision)
153
+
154
+ - Calipers stacked at the origin.
155
+ - Wheelbase from jbeam instead of the arches.
156
+ - A body 6–8 cm too low.
157
+ - Lamp housings lit whole.
158
+ - An sRGB-tagged roughness atlas.
159
+ - A paint mask on add-ons.
160
+ - Parts missing UVs.
161
+ - Base-game maps missing.
162
+ - `[SECONDARY]` painting the wheels white.
163
+ - A mod engine stronger than the real car.
164
+ - Seat H-points that were not checked against the mod's low roof: heads went through it.
165
+ - A final GLB without `asset.extras.HELIX_vehicle`: after your last Blender or gltf-transform pass,
166
+ check it is still there (`validate_vehicle` refuses it otherwise). The importer's geometry
167
+ receipt holds the measured sockets; write them with the `host-manifest` reference.
@@ -0,0 +1,38 @@
1
+ # Source bridge: images, concepts, fictional cars
2
+
3
+ Use this when all you have is a Midjourney concept, a sketch, a photo set, or a name with no
4
+ downloadable model.
5
+
6
+ ## What AI image-to-3D can and cannot do
7
+
8
+ **Do not ship an AI-generated car body.** Five rebuilds of a generated sedan body all failed the
9
+ platform's `render.intact` gate. The critic saw a "lumpy, warped body, torn hood, crumpled rear
10
+ quarter": generator surface noise plus real holes at panel seams. A blind A/B picked the
11
+ candidate over the old model at 8/10, yet it still failed the gate. Picking the better of two is
12
+ not the gate; the gate asks "is it intact".
13
+
14
+ Generated 3D is still useful for:
15
+
16
+ - a **proportion blockout**: rough volume to trace over;
17
+ - **small detail props**, such as a mirror, a light housing or a wheel centre, after cleanup;
18
+ - **reference renders** from new angles.
19
+
20
+ ## The route
21
+
22
+ 1. **Pin down the design.** Produce or collect orthographic views: front, side, rear and top.
23
+ `generate_image` can turn a concept into consistent orthographic views. Lock in the
24
+ proportions: length, width, height, wheelbase, track and wheel size. For a fictional car,
25
+ choose a real analogue and say so ("proportions of a 1990s mid-engine supercar, 4.4 m").
26
+ 2. **Model clean geometry over the blueprints** in Blender. Follow the `source-scratch` reference
27
+ and build to the contract from the start: separate wheels with hub origins, doors, lids,
28
+ glass, lamp functions, and a simple interior with seats, dash and steering wheel.
29
+ 3. **Texture it.** Use PBR paint, trim and glass, with lamp emissive. Project the concept's
30
+ graphics (livery, badges) as decals into the atlas.
31
+ 4. **Physics** comes from the named real analogue, each value `derivedFrom` it
32
+ (`physics-from-specs`). **Audio** is sourced, never synthesised: the most accurate simulator mod of an analogous real
33
+ engine, extracted with `import_vehicle_audio` (`audio`).
34
+ 5. **Host manifest.** Write `asset.extras.HELIX_vehicle` into the final GLB (the `host-manifest`
35
+ reference).
36
+ 6. **QA** against the concept images, not photos of a real car: the `appearance` method, with the
37
+ concept's views as the reference set. Judge proportion, stance and wheel fitment from the same
38
+ angles as the concept.
@@ -0,0 +1,100 @@
1
+ # Source bridge: downloaded or ripped models
2
+
3
+ This covers Sketchfab, store assets, game rips, CAD exports and any GLB, FBX, OBJ or USD file.
4
+ Such models are usually built to *look* right from outside: one merged mesh, wheels welded in,
5
+ no interior, and scale and axes anywhere. Your job is to make them *work*.
6
+
7
+ ## First choice: for a real named car, download the exact car first
8
+
9
+ **For a REAL named car, download a quality CC0/licensed Sketchfab model of that exact car first.**
10
+ A good existing model of the named car — cleaned up and re-split to the contract — beats modelling
11
+ the body from scratch, which has read as a crude kit-car across three rounds. Search the exact
12
+ model name before deciding to build. Only fall back to `source-scratch` when **no** model of the
13
+ named car exists, or the car is fictional. Whatever you download, **verify the licence (CC0, or
14
+ otherwise redistributable for a package you sell) and set `provenance.distributable`** — that is
15
+ already a publish gate. Then continue with the cleanup and splitting below.
16
+
17
+ ## 1. Choose the model
18
+
19
+ Score each candidate before downloading the expensive one:
20
+
21
+ | Criterion | Why |
22
+ | --- | --- |
23
+ | Separate objects for each wheel, door, lid, steering wheel and the glass | Splitting a merged mesh is the costliest step |
24
+ | Real interior: seats, dash, steering wheel | First-person view, and occupants |
25
+ | Separate lamp lenses | Working lights |
26
+ | Clean topology, no holes at panel seams | Holes fail visual QA's `render.intact` |
27
+ | Triangle count | Under 300k is workable; you will reduce to about 85k |
28
+ | PBR textures you can bake | |
29
+
30
+ A model that is one merged mesh with thousands of disconnected islands (3,026 on one sedan) is
31
+ not convertible by splitting. It needs a rebuild, or it ships as a static prop item instead of a
32
+ vehicle.
33
+
34
+ ## 2. Normalise (Blender, headless)
35
+
36
+ 1. Import the model, apply every transform, and **bake scale and rotation into the vertices**.
37
+ 2. Orient it with the nose at +Z, left at +X and up at +Y. Convert to metres. Check the real
38
+ length.
39
+ 3. If the model is off-size, **scale it uniformly**, anchored on the real **width**. Never scale
40
+ per axis: round wheels become ellipses. Anchoring on length turned one sedan into a 2.44 m-wide
41
+ van.
42
+ 4. Centre it on X and Z, and put the lowest tyre point at Y = 0.
43
+ 5. Delete what the platform does not use: animation, armatures, cameras, lights, hidden and
44
+ damage-state duplicates, co-located variants (z-fighting), and the 1 mm inner twins of
45
+ double-sided panes. Delete enclosed parts nobody can see, unless something can open to reveal
46
+ them.
47
+
48
+ ## 3. Split into contract parts
49
+
50
+ - **Wheels.** Separate each wheel: rim, tyre and disc as one object per corner. Set the origin at
51
+ the centre of the tyre cylinder, which you can fit from the tread vertices. Name them
52
+ `rim_lf`, `rim_rf`, `rim_lr` and `rim_rr`.
53
+ - **Merged mesh.** If the wheels were welded into the body, select by connected component, by
54
+ the cylinder around each hub, or by material, then separate.
55
+ - **Doors, lids, steering wheel, glass and each lamp function** each become their own objects
56
+ (contract section 2).
57
+ - If a door is fused to the body, cut it along its panel gap and cap the hole behind it.
58
+ - If you cannot make a clean opening, **do not declare the panel**. A static door is better
59
+ than a torn one.
60
+ - **Everything else** merges into `bodyshell`. Cabin parts may stay in it.
61
+ - **Seats.** Measure the H-point of each seat: the hip joint, about 0.1 m above the compressed
62
+ cushion and 0.1–0.15 m forward of the backrest. Write it into `mesh.sockets` and the host
63
+ manifest. A model with no real interior needs one built to the `cabin` reference.
64
+
65
+ ## 4. Materials
66
+
67
+ Bake each node's materials to one atlas material, and use KTX2. Then:
68
+
69
+ - set the paint role and the semantic family ids;
70
+ - set up lamp emissive per the contract;
71
+ - fix the tyres: metallic 0, roughness about 0.6.
72
+
73
+ Check each material:
74
+
75
+ - Is a normal map bound as base colour?
76
+ - Are any metallic values at 1?
77
+ - Does any primitive bind a texture with no UVs?
78
+
79
+ Look at the renders: the lavender sheen of a normal map read as albedo is unmistakable once seen.
80
+
81
+ ## 5. The host manifest
82
+
83
+ Write the class and every socket position (seats, wheel hubs, steering, engine, exhaust, lamps,
84
+ `boot`, `bonnet`) into the final GLB's **`asset.extras.HELIX_vehicle`**. A downloaded model never
85
+ has one, and Blender's exporter cannot put one there: patch the exported file with a snippet from
86
+ the `host-manifest` reference. Without it the car mints as a non-host and no add-on can ever fit.
87
+
88
+ ## 6. Appearance acceptance
89
+
90
+ A downloaded model's materials were tuned for another renderer. Render the fixed poses with
91
+ `preview_vehicle`, put each beside the reference photo at the same pose, and overlay the side,
92
+ front and top silhouettes. Check the lamp pods, the wheels and the tail against detail photos, and
93
+ retune the paint to its targets (linear `baseColorFactor`). Judge the platform render, never the
94
+ source viewer. The method is in the `appearance` reference.
95
+
96
+ ## 7. Then follow the skill from step 5
97
+
98
+ A downloaded model has no physics or audio, so `physics-from-specs` and `audio` do all of that
99
+ work. The audio never follows the mesh: find the most accurate simulator mod of the exact car
100
+ (BeamNG first) and extract it with `import_vehicle_audio`; never synthesise.
@@ -0,0 +1,60 @@
1
+ # Source bridge: building a car from scratch
2
+
3
+ **Use this for a fictional car, a concept with no production model, or a real car for which no
4
+ licensed/CC0 base mesh exists. It is NOT the default for a real named car.** For a real named car,
5
+ download a quality CC0/licensed model of that exact car and re-split it (`source-model`) — the
6
+ from-scratch body has read as a crude kit-car across three rounds. Model from primitives only when
7
+ there is genuinely no source (and `source-concept` covers a fictional car from images).
8
+
9
+ Build to the contract from the first primitive, so there is nothing to restructure later. Work in
10
+ Blender with headless Python scripts, and keep the script as the source of truth so the build
11
+ can be re-run.
12
+
13
+ ## Order of work
14
+
15
+ 1. **Set up the frame.** Use metres, a +Z-forward nose and +X left. In Blender, which is
16
+ −Y-forward and Z-up, author in Blender axes and export glTF with +Y up. Check the exported
17
+ nose is +Z by measuring the GLB.
18
+ 2. **Block in the volumes** from the real dimensions or the analogue's: length, width, height,
19
+ wheelbase and both tracks. Place the wheels first, and build the body around their arches.
20
+ 3. **Build the wheels.**
21
+ - Model one wheel (rim, tyre and disc) **centred at the origin, with its axle along X**.
22
+ - Instance it at the four hubs as `rim_lf`, `rim_rf`, `rim_lr` and `rim_rr`, with the
23
+ origins at the hubs. Mirror the right side.
24
+ - Size the tyre from the tyre code: radius = (rim_in × 25.4 / 2 + width × aspect / 100) / 1000.
25
+ 4. **Build the body.**
26
+ - Make it one `bodyshell` with real wheel arches. Leave at least 10 mm of tyre clearance at
27
+ full lock plus 0.07 m of bump, and keep the lowest body point at least 5 cm off the road.
28
+ - Make the doors (`door_lf`, `door_rf`, …), `bonnet` and `boot` separate, with clean cut lines
29
+ and capped edges.
30
+ - Make `glass` separate, as blended material.
31
+ - Give each lamp function its own node: `headlamps`, `taillamps`, `reverselights`,
32
+ `indicator_l`, `indicator_r`, and so on.
33
+ 5. **Build the interior** to the interior reference photos: seat shells, dash and binnacle,
34
+ legible gauges, the real steering wheel design (`steering`, origin at the column), pedals,
35
+ door cards, floor and roof lining. Place the seat H-points where the real seats put the hips,
36
+ so that an adult's crown clears the roof lining by at least 10 mm and the driver's hands reach
37
+ the rim. The rules and thresholds are in the `cabin` reference.
38
+ 6. **Keep the budget in mind as you model.** The node list is the draw budget: aim for 12–22
39
+ draws, with about 60–85k triangles, most of them in the body and wheels.
40
+ 7. **Materials.** Use one material per node, baked into atlas pages of 2048 px or smaller, and
41
+ KTX2. Add the paint role and family ids, and the lamp emissive setup.
42
+ 8. **Export, then measure** the GLB as the `qa` reference describes before writing the package.
43
+ 9. **Write the host manifest into `asset.extras.HELIX_vehicle`** of the final GLB: the class and
44
+ every socket position (seats, wheel hubs, steering, engine, exhaust, lamps, `boot`, `bonnet`).
45
+ Blender's exporter writes custom properties to node and scene `extras`, never to
46
+ `asset.extras`, so patch the exported file. The snippets are in the `host-manifest` reference.
47
+ Without it the car mints as a non-host and no add-on can ever fit it.
48
+ 10. **Appearance acceptance — the reference-overlay gate.** Render the fixed poses with
49
+ `preview_vehicle`, then, for `side`, `front` and `top`, blend each render onto its reference
50
+ photo at the same pose and read the silhouette IoU and landmarks against the thresholds. Check
51
+ the lamp treatment (recessed lamps and body-colour panels where the reference has them), the
52
+ panel relief, the wheel design and the tail against detail photos, and the paint against the
53
+ sampled-from-reference targets. Judge the platform render, never the Blender viewport. The
54
+ checklist, thresholds and paint values are in the `appearance` reference. Mint only when every
55
+ overlay passes.
56
+
57
+ A from-scratch car has every design choice in your hands. The commonest misses are wheels
58
+ slightly small for the arches, a body sitting too low, and a roof too low for an adult occupant.
59
+ All three are visible in a side-on render next to a real reference photo. Make that render first,
60
+ and repeat the full pose comparison after every change.