@helix3/helix-cli 0.1.18-helix3.274 → 0.1.18-helix3.276

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 (48) hide show
  1. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-2.addon.json +1 -0
  2. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-2.json +1 -0
  3. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-3.addon.json +1 -0
  4. package/bridge-recipes/unreal/helix-character-creator-male/male-carnival-mask-3.json +1 -0
  5. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask-2.addon.json +1 -0
  6. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask-2.json +1 -0
  7. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask.addon.json +1 -0
  8. package/bridge-recipes/unreal/helix-character-creator-male/male-eye-hole-mask.json +1 -0
  9. package/bridge-recipes/unreal/helix-character-creator-male/male-marshmallow-mask.addon.json +1 -0
  10. package/bridge-recipes/unreal/helix-character-creator-male/male-marshmallow-mask.json +1 -0
  11. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-coat.addon.json +1 -0
  12. package/bridge-recipes/unreal/helix-character-creator-male/male-nebula-executive-coat.json +1 -0
  13. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-mantle.addon.json +1 -0
  14. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-mantle.json +1 -0
  15. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-tunic.addon.json +1 -0
  16. package/bridge-recipes/unreal/helix-character-creator-male/male-neo-arcane-heir-tunic.json +1 -0
  17. package/bridge-recipes/unreal/helix-character-creator-male/male-paper-bag-mask.addon.json +1 -0
  18. package/bridge-recipes/unreal/helix-character-creator-male/male-paper-bag-mask.json +1 -0
  19. package/bridge-recipes/unreal/helix-character-creator-male/male-pig-mask.addon.json +1 -0
  20. package/bridge-recipes/unreal/helix-character-creator-male/male-pig-mask.json +1 -0
  21. package/bridge-recipes/unreal/helix-character-creator-male/male-puffer-jacket.addon.json +1 -0
  22. package/bridge-recipes/unreal/helix-character-creator-male/male-puffer-jacket.json +1 -0
  23. package/bridge-recipes/unreal/helix-character-creator-male/male-pullover-hoodie.addon.json +1 -0
  24. package/bridge-recipes/unreal/helix-character-creator-male/male-pullover-hoodie.json +1 -0
  25. package/bridge-recipes/unreal/helix-character-creator-male/male-sinth-ascetic-jacket.addon.json +1 -0
  26. package/bridge-recipes/unreal/helix-character-creator-male/male-sinth-ascetic-jacket.json +1 -0
  27. package/bridge-recipes/unreal/helix-character-creator-male/male-surgeon-mask.addon.json +1 -0
  28. package/bridge-recipes/unreal/helix-character-creator-male/male-surgeon-mask.json +1 -0
  29. package/bridge-recipes/unreal/helix-character-creator-male/male-tech-hoodie.addon.json +1 -0
  30. package/bridge-recipes/unreal/helix-character-creator-male/male-tech-hoodie.json +1 -0
  31. package/bridge-recipes/unreal/helix-character-creator-male/test/recipes.test.js +31 -8
  32. package/dist/bridge/characterAddon.js +18 -3
  33. package/dist/bridge/characterAddon.js.map +1 -1
  34. package/dist/character/addonPushOut.d.ts +66 -0
  35. package/dist/character/addonPushOut.js +211 -0
  36. package/dist/character/addonPushOut.js.map +1 -0
  37. package/dist/character/bodyRegionHide.d.ts +40 -0
  38. package/dist/character/bodyRegionHide.js +75 -0
  39. package/dist/character/bodyRegionHide.js.map +1 -0
  40. package/dist/character/importCharacter.d.ts +11 -0
  41. package/dist/character/importCharacter.js +23 -0
  42. package/dist/character/importCharacter.js.map +1 -1
  43. package/dist/tsconfig.build.tsbuildinfo +1 -1
  44. package/package.json +1 -1
  45. package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
  46. package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
  47. package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
  48. package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Carnival Mask 2", "slug": "male-carnival-mask-2", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-carnival-mask-2", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Carnival_Mask_2/M_Carnival_Mask_2", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Carnival Mask 3", "slug": "male-carnival-mask-3", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-carnival-mask-3", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Carnival_Mask_3/M_Carnival_Mask_3", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Eye Hole Mask 2", "slug": "male-eye-hole-mask-2", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-eye-hole-mask-2", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Eye_Hole_Mask_2/M_Eye_Hole_Mask_2_skinning", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Eye Hole Mask", "slug": "male-eye-hole-mask", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-eye-hole-mask", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Eye_Hole_Mask/M_Eye_Hole_Mask_skinning", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Marshmallow Mask", "slug": "male-marshmallow-mask", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-marshmallow-mask", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/Marshmallow_Mask/M_Marshmallow_Mask", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Nebula Executive Coat", "slug": "male-nebula-executive-coat", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-nebula-executive-coat", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/Nebula_Executive_Coat/SKM_M_Nebula_Executive_West_Coat", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "NEO Arcane Heir Mantle", "slug": "male-neo-arcane-heir-mantle", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-neo-arcane-heir-mantle", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/NEO_Arcane_Heir_Mantle/SKM_M_NEO_Arcane_Heir_Mantle", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "NEO Arcane Heir Tunic", "slug": "male-neo-arcane-heir-tunic", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-neo-arcane-heir-tunic", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/NEO_Arcane_Heir_Tunic/SKM_M_NEO_Arcane_Heir_Tunic", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Paper Bag Mask", "slug": "male-paper-bag-mask", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-paper-bag-mask", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Paper_Bag_Mask/M_Paper_Bag_Mask", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Pig Mask", "slug": "male-pig-mask", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-pig-mask", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Pig_Mask/M_Pig_Mask", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Puffer Jacket", "slug": "male-puffer-jacket", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-puffer-jacket", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/Puffer_Jacket/SKM_M_Puffer_Jacket", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Pullover Hoodie", "slug": "male-pullover-hoodie", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-pullover-hoodie", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/Pullover_Hoodie/SKM_M_Pullover_Hoodie", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Sinth Ascetic Jacket", "slug": "male-sinth-ascetic-jacket", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-sinth-ascetic-jacket", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/Sinth_Ascetic_Jacket/SKM_M_Sinth_Ascetic_Jacket", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Surgeon Mask", "slug": "male-surgeon-mask", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.mask@1", "socket": "head", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-surgeon-mask", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/M_Surgeon_Mask/M_Surgeon_Mask_skinning", "exportMorphTargets": true}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge-character-addon-profile/1", "title": "Tech Hoodie", "slug": "male-tech-hoodie", "hostItemId": "b45e4a1e-0000-4000-8000-000000000001", "hostSha256": "3fdb769bf0dbcc0e1b1b1ae8dd800d6ddedfda976c6422d4de15b80ce71c132b", "slotType": "character.top@1", "socket": "chest", "nodes": [], "ops": []}
