@helix3/helix-cli 0.1.18-helix3.275 → 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.
@@ -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[];
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Default body-region `hide` grants for HELIX character add-on slot types.
4
+ *
5
+ * ── WHY THIS EXISTS ────────────────────────────────────────────────────────────────────────────────────────────
6
+ * The platform ALREADY grants every `wearable`-archetype character slot the right to hide the base body's
7
+ * skin underneath it (`helix-backend-api/src/add-ons/character-slot-pack.ts`, mirrored at runtime by
8
+ * `helix-web-engine-new/systems/humanoid-character/.../core/vehicle/parts/characterBase.ts`) — a
9
+ * `character.bottom@1` slot may hide `body_thigh_*`/`body_shin_*`, a `character.top@1` slot may hide
10
+ * `body_torso`/`body_upperarm_*`, and so on. That grant is a CEILING, not a behaviour: nothing hides until
11
+ * the add-on's own `ops` carries a `{v: 'hide', nodes: [...]}` entry naming regions within it. Before this
12
+ * file, no code path in this CLI ever emitted one — every conformed character add-on shipped `ops: []`
13
+ * regardless of slot type, so the base body's skin rendered in full under every published garment. That is
14
+ * the root cause of the Base Female/Sinth Ascetic Pants poke-through: nothing was ever hiding the thigh/shin
15
+ * skin the pants sit over.
16
+ *
17
+ * This table is this CLI's data-driven default: conform a garment for a given `slotType` and it now emits
18
+ * the region hide the platform already permits for that slot, with no per-item authoring required. It is a
19
+ * DEFAULT, not a mandate — a garment whose geometry does not cover the platform's full granted region (cropped
20
+ * trousers, fingerless gloves, a cap-sleeve top) should pass a narrower `hideRegions` override in its profile
21
+ * JSON (see `characterAddon.ts`) naming only the globs it actually encloses, or `hideRegions: []` to hide
22
+ * nothing and rely on the conform-time push-out step instead.
23
+ *
24
+ * The glob strings below are copied VERBATIM from `CHARACTER_SLOT_PACK`'s `hide` arrays, not re-derived —
25
+ * `bodyRegionHide.spec.ts` locks every entry down, so a platform-side rename is a failing test here rather
26
+ * than a hide op the server rejects as "not covered by fits.requires.nodes".
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.DEFAULT_HIDE_REGIONS_BY_SLOT_TYPE = exports.HELIX_BODY_REGIONS = void 0;
30
+ exports.defaultHideRegionsForSlotType = defaultHideRegionsForSlotType;
31
+ /** The canonical body-region node vocabulary a HELIX base body's publish-time classifier assigns. */
32
+ exports.HELIX_BODY_REGIONS = [
33
+ 'body_head',
34
+ 'body_neck',
35
+ 'body_torso',
36
+ 'body_upperarm_l',
37
+ 'body_upperarm_r',
38
+ 'body_forearm_l',
39
+ 'body_forearm_r',
40
+ 'body_hand_l',
41
+ 'body_hand_r',
42
+ 'body_thigh_l',
43
+ 'body_thigh_r',
44
+ 'body_shin_l',
45
+ 'body_shin_r',
46
+ 'body_foot_l',
47
+ 'body_foot_r',
48
+ 'hair_base',
49
+ ];
50
+ /**
51
+ * `character.<slot>@1` -> the region globs that slot type's platform grant permits it to hide, copied
52
+ * verbatim from `CHARACTER_SLOT_PACK` (`helix-backend-api/src/add-ons/character-slot-pack.ts`). A slot type
53
+ * absent here, or mapped to `[]`, hides nothing by default — this matches the platform: accessories,
54
+ * cosmetics, underwear and identity slots (eyewear, neck, earrings, nails, back, held props, shoulder,
55
+ * brows/lashes/facial-hair, underwear, skin/eyes/makeup/tattoo, emote/locomotion/aura/voice, nameplate) are
56
+ * granted no `hide` at all and are expected to layer over the body rather than occlude it.
57
+ */
58
+ exports.DEFAULT_HIDE_REGIONS_BY_SLOT_TYPE = {
59
+ 'character.top@1': ['body_torso', 'body_upperarm_*'],
60
+ 'character.bottom@1': ['body_thigh_*', 'body_shin_*'],
61
+ 'character.outfit@1': ['body_torso', 'body_upperarm_*', 'body_forearm_*', 'body_thigh_*', 'body_shin_*'],
62
+ 'character.footwear@1': ['body_foot_*'],
63
+ 'character.gloves@1': ['body_hand_*', 'body_forearm_*'],
64
+ 'character.socks@1': ['body_shin_*'],
65
+ 'character.headwear@1': ['hair_base'],
66
+ 'character.mask@1': ['body_head'],
67
+ 'character.hair@1': ['hair_base'],
68
+ 'character.head@1': ['body_head'],
69
+ 'character.look@1': ['body_torso', 'body_upperarm_*', 'body_thigh_*', 'body_shin_*'],
70
+ };
71
+ /** The default hide-region globs for `slotType`, or `[]` when the platform grants that slot no `hide` at all. */
72
+ function defaultHideRegionsForSlotType(slotType) {
73
+ return exports.DEFAULT_HIDE_REGIONS_BY_SLOT_TYPE[slotType] ?? [];
74
+ }
75
+ //# sourceMappingURL=bodyRegionHide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bodyRegionHide.js","sourceRoot":"","sources":["../../src/character/bodyRegionHide.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;;AA+CH,sEAEC;AA/CD,qGAAqG;AACxF,QAAA,kBAAkB,GAAG;IAChC,WAAW;IACX,WAAW;IACX,YAAY;IACZ,iBAAiB;IACjB,iBAAiB;IACjB,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,aAAa;IACb,cAAc;IACd,cAAc;IACd,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;IACb,WAAW;CACH,CAAC;AAIX;;;;;;;GAOG;AACU,QAAA,iCAAiC,GAAgD;IAC5F,iBAAiB,EAAE,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACpD,oBAAoB,EAAE,CAAC,cAAc,EAAE,aAAa,CAAC;IACrD,oBAAoB,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,CAAC;IACxG,sBAAsB,EAAE,CAAC,aAAa,CAAC;IACvC,oBAAoB,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;IACvD,mBAAmB,EAAE,CAAC,aAAa,CAAC;IACpC,sBAAsB,EAAE,CAAC,WAAW,CAAC;IACrC,kBAAkB,EAAE,CAAC,WAAW,CAAC;IACjC,kBAAkB,EAAE,CAAC,WAAW,CAAC;IACjC,kBAAkB,EAAE,CAAC,WAAW,CAAC;IACjC,kBAAkB,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,CAAC;CACrF,CAAC;AAEF,iHAAiH;AACjH,SAAgB,6BAA6B,CAAC,QAAgB;IAC5D,OAAO,yCAAiC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;AAC3D,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { type Document } from '@gltf-transform/core';
2
2
  import { type BoneMap } from './stages/retarget';
