three-usd-robot 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -10,11 +10,13 @@ and drives forward kinematics on a Three.js `Object3D` hierarchy.
10
10
 
11
11
  > 🚧 **Status: v0.2 + USDC.** Loads **ASCII `.usda`**, **binary `.usdc` / `.usd`
12
12
  > (crate)**, and **`.usdz`** robots — including multi-file assets via
13
- > references/payloads/sublayers drives forward kinematics with meshes,
14
- > normalizes up-axis & units, and seeds the initial pose from joint drives. The
13
+ > references/payloads/sublayers, **variant selections**, and **instanceable**
14
+ > prims (internal-reference prototypes) drives forward kinematics with meshes,
15
+ > applies flat **`UsdShade` material colors** (UsdPreviewSurface / OmniPBR
16
+ > constants), normalizes up-axis & units, and seeds the initial pose. The
15
17
  > crate reader is a from-scratch TypeScript implementation (no OpenUSD/WASM
16
- > dependency). Not yet: variants, instancing, and time-sampled (animated)
17
- > values (see [`MILESTONES.md`](./MILESTONES.md), M10–M11).
18
+ > dependency). Not yet: variant resolution inside binary crate, and time-sampled
19
+ > (animated) values.
18
20
 
19
21
  ```ts
20
22
  // .usda / .usdc / binary .usd / .usdz are all auto-detected:
@@ -1,5 +1,5 @@
1
1
  import * as THREE from 'three';
2
- import { d as JointType, A as Axis, J as JointDescription, L as LinkDescription, R as RobotDescription, e as KinematicTree } from './buildKinematicTree-2fg6ZN8m.js';
2
+ import { J as JointType, A as Axis, a as JointDescription, L as LinkDescription, R as RobotDescription, K as KinematicTree } from './buildKinematicTree-BJuC_-5_.js';
3
3
 
4
4
  /**
5
5
  * The articulated "motion" node of a joint, inserted between the joint's two
@@ -80,9 +80,22 @@ type PrimSpec = {
80
80
  metadata: MetadataMap;
81
81
  properties: PropertySpec[];
82
82
  children: PrimSpec[];
83
+ /** Authored variant sets (`variantSet "name" = { ... }`), if any. */
84
+ variantSets?: VariantSetMap;
83
85
  /** 1-based source line of the prim declaration (for diagnostics). */
84
86
  line: number;
85
87
  };
88
+ /** The opinions a single variant contributes when selected. */
89
+ type VariantContent = {
90
+ properties: PropertySpec[];
91
+ children: PrimSpec[];
92
+ };
93
+ /** `variantSetName → variantName → content`. */
94
+ type VariantSetMap = {
95
+ [setName: string]: {
96
+ [variantName: string]: VariantContent;
97
+ };
98
+ };
86
99
  type PropertySpec = AttributeSpec | RelationshipSpec;
87
100
  type AttributeSpec = {
88
101
  kind: "attribute";
@@ -280,4 +293,4 @@ type BuildTreeOptions = {
280
293
  };
281
294
  declare function buildKinematicTree(robot: RobotDescription, options?: BuildTreeOptions): KinematicTree;
282
295
 
283
- export { type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEG2RAD as D, makeRotationZ as E, makeScale as F, makeTranslation as G, multiply as H, multiplyAll as I, type JointDescription as J, type KinematicNode as K, type LinkDescription as L, type Mat4 as M, type PrimSpec as P, Quat as Q, type RobotDescription as R, type SdfPath as S, type TreeEdge as T, type UsdDictionary as U, type Variability as V, AssetPath as a, type AttributeSpec as b, type JointDriveDescription as c, type JointType as d, type KinematicTree as e, type ListOp as f, type MetadataMap as g, type PropertySpec as h, RAD2DEG as i, type RelationshipSpec as j, type Specifier as k, UsdMatrix as l, type UsdValue as m, type UsdaFile as n, type Vec2 as o, type Vec3 as p, type Vec4 as q, buildKinematicTree as r, fromUsdMatrix as s, getTranslation as t, identity4 as u, invert as v, makeEuler as w, makeRotationFromQuat as x, makeRotationX as y, makeRotationY as z };
296
+ export { type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEG2RAD as D, makeRotationZ as E, makeScale as F, makeTranslation as G, multiply as H, multiplyAll as I, type JointType as J, type KinematicTree as K, type LinkDescription as L, type Mat4 as M, type PrimSpec as P, Quat as Q, type RobotDescription as R, type SdfPath as S, type TreeEdge as T, type UsdDictionary as U, type Vec3 as V, type JointDescription as a, AssetPath as b, type AttributeSpec as c, type JointDriveDescription as d, type KinematicNode as e, type ListOp as f, type MetadataMap as g, type PropertySpec as h, RAD2DEG as i, type RelationshipSpec as j, type Specifier as k, UsdMatrix as l, type UsdValue as m, type UsdaFile as n, type Variability as o, type Vec2 as p, type Vec4 as q, buildKinematicTree as r, fromUsdMatrix as s, getTranslation as t, identity4 as u, invert as v, makeEuler as w, makeRotationFromQuat as x, makeRotationX as y, makeRotationY as z };
@@ -507,26 +507,54 @@ function parsePrim(r) {
507
507
  if (r.is("ident")) typeName = r.expectIdent();
508
508
  const name = r.expect("string").value;
509
509
  const metadata = r.is("lparen") ? parseMetadataBlock(r) : {};
510
+ r.expect("lbrace");
511
+ const body = parseBody(r);
512
+ r.expect("rbrace");
513
+ const prim = {
514
+ specifier,
515
+ typeName,
516
+ name,
517
+ metadata,
518
+ properties: body.properties,
519
+ children: body.children,
520
+ line: head.line
521
+ };
522
+ if (body.variantSets) prim.variantSets = body.variantSets;
523
+ return prim;
524
+ }
525
+ function parseBody(r) {
510
526
  const properties = [];
511
527
  const children = [];
512
- r.expect("lbrace");
528
+ let variantSets;
513
529
  while (!r.is("rbrace") && !r.atEnd()) {
514
530
  if (r.is("ident") && SPECIFIERS.has(r.peek().value)) {
515
531
  children.push(parsePrim(r));
532
+ } else if (r.isIdent("variantSet")) {
533
+ const { setName, variants } = parseVariantSet(r);
534
+ variantSets ??= {};
535
+ variantSets[setName] = variants;
516
536
  } else {
517
537
  properties.push(parseProperty(r));
518
538
  }
519
539
  }
540
+ return variantSets ? { properties, children, variantSets } : { properties, children };
541
+ }
542
+ function parseVariantSet(r) {
543
+ r.expectIdent();
544
+ const setName = r.expect("string").value;
545
+ r.expect("equals");
546
+ r.expect("lbrace");
547
+ const variants = {};
548
+ while (!r.is("rbrace") && !r.atEnd()) {
549
+ const variantName = r.expect("string").value;
550
+ if (r.is("lparen")) parseMetadataBlock(r);
551
+ r.expect("lbrace");
552
+ const body = parseBody(r);
553
+ r.expect("rbrace");
554
+ variants[variantName] = { properties: body.properties, children: body.children };
555
+ }
520
556
  r.expect("rbrace");
521
- return {
522
- specifier,
523
- typeName,
524
- name,
525
- metadata,
526
- properties,
527
- children,
528
- line: head.line
529
- };
557
+ return { setName, variants };
530
558
  }
531
559
  function parseProperty(r) {
532
560
  const line = r.peek().line;
@@ -652,6 +680,10 @@ function parseMetadataValue(r) {
652
680
  if (raw.t === "asset") {
653
681
  return raw.v;
654
682
  }
683
+ if (raw.t === "path") {
684
+ const arc = { primPath: raw.v };
685
+ return arc;
686
+ }
655
687
  return rawToUsdValue(raw);
656
688
  }
657
689
  function parseMetadataList(r) {
@@ -666,6 +698,9 @@ function parseMetadataList(r) {
666
698
  } else {
667
699
  items.push(assetPath);
668
700
  }
701
+ } else if (r.is("path")) {
702
+ const arc = { primPath: r.next().value };
703
+ items.push(arc);
669
704
  } else {
670
705
  items.push(rawToUsdValue(parseLiteral(r)));
671
706
  }
@@ -1056,7 +1091,8 @@ function normalizePosix(path) {
1056
1091
  }
1057
1092
 
1058
1093
  // src/usd/composition.ts
1059
- var ARC_KEYS = ["references", "payload", "payloads"];
1094
+ var ARC_KEYS = ["references", "payload", "payloads", "inherits", "specializes"];
1095
+ var STRIP_KEYS = [...ARC_KEYS, "variants", "variantSets"];
1060
1096
  async function composeLayer(text, baseUrl, resolver, options = {}, stack = /* @__PURE__ */ new Set()) {
1061
1097
  const warn = options.onWarn ?? (() => {
1062
1098
  });
@@ -1064,47 +1100,89 @@ async function composeLayer(text, baseUrl, resolver, options = {}, stack = /* @_
1064
1100
  let weak = [];
1065
1101
  const subLayers = toArcs(file.metadata.subLayers);
1066
1102
  for (let i = subLayers.length - 1; i >= 0; i--) {
1067
- const sub = await loadComposedFile(subLayers[i], baseUrl, resolver, options, stack, warn);
1103
+ const sub = await loadExternalFile(subLayers[i], baseUrl, resolver, options, stack, warn);
1068
1104
  if (sub) weak = mergePrimLists(weak, sub.prims);
1069
1105
  }
1106
+ const ctx = {
1107
+ baseUrl,
1108
+ resolver,
1109
+ options,
1110
+ warn,
1111
+ stack,
1112
+ index: buildPathIndex(file.prims),
1113
+ resolving: /* @__PURE__ */ new Set()
1114
+ };
1070
1115
  const resolved = [];
1071
- for (const prim of file.prims) {
1072
- resolved.push(await resolvePrimArcs(prim, baseUrl, resolver, options, stack, warn));
1073
- }
1116
+ for (const prim of file.prims) resolved.push(await resolvePrim(prim, ctx));
1074
1117
  const prims = weak.length > 0 ? mergePrimLists(weak, resolved) : resolved;
1075
- return { version: file.version, metadata: stripKeys(file.metadata, ["subLayers"]), prims };
1076
- }
1077
- async function resolvePrimArcs(spec, baseUrl, resolver, options, stack, warn) {
1078
- const children = [];
1079
- for (const child of spec.children) {
1080
- children.push(await resolvePrimArcs(child, baseUrl, resolver, options, stack, warn));
1081
- }
1082
- const local = { ...spec, children, metadata: stripKeys(spec.metadata, ARC_KEYS) };
1118
+ return { version: file.version, metadata: stripKeys(file.metadata, STRIP_KEYS), prims };
1119
+ }
1120
+ async function resolvePrim(spec, ctx) {
1121
+ let properties = spec.properties;
1122
+ let children = spec.children;
1123
+ const selection = spec.metadata.variants;
1124
+ if (spec.variantSets && isDictionary(selection)) {
1125
+ for (const [setName, variantName] of Object.entries(selection)) {
1126
+ const variant = spec.variantSets[setName]?.[String(variantName)];
1127
+ if (!variant) continue;
1128
+ properties = mergeProperties(variant.properties, properties);
1129
+ children = mergePrimLists(variant.children, children);
1130
+ }
1131
+ }
1132
+ const resolvedChildren = [];
1133
+ for (const child of children) resolvedChildren.push(await resolvePrim(child, ctx));
1134
+ const local = {
1135
+ specifier: spec.specifier,
1136
+ typeName: spec.typeName,
1137
+ name: spec.name,
1138
+ metadata: stripKeys(spec.metadata, STRIP_KEYS),
1139
+ properties,
1140
+ children: resolvedChildren,
1141
+ line: spec.line
1142
+ };
1083
1143
  const arcs = ARC_KEYS.flatMap((k) => toArcs(spec.metadata[k]));
1084
- if (arcs.length === 0) return local;
1085
1144
  let base = null;
1086
1145
  for (const arc of arcs) {
1087
- const target = await loadReferencedPrim(arc, baseUrl, resolver, options, stack, warn);
1088
- if (!target) continue;
1089
- base = base ? mergePrim(target, base) : target;
1146
+ const target = await loadReferencedPrim(arc, ctx);
1147
+ if (target) base = base ? mergePrim(target, base) : target;
1090
1148
  }
1091
1149
  return base ? mergePrim(base, local) : local;
1092
1150
  }
1093
- async function loadReferencedPrim(arc, baseUrl, resolver, options, stack, warn) {
1094
- if (!arc.assetPath) {
1095
- warn(`internal references (no asset path) are not supported yet: <${arc.primPath ?? "?"}>`);
1096
- return null;
1151
+ async function loadReferencedPrim(arc, ctx) {
1152
+ if (arc.assetPath) {
1153
+ const composed = await loadExternalFile(
1154
+ arc,
1155
+ ctx.baseUrl,
1156
+ ctx.resolver,
1157
+ ctx.options,
1158
+ ctx.stack,
1159
+ ctx.warn
1160
+ );
1161
+ if (!composed) return null;
1162
+ const target = arc.primPath ? findPrimByPath(composed, arc.primPath) : defaultPrim(composed, ctx.warn);
1163
+ if (!target) {
1164
+ ctx.warn(
1165
+ `reference target ${arc.primPath ?? "(defaultPrim)"} not found in ${arc.assetPath.path}`
1166
+ );
1167
+ return null;
1168
+ }
1169
+ return target;
1097
1170
  }
1098
- const composed = await loadComposedFile(arc, baseUrl, resolver, options, stack, warn);
1099
- if (!composed) return null;
1100
- const target = arc.primPath ? findPrimByPath(composed, arc.primPath) : defaultPrim(composed, warn);
1101
- if (!target) {
1102
- warn(`reference target ${arc.primPath ?? "(defaultPrim)"} not found in ${arc.assetPath.path}`);
1103
- return null;
1171
+ if (arc.primPath) {
1172
+ if (ctx.resolving.has(arc.primPath)) {
1173
+ ctx.warn(`internal composition cycle at ${arc.primPath}; skipping`);
1174
+ return null;
1175
+ }
1176
+ const target = ctx.index.get(arc.primPath);
1177
+ if (!target) {
1178
+ ctx.warn(`internal reference target ${arc.primPath} not found`);
1179
+ return null;
1180
+ }
1181
+ return resolvePrim(target, { ...ctx, resolving: /* @__PURE__ */ new Set([...ctx.resolving, arc.primPath]) });
1104
1182
  }
1105
- return target;
1183
+ return null;
1106
1184
  }
1107
- async function loadComposedFile(arc, baseUrl, resolver, options, stack, warn) {
1185
+ async function loadExternalFile(arc, baseUrl, resolver, options, stack, warn) {
1108
1186
  if (!arc.assetPath) return null;
1109
1187
  const url = resolver.resolve(arc.assetPath.path, baseUrl);
1110
1188
  if (stack.has(url)) {
@@ -1126,7 +1204,6 @@ async function loadComposedFile(arc, baseUrl, resolver, options, stack, warn) {
1126
1204
  }
1127
1205
  function mergePrim(base, over) {
1128
1206
  return {
1129
- // `over` (def) wins; a pure `over` opinion keeps the base's specifier.
1130
1207
  specifier: over.specifier === "over" ? base.specifier : over.specifier,
1131
1208
  typeName: over.typeName || base.typeName,
1132
1209
  name: over.name,
@@ -1196,6 +1273,14 @@ function mergeMetadata(base, over) {
1196
1273
  }
1197
1274
  return merged;
1198
1275
  }
1276
+ function buildPathIndex(prims, parent = "/", map = /* @__PURE__ */ new Map()) {
1277
+ for (const p of prims) {
1278
+ const path = parent === "/" ? `/${p.name}` : `${parent}/${p.name}`;
1279
+ map.set(path, p);
1280
+ buildPathIndex(p.children, path, map);
1281
+ }
1282
+ return map;
1283
+ }
1199
1284
  function findPrimByPath(file, path) {
1200
1285
  const segments = path.split("/").filter(Boolean);
1201
1286
  let level = file.prims;
@@ -1209,9 +1294,7 @@ function findPrimByPath(file, path) {
1209
1294
  }
1210
1295
  function defaultPrim(file, warn) {
1211
1296
  const name = file.metadata.defaultPrim;
1212
- if (typeof name === "string") {
1213
- return file.prims.find((p) => p.name === name) ?? null;
1214
- }
1297
+ if (typeof name === "string") return file.prims.find((p) => p.name === name) ?? null;
1215
1298
  const first = file.prims[0];
1216
1299
  if (first) warn(`referenced layer has no defaultPrim; using first root prim "${first.name}"`);
1217
1300
  return first ?? null;
@@ -1222,10 +1305,14 @@ function toArcs(value) {
1222
1305
  const arcs = [];
1223
1306
  for (const v of list) {
1224
1307
  if (v instanceof AssetPath) arcs.push({ assetPath: v });
1225
- else if (v && typeof v === "object" && "assetPath" in v) arcs.push(v);
1308
+ else if (v && typeof v === "object" && ("assetPath" in v || "primPath" in v))
1309
+ arcs.push(v);
1226
1310
  }
1227
1311
  return arcs;
1228
1312
  }
1313
+ function isDictionary(v) {
1314
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Quat) && !(v instanceof UsdMatrix) && !(v instanceof AssetPath);
1315
+ }
1229
1316
  function stripKeys(meta, keys) {
1230
1317
  const out = {};
1231
1318
  for (const [k, v] of Object.entries(meta)) {
@@ -2681,5 +2768,5 @@ function leafName(path) {
2681
2768
  }
2682
2769
 
2683
2770
  export { ARTICULATION_ROOT_API, AssetPath, Attribute, COLLISION_API, CrateReader, DEFAULT_METERS_PER_UNIT, DEG2RAD, DefaultAssetResolver, Layer, PACKAGE_NAME, ParseError, Prim, Quat, RAD2DEG, RIGID_BODY_API, Relationship, Stage, TokenizeError, UsdMatrix, VERSION, buildKinematicTree, composeLayer, computeLocalTransform, crateToUsdaFile, createMemoryResolver, driveKindFor, extractRobotDescription, fromUsdMatrix, gatherMeshDescendants, getJointAxis, getJointBodies, getJointDrive, getJointLimits, getJointLocalFrame, getJointStatePosition, getJointType, getTranslation, hasArticulationRootAPI, hasCollisionAPI, hasRigidBodyAPI, identity4, invert, isMesh, isScope, isXform, iterDescendants, joinPosix, jointValueToSI, makeEuler, makeRotationFromQuat, makeRotationX, makeRotationY, makeRotationZ, makeScale, makeTranslation, multiply, multiplyAll, normalizeJointLimits, openUsdz, parseOpType, parseUsda, refineJointType, tokenize };
2684
- //# sourceMappingURL=chunk-XCP5GZPY.js.map
2685
- //# sourceMappingURL=chunk-XCP5GZPY.js.map
2771
+ //# sourceMappingURL=chunk-XH3L7XDJ.js.map
2772
+ //# sourceMappingURL=chunk-XH3L7XDJ.js.map