@@ -0,0 +1 @@
1
+ {"schemaVersion": "helix.bridge.unreal-asset/1", "slug": "male-tech-hoodie", "assetPath": "/HelixCharacterCreator/Characters/Clothes/Man/Tech_Hoodie/SKM_M_Tech_Hoodie", "exportMorphTargets": true}
@@ -8,15 +8,28 @@ const test = require('node:test');
8
8
  const directory = resolve(__dirname, '..');
9
9
  const hostItemId = 'b45e4a1e-0000-4000-8000-000000000001';
10
10
 
11
+ // The fixed, one-representative-item-per-customizer-slot pilot set that
12
+ // `all.json` is a canonical batch of. This list is intentionally NOT derived
13
+ // from a directory scan: the batch phase adds many more `<item-slug>.json` /
14
+ // `<item-slug>.addon.json` recipe pairs to this same directory (one per
15
+ // wearable, not one per slot), and those must coexist without being folded
16
+ // into — or breaking the identity check against — this specific pilot batch.
17
+ const canonicalSlotFiles = [
18
+ 'base-male.json',
19
+ 'base-male-face.json',
20
+ 'back.json', 'bottom.json', 'eyewear.json', 'footwear.json', 'gloves.json',
21
+ 'hair.json', 'headwear.json', 'mask.json', 'neck.json', 'outfit.json', 'top.json',
22
+ ];
23
+ const canonicalSlots = [
24
+ 'back', 'bottom', 'eyewear', 'footwear', 'gloves', 'hair', 'headwear', 'mask', 'neck', 'outfit', 'top',
25
+ ];
26
+
11
27
  function read(file) {
12
28
  return JSON.parse(readFileSync(join(directory, file), 'utf8'));
13
29
  }
14
30
 