3
3
  import { type ConformResult } from './stages/conform';
4
+ import { type PushOutOptions, type PushOutReport } from './addonPushOut';
4
5
  import { type Ktx2Mode, type Ktx2Result } from './stages/ktx2';
5
6
  import { type UnrealMaterialSidecarApplyResult } from './unrealMaterialSidecar';
6
7
  import { type CharacterSourcePreflight } from './untrustedSource';
@@ -79,6 +80,14 @@ export interface ImportCharacterOptions {
79
80
  path: string;
80
81
  selection: HelixPaletteSelection;
81
82
  };
83
+ /**
84
+ * Conform-time poke-through push-out (`rigScope: 'addon'` only, and only when `canonical` names a real
85
+ * host body GLB — for a plain character import there is no worn-against body to push away from). Moves
86
+ * garment vertices that sit too close to the host body's surface out along the body's own normal by a
87
+ * small amount; see `addonPushOut.ts` for the algorithm and defaults. Pass `false` to disable it outright
88
+ * (e.g. a garment already proven poke-through-free, where the small extra pass is not worth the cost).
89
+ */
90
+ pushOut?: PushOutOptions | false;
82
91
  }
83
92
  export interface ImportCharacterClipResult {
84
93
  name: string;
@@ -117,6 +126,8 @@ export interface ImportCharacterResult {
117
126
  manifest: AvatarFaceManifestV1;
118
127
  };
119
128
  palette: HelixPaletteApplyResult | null;
129
+ /** Poke-through push-out report, or null when it did not run (not an addon, no host body, or disabled). */
130
+ pushOut: PushOutReport | null;
120
131
  }
