rig-parts 1.72.0 → 1.72.1
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.
- package/docs/AUTHORING.md +8 -1
- package/package.json +1 -1
- package/src/turn.ts +28 -17
package/docs/AUTHORING.md
CHANGED
|
@@ -4518,7 +4518,7 @@ and stay absent).
|
|
|
4518
4518
|
| <a id="CONFIG_TURN_HEAD_BONE"></a>`CONFIG_TURN_HEAD_BONE` | `config.turn.roles` is declared without a single bone `"head"` in `config.bones`, or `config.turn.body` without a single bone `"chest"` / `"hip"` (or its declared `bone`) | declare `"head"` (or the body turn root bone) as a single bone in `config.bones` |
|
|
4519
4519
|
| <a id="CONFIG_TURN_SLIDER_NAME_FREE"></a>`CONFIG_TURN_SLIDER_NAME_FREE` | `config.turn.body.slider` (or the default `"body_yaw_dial"`) collides with a head turn dial (`"yaw"`, `"pitch"`, `"yaw_dial"`, `"pitch_dial"`) or an existing slider constraint in `config.constraints` (#787) | choose a distinct slider name in `config.turn.body.slider` |
|
|
4520
4520
|
| <a id="RIG_TURN_SURFACE_MESH_GROUPS"></a>`RIG_TURN_SURFACE_MESH_GROUPS` | a surface part contains a blended attachment but lacks one 2.5D group, includes an ungrouped positive influence in any attachment, or also declares a unit/rear role | declare both `turn.roles` and `turn.body`, a body root outside head, surface in one or both groups, and bindings wholly inside those two subtrees |
|
|
4521
|
-
| <a id="RIG_TURN_SURFACE_MESH_BINDINGS"></a>`RIG_TURN_SURFACE_MESH_BINDINGS` | an authored surface mesh has unresolved/nonclosing weights or shares a frame moved by a unit/rear turn carrier | provide weighted bindings closing at 1 in the authored frame; use independent chains for parts with different roles |
|
|
4521
|
+
| <a id="RIG_TURN_SURFACE_MESH_BINDINGS"></a>`RIG_TURN_SURFACE_MESH_BINDINGS` | an authored surface mesh has unresolved/nonclosing weights or shares a frame moved by a unit/rear turn carrier; declared stop draw aliases resolve to their links for setup sampling | provide weighted bindings closing at 1 in the authored frame; `<link>_d` resolves only for links declared in `config.stops`; use independent chains for parts with different roles |
|
|
4522
4522
|
| <a id="RIG_TURN_SURFACE_MESH_ATTACHMENT"></a>`RIG_TURN_SURFACE_MESH_ATTACHMENT` | a surface mesh has an alternate drawing without its own registered depth sampling frame | use the base attachment; explicit islands retain their own slot, attachment and bindings |
|
|
4523
4523
|
| <a id="RIG_TURN_SURFACE_DEPTH_REQUIRED"></a>`RIG_TURN_SURFACE_DEPTH_REQUIRED` | a part with role `"surface"` in `config.turn.roles` or `config.turn.body.roles` carries no depth sheet in `parts.json` | run `assemble` with `config.depth` or provide `<part>_depth.png` in `parts.json` |
|
|
4524
4524
|
| <a id="RIG_TURN_GRID_BUDGET"></a>`RIG_TURN_GRID_BUDGET` | a `"surface"` grid exceeds 2,400 triangles on one slot or 4,000 triangles across all `"surface"` slots | lower `config.turn.grid` / `config.turn.body.grid` `[cols, rows]` within the per-slot and total budgets |
|
|
@@ -5575,6 +5575,13 @@ dial; see [`CONFIG_TURN_ROLE_UNKNOWN`](#CONFIG_TURN_ROLE_UNKNOWN),
|
|
|
5575
5575
|
[`RIG_TURN_SURFACE_MESH_BINDINGS`](#RIG_TURN_SURFACE_MESH_BINDINGS),
|
|
5576
5576
|
[`CONFIG_TURN_UNIT_MESH_BONES`](#CONFIG_TURN_UNIT_MESH_BONES) and
|
|
5577
5577
|
[`CONFIG_TURN_SLIDER_NAME_FREE`](#CONFIG_TURN_SLIDER_NAME_FREE).
|
|
5578
|
+
Weighted surfaces may bind chains declared in `config.stops` (#800). The
|
|
5579
|
+
sampler resolves each declared `<link>_d` draw alias and any child parent using
|
|
5580
|
+
it to the original link's setup frame, as `flattenRig` does. Weights, child
|
|
5581
|
+
parents and stops keep their emitted names; Core creates the draw bones later.
|
|
5582
|
+
A name ending in `_d` alone is not an alias: a missing non-stop bone still
|
|
5583
|
+
raises `RIG_TURN_SURFACE_MESH_BINDINGS`. No new config field is required.
|
|
5584
|
+
|
|
5578
5585
|
A weighted surface spanning the head and body subtrees (#796) takes both
|
|
5579
5586
|
fields. Declare it as `"surface"` in `turn.roles` or `turn.body.roles` (both is
|
|
5580
5587
|
also accepted), with both 2.5D groups present. Head descendants belong to the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-parts",
|
|
3
|
-
"version": "1.72.
|
|
3
|
+
"version": "1.72.1",
|
|
4
4
|
"description": "AI-authored 2D character parts from one anime painting and its See-through layer decomposition: measured parts, weighted-mesh rig specs, verified through rig-c before anything is written. A CLI for agents that cannot see the image.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/turn.ts
CHANGED
|
@@ -1512,12 +1512,25 @@ interface Depth25DGroupSpec {
|
|
|
1512
1512
|
}>;
|
|
1513
1513
|
}
|
|
1514
1514
|
|
|
1515
|
+
interface Depth25DTurnRig {
|
|
1516
|
+
bones: Array<{ name: string; parent?: string; x: number; y: number; rotation?: number; length?: number }>;
|
|
1517
|
+
slots: Array<{ name: string; bone: string; attachment: string | null }>;
|
|
1518
|
+
constraints?: Array<Record<string, unknown>>;
|
|
1519
|
+
stops?: ReadonlyArray<{ links: readonly string[] }>;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
/** Stop draw bones share their link's setup frame; Core emits them later. */
|
|
1523
|
+
function surfaceSetupGraph(rig: Depth25DTurnRig) {
|
|
1524
|
+
const aliases = new Map<string, string>();
|
|
1525
|
+
for (const stop of rig.stops ?? []) for (const link of stop.links) aliases.set(`${link}_d`, link);
|
|
1526
|
+
const resolve = (name: string): string => aliases.get(name) ?? name;
|
|
1527
|
+
// Like flattenRig: resolve parents as well as weights, without changing the rig.
|
|
1528
|
+
const bones = rig.bones.map(b => b.parent === undefined ? b : { ...b, parent: resolve(b.parent) });
|
|
1529
|
+
return { bones, resolve };
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1515
1532
|
function buildDepth25DTurnGroup(
|
|
1516
|
-
rig:
|
|
1517
|
-
bones: Array<{ name: string; parent?: string; x: number; y: number; rotation?: number; length?: number }>;
|
|
1518
|
-
slots: Array<{ name: string; bone: string; attachment: string | null }>;
|
|
1519
|
-
constraints?: Array<Record<string, unknown>>;
|
|
1520
|
-
},
|
|
1533
|
+
rig: Depth25DTurnRig,
|
|
1521
1534
|
motion: {
|
|
1522
1535
|
groups?: Record<string, string[]>;
|
|
1523
1536
|
animations: Record<string, unknown>;
|
|
@@ -1814,7 +1827,8 @@ function buildDepth25DTurnGroup(
|
|
|
1814
1827
|
for (const [part, { rec, ext, zRef }] of surfaceSheets) {
|
|
1815
1828
|
const authored = opts.surfaceMeshes?.filter((m) => m.part === part) ?? [];
|
|
1816
1829
|
if (authored.length > 0) {
|
|
1817
|
-
const
|
|
1830
|
+
const setup = surfaceSetupGraph(rig);
|
|
1831
|
+
const world = computeWorldTransforms(setup.bones);
|
|
1818
1832
|
for (const mesh of authored) {
|
|
1819
1833
|
// An all-zero timeline can overwrite another slider's deform (Core A40).
|
|
1820
1834
|
// A sibling with no positive binding in this group owns no timeline here.
|
|
@@ -1822,19 +1836,19 @@ function buildDepth25DTurnGroup(
|
|
|
1822
1836
|
const vertices = mesh.weights.map((row, index) => {
|
|
1823
1837
|
let wx = 0, wy = 0, total = 0;
|
|
1824
1838
|
for (const w of row) {
|
|
1825
|
-
const frame = world.get(w.bone);
|
|
1839
|
+
const frame = world.get(setup.resolve(w.bone));
|
|
1826
1840
|
if (frame === undefined) {
|
|
1827
1841
|
problems.push({ code: 'RIG_TURN_SURFACE_MESH_BINDINGS', object: `surface mesh "${mesh.slot}/${mesh.attachment}" vertex ${index}`, detail: `names missing bone "${w.bone}"; every influence must resolve in the final setup` });
|
|
1828
1842
|
continue;
|
|
1829
1843
|
}
|
|
1830
1844
|
// A unit/rear mesh sharing this root would move our bindings too.
|
|
1831
|
-
let ancestor =
|
|
1845
|
+
let ancestor = setup.bones.find((b) => b.name === setup.resolve(w.bone));
|
|
1832
1846
|
while (ancestor !== undefined) {
|
|
1833
1847
|
if ([FACESHIFT_BONE, REAR_SHIFT_BONE, BODYSHIFT_BONE, BODY_REAR_SHIFT_BONE].includes(ancestor.name)) {
|
|
1834
1848
|
problems.push({ code: 'RIG_TURN_SURFACE_MESH_BINDINGS', object: `${group.fieldPrefix}.roles.${part}`, detail: `binds "${w.bone}" through turn carrier "${ancestor.name}" shared with another role; surface mesh bindings must retain their authored frame` });
|
|
1835
1849
|
break;
|
|
1836
1850
|
}
|
|
1837
|
-
ancestor =
|
|
1851
|
+
ancestor = setup.bones.find((b) => b.name === ancestor?.parent);
|
|
1838
1852
|
}
|
|
1839
1853
|
const [x, y] = toWorld(frame, w.x, w.y);
|
|
1840
1854
|
wx += x * w.weight; wy += y * w.weight; total += w.weight;
|
|
@@ -1858,7 +1872,7 @@ function buildDepth25DTurnGroup(
|
|
|
1858
1872
|
const d = -depth * Math.sin(deg * Math.PI / 180);
|
|
1859
1873
|
return row.flatMap((w) => {
|
|
1860
1874
|
if (mesh.groups !== undefined && (w.weight === 0 || mesh.groups.get(w.bone) !== (group.fieldPrefix === 'config.turn' ? 'head' : 'body'))) return [0, 0];
|
|
1861
|
-
const frame = world.get(w.bone)!; // every influence resolved before emitting keys
|
|
1875
|
+
const frame = world.get(setup.resolve(w.bone))!; // every influence resolved before emitting keys
|
|
1862
1876
|
const [x, y] = toBoneLocalVector(frame, kind === 'yaw' ? d : 0, kind === 'pitch' ? d : 0);
|
|
1863
1877
|
return [pyRound(x, 6), pyRound(y, 6)];
|
|
1864
1878
|
});
|
|
@@ -1947,11 +1961,7 @@ function buildDepth25DTurnGroup(
|
|
|
1947
1961
|
* `derive` and `deform` `transform` (`depth: true, narrowing: false`).
|
|
1948
1962
|
*/
|
|
1949
1963
|
export function buildDepth25DTurn(
|
|
1950
|
-
rig:
|
|
1951
|
-
bones: Array<{ name: string; parent?: string; x: number; y: number; rotation?: number; length?: number }>;
|
|
1952
|
-
slots: Array<{ name: string; bone: string; attachment: string | null }>;
|
|
1953
|
-
constraints?: Array<Record<string, unknown>>;
|
|
1954
|
-
},
|
|
1964
|
+
rig: Depth25DTurnRig,
|
|
1955
1965
|
motion: {
|
|
1956
1966
|
groups?: Record<string, string[]>;
|
|
1957
1967
|
animations: Record<string, unknown>;
|
|
@@ -1963,11 +1973,12 @@ export function buildDepth25DTurn(
|
|
|
1963
1973
|
|
|
1964
1974
|
// Only a surface opt-in spanning both authored subtrees changes paths (#796).
|
|
1965
1975
|
// Single-group meshes keep #795's role and bytes, including explicit islands.
|
|
1966
|
-
const
|
|
1976
|
+
const setup = surfaceSetupGraph(rig);
|
|
1977
|
+
const parents = new Map(setup.bones.map((b) => [b.name, b.parent]));
|
|
1967
1978
|
const bodyRoot = opts.turn.body?.bone ?? (parents.has('chest') ? 'chest' : 'hip');
|
|
1968
1979
|
const descends = (bone: string, root: string): boolean => {
|
|
1969
1980
|
const seen = new Set<string>();
|
|
1970
|
-
for (let cur: string | undefined = bone; cur !== undefined && !seen.has(cur); cur = parents.get(cur)) {
|
|
1981
|
+
for (let cur: string | undefined = setup.resolve(bone); cur !== undefined && !seen.has(cur); cur = parents.get(cur)) {
|
|
1971
1982
|
if (cur === root) return true;
|
|
1972
1983
|
seen.add(cur);
|
|
1973
1984
|
}
|