15
31
  test('keeps the canonical batch identical to its individual source profiles', () => {
16
- const files = readdirSync(directory)
17
- .filter((file) => file.endsWith('.json') && !['all.json', 'palette-pilot.json'].includes(file) && !file.endsWith('.addon.json'))
18
- .sort();
19
- const profiles = files.map(read);
32
+ const profiles = canonicalSlotFiles.map(read);
20
33
  const batch = read('all.json');
21
34
  assert.equal(batch.schemaVersion, 'helix.bridge.unreal-asset-set/1');
22
35
  assert.deepEqual(
@@ -26,11 +39,19 @@ test('keeps the canonical batch identical to its individual source profiles', ()
26
39
  assert.equal(new Set(batch.assets.map((asset) => asset.slug)).size, batch.assets.length);
27
40
  });
28
41
 
29
- test('pairs every wearable source with a runtime customizer add-on profile', () => {
42
+ test('every add-on profile in this directory pairs with a runtime customizer add-on profile of matching shape', () => {
43
+ // Covers every `.addon.json` file present — the fixed 11-slot pilot set AND
44
+ // any number of additional per-item batch-phase recipes — rather than a
45
+ // closed exact-list. A new wearable's recipe pair must satisfy the same
46
+ // schema contract as the originals; it does not need to be one of the 11
47
+ // canonical slot names, and there may be many per slot.
30
48
  const addonFiles = readdirSync(directory).filter((file) => file.endsWith('.addon.json')).sort();
31
- assert.deepEqual(addonFiles.map((file) => basename(file, '.addon.json')), [
32
- 'back', 'bottom', 'eyewear', 'footwear', 'gloves', 'hair', 'headwear', 'mask', 'neck', 'outfit', 'top',
33
- ]);
49
+ // The 11 canonical per-slot pilot add-ons must still exist under their
50
+ // original slot-named files.
51
+ for (const slot of canonicalSlots) {
52
+ assert.ok(addonFiles.includes(`${slot}.addon.json`), `missing canonical ${slot}.addon.json`);
53
+ }
54
+ const seenSlugs = new Set();
34
55
  for (const file of addonFiles) {
35
56
  const addon = read(file);
36
57
  const source = read(`${basename(file, '.addon.json')}.json`);
@@ -39,6 +60,8 @@ test('pairs every wearable source with a runtime customizer add-on profile', ()
39
60
  assert.equal(addon.hostItemId, hostItemId);
40
61
  assert.match(addon.slotType, /^character\.[a-z]+@1$/);
41
62
  assert.equal(addon.slotType.startsWith('Cosmetic.Slot.'), false);
63
+ assert.equal(seenSlugs.has(addon.slug), false, `duplicate add-on slug ${addon.slug} (${file})`);
64
+ seenSlugs.add(addon.slug);
42
65
  }
43
66
  });
44
67
 
@@ -7,6 +7,7 @@ exports.parseCharacterAddonProfileJson = parseCharacterAddonProfileJson;
7
7
  const node_fs_1 = require("node:fs");
8
8
  const item_1 = require("../item");
9
9
  const helixPalette_1 = require("../character/helixPalette");
10
+ const bodyRegionHide_1 = require("../character/bodyRegionHide");
10
11
  exports.BRIDGE_CHARACTER_ADDON_PROFILE_SCHEMA = 'helix.bridge-character-addon-profile/1';
11
12
  function requiredString(value, label) {
12
13
  if (typeof value !== 'string' || value.trim() === '') {
@@ -27,7 +28,7 @@ function validateCharacterAddonProfile(value) {
27
28
  throw new Error('Character add-on profile must be an object.');
28
29
  }
29
30
  const row = value;
30
- const allowed = new Set(['schemaVersion', 'title', 'slug', 'hostItemId', 'hostSha256', 'slotType', 'socket', 'nodes', 'ops', 'palette']);
31
+ const allowed = new Set(['schemaVersion', 'title', 'slug', 'hostItemId', 'hostSha256', 'slotType', 'socket', 'nodes', 'ops', 'palette', 'hideRegions']);
31
32
  const unknown = Object.keys(row).filter((key) => !allowed.has(key));
32
33
  if (unknown.length > 0)
33
34
  throw new Error(`Character add-on profile has unsupported field(s): ${unknown.join(', ')}.`);
@@ -51,8 +52,22 @@ function validateCharacterAddonProfile(value) {
51
52
  throw new Error('Character add-on profile slotType must use the runtime character.<slot>@1 vocabulary.');
52
53
  }
53
54
  const socket = requiredString(row.socket, 'socket');
54
- const nodes = stringList(row.nodes, 'nodes');
55
- const ops = row.ops === undefined ? [] : row.ops;
55
+ const rawNodes = stringList(row.nodes, 'nodes');
56
+ const rawOps = row.ops === undefined ? [] : row.ops;
57
+ if (!Array.isArray(rawOps))
58
+ throw new Error('Character add-on profile ops must be an array.');
59
+ const hasExplicitHideOp = rawOps.some((op) => typeof op === 'object' && op !== null && op.v === 'hide');
60
+ // `hideRegions` picks which of the platform's `character.<slot>@1` hide grant this add-on actually uses.
61
+ // Explicit wins outright (including an explicit `[]`, opting a garment out of hiding anything). With
62
+ // neither an explicit list nor a hand-authored `hide` op already in `ops`, default to the FULL region set
63
+ // the slot type's platform grant permits (see `bodyRegionHide.ts`) — the fix for the poke-through defect:
64
+ // a conformed garment now hides the skin underneath it by default instead of requiring per-item authoring.
65
+ const hideRegionsOverride = row.hideRegions === undefined ? undefined : stringList(row.hideRegions, 'hideRegions');
66
+ const hideRegions = hideRegionsOverride ?? (hasExplicitHideOp ? [] : (0, bodyRegionHide_1.defaultHideRegionsForSlotType)(slotType));
67
+ const nodes = [...new Set([...rawNodes, ...hideRegions])];
68
+ const ops = hideRegions.length > 0 && !hasExplicitHideOp
69
+ ? [...rawOps, { v: 'hide', nodes: hideRegions }]
70
+ : rawOps;
56
71
  const palette = row.palette === undefined ? undefined : (0, helixPalette_1.validateHelixPaletteSelection)(row.palette);
57
72
  const fits = {
58
73
  slotType,
@@ -1 +1 @@
1
- {"version":3,"file":"characterAddon.js","sourceRoot":"","sources":["../../src/bridge/characterAddon.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"characterAddon.js","sourceRoot":"","sources":["../../src/bridge/characterAddon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import type { Document } from '@gltf-transform/core';
2
+ /**
3
+ * Conform-time vertex push-out — the SECOND line of defence against poke-through, for the cases a `hide`
4
+ * op cannot fix.
5
+ *
6
+ * `bodyRegionHide.ts` removes the skin a garment fully covers, which is the right fix for a garment that
7
+ * loosely bags a whole region (the Sinth Ascetic Pants case: pants that reach the ankle, hiding the whole
8
+ * thigh/shin). It is the WRONG fix for a garment that is merely too tight at one edge — a hem, a cuff, a
9
+ * waistband — because hiding the region would also hide the skin the garment does NOT cover (bare ankle
10
+ * below a cropped trouser leg, bare wrist past a short sleeve). For that case the body must stay visible and
11
+ * the GARMENT geometry has to move a little further from the skin instead.
12
+ *
13
+ * This step runs on the conformed garment mesh (after `conformToHelixRest`, in the same rest-pose space as
14
+ * the host body) and the host body's own conformed mesh. For every garment vertex closer to the host
15
+ * surface than `threshold` (measured along the host surface's own outward normal, so a vertex already
16
+ * clear of the body is left alone and one merely GRAZING it is not overcorrected), it is pushed out along
17
+ * that normal until it clears the body by `offset`. Vertices already at or beyond `offset` are untouched —
18
+ * this only closes gaps, it never inflates a garment that already fits.
19
+ *
20
+ * ── WHY THIS IS CONSERVATIVE ────────────────────────────────────────────────────────────────────────────────
21
+ * - Only vertices within `threshold` of the body move at all; the overwhelming majority of a garment's
22
+ * vertices (anywhere away from the skin-tight edges) are never touched, so the garment's silhouette away
23
+ * from those edges is bit-for-bit identical to what conform already produced.
24
+ * - The push distance is clamped to `maxPushOut` regardless of how deep a vertex is embedded, so a
25
+ * badly-fit garment is not silently inflated into a different shape — a violation past the clamp should
26
+ * fail the automated poke-through check (`pokethrough-check.mjs`) and get creator attention instead.
27
+ * - Movement is along the BODY's outward normal at the nearest surface point, not the garment vertex's own
28
+ * normal — stable even where the garment normal is noisy (thin cloth, near-tangent faces) because it is
29
+ * anchored to the coarser, more reliable body surface.
30
+ */
31
+ export interface PushOutOptions {
32
+ /**
33
+ * A garment vertex closer to the body than this (metres) is considered at risk and pushed out. Default
34
+ * 4mm: the platform's canonical body scale is metres-real-world, and this is comfortably inside normal
35
+ * cloth-to-skin clearance for a well-conformed garment while still catching genuine near-misses from
36
+ * automatic weight transfer. Tune per item family if a garment's natural clearance is tighter (e.g. a
37
+ * skin-suit) or looser (e.g. bulky outerwear).
38
+ */
39
+ thresholdMeters?: number;
40
+ /** How far past the body surface a corrected vertex ends up (metres). Default 2mm — a hairline gap that
41
+ * reads as "not touching" up close without visibly separating the garment from the body at a distance. */
42
+ offsetMeters?: number;
43
+ /** Hard clamp on how far any single vertex may move (metres). Default 15mm — beyond this the garment is
44
+ * not merely tight, it is the wrong shape for the body, and moving it further would hide that rather
45
+ * than surface it. */
46
+ maxPushOutMeters?: number;
47
+ }
48
+ export interface PushOutReport {
49
+ /** Garment vertices examined across every mesh primitive. */
50
+ verticesExamined: number;
51
+ /** Garment vertices that were within `thresholdMeters` of the body surface. */
52
+ verticesAtRisk: number;
53
+ /** Of those, how many were actually moved (a vertex already exactly at `offsetMeters` needs no push). */
54
+ verticesPushed: number;
55
+ /** Vertices where the required push exceeded `maxPushOutMeters` — moved only to the clamp, still flagged. */
56
+ verticesClamped: number;
57
+ /** The largest correction actually applied, in metres. 0 if nothing needed moving. */
58
+ maxCorrectionMeters: number;
59
+ }
60
+ /**
61
+ * Push garment vertices out of the host body surface where they are closer than `thresholdMeters`. Mutates
62
+ * `garment`'s POSITION accessors in place (NORMAL is left untouched — the push distances are small enough
63
+ * that re-lighting is not worth the extra pass, and a shading seam from a hairline offset is not visible at
64
+ * platform LOD triangle counts).
65
+ */
66
+ export declare function pushGarmentOutOfBody(garment: Document, hostBody: Document, options?: PushOutOptions): PushOutReport;
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pushGarmentOutOfBody = pushGarmentOutOfBody;
4
+ const mat4_1 = require("./mat4");
5
+ /** Cell size for the uniform grid that buckets host triangles for approximate-nearest lookup. */
6
+ function gridKey(p, cell) {
7
+ return `${Math.floor(p[0] / cell)},${Math.floor(p[1] / cell)},${Math.floor(p[2] / cell)}`;
8
+ }
9
+ function collectHostTriangles(hostBody) {
10
+ const triangles = [];
11
+ for (const mesh of hostBody.getRoot().listMeshes()) {
12
+ for (const prim of mesh.listPrimitives()) {
13
+ const pos = prim.getAttribute('POSITION');
14
+ const indices = prim.getIndices();
15
+ if (!pos)
16
+ continue;
17
+ const posArray = pos.getArray();
18
+ if (!posArray)
19
+ continue;
20
+ const vertexAt = (i) => [posArray[i * 3], posArray[i * 3 + 1], posArray[i * 3 + 2]];
21
+ const idxArray = indices?.getArray();
22
+ const triCount = idxArray ? idxArray.length / 3 : pos.getCount() / 3;
23
+ for (let t = 0; t < triCount; t++) {
24
+ const [ia, ib, ic] = idxArray
25
+ ? [idxArray[t * 3], idxArray[t * 3 + 1], idxArray[t * 3 + 2]]
26
+ : [t * 3, t * 3 + 1, t * 3 + 2];
27
+ const a = vertexAt(ia);
28
+ const b = vertexAt(ib);
29
+ const c = vertexAt(ic);
30
+ const normal = (0, mat4_1.normalize3)((0, mat4_1.cross3)((0, mat4_1.sub3)(b, a), (0, mat4_1.sub3)(c, a)));
31
+ if (!Number.isFinite(normal[0]))
32
+ continue; // degenerate triangle
33
+ const centroid = [(a[0] + b[0] + c[0]) / 3, (a[1] + b[1] + c[1]) / 3, (a[2] + b[2] + c[2]) / 3];
34
+ triangles.push({ a, b, c, normal, centroid });
35
+ }
36
+ }
37
+ }
38
+ // A cell of ~5cm keeps the neighbour search (27 cells) small on a canonical-scale humanoid body while
39
+ // still bucketing enough triangles per cell to be worth indexing.
40
+ const cell = 0.05;
41
+ return { triangles, cell };
42
+ }
43
+ /**
44
+ * Buckets every triangle into EVERY cell its axis-aligned bounding box overlaps, not just its centroid's
45
+ * cell. A character-scale body/garment mesh at platform LOD triangle counts has edge lengths far under one
46
+ * grid cell, so this is normally one cell per triangle in practice — but bucketing by centroid alone is
47
+ * silently wrong for any triangle bigger than the cell (its centroid's neighbourhood search would never
48
+ * see it even though its surface passes right by the query point), and getting that wrong produces a
49
+ * push-out step that quietly does nothing rather than a visible error. The `+ 1` cap on cells-per-axis
50
+ * guards the pathological case (a huge single triangle) from indexing an unbounded number of cells.
51
+ */
52
+ function indexTriangles(triangles, cell) {
53
+ const grid = new Map();
54
+ const MAX_CELLS_PER_AXIS = 64;
55
+ for (const tri of triangles) {
56
+ const min = [
57
+ Math.min(tri.a[0], tri.b[0], tri.c[0]),
58
+ Math.min(tri.a[1], tri.b[1], tri.c[1]),
59
+ Math.min(tri.a[2], tri.b[2], tri.c[2]),
60
+ ];
61
+ const max = [
62
+ Math.max(tri.a[0], tri.b[0], tri.c[0]),
63
+ Math.max(tri.a[1], tri.b[1], tri.c[1]),
64
+ Math.max(tri.a[2], tri.b[2], tri.c[2]),
65
+ ];
66
+ const cellMin = min.map((v) => Math.floor(v / cell));
67
+ const cellMax = max.map((v) => Math.floor(v / cell));
68
+ const span = (i) => Math.min(cellMax[i] - cellMin[i], MAX_CELLS_PER_AXIS);
69
+ for (let x = 0; x <= span(0); x++) {
70
+ for (let y = 0; y <= span(1); y++) {
71
+ for (let z = 0; z <= span(2); z++) {
72
+ const key = `${cellMin[0] + x},${cellMin[1] + y},${cellMin[2] + z}`;
73
+ const bucket = grid.get(key);
74
+ if (bucket)
75
+ bucket.push(tri);
76
+ else
77
+ grid.set(key, [tri]);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return grid;
83
+ }
84
+ /** Closest point on triangle `tri` to `p`, via the standard barycentric clamp (Ericson, RTCD 5.1.5). */
85
+ function closestPointOnTriangle(p, tri) {
86
+ const { a, b, c } = tri;
87
+ const ab = (0, mat4_1.sub3)(b, a);
88
+ const ac = (0, mat4_1.sub3)(c, a);
89
+ const ap = (0, mat4_1.sub3)(p, a);
90
+ const d1 = (0, mat4_1.dot3)(ab, ap);
91
+ const d2 = (0, mat4_1.dot3)(ac, ap);
92
+ if (d1 <= 0 && d2 <= 0)
93
+ return a;
94
+ const bp = (0, mat4_1.sub3)(p, b);
95
+ const d3 = (0, mat4_1.dot3)(ab, bp);
96
+ const d4 = (0, mat4_1.dot3)(ac, bp);
97
+ if (d3 >= 0 && d4 <= d3)
98
+ return b;
99
+ const vc = d1 * d4 - d3 * d2;
100
+ if (vc <= 0 && d1 >= 0 && d3 <= 0) {
101
+ const v = d1 / (d1 - d3);
102
+ return [a[0] + v * ab[0], a[1] + v * ab[1], a[2] + v * ab[2]];
103
+ }
104
+ const cp = (0, mat4_1.sub3)(p, c);
105
+ const d5 = (0, mat4_1.dot3)(ab, cp);
106
+ const d6 = (0, mat4_1.dot3)(ac, cp);
107
+ if (d6 >= 0 && d5 <= d6)
108
+ return c;
109
+ const vb = d5 * d2 - d1 * d6;
110
+ if (vb <= 0 && d2 >= 0 && d6 <= 0) {
111
+ const w = d2 / (d2 - d6);
112
+ return [a[0] + w * ac[0], a[1] + w * ac[1], a[2] + w * ac[2]];
113
+ }
114
+ const va = d3 * d6 - d5 * d4;
115
+ if (va <= 0 && d4 - d3 >= 0 && d5 - d6 >= 0) {
116
+ const w = (d4 - d3) / (d4 - d3 + (d5 - d6));
117
+ return [b[0] + w * (c[0] - b[0]), b[1] + w * (c[1] - b[1]), b[2] + w * (c[2] - b[2])];
118
+ }
119
+ const denom = 1 / (va + vb + vc);
120
+ const v = vb * denom;
121
+ const w = vc * denom;
122
+ return [a[0] + ab[0] * v + ac[0] * w, a[1] + ab[1] * v + ac[1] * w, a[2] + ab[2] * v + ac[2] * w];
123
+ }
124
+ function nearestSurfacePoint(p, grid, cell) {
125
+ const [cx, cy, cz] = [Math.floor(p[0] / cell), Math.floor(p[1] / cell), Math.floor(p[2] / cell)];
126
+ let best = null;
127
+ // Search the 3x3x3 neighbourhood first; if nothing is found (a sparse region, or the garment vertex sits
128
+ // further than one cell from any body geometry), widen the ring until a triangle is found or we give up.
129
+ for (let radius = 1; radius <= 4 && best === null; radius++) {
130
+ for (let dx = -radius; dx <= radius; dx++) {
131
+ for (let dy = -radius; dy <= radius; dy++) {
132
+ for (let dz = -radius; dz <= radius; dz++) {
133
+ const bucket = grid.get(`${cx + dx},${cy + dy},${cz + dz}`);
134
+ if (!bucket)
135
+ continue;
136
+ for (const tri of bucket) {
137
+ const cp = closestPointOnTriangle(p, tri);
138
+ const d = Math.hypot(p[0] - cp[0], p[1] - cp[1], p[2] - cp[2]);
139
+ if (best === null || d < best.distance)
140
+ best = { point: cp, normal: tri.normal, distance: d };
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ return best;
147
+ }
148
+ /**
149
+ * Push garment vertices out of the host body surface where they are closer than `thresholdMeters`. Mutates
150
+ * `garment`'s POSITION accessors in place (NORMAL is left untouched — the push distances are small enough
151
+ * that re-lighting is not worth the extra pass, and a shading seam from a hairline offset is not visible at
152
+ * platform LOD triangle counts).
153
+ */
154
+ function pushGarmentOutOfBody(garment, hostBody, options = {}) {
155
+ const threshold = options.thresholdMeters ?? 0.004;
156
+ const offset = options.offsetMeters ?? 0.002;
157
+ const maxPushOut = options.maxPushOutMeters ?? 0.015;
158
+ if (offset > threshold) {
159
+ throw new Error(`addonPushOut: offsetMeters (${offset}) must not exceed thresholdMeters (${threshold}) — otherwise every push-out would immediately re-trigger the risk test.`);
160
+ }
161
+ const { triangles, cell } = collectHostTriangles(hostBody);
162
+ const report = {
163
+ verticesExamined: 0,
164
+ verticesAtRisk: 0,
165
+ verticesPushed: 0,
166
+ verticesClamped: 0,
167
+ maxCorrectionMeters: 0,
168
+ };
169
+ if (triangles.length === 0)
170
+ return report; // nothing to push away from — leave the garment untouched.
171
+ const grid = indexTriangles(triangles, cell);
172
+ for (const mesh of garment.getRoot().listMeshes()) {
173
+ for (const prim of mesh.listPrimitives()) {
174
+ const pos = prim.getAttribute('POSITION');
175
+ if (!pos)
176
+ continue;
177
+ const array = pos.getArray();
178
+ if (!array)
179
+ continue;
180
+ const mutable = Float32Array.from(array);
181
+ const count = pos.getCount();
182
+ for (let i = 0; i < count; i++) {
183
+ report.verticesExamined++;
184
+ const v = [mutable[i * 3], mutable[i * 3 + 1], mutable[i * 3 + 2]];
185
+ const nearest = nearestSurfacePoint(v, grid, cell);
186
+ if (!nearest)
187
+ continue;
188
+ // Signed distance along the BODY's own outward normal: positive means the garment vertex is
189
+ // already outside the body along that normal, negative means it has sunk past the surface.
190
+ const signedDistance = (0, mat4_1.dot3)((0, mat4_1.sub3)(v, nearest.point), nearest.normal);
191
+ if (signedDistance >= threshold)
192
+ continue; // already clear — leave it exactly where conform put it.
193
+ report.verticesAtRisk++;
194
+ const rawPush = offset - signedDistance;
195
+ const push = Math.min(rawPush, maxPushOut);
196
+ if (push < rawPush)
197
+ report.verticesClamped++;
198
+ if (push <= 0)
199
+ continue;
200
+ mutable[i * 3] = v[0] + nearest.normal[0] * push;
201
+ mutable[i * 3 + 1] = v[1] + nearest.normal[1] * push;
202
+ mutable[i * 3 + 2] = v[2] + nearest.normal[2] * push;
203
+ report.verticesPushed++;
204
+ report.maxCorrectionMeters = Math.max(report.maxCorrectionMeters, push);
205
+ }
206
+ pos.setArray(mutable);
207
+ }
208
+ }
209
+ return report;
210
+ }
211
+ //# sourceMappingURL=addonPushOut.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addonPushOut.js","sourceRoot":"","sources":["../../src/character/addonPushOut.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Default body-region `hide` grants for HELIX character add-on slot types.
3
+ *
4
+ * ── WHY THIS EXISTS ────────────────────────────────────────────────────────────────────────────────────────────
5
+ * The platform ALREADY grants every `wearable`-archetype character slot the right to hide the base body's
6
+ * skin underneath it (`helix-backend-api/src/add-ons/character-slot-pack.ts`, mirrored at runtime by
7
+ * `helix-web-engine-new/systems/humanoid-character/.../core/vehicle/parts/characterBase.ts`) — a
8
+ * `character.bottom@1` slot may hide `body_thigh_*`/`body_shin_*`, a `character.top@1` slot may hide
9
+ * `body_torso`/`body_upperarm_*`, and so on. That grant is a CEILING, not a behaviour: nothing hides until
10
+ * the add-on's own `ops` carries a `{v: 'hide', nodes: [...]}` entry naming regions within it. Before this
11
+ * file, no code path in this CLI ever emitted one — every conformed character add-on shipped `ops: []`
12
+ * regardless of slot type, so the base body's skin rendered in full under every published garment. That is
13
+ * the root cause of the Base Female/Sinth Ascetic Pants poke-through: nothing was ever hiding the thigh/shin
14
+ * skin the pants sit over.
15
+ *
16
+ * This table is this CLI's data-driven default: conform a garment for a given `slotType` and it now emits
17
+ * the region hide the platform already permits for that slot, with no per-item authoring required. It is a
18
+ * DEFAULT, not a mandate — a garment whose geometry does not cover the platform's full granted region (cropped
19
+ * trousers, fingerless gloves, a cap-sleeve top) should pass a narrower `hideRegions` override in its profile
20
+ * JSON (see `characterAddon.ts`) naming only the globs it actually encloses, or `hideRegions: []` to hide
21
+ * nothing and rely on the conform-time push-out step instead.
22
+ *
23
+ * The glob strings below are copied VERBATIM from `CHARACTER_SLOT_PACK`'s `hide` arrays, not re-derived —
24
+ * `bodyRegionHide.spec.ts` locks every entry down, so a platform-side rename is a failing test here rather
25
+ * than a hide op the server rejects as "not covered by fits.requires.nodes".
26
+ */
27
+ /** The canonical body-region node vocabulary a HELIX base body's publish-time classifier assigns. */
28
+ export declare const HELIX_BODY_REGIONS: readonly ["body_head", "body_neck", "body_torso", "body_upperarm_l", "body_upperarm_r", "body_forearm_l", "body_forearm_r", "body_hand_l", "body_hand_r", "body_thigh_l", "body_thigh_r", "body_shin_l", "body_shin_r", "body_foot_l", "body_foot_r", "hair_base"];
29
+ export type HelixBodyRegion = (typeof HELIX_BODY_REGIONS)[number];
30
+ /**
31
+ * `character.<slot>@1` -> the region globs that slot type's platform grant permits it to hide, copied
32
+ * verbatim from `CHARACTER_SLOT_PACK` (`helix-backend-api/src/add-ons/character-slot-pack.ts`). A slot type
33
+ * absent here, or mapped to `[]`, hides nothing by default — this matches the platform: accessories,
34
+ * cosmetics, underwear and identity slots (eyewear, neck, earrings, nails, back, held props, shoulder,
35
+ * brows/lashes/facial-hair, underwear, skin/eyes/makeup/tattoo, emote/locomotion/aura/voice, nameplate) are
36
+ * granted no `hide` at all and are expected to layer over the body rather than occlude it.
37
+ */
38
+ export declare const DEFAULT_HIDE_REGIONS_BY_SLOT_TYPE: Readonly<Record<string, readonly string[]>>;
39
+ /** The default hide-region globs for `slotType`, or `[]` when the platform grants that slot no `hide` at all. */
40
+ export declare function defaultHideRegionsForSlotType(slotType: string): readonly string[];