121
132
  export interface RetargetAnimationOptions {
122
133
  /** Raw Mixamo/Meshy animation FBX or GLB. */
@@ -25,6 +25,7 @@ const meshyToHelixMap_1 = require("./meshyToHelixMap");
25
25
  const metahumanToHelixMap_1 = require("./metahumanToHelixMap");
26
26
  const retarget_1 = require("./stages/retarget");
27
27
  const conform_1 = require("./stages/conform");
28
+ const addonPushOut_1 = require("./addonPushOut");
28
29
  const mat4_1 = require("./mat4");
29
30
  const sanitizeMaterials_1 = require("./stages/sanitizeMaterials");
30
31
  const canonicalSkeleton_1 = require("./canonicalSkeleton");
@@ -600,6 +601,26 @@ async function importCharacter(opts) {
600
601
  log(` stripped baked emissive on ${san.emissiveStripped} material(s)${san.metallicZeroed > 0 ? ` · metallic → 0 on ${san.metallicZeroed}` : ''}`);
601
602
  if (san.metadataStripped > 0)
602
603
  log(` stripped untrusted material metadata on ${san.metadataStripped} material(s)`);
604
+ // 2b. Push-out (add-ons only): the conformed garment now sits in the SAME rest-pose space as the host
605
+ // body it was fit against (that is what `fit: 'canonical'` + `canonical: <host.glb>` means), so a
606
+ // second, independent host-body read can serve as the poke-through reference here — a hem, cuff or
607
+ // waistband too tight for `bodyRegionHide.ts`'s region hide gets a small push clear of the skin instead
608
+ // of relying on the creator to notice. No host GLB (a plain character import, or an addon conformed
609
+ // without an explicit `--canonical`) means nothing to push away from, so this is skipped rather than
610
+ // guessed at.
611
+ let pushOutReport = null;
612
+ if (rigScope === 'addon' && opts.pushOut !== false && canonicalPath !== null) {
613
+ const hostDocument = await io.read(canonicalPath);
614
+ pushOutReport = (0, addonPushOut_1.pushGarmentOutOfBody)(body, hostDocument, opts.pushOut ?? {});
615
+ if (pushOutReport.verticesPushed > 0) {
616
+ log(`push-out: ${pushOutReport.verticesPushed}/${pushOutReport.verticesAtRisk} at-risk vert(s) moved clear `
617
+ + `of the body (max correction ${(pushOutReport.maxCorrectionMeters * 1000).toFixed(1)}mm)`
618
+ + `${pushOutReport.verticesClamped > 0 ? `, ${pushOutReport.verticesClamped} clamped at the max` : ''}`);
619
+ }
620
+ else if (pushOutReport.verticesAtRisk > 0) {
621
+ log('push-out: at-risk vertex(es) found but none needed moving (already at or beyond the target offset)');
622
+ }
623
+ }
603
624
  // 3. Package (the engine package-cli flow). The conformed doc is serialized once so each LOD level
604
625
  // decimates a fresh copy, exactly like package-cli re-reading its --body input per level.
605
626
  const conformedBytes = await io.writeBinary(body);
@@ -757,6 +778,7 @@ async function importCharacter(opts) {
757
778
  createdTextureCount: prepared.materialSidecar.createdTextureCount,
758
779
  },
759
780
  helixPalette: palette,
781
+ pushOut: pushOutReport,
760
782
  },
761
783
  limits: untrustedSource_1.CHARACTER_SOURCE_LIMITS,
762
784
  }, null, 2));
@@ -772,6 +794,7 @@ async function importCharacter(opts) {
772
794
  receiptPath,
773
795
  face: { capability: faceMode, manifest: face },
774
796
  palette,
797
+ pushOut: pushOutReport,
775
798
  };
776
799
  }
777
800
  //# sourceMappingURL=importCharacter.js.map