three-usd-robot 0.2.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.
@@ -0,0 +1,92 @@
1
+ import * as THREE2 from 'three';
2
+
3
+ // src/helpers/JointAxisHelper.ts
4
+ var JointAxisHelper = class extends THREE2.ArrowHelper {
5
+ isJointAxisHelper = true;
6
+ constructor(joint, length = 0.2, color = 16764928) {
7
+ super(joint.axis.clone().normalize(), new THREE2.Vector3(0, 0, 0), length, color);
8
+ this.name = `${joint.jointName}:axis`;
9
+ }
10
+ };
11
+ var JointLimitHelper = class extends THREE2.Line {
12
+ isJointLimitHelper = true;
13
+ constructor(joint, radius = 0.15, color = 43775) {
14
+ super(buildLimitGeometry(joint, radius), new THREE2.LineBasicMaterial({ color }));
15
+ this.name = `${joint.jointName}:limit`;
16
+ }
17
+ };
18
+ function buildLimitGeometry(joint, radius) {
19
+ const axis = joint.axis.clone().normalize();
20
+ if (joint.jointType === "prismatic") {
21
+ const lo2 = joint.lower ?? -radius;
22
+ const hi2 = joint.upper ?? radius;
23
+ return new THREE2.BufferGeometry().setFromPoints([
24
+ axis.clone().multiplyScalar(lo2),
25
+ axis.clone().multiplyScalar(hi2)
26
+ ]);
27
+ }
28
+ const lo = joint.lower ?? 0;
29
+ const hi = joint.upper ?? Math.PI * 2;
30
+ const { u, v } = perpendicularBasis(axis);
31
+ const segments = 48;
32
+ const points = [];
33
+ for (let i = 0; i <= segments; i++) {
34
+ const t = lo + (hi - lo) * i / segments;
35
+ points.push(
36
+ u.clone().multiplyScalar(Math.cos(t) * radius).addScaledVector(v, Math.sin(t) * radius)
37
+ );
38
+ }
39
+ return new THREE2.BufferGeometry().setFromPoints(points);
40
+ }
41
+ function perpendicularBasis(axis) {
42
+ const ref = Math.abs(axis.x) < 0.9 ? new THREE2.Vector3(1, 0, 0) : new THREE2.Vector3(0, 1, 0);
43
+ const u = new THREE2.Vector3().crossVectors(axis, ref).normalize();
44
+ const v = new THREE2.Vector3().crossVectors(axis, u).normalize();
45
+ return { u, v };
46
+ }
47
+ var LinkFrameHelper = class extends THREE2.AxesHelper {
48
+ isLinkFrameHelper = true;
49
+ constructor(size = 0.2) {
50
+ super(size);
51
+ this.name = "linkFrame";
52
+ }
53
+ };
54
+
55
+ // src/helpers/attach.ts
56
+ function addJointAxisHelpers(robot, length, color) {
57
+ const out = [];
58
+ for (const name of robot.getJointNames()) {
59
+ const joint = robot.getJointObject(name);
60
+ if (!joint?.articulated) continue;
61
+ const helper = new JointAxisHelper(joint, length, color);
62
+ joint.add(helper);
63
+ out.push(helper);
64
+ }
65
+ return out;
66
+ }
67
+ function addJointLimitHelpers(robot, radius, color) {
68
+ const out = [];
69
+ for (const name of robot.getJointNames()) {
70
+ const joint = robot.getJointObject(name);
71
+ if (!joint?.articulated) continue;
72
+ const helper = new JointLimitHelper(joint, radius, color);
73
+ joint.add(helper);
74
+ out.push(helper);
75
+ }
76
+ return out;
77
+ }
78
+ function addLinkFrameHelpers(robot, size) {
79
+ const out = [];
80
+ for (const name of robot.getLinkNames()) {
81
+ const link = robot.getLinkObject(name);
82
+ if (!link) continue;
83
+ const helper = new LinkFrameHelper(size);
84
+ link.add(helper);
85
+ out.push(helper);
86
+ }
87
+ return out;
88
+ }
89
+
90
+ export { JointAxisHelper, JointLimitHelper, LinkFrameHelper, addJointAxisHelpers, addJointLimitHelpers, addLinkFrameHelpers };
91
+ //# sourceMappingURL=helpers.js.map
92
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/helpers/JointAxisHelper.ts","../src/helpers/JointLimitHelper.ts","../src/helpers/LinkFrameHelper.ts","../src/helpers/attach.ts"],"names":["THREE","lo","hi","THREE3"],"mappings":"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","file":"helpers.js","sourcesContent":["import * as THREE from \"three\";\nimport type { JointObject } from \"../three/JointObject.js\";\n\n/**\n * An arrow drawn along a joint's motion axis (the rotation axis for\n * revolute/continuous joints, the slide direction for prismatic). Add it to the\n * joint's motion node so it tracks the joint.\n */\nexport class JointAxisHelper extends THREE.ArrowHelper {\n readonly isJointAxisHelper = true;\n\n constructor(joint: JointObject, length = 0.2, color: THREE.ColorRepresentation = 0xffd000) {\n super(joint.axis.clone().normalize(), new THREE.Vector3(0, 0, 0), length, color);\n this.name = `${joint.jointName}:axis`;\n }\n}\n","import * as THREE from \"three\";\nimport type { JointObject } from \"../three/JointObject.js\";\n\n/**\n * Visualizes a joint's range of motion: an arc swept between the lower and\n * upper limits (revolute/continuous) or a line segment between them\n * (prismatic). A limitless revolute joint draws a full circle. Add it to the\n * joint's motion node's parent frame (or the joint node) to anchor it.\n */\nexport class JointLimitHelper extends THREE.Line {\n readonly isJointLimitHelper = true;\n\n constructor(joint: JointObject, radius = 0.15, color: THREE.ColorRepresentation = 0x00aaff) {\n super(buildLimitGeometry(joint, radius), new THREE.LineBasicMaterial({ color }));\n this.name = `${joint.jointName}:limit`;\n }\n}\n\nfunction buildLimitGeometry(joint: JointObject, radius: number): THREE.BufferGeometry {\n const axis = joint.axis.clone().normalize();\n\n if (joint.jointType === \"prismatic\") {\n const lo = joint.lower ?? -radius;\n const hi = joint.upper ?? radius;\n return new THREE.BufferGeometry().setFromPoints([\n axis.clone().multiplyScalar(lo),\n axis.clone().multiplyScalar(hi),\n ]);\n }\n\n // Revolute / continuous: sweep an arc in the plane perpendicular to the axis.\n const lo = joint.lower ?? 0;\n const hi = joint.upper ?? Math.PI * 2;\n const { u, v } = perpendicularBasis(axis);\n const segments = 48;\n const points: THREE.Vector3[] = [];\n for (let i = 0; i <= segments; i++) {\n const t = lo + ((hi - lo) * i) / segments;\n points.push(\n u\n .clone()\n .multiplyScalar(Math.cos(t) * radius)\n .addScaledVector(v, Math.sin(t) * radius),\n );\n }\n return new THREE.BufferGeometry().setFromPoints(points);\n}\n\nfunction perpendicularBasis(axis: THREE.Vector3): { u: THREE.Vector3; v: THREE.Vector3 } {\n const ref = Math.abs(axis.x) < 0.9 ? new THREE.Vector3(1, 0, 0) : new THREE.Vector3(0, 1, 0);\n const u = new THREE.Vector3().crossVectors(axis, ref).normalize();\n const v = new THREE.Vector3().crossVectors(axis, u).normalize();\n return { u, v };\n}\n","import * as THREE from \"three\";\n\n/** A small RGB axes gizmo for a link's local frame. Add it to a `LinkObject`. */\nexport class LinkFrameHelper extends THREE.AxesHelper {\n readonly isLinkFrameHelper = true;\n\n constructor(size = 0.2) {\n super(size);\n this.name = \"linkFrame\";\n }\n}\n","import type * as THREE from \"three\";\nimport type { ThreeUsdRobot } from \"../three/ThreeUsdRobot.js\";\nimport { JointAxisHelper } from \"./JointAxisHelper.js\";\nimport { JointLimitHelper } from \"./JointLimitHelper.js\";\nimport { LinkFrameHelper } from \"./LinkFrameHelper.js\";\n\n/** Attach a {@link JointAxisHelper} to every articulated joint. Returns the helpers. */\nexport function addJointAxisHelpers(\n robot: ThreeUsdRobot,\n length?: number,\n color?: THREE.ColorRepresentation,\n): JointAxisHelper[] {\n const out: JointAxisHelper[] = [];\n for (const name of robot.getJointNames()) {\n const joint = robot.getJointObject(name);\n if (!joint?.articulated) continue;\n const helper = new JointAxisHelper(joint, length, color);\n joint.add(helper);\n out.push(helper);\n }\n return out;\n}\n\n/** Attach a {@link JointLimitHelper} to every articulated joint. Returns the helpers. */\nexport function addJointLimitHelpers(\n robot: ThreeUsdRobot,\n radius?: number,\n color?: THREE.ColorRepresentation,\n): JointLimitHelper[] {\n const out: JointLimitHelper[] = [];\n for (const name of robot.getJointNames()) {\n const joint = robot.getJointObject(name);\n if (!joint?.articulated) continue;\n const helper = new JointLimitHelper(joint, radius, color);\n joint.add(helper);\n out.push(helper);\n }\n return out;\n}\n\n/** Attach a {@link LinkFrameHelper} to every link. Returns the helpers. */\nexport function addLinkFrameHelpers(robot: ThreeUsdRobot, size?: number): LinkFrameHelper[] {\n const out: LinkFrameHelper[] = [];\n for (const name of robot.getLinkNames()) {\n const link = robot.getLinkObject(name);\n if (!link) continue;\n const helper = new LinkFrameHelper(size);\n link.add(helper);\n out.push(helper);\n }\n return out;\n}\n"]}
@@ -0,0 +1,89 @@
1
+ import { Stage, Prim, AssetResolver } from './core.js';
2
+ export { ARTICULATION_ROOT_API, Attribute, COLLISION_API, ComposeOptions, CrateReader, DEFAULT_METERS_PER_UNIT, DefaultAssetResolver, ExtractOptions, Layer, PACKAGE_NAME, ParseError, RIGID_BODY_API, Relationship, ResolvedXform, TokenizeError, UpAxis, UsdzPackage, VERSION, composeLayer, computeLocalTransform, crateToUsdaFile, createMemoryResolver, driveKindFor, extractRobotDescription, gatherMeshDescendants, getJointAxis, getJointBodies, getJointDrive, getJointLimits, getJointLocalFrame, getJointStatePosition, getJointType, hasArticulationRootAPI, hasCollisionAPI, hasRigidBodyAPI, isMesh, isScope, isXform, iterDescendants, joinPosix, jointValueToSI, normalizeJointLimits, openUsdz, parseOpType, parseUsda, refineJointType, tokenize } from './core.js';
3
+ import * as THREE from 'three';
4
+ import { A as Axis, R as RobotDescription } from './buildKinematicTree-2fg6ZN8m.js';
5
+ export { a as AssetPath, b as AttributeSpec, B as BuildTreeOptions, C as CompositionArc, D as DEG2RAD, J as JointDescription, c as JointDriveDescription, d as JointType, K as KinematicNode, e as KinematicTree, L as LinkDescription, f as ListOp, M as Mat4, g as MetadataMap, P as PrimSpec, h as PropertySpec, Q as Quat, i as RAD2DEG, j as RelationshipSpec, S as SdfPath, k as Specifier, T as TreeEdge, U as UsdDictionary, l as UsdMatrix, m as UsdValue, n as UsdaFile, V as Variability, o as Vec2, p as Vec3, q as Vec4, r as buildKinematicTree, s as fromUsdMatrix, t as getTranslation, u as identity4, v as invert, w as makeEuler, x as makeRotationFromQuat, y as makeRotationX, z as makeRotationY, E as makeRotationZ, F as makeScale, G as makeTranslation, H as multiply, I as multiplyAll } from './buildKinematicTree-2fg6ZN8m.js';
6
+ import { T as ThreeUsdRobot } from './ThreeUsdRobot-lMZHW-it.js';
7
+ export { J as JointObject, L as LinkObject, a as ThreeUsdRobotOptions } from './ThreeUsdRobot-lMZHW-it.js';
8
+
9
+ /** Unit vector for a USD joint axis token. Returns a fresh vector each call. */
10
+ declare function axisVector(axis: Axis): THREE.Vector3;
11
+
12
+ /**
13
+ * Binds `UsdGeom.Mesh` prims to Three.js geometry and attaches them under the
14
+ * robot's link objects.
15
+ *
16
+ * Triangulates polygons with a simple fan, uses authored per-vertex normals
17
+ * when present (else computes smooth normals), and reads `primvars:st` UVs and
18
+ * `primvars:displayColor`. Geometry is left in stage units; the global
19
+ * `metersPerUnit` scale is applied at the root in M9.
20
+ */
21
+
22
+ type MeshKind = "visual" | "collision";
23
+ type BindMeshesOptions = {
24
+ loadVisuals?: boolean;
25
+ loadCollisions?: boolean;
26
+ };
27
+ /** Build a `BufferGeometry` from a Mesh prim, or `null` if it has no points. */
28
+ declare function buildMeshGeometry(meshPrim: Prim): THREE.BufferGeometry | null;
29
+ /** Build a default material for a Mesh prim from `displayColor` / `doubleSided`. */
30
+ declare function buildMeshMaterial(meshPrim: Prim): THREE.Material;
31
+ /**
32
+ * Attach visual (and optionally collision) meshes to every link of a built
33
+ * {@link ThreeUsdRobot}. Each mesh is positioned by its transform relative to
34
+ * the owning link prim.
35
+ */
36
+ declare function bindRobotMeshes(stage: Stage, robot3d: ThreeUsdRobot, desc: RobotDescription, options?: BindMeshesOptions): void;
37
+
38
+ type ThreeUsdRobotLoaderOptions = {
39
+ /** Resolver for references / payloads / sublayers (default {@link DefaultAssetResolver}). */
40
+ assetResolver?: AssetResolver;
41
+ /** Render visual meshes (M6). */
42
+ loadVisuals?: boolean;
43
+ /** Render collision meshes (M6). */
44
+ loadCollisions?: boolean;
45
+ /** Up-axis correction strategy (M9). */
46
+ upAxisConversion?: "auto" | "Y" | "Z" | "none";
47
+ /** Extra uniform scale multiplied with `metersPerUnit` (M9). */
48
+ unitScale?: number;
49
+ /** Clamp `setJointValue` to authored limits (default `true`). */
50
+ clampJointLimits?: boolean;
51
+ /** Seed joints from drive targets / joint state (M9). */
52
+ applyDriveTargetsAsInitialPose?: boolean;
53
+ /** Override the robot name. */
54
+ robotName?: string;
55
+ /** Receives non-fatal load diagnostics. */
56
+ onWarn?: (message: string) => void;
57
+ };
58
+ /**
59
+ * Loads Isaac Sim / OpenUSD robot assets into a controllable {@link ThreeUsdRobot}.
60
+ *
61
+ * Composition (references / payloads / sublayers, M8) is resolved through an
62
+ * {@link AssetResolver}. Mesh rendering is M6; unit / up-axis / initial-pose
63
+ * handling is M9; USDC and variants are M10.
64
+ */
65
+ declare class ThreeUsdRobotLoader {
66
+ readonly options: ThreeUsdRobotLoaderOptions;
67
+ constructor(options?: ThreeUsdRobotLoaderOptions);
68
+ private get resolver();
69
+ /**
70
+ * Fetch an asset by URL and build the robot. `.usdz` packages are unzipped and
71
+ * composed from their entries; everything else is treated as USDA text.
72
+ */
73
+ loadAsync(url: string): Promise<ThreeUsdRobot>;
74
+ private fetchRootBytes;
75
+ /** Build a robot (composed, with meshes) from USDA source text. */
76
+ parse(text: string, baseUrl?: string): Promise<ThreeUsdRobot>;
77
+ /** Build a robot from the bytes of a `.usdz` package. */
78
+ parseUsdz(bytes: Uint8Array): Promise<ThreeUsdRobot>;
79
+ /** Build a robot from the bytes of a binary crate (`.usdc` / binary `.usd`). */
80
+ parseCrate(bytes: Uint8Array): Promise<ThreeUsdRobot>;
81
+ /** Parse + compose USDA source into the Three.js-independent robot IR. */
82
+ parseRobotDescription(text: string, baseUrl?: string): Promise<RobotDescription>;
83
+ private buildFromStage;
84
+ private composeStage;
85
+ private robotOptions;
86
+ private extractOptions;
87
+ }
88
+
89
+ export { AssetResolver, Axis, type BindMeshesOptions, type MeshKind, Prim, RobotDescription, Stage, ThreeUsdRobot, ThreeUsdRobotLoader, type ThreeUsdRobotLoaderOptions, axisVector, bindRobotMeshes, buildMeshGeometry, buildMeshMaterial };
package/dist/index.js ADDED
@@ -0,0 +1,493 @@
1
+ import { identity4, multiply, computeLocalTransform, invert, DefaultAssetResolver, CrateReader, openUsdz, crateToUsdaFile, Stage, extractRobotDescription, buildKinematicTree, composeLayer } from './chunk-XCP5GZPY.js';
2
+ 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 } from './chunk-XCP5GZPY.js';
3
+ import * as THREE4 from 'three';
4
+
5
+ function axisVector(axis) {
6
+ switch (axis) {
7
+ case "X":
8
+ return new THREE4.Vector3(1, 0, 0);
9
+ case "Y":
10
+ return new THREE4.Vector3(0, 1, 0);
11
+ case "Z":
12
+ return new THREE4.Vector3(0, 0, 1);
13
+ }
14
+ }
15
+ var JointObject = class extends THREE4.Object3D {
16
+ isJointObject = true;
17
+ jointName;
18
+ jointType;
19
+ axisToken;
20
+ axis;
21
+ lower;
22
+ upper;
23
+ _value = 0;
24
+ constructor(joint) {
25
+ super();
26
+ this.name = joint.name;
27
+ this.jointName = joint.name;
28
+ this.jointType = joint.type;
29
+ this.axisToken = joint.axis;
30
+ this.axis = axisVector(joint.axis);
31
+ this.lower = joint.lower;
32
+ this.upper = joint.upper;
33
+ }
34
+ get value() {
35
+ return this._value;
36
+ }
37
+ get articulated() {
38
+ return this.jointType !== "fixed";
39
+ }
40
+ /**
41
+ * Set the joint value (radians for revolute/continuous, length for prismatic).
42
+ * Optionally clamps to authored limits. Returns the value actually applied.
43
+ */
44
+ setValue(value, clampToLimits = true) {
45
+ if (!this.articulated) return this._value;
46
+ let v = value;
47
+ if (clampToLimits) {
48
+ if (this.lower !== void 0 && v < this.lower) v = this.lower;
49
+ if (this.upper !== void 0 && v > this.upper) v = this.upper;
50
+ }
51
+ this._value = v;
52
+ if (this.jointType === "prismatic") {
53
+ this.position.copy(this.axis).multiplyScalar(v);
54
+ this.quaternion.identity();
55
+ } else {
56
+ this.quaternion.setFromAxisAngle(this.axis, v);
57
+ this.position.set(0, 0, 0);
58
+ }
59
+ return v;
60
+ }
61
+ };
62
+ var LinkObject = class extends THREE4.Object3D {
63
+ isLinkObject = true;
64
+ linkName;
65
+ primPath;
66
+ constructor(link) {
67
+ super();
68
+ this.name = link.name;
69
+ this.linkName = link.name;
70
+ this.primPath = link.primPath;
71
+ this.matrixAutoUpdate = false;
72
+ }
73
+ };
74
+ var DEFAULT_COLOR = 10132122;
75
+ function buildMeshGeometry(meshPrim) {
76
+ const points = meshPrim.GetAttribute("points").Get();
77
+ if (!isVec3Array(points) || points.length === 0) return null;
78
+ const geometry = new THREE4.BufferGeometry();
79
+ geometry.setAttribute("position", new THREE4.Float32BufferAttribute(flat3(points), 3));
80
+ const counts = meshPrim.GetAttribute("faceVertexCounts").Get();
81
+ const indices = meshPrim.GetAttribute("faceVertexIndices").Get();
82
+ if (isNumberArray(counts) && isNumberArray(indices)) {
83
+ geometry.setIndex(triangulate(counts, indices));
84
+ } else if (isNumberArray(indices)) {
85
+ geometry.setIndex(indices.slice());
86
+ }
87
+ const normals = meshPrim.GetAttribute("normals").Get();
88
+ if (isVec3Array(normals) && normals.length === points.length) {
89
+ geometry.setAttribute("normal", new THREE4.Float32BufferAttribute(flat3(normals), 3));
90
+ } else {
91
+ geometry.computeVertexNormals();
92
+ }
93
+ const st = meshPrim.GetAttribute("primvars:st").Get();
94
+ if (isVec2Array(st) && st.length === points.length) {
95
+ geometry.setAttribute("uv", new THREE4.Float32BufferAttribute(flat2(st), 2));
96
+ }
97
+ return geometry;
98
+ }
99
+ function buildMeshMaterial(meshPrim) {
100
+ const color = new THREE4.Color(DEFAULT_COLOR);
101
+ const displayColor = meshPrim.GetAttribute("primvars:displayColor").Get();
102
+ if (isVec3Array(displayColor) && displayColor[0]) {
103
+ const [r, g, b] = displayColor[0];
104
+ color.setRGB(r, g, b);
105
+ }
106
+ const doubleSided = meshPrim.GetAttribute("doubleSided").Get() === true;
107
+ return new THREE4.MeshStandardMaterial({
108
+ color,
109
+ metalness: 0.1,
110
+ roughness: 0.8,
111
+ side: doubleSided ? THREE4.DoubleSide : THREE4.FrontSide
112
+ });
113
+ }
114
+ function bindRobotMeshes(stage, robot3d, desc, options = {}) {
115
+ const loadVisuals = options.loadVisuals ?? true;
116
+ const loadCollisions = options.loadCollisions ?? false;
117
+ for (const [key, link] of Object.entries(desc.links)) {
118
+ const linkObj = robot3d.getLinkObject(key);
119
+ const linkPrim = stage.GetPrimAtPath(link.primPath);
120
+ if (!linkObj || !linkPrim) continue;
121
+ const collisionSet = new Set(link.collisionPrims ?? []);
122
+ if (loadVisuals) {
123
+ for (const meshPath of link.visualPrims) {
124
+ if (collisionSet.has(meshPath)) continue;
125
+ attachMesh(stage, linkPrim, meshPath, linkObj, "visual");
126
+ }
127
+ }
128
+ if (loadCollisions) {
129
+ for (const meshPath of link.collisionPrims ?? []) {
130
+ attachMesh(stage, linkPrim, meshPath, linkObj, "collision");
131
+ }
132
+ }
133
+ }
134
+ }
135
+ function attachMesh(stage, linkPrim, meshPath, parent, kind) {
136
+ const meshPrim = stage.GetPrimAtPath(meshPath);
137
+ if (!meshPrim) return;
138
+ const geometry = buildMeshGeometry(meshPrim);
139
+ if (!geometry) return;
140
+ const mesh = new THREE4.Mesh(geometry, buildMeshMaterial(meshPrim));
141
+ mesh.name = meshPrim.GetName();
142
+ mesh.userData.kind = kind;
143
+ mesh.userData.primPath = meshPath;
144
+ if (kind === "collision") mesh.visible = false;
145
+ mesh.matrixAutoUpdate = false;
146
+ mesh.matrix.fromArray(relativeTransform(linkPrim, meshPrim));
147
+ mesh.matrixWorldNeedsUpdate = true;
148
+ parent.add(mesh);
149
+ }
150
+ function relativeTransform(linkPrim, meshPrim) {
151
+ const chain = [];
152
+ let p = meshPrim;
153
+ const stop = linkPrim.GetPath();
154
+ while (p && p.GetPath() !== stop) {
155
+ chain.push(p);
156
+ p = p.GetParent();
157
+ }
158
+ chain.reverse();
159
+ let m = identity4();
160
+ for (const prim of chain) {
161
+ m = multiply(m, computeLocalTransform(prim).matrix);
162
+ }
163
+ return m;
164
+ }
165
+ function triangulate(faceVertexCounts, faceVertexIndices) {
166
+ const tris = [];
167
+ let offset = 0;
168
+ for (const count of faceVertexCounts) {
169
+ for (let k = 2; k < count; k++) {
170
+ tris.push(
171
+ faceVertexIndices[offset],
172
+ faceVertexIndices[offset + k - 1],
173
+ faceVertexIndices[offset + k]
174
+ );
175
+ }
176
+ offset += count;
177
+ }
178
+ return tris;
179
+ }
180
+ function flat3(v) {
181
+ const out = new Array(v.length * 3);
182
+ for (let i = 0; i < v.length; i++) {
183
+ out[i * 3] = v[i][0];
184
+ out[i * 3 + 1] = v[i][1];
185
+ out[i * 3 + 2] = v[i][2];
186
+ }
187
+ return out;
188
+ }
189
+ function flat2(v) {
190
+ const out = new Array(v.length * 2);
191
+ for (let i = 0; i < v.length; i++) {
192
+ out[i * 2] = v[i][0];
193
+ out[i * 2 + 1] = v[i][1];
194
+ }
195
+ return out;
196
+ }
197
+ function isNumberArray(v) {
198
+ return Array.isArray(v) && v.every((n) => typeof n === "number");
199
+ }
200
+ function isVec3Array(v) {
201
+ return Array.isArray(v) && v.every((e) => Array.isArray(e) && e.length === 3);
202
+ }
203
+ function isVec2Array(v) {
204
+ return Array.isArray(v) && v.every((e) => Array.isArray(e) && e.length === 2);
205
+ }
206
+ var ThreeUsdRobot = class extends THREE4.Object3D {
207
+ isThreeUsdRobot = true;
208
+ robot;
209
+ tree;
210
+ clampJointLimits;
211
+ linkObjects = /* @__PURE__ */ new Map();
212
+ jointObjects = /* @__PURE__ */ new Map();
213
+ dirty = true;
214
+ helperSize;
215
+ _showVisual = true;
216
+ _showCollision = false;
217
+ _showJointAxes = false;
218
+ _showLinkFrames = false;
219
+ jointAxesHelpers = [];
220
+ linkFrameHelpers = [];
221
+ constructor(robot, tree, options = {}) {
222
+ super();
223
+ this.name = robot.name;
224
+ this.robot = robot;
225
+ this.tree = tree;
226
+ this.clampJointLimits = options.clampJointLimits ?? true;
227
+ this.helperSize = options.helperSize ?? 0.15;
228
+ for (const [key, link] of Object.entries(robot.links)) {
229
+ this.linkObjects.set(key, new LinkObject(link));
230
+ }
231
+ this.attachRoot();
232
+ this.attachTreeEdges();
233
+ this.attachIsolatedLinks();
234
+ this.applyStageNormalization(robot, options);
235
+ if (options.applyInitialPose ?? true) this.applyInitialPose(robot);
236
+ }
237
+ /** Orient (Z-up → Y-up) and scale (metersPerUnit × unitScale) the robot root. */
238
+ applyStageNormalization(robot, options) {
239
+ const scale = (robot.metersPerUnit || 1) * (options.unitScale ?? 1);
240
+ if (scale !== 1) this.scale.setScalar(scale);
241
+ const conv = options.upAxisConversion ?? "none";
242
+ const toY = conv === "Z" || conv === "auto" && robot.upAxis === "Z";
243
+ if (toY) this.quaternion.setFromAxisAngle(new THREE4.Vector3(1, 0, 0), -Math.PI / 2);
244
+ }
245
+ /** Apply each joint's authored initial value, if any. */
246
+ applyInitialPose(robot) {
247
+ for (const [key, joint] of Object.entries(robot.joints)) {
248
+ if (joint.initialValue === void 0) continue;
249
+ this.jointObjects.get(key)?.setValue(joint.initialValue, this.clampJointLimits);
250
+ }
251
+ this.dirty = true;
252
+ }
253
+ attachRoot() {
254
+ const rootObj = this.linkObjects.get(this.tree.root);
255
+ if (!rootObj) return;
256
+ const rootJointKey = this.tree.rootJoint;
257
+ if (rootJointKey) {
258
+ const j = this.robot.joints[rootJointKey];
259
+ if (j) setMatrix(rootObj, multiply(j.jointFrame0, invert(j.jointFrame1)));
260
+ }
261
+ this.add(rootObj);
262
+ }
263
+ attachTreeEdges() {
264
+ for (const linkKey of this.tree.order) {
265
+ const node = this.tree.nodes[linkKey];
266
+ if (!node || node.parent === null || node.jointToParent === null) continue;
267
+ const parentObj = this.linkObjects.get(node.parent);
268
+ const childObj = this.linkObjects.get(linkKey);
269
+ const joint = this.robot.joints[node.jointToParent];
270
+ if (!parentObj || !childObj || !joint) continue;
271
+ this.attachJointChain(parentObj, childObj, node.jointToParent, joint);
272
+ }
273
+ }
274
+ /** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
275
+ attachJointChain(parent, child, jointKey, joint) {
276
+ const frame0 = new THREE4.Group();
277
+ frame0.name = `${joint.name}:frame0`;
278
+ setMatrix(frame0, joint.jointFrame0);
279
+ const motion = new JointObject(joint);
280
+ const frame1Inv = new THREE4.Group();
281
+ frame1Inv.name = `${joint.name}:frame1Inv`;
282
+ setMatrix(frame1Inv, invert(joint.jointFrame1));
283
+ parent.add(frame0);
284
+ frame0.add(motion);
285
+ motion.add(frame1Inv);
286
+ frame1Inv.add(child);
287
+ this.jointObjects.set(jointKey, motion);
288
+ }
289
+ attachIsolatedLinks() {
290
+ for (const key of this.tree.isolatedLinks) {
291
+ const obj = this.linkObjects.get(key);
292
+ if (obj && !obj.parent) this.add(obj);
293
+ }
294
+ }
295
+ // -- Joint control -------------------------------------------------------
296
+ /** Set one joint value. Unknown joints are ignored. Returns whether it applied. */
297
+ setJointValue(name, value) {
298
+ const joint = this.jointObjects.get(name);
299
+ if (!joint) return false;
300
+ joint.setValue(value, this.clampJointLimits);
301
+ this.dirty = true;
302
+ return true;
303
+ }
304
+ /** Set several joint values at once (matrix update is coalesced). */
305
+ setJointValues(values) {
306
+ for (const [name, value] of Object.entries(values)) {
307
+ const joint = this.jointObjects.get(name);
308
+ if (joint) {
309
+ joint.setValue(value, this.clampJointLimits);
310
+ this.dirty = true;
311
+ }
312
+ }
313
+ }
314
+ getJointValue(name) {
315
+ return this.jointObjects.get(name)?.value;
316
+ }
317
+ /** Recompute world matrices. Called lazily by the getters; safe to call directly. */
318
+ updateKinematics() {
319
+ this.updateMatrixWorld(true);
320
+ this.dirty = false;
321
+ }
322
+ ensureUpdated() {
323
+ if (this.dirty) this.updateKinematics();
324
+ }
325
+ // -- Queries -------------------------------------------------------------
326
+ getLinkWorldMatrix(name) {
327
+ const obj = this.linkObjects.get(name);
328
+ if (!obj) throw new Error(`unknown link "${name}"`);
329
+ this.ensureUpdated();
330
+ return obj.matrixWorld.clone();
331
+ }
332
+ getLinkWorldPosition(name) {
333
+ return new THREE4.Vector3().setFromMatrixPosition(this.getLinkWorldMatrix(name));
334
+ }
335
+ getLinkObject(name) {
336
+ return this.linkObjects.get(name);
337
+ }
338
+ getJointObject(name) {
339
+ return this.jointObjects.get(name);
340
+ }
341
+ getJoints() {
342
+ return Object.values(this.robot.joints);
343
+ }
344
+ getLinks() {
345
+ return Object.values(this.robot.links);
346
+ }
347
+ /** Names of the articulated (controllable) joints. */
348
+ getJointNames() {
349
+ return [...this.jointObjects.keys()];
350
+ }
351
+ getLinkNames() {
352
+ return [...this.linkObjects.keys()];
353
+ }
354
+ getKinematicTree() {
355
+ return this.tree;
356
+ }
357
+ // -- Display toggles -----------------------------------------------------
358
+ get showVisual() {
359
+ return this._showVisual;
360
+ }
361
+ set showVisual(v) {
362
+ this._showVisual = v;
363
+ this.setKindVisibility("visual", v);
364
+ }
365
+ get showCollision() {
366
+ return this._showCollision;
367
+ }
368
+ set showCollision(v) {
369
+ this._showCollision = v;
370
+ this.setKindVisibility("collision", v);
371
+ }
372
+ get showJointAxes() {
373
+ return this._showJointAxes;
374
+ }
375
+ set showJointAxes(v) {
376
+ this._showJointAxes = v;
377
+ if (v && this.jointAxesHelpers.length === 0) {
378
+ for (const joint of this.jointObjects.values()) {
379
+ const h = new THREE4.AxesHelper(this.helperSize);
380
+ h.name = `${joint.jointName}:axes`;
381
+ joint.add(h);
382
+ this.jointAxesHelpers.push(h);
383
+ }
384
+ }
385
+ for (const h of this.jointAxesHelpers) h.visible = v;
386
+ }
387
+ get showLinkFrames() {
388
+ return this._showLinkFrames;
389
+ }
390
+ set showLinkFrames(v) {
391
+ this._showLinkFrames = v;
392
+ if (v && this.linkFrameHelpers.length === 0) {
393
+ for (const link of this.linkObjects.values()) {
394
+ const h = new THREE4.AxesHelper(this.helperSize);
395
+ h.name = `${link.linkName}:frame`;
396
+ link.add(h);
397
+ this.linkFrameHelpers.push(h);
398
+ }
399
+ }
400
+ for (const h of this.linkFrameHelpers) h.visible = v;
401
+ }
402
+ setKindVisibility(kind, visible) {
403
+ this.traverse((o) => {
404
+ if (o.userData.kind === kind) o.visible = visible;
405
+ });
406
+ }
407
+ };
408
+ function setMatrix(obj, m) {
409
+ obj.matrixAutoUpdate = false;
410
+ obj.matrix.fromArray(m);
411
+ obj.matrixWorldNeedsUpdate = true;
412
+ }
413
+
414
+ // src/three/ThreeUsdRobotLoader.ts
415
+ var ThreeUsdRobotLoader = class {
416
+ options;
417
+ constructor(options = {}) {
418
+ this.options = options;
419
+ }
420
+ get resolver() {
421
+ return this.options.assetResolver ?? new DefaultAssetResolver();
422
+ }
423
+ /**
424
+ * Fetch an asset by URL and build the robot. `.usdz` packages are unzipped and
425
+ * composed from their entries; everything else is treated as USDA text.
426
+ */
427
+ async loadAsync(url) {
428
+ if (/\.usdz$/i.test(url)) {
429
+ return this.parseUsdz(await this.fetchRootBytes(url));
430
+ }
431
+ const bytes = await this.fetchRootBytes(url);
432
+ if (CrateReader.isCrate(bytes)) return this.parseCrate(bytes);
433
+ return this.parse(new TextDecoder().decode(bytes), url);
434
+ }
435
+ async fetchRootBytes(url) {
436
+ const resolver = this.resolver;
437
+ if (resolver.fetchBytes) return resolver.fetchBytes(url);
438
+ return new TextEncoder().encode(await resolver.fetchText(url));
439
+ }
440
+ /** Build a robot (composed, with meshes) from USDA source text. */
441
+ async parse(text, baseUrl = "") {
442
+ return this.buildFromStage(await this.composeStage(text, baseUrl, this.resolver));
443
+ }
444
+ /** Build a robot from the bytes of a `.usdz` package. */
445
+ async parseUsdz(bytes) {
446
+ const pkg = openUsdz(bytes);
447
+ const rootText = await pkg.resolver.fetchText(pkg.rootEntry);
448
+ return this.buildFromStage(await this.composeStage(rootText, pkg.rootEntry, pkg.resolver));
449
+ }
450
+ /** Build a robot from the bytes of a binary crate (`.usdc` / binary `.usd`). */
451
+ parseCrate(bytes) {
452
+ const file = crateToUsdaFile(new CrateReader(bytes));
453
+ return Promise.resolve(this.buildFromStage(Stage.OpenFromFile(file)));
454
+ }
455
+ /** Parse + compose USDA source into the Three.js-independent robot IR. */
456
+ async parseRobotDescription(text, baseUrl = "") {
457
+ const stage = await this.composeStage(text, baseUrl, this.resolver);
458
+ return extractRobotDescription(stage, this.extractOptions());
459
+ }
460
+ buildFromStage(stage) {
461
+ const robot = extractRobotDescription(stage, this.extractOptions());
462
+ const tree = buildKinematicTree(robot);
463
+ const robot3d = new ThreeUsdRobot(robot, tree, this.robotOptions());
464
+ const loadVisuals = this.options.loadVisuals ?? true;
465
+ const loadCollisions = this.options.loadCollisions ?? false;
466
+ if (loadVisuals || loadCollisions) {
467
+ bindRobotMeshes(stage, robot3d, robot, { loadVisuals, loadCollisions });
468
+ }
469
+ return robot3d;
470
+ }
471
+ async composeStage(text, baseUrl, resolver) {
472
+ const composeOptions = this.options.onWarn ? { onWarn: this.options.onWarn } : {};
473
+ return Stage.OpenFromFile(await composeLayer(text, baseUrl, resolver, composeOptions));
474
+ }
475
+ robotOptions() {
476
+ return {
477
+ upAxisConversion: this.options.upAxisConversion ?? "auto",
478
+ ...this.options.clampJointLimits !== void 0 ? { clampJointLimits: this.options.clampJointLimits } : {},
479
+ ...this.options.unitScale !== void 0 ? { unitScale: this.options.unitScale } : {},
480
+ ...this.options.applyDriveTargetsAsInitialPose !== void 0 ? { applyInitialPose: this.options.applyDriveTargetsAsInitialPose } : {}
481
+ };
482
+ }
483
+ extractOptions() {
484
+ return {
485
+ ...this.options.robotName !== void 0 ? { robotName: this.options.robotName } : {},
486
+ ...this.options.onWarn !== void 0 ? { onWarn: this.options.onWarn } : {}
487
+ };
488
+ }
489
+ };
490
+
491
+ export { JointObject, LinkObject, ThreeUsdRobot, ThreeUsdRobotLoader, axisVector, bindRobotMeshes, buildMeshGeometry, buildMeshMaterial };
492
+ //# sourceMappingURL=index.js.map
493
+ //# sourceMappingURL=index.js.map