three-usd-robot 0.7.0 → 0.8.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/README.md +87 -7
- package/dist/{ThreeUsdRobot-Ljh4PDVD.d.ts → ThreeUsdRobot-BZ_vlr1R.d.ts} +51 -7
- package/dist/ThreeUsdRobotLoader-HteyMZf1.d.ts +92 -0
- package/dist/{buildKinematicTree-Ccd2DIKH.d.ts → buildKinematicTree-D4R0hEtN.d.ts} +2 -2
- package/dist/{AssetResolver-CpIJNgWZ.d.ts → bytes-MOJ2oN-u.d.ts} +14 -1
- package/dist/chunk-FUPFJOXW.js +387 -0
- package/dist/chunk-FUPFJOXW.js.map +1 -0
- package/dist/chunk-IYKPVUZ2.js +406 -0
- package/dist/chunk-IYKPVUZ2.js.map +1 -0
- package/dist/{chunk-FL554IS5.js → chunk-JHOX3VUW.js} +115 -367
- package/dist/chunk-JHOX3VUW.js.map +1 -0
- package/dist/{chunk-O7XP5FQ4.js → chunk-P6CHTMGW.js} +4 -3
- package/dist/chunk-P6CHTMGW.js.map +1 -0
- package/dist/{chunk-4GPCBXUS.js → chunk-PHHMWMCA.js} +45 -410
- package/dist/chunk-PHHMWMCA.js.map +1 -0
- package/dist/core.d.ts +17 -5
- package/dist/core.js +3 -2
- package/dist/extras.d.ts +2 -2
- package/dist/helpers.d.ts +73 -3
- package/dist/helpers.js +125 -13
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +28 -18
- package/dist/index.js +8 -5
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +4 -4
- package/dist/react.js +4 -2
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/dist/ThreeUsdRobotLoader-BSIVFA48.d.ts +0 -67
- package/dist/chunk-4GPCBXUS.js.map +0 -1
- package/dist/chunk-FL554IS5.js.map +0 -1
- package/dist/chunk-O7XP5FQ4.js.map +0 -1
|
@@ -1,81 +1,14 @@
|
|
|
1
|
-
import { getMaterialSubsets, resolveBoundMaterial, COLLISION_API, isNonVisualPurpose, computeWorldTransform,
|
|
2
|
-
import
|
|
1
|
+
import { getMaterialSubsets, resolveBoundMaterial, isRenderableGprim, COLLISION_API, isNonVisualPurpose, computeWorldTransform, computeLocalTransform, DefaultAssetResolver, toBytes, extractRobotDescription, isZip, CrateReader, openUsdz, buildKinematicTree, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-PHHMWMCA.js';
|
|
2
|
+
import { ThreeUsdRobot } from './chunk-FUPFJOXW.js';
|
|
3
|
+
import { identity4, multiply } from './chunk-IYKPVUZ2.js';
|
|
4
|
+
import * as THREE from 'three';
|
|
3
5
|
|
|
4
|
-
function axisVector(axis) {
|
|
5
|
-
switch (axis) {
|
|
6
|
-
case "X":
|
|
7
|
-
return new THREE4.Vector3(1, 0, 0);
|
|
8
|
-
case "Y":
|
|
9
|
-
return new THREE4.Vector3(0, 1, 0);
|
|
10
|
-
case "Z":
|
|
11
|
-
return new THREE4.Vector3(0, 0, 1);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
var JointObject = class extends THREE4.Object3D {
|
|
15
|
-
isJointObject = true;
|
|
16
|
-
jointName;
|
|
17
|
-
jointType;
|
|
18
|
-
axisToken;
|
|
19
|
-
axis;
|
|
20
|
-
lower;
|
|
21
|
-
upper;
|
|
22
|
-
_value = 0;
|
|
23
|
-
constructor(joint) {
|
|
24
|
-
super();
|
|
25
|
-
this.name = joint.name;
|
|
26
|
-
this.jointName = joint.name;
|
|
27
|
-
this.jointType = joint.type;
|
|
28
|
-
this.axisToken = joint.axis;
|
|
29
|
-
this.axis = axisVector(joint.axis);
|
|
30
|
-
this.lower = joint.lower;
|
|
31
|
-
this.upper = joint.upper;
|
|
32
|
-
}
|
|
33
|
-
get value() {
|
|
34
|
-
return this._value;
|
|
35
|
-
}
|
|
36
|
-
get articulated() {
|
|
37
|
-
return this.jointType !== "fixed";
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Set the joint value (radians for revolute/continuous, length for prismatic).
|
|
41
|
-
* Optionally clamps to authored limits. Returns the value actually applied.
|
|
42
|
-
*/
|
|
43
|
-
setValue(value, clampToLimits = true) {
|
|
44
|
-
if (!this.articulated) return this._value;
|
|
45
|
-
let v = value;
|
|
46
|
-
if (clampToLimits) {
|
|
47
|
-
if (this.lower !== void 0 && v < this.lower) v = this.lower;
|
|
48
|
-
if (this.upper !== void 0 && v > this.upper) v = this.upper;
|
|
49
|
-
}
|
|
50
|
-
this._value = v;
|
|
51
|
-
if (this.jointType === "prismatic") {
|
|
52
|
-
this.position.copy(this.axis).multiplyScalar(v);
|
|
53
|
-
this.quaternion.identity();
|
|
54
|
-
} else {
|
|
55
|
-
this.quaternion.setFromAxisAngle(this.axis, v);
|
|
56
|
-
this.position.set(0, 0, 0);
|
|
57
|
-
}
|
|
58
|
-
return v;
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
var LinkObject = class extends THREE4.Object3D {
|
|
62
|
-
isLinkObject = true;
|
|
63
|
-
linkName;
|
|
64
|
-
primPath;
|
|
65
|
-
constructor(link) {
|
|
66
|
-
super();
|
|
67
|
-
this.name = link.name;
|
|
68
|
-
this.linkName = link.name;
|
|
69
|
-
this.primPath = link.primPath;
|
|
70
|
-
this.matrixAutoUpdate = false;
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
6
|
var DEFAULT_COLOR = 10132122;
|
|
74
7
|
function buildMeshGeometry(meshPrim) {
|
|
75
8
|
const points = meshPrim.GetAttribute("points").Get();
|
|
76
9
|
if (!isVec3Array(points) || points.length === 0) return null;
|
|
77
|
-
const geometry = new
|
|
78
|
-
geometry.setAttribute("position", new
|
|
10
|
+
const geometry = new THREE.BufferGeometry();
|
|
11
|
+
geometry.setAttribute("position", new THREE.Float32BufferAttribute(flat3(points), 3));
|
|
79
12
|
const counts = meshPrim.GetAttribute("faceVertexCounts").Get();
|
|
80
13
|
const indices = meshPrim.GetAttribute("faceVertexIndices").Get();
|
|
81
14
|
if (isNumberArray(counts) && isNumberArray(indices)) {
|
|
@@ -90,18 +23,57 @@ function buildMeshGeometry(meshPrim) {
|
|
|
90
23
|
}
|
|
91
24
|
const normals = meshPrim.GetAttribute("normals").Get();
|
|
92
25
|
if (isVec3Array(normals) && normals.length === points.length) {
|
|
93
|
-
geometry.setAttribute("normal", new
|
|
26
|
+
geometry.setAttribute("normal", new THREE.Float32BufferAttribute(flat3(normals), 3));
|
|
94
27
|
} else {
|
|
95
28
|
geometry.computeVertexNormals();
|
|
96
29
|
}
|
|
97
30
|
const st = meshPrim.GetAttribute("primvars:st").Get();
|
|
98
31
|
if (isVec2Array(st) && st.length === points.length) {
|
|
99
|
-
geometry.setAttribute("uv", new
|
|
32
|
+
geometry.setAttribute("uv", new THREE.Float32BufferAttribute(flat2(st), 2));
|
|
100
33
|
}
|
|
101
34
|
return geometry;
|
|
102
35
|
}
|
|
36
|
+
function buildGprimGeometry(prim) {
|
|
37
|
+
switch (prim.GetTypeName()) {
|
|
38
|
+
case "Mesh":
|
|
39
|
+
return buildMeshGeometry(prim);
|
|
40
|
+
case "Cube": {
|
|
41
|
+
const size = getNumber(prim, "size") ?? 2;
|
|
42
|
+
return new THREE.BoxGeometry(size, size, size);
|
|
43
|
+
}
|
|
44
|
+
case "Sphere":
|
|
45
|
+
return new THREE.SphereGeometry(getNumber(prim, "radius") ?? 1, 32, 16);
|
|
46
|
+
case "Cylinder": {
|
|
47
|
+
const radius = getNumber(prim, "radius") ?? 1;
|
|
48
|
+
const height = getNumber(prim, "height") ?? 2;
|
|
49
|
+
return orientSpine(new THREE.CylinderGeometry(radius, radius, height, 32), prim);
|
|
50
|
+
}
|
|
51
|
+
case "Capsule": {
|
|
52
|
+
const radius = getNumber(prim, "radius") ?? 0.5;
|
|
53
|
+
const height = getNumber(prim, "height") ?? 1;
|
|
54
|
+
return orientSpine(new THREE.CapsuleGeometry(radius, height, 8, 32), prim);
|
|
55
|
+
}
|
|
56
|
+
case "Cone": {
|
|
57
|
+
const radius = getNumber(prim, "radius") ?? 1;
|
|
58
|
+
const height = getNumber(prim, "height") ?? 2;
|
|
59
|
+
return orientSpine(new THREE.ConeGeometry(radius, height, 32), prim);
|
|
60
|
+
}
|
|
61
|
+
default:
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function getNumber(prim, name) {
|
|
66
|
+
const v = prim.GetAttribute(name).Get();
|
|
67
|
+
return typeof v === "number" ? v : void 0;
|
|
68
|
+
}
|
|
69
|
+
function orientSpine(geometry, prim) {
|
|
70
|
+
const axis = prim.GetAttribute("axis").Get();
|
|
71
|
+
if (axis === "Y") return geometry;
|
|
72
|
+
if (axis === "X") return geometry.rotateZ(-Math.PI / 2);
|
|
73
|
+
return geometry.rotateX(Math.PI / 2);
|
|
74
|
+
}
|
|
103
75
|
function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
|
|
104
|
-
const color = new
|
|
76
|
+
const color = new THREE.Color(DEFAULT_COLOR);
|
|
105
77
|
let opacity = 1;
|
|
106
78
|
const bound = stage ? resolveBoundMaterial(stage, bindingPrim ?? meshPrim) : void 0;
|
|
107
79
|
if (bound?.color) {
|
|
@@ -133,7 +105,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
|
|
|
133
105
|
const emissiveMap = tex(bound?.emissiveTexture, "srgb");
|
|
134
106
|
const metalness = bound?.metalness ?? bound?.metalnessTexture?.scale?.[0] ?? (metalnessMap ? 1 : 0.1);
|
|
135
107
|
const roughness = bound?.roughness ?? bound?.roughnessTexture?.scale?.[0] ?? (roughnessMap ? 1 : 0.8);
|
|
136
|
-
const emissive = new
|
|
108
|
+
const emissive = new THREE.Color(0);
|
|
137
109
|
if (bound?.emissiveColor) {
|
|
138
110
|
emissive.setRGB(bound.emissiveColor[0], bound.emissiveColor[1], bound.emissiveColor[2]);
|
|
139
111
|
} else if (emissiveMap) {
|
|
@@ -147,7 +119,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
|
|
|
147
119
|
const alphaTest = threshold !== void 0 && threshold > 0 ? threshold : 0;
|
|
148
120
|
const transparent = alphaTest === 0 && hasAlphaSource;
|
|
149
121
|
const doubleSided = meshPrim.GetAttribute("doubleSided").Get() === true;
|
|
150
|
-
const material = new
|
|
122
|
+
const material = new THREE.MeshStandardMaterial({
|
|
151
123
|
color,
|
|
152
124
|
metalness,
|
|
153
125
|
roughness,
|
|
@@ -155,7 +127,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
|
|
|
155
127
|
transparent,
|
|
156
128
|
opacity,
|
|
157
129
|
...alphaTest > 0 ? { alphaTest } : {},
|
|
158
|
-
side: doubleSided ?
|
|
130
|
+
side: doubleSided ? THREE.DoubleSide : THREE.FrontSide,
|
|
159
131
|
...map ? { map } : {},
|
|
160
132
|
...alphaMap ? { alphaMap } : {},
|
|
161
133
|
...normalMap ? { normalMap } : {},
|
|
@@ -198,12 +170,12 @@ function bindSceneMeshes(stage, robot3d, desc, options = {}) {
|
|
|
198
170
|
}
|
|
199
171
|
let attached = 0;
|
|
200
172
|
for (const prim of stage.Traverse()) {
|
|
201
|
-
if (prim
|
|
173
|
+
if (!isRenderableGprim(prim) || owned.has(prim.GetPath())) continue;
|
|
202
174
|
if (prim.HasAPI(COLLISION_API) || isNonVisualPurpose(prim)) continue;
|
|
203
175
|
if ([...owned].some((path) => prim.GetPath().startsWith(`${path}/`))) continue;
|
|
204
|
-
const geometry =
|
|
176
|
+
const geometry = buildGprimGeometry(prim);
|
|
205
177
|
if (!geometry) continue;
|
|
206
|
-
const mesh = new
|
|
178
|
+
const mesh = new THREE.Mesh(geometry, buildMeshMaterial(prim, stage, options.textureProvider));
|
|
207
179
|
mesh.name = prim.GetName();
|
|
208
180
|
mesh.userData.kind = "scene";
|
|
209
181
|
mesh.userData.primPath = prim.GetPath();
|
|
@@ -226,9 +198,9 @@ function buildMeshMaterials(meshPrim, stage, textures) {
|
|
|
226
198
|
function attachMesh(stage, linkPrim, meshPath, parent, kind, textures) {
|
|
227
199
|
const meshPrim = stage.GetPrimAtPath(meshPath);
|
|
228
200
|
if (!meshPrim) return;
|
|
229
|
-
const geometry =
|
|
201
|
+
const geometry = buildGprimGeometry(meshPrim);
|
|
230
202
|
if (!geometry) return;
|
|
231
|
-
const mesh = new
|
|
203
|
+
const mesh = new THREE.Mesh(geometry, buildMeshMaterials(meshPrim, stage, textures));
|
|
232
204
|
mesh.name = meshPrim.GetName();
|
|
233
205
|
mesh.userData.kind = kind;
|
|
234
206
|
mesh.userData.primPath = meshPath;
|
|
@@ -348,8 +320,8 @@ function createTextureProvider(resolver, baseUrl) {
|
|
|
348
320
|
} catch {
|
|
349
321
|
return null;
|
|
350
322
|
}
|
|
351
|
-
const texture = new
|
|
352
|
-
texture.colorSpace = options.colorSpace === "linear" ?
|
|
323
|
+
const texture = new THREE.Texture();
|
|
324
|
+
texture.colorSpace = options.colorSpace === "linear" ? THREE.NoColorSpace : THREE.SRGBColorSpace;
|
|
353
325
|
texture.wrapS = toThreeWrap(options.wrapS);
|
|
354
326
|
texture.wrapT = toThreeWrap(options.wrapT);
|
|
355
327
|
applyTransform(texture, options.transform);
|
|
@@ -380,11 +352,11 @@ function toThreeWrap(wrap) {
|
|
|
380
352
|
switch (wrap) {
|
|
381
353
|
case "clamp":
|
|
382
354
|
case "black":
|
|
383
|
-
return
|
|
355
|
+
return THREE.ClampToEdgeWrapping;
|
|
384
356
|
case "mirror":
|
|
385
|
-
return
|
|
357
|
+
return THREE.MirroredRepeatWrapping;
|
|
386
358
|
default:
|
|
387
|
-
return
|
|
359
|
+
return THREE.RepeatWrapping;
|
|
388
360
|
}
|
|
389
361
|
}
|
|
390
362
|
function applyTransform(texture, t) {
|
|
@@ -401,254 +373,6 @@ function mimeOf(url) {
|
|
|
401
373
|
if (/\.webp$/i.test(url)) return "image/webp";
|
|
402
374
|
return "image/png";
|
|
403
375
|
}
|
|
404
|
-
var ThreeUsdRobot = class extends THREE4.Object3D {
|
|
405
|
-
isThreeUsdRobot = true;
|
|
406
|
-
robot;
|
|
407
|
-
tree;
|
|
408
|
-
clampJointLimits;
|
|
409
|
-
linkObjects = /* @__PURE__ */ new Map();
|
|
410
|
-
jointObjects = /* @__PURE__ */ new Map();
|
|
411
|
-
dirty = true;
|
|
412
|
-
helperSize;
|
|
413
|
-
_showVisual = true;
|
|
414
|
-
_showCollision = false;
|
|
415
|
-
_showJointAxes = false;
|
|
416
|
-
_showLinkFrames = false;
|
|
417
|
-
jointAxesHelpers = [];
|
|
418
|
-
linkFrameHelpers = [];
|
|
419
|
-
constructor(robot, tree, options = {}) {
|
|
420
|
-
super();
|
|
421
|
-
this.name = robot.name;
|
|
422
|
-
this.robot = robot;
|
|
423
|
-
this.tree = tree;
|
|
424
|
-
this.clampJointLimits = options.clampJointLimits ?? true;
|
|
425
|
-
this.helperSize = options.helperSize ?? 0.15;
|
|
426
|
-
for (const [key, link] of Object.entries(robot.links)) {
|
|
427
|
-
this.linkObjects.set(key, new LinkObject(link));
|
|
428
|
-
}
|
|
429
|
-
this.attachRoot();
|
|
430
|
-
this.attachTreeEdges();
|
|
431
|
-
this.attachIsolatedLinks();
|
|
432
|
-
this.applyStageNormalization(robot, options);
|
|
433
|
-
if (options.applyInitialPose ?? true) this.applyInitialPose(robot);
|
|
434
|
-
}
|
|
435
|
-
/** Orient (Z-up → Y-up) and scale (metersPerUnit × unitScale) the robot root. */
|
|
436
|
-
applyStageNormalization(robot, options) {
|
|
437
|
-
const scale = (robot.metersPerUnit || 1) * (options.unitScale ?? 1);
|
|
438
|
-
if (scale !== 1) this.scale.setScalar(scale);
|
|
439
|
-
const conv = options.upAxisConversion ?? "none";
|
|
440
|
-
const toY = conv === "Z" || conv === "auto" && robot.upAxis === "Z";
|
|
441
|
-
if (toY) this.quaternion.setFromAxisAngle(new THREE4.Vector3(1, 0, 0), -Math.PI / 2);
|
|
442
|
-
}
|
|
443
|
-
/** Apply each joint's authored initial value, if any. */
|
|
444
|
-
applyInitialPose(robot) {
|
|
445
|
-
for (const [key, joint] of Object.entries(robot.joints)) {
|
|
446
|
-
if (joint.initialValue === void 0) continue;
|
|
447
|
-
this.jointObjects.get(key)?.setValue(joint.initialValue, this.clampJointLimits);
|
|
448
|
-
}
|
|
449
|
-
this.dirty = true;
|
|
450
|
-
}
|
|
451
|
-
attachRoot() {
|
|
452
|
-
const rootObj = this.linkObjects.get(this.tree.root);
|
|
453
|
-
if (!rootObj) return;
|
|
454
|
-
const rootJointKey = this.tree.rootJoint;
|
|
455
|
-
const rootLink = this.robot.links[this.tree.root];
|
|
456
|
-
if (rootJointKey) {
|
|
457
|
-
const j = this.robot.joints[rootJointKey];
|
|
458
|
-
if (j) setMatrix(rootObj, multiply(j.jointFrame0, invert(j.jointFrame1)));
|
|
459
|
-
} else if (rootLink?.worldTransform) {
|
|
460
|
-
setMatrix(rootObj, rootLink.worldTransform);
|
|
461
|
-
}
|
|
462
|
-
this.add(rootObj);
|
|
463
|
-
}
|
|
464
|
-
attachTreeEdges() {
|
|
465
|
-
for (const linkKey of this.tree.order) {
|
|
466
|
-
const node = this.tree.nodes[linkKey];
|
|
467
|
-
if (!node || node.parent === null || node.jointToParent === null) continue;
|
|
468
|
-
const parentObj = this.linkObjects.get(node.parent);
|
|
469
|
-
const childObj = this.linkObjects.get(linkKey);
|
|
470
|
-
const joint = this.robot.joints[node.jointToParent];
|
|
471
|
-
if (!parentObj || !childObj || !joint) continue;
|
|
472
|
-
this.attachJointChain(parentObj, childObj, node.jointToParent, joint);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
/** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
|
|
476
|
-
attachJointChain(parent, child, jointKey, joint) {
|
|
477
|
-
const frame0 = new THREE4.Group();
|
|
478
|
-
frame0.name = `${joint.name}:frame0`;
|
|
479
|
-
setMatrix(frame0, joint.jointFrame0);
|
|
480
|
-
const motion = new JointObject(joint);
|
|
481
|
-
const frame1Inv = new THREE4.Group();
|
|
482
|
-
frame1Inv.name = `${joint.name}:frame1Inv`;
|
|
483
|
-
setMatrix(frame1Inv, invert(joint.jointFrame1));
|
|
484
|
-
parent.add(frame0);
|
|
485
|
-
frame0.add(motion);
|
|
486
|
-
motion.add(frame1Inv);
|
|
487
|
-
frame1Inv.add(child);
|
|
488
|
-
this.jointObjects.set(jointKey, motion);
|
|
489
|
-
}
|
|
490
|
-
attachIsolatedLinks() {
|
|
491
|
-
for (const key of this.tree.isolatedLinks) {
|
|
492
|
-
const obj = this.linkObjects.get(key);
|
|
493
|
-
if (!obj || obj.parent) continue;
|
|
494
|
-
const worldTransform = this.robot.links[key]?.worldTransform;
|
|
495
|
-
if (worldTransform) setMatrix(obj, worldTransform);
|
|
496
|
-
this.add(obj);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
// -- Joint control -------------------------------------------------------
|
|
500
|
-
/** Set one joint value. Unknown joints are ignored. Returns whether it applied. */
|
|
501
|
-
setJointValue(name, value) {
|
|
502
|
-
const joint = this.jointObjects.get(name);
|
|
503
|
-
if (!joint) return false;
|
|
504
|
-
joint.setValue(value, this.clampJointLimits);
|
|
505
|
-
this.dirty = true;
|
|
506
|
-
return true;
|
|
507
|
-
}
|
|
508
|
-
/** Set several joint values at once (matrix update is coalesced). */
|
|
509
|
-
setJointValues(values) {
|
|
510
|
-
for (const [name, value] of Object.entries(values)) {
|
|
511
|
-
const joint = this.jointObjects.get(name);
|
|
512
|
-
if (joint) {
|
|
513
|
-
joint.setValue(value, this.clampJointLimits);
|
|
514
|
-
this.dirty = true;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
getJointValue(name) {
|
|
519
|
-
return this.jointObjects.get(name)?.value;
|
|
520
|
-
}
|
|
521
|
-
/** Recompute world matrices. Called lazily by the getters; safe to call directly. */
|
|
522
|
-
updateKinematics() {
|
|
523
|
-
this.updateMatrixWorld(true);
|
|
524
|
-
this.dirty = false;
|
|
525
|
-
}
|
|
526
|
-
ensureUpdated() {
|
|
527
|
-
if (this.dirty) this.updateKinematics();
|
|
528
|
-
}
|
|
529
|
-
// -- Queries -------------------------------------------------------------
|
|
530
|
-
getLinkWorldMatrix(name) {
|
|
531
|
-
const obj = this.linkObjects.get(name);
|
|
532
|
-
if (!obj) throw new Error(`unknown link "${name}"`);
|
|
533
|
-
this.ensureUpdated();
|
|
534
|
-
return obj.matrixWorld.clone();
|
|
535
|
-
}
|
|
536
|
-
getLinkWorldPosition(name) {
|
|
537
|
-
return new THREE4.Vector3().setFromMatrixPosition(this.getLinkWorldMatrix(name));
|
|
538
|
-
}
|
|
539
|
-
getLinkObject(name) {
|
|
540
|
-
return this.linkObjects.get(name);
|
|
541
|
-
}
|
|
542
|
-
getJointObject(name) {
|
|
543
|
-
return this.jointObjects.get(name);
|
|
544
|
-
}
|
|
545
|
-
getJoints() {
|
|
546
|
-
return Object.values(this.robot.joints);
|
|
547
|
-
}
|
|
548
|
-
getLinks() {
|
|
549
|
-
return Object.values(this.robot.links);
|
|
550
|
-
}
|
|
551
|
-
/** Names of the articulated (controllable) joints. */
|
|
552
|
-
getJointNames() {
|
|
553
|
-
return [...this.jointObjects.keys()];
|
|
554
|
-
}
|
|
555
|
-
getLinkNames() {
|
|
556
|
-
return [...this.linkObjects.keys()];
|
|
557
|
-
}
|
|
558
|
-
getKinematicTree() {
|
|
559
|
-
return this.tree;
|
|
560
|
-
}
|
|
561
|
-
// -- Animation playback --------------------------------------------------
|
|
562
|
-
/** Playback rate in time codes per second (from the stage; default 24). */
|
|
563
|
-
getTimeCodesPerSecond() {
|
|
564
|
-
return this.robot.timeCodesPerSecond ?? 24;
|
|
565
|
-
}
|
|
566
|
-
/** Whether any joint has a time-sampled trajectory. */
|
|
567
|
-
hasAnimation() {
|
|
568
|
-
return Object.values(this.robot.joints).some((j) => j.valueSamples !== void 0);
|
|
569
|
-
}
|
|
570
|
-
/**
|
|
571
|
-
* Animation range in time codes: the union of authored joint sample ranges,
|
|
572
|
-
* falling back to the stage `startTimeCode`/`endTimeCode`. `null` if neither.
|
|
573
|
-
*/
|
|
574
|
-
getTimeRange() {
|
|
575
|
-
let start = Number.POSITIVE_INFINITY;
|
|
576
|
-
let end = Number.NEGATIVE_INFINITY;
|
|
577
|
-
for (const joint of Object.values(this.robot.joints)) {
|
|
578
|
-
const times = joint.valueSamples?.times;
|
|
579
|
-
if (!times || times.length === 0) continue;
|
|
580
|
-
start = Math.min(start, times[0]);
|
|
581
|
-
end = Math.max(end, times[times.length - 1]);
|
|
582
|
-
}
|
|
583
|
-
if (start <= end) return { start, end };
|
|
584
|
-
const { startTimeCode, endTimeCode } = this.robot;
|
|
585
|
-
if (startTimeCode !== void 0 && endTimeCode !== void 0) {
|
|
586
|
-
return { start: startTimeCode, end: endTimeCode };
|
|
587
|
-
}
|
|
588
|
-
return null;
|
|
589
|
-
}
|
|
590
|
-
/** Sample every animated joint at time code `t` and apply the values. */
|
|
591
|
-
setTime(t) {
|
|
592
|
-
for (const [key, joint] of Object.entries(this.robot.joints)) {
|
|
593
|
-
if (joint.valueSamples) this.setJointValue(key, interpolate(joint.valueSamples, t));
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
// -- Display toggles -----------------------------------------------------
|
|
597
|
-
get showVisual() {
|
|
598
|
-
return this._showVisual;
|
|
599
|
-
}
|
|
600
|
-
set showVisual(v) {
|
|
601
|
-
this._showVisual = v;
|
|
602
|
-
this.setKindVisibility("visual", v);
|
|
603
|
-
}
|
|
604
|
-
get showCollision() {
|
|
605
|
-
return this._showCollision;
|
|
606
|
-
}
|
|
607
|
-
set showCollision(v) {
|
|
608
|
-
this._showCollision = v;
|
|
609
|
-
this.setKindVisibility("collision", v);
|
|
610
|
-
}
|
|
611
|
-
get showJointAxes() {
|
|
612
|
-
return this._showJointAxes;
|
|
613
|
-
}
|
|
614
|
-
set showJointAxes(v) {
|
|
615
|
-
this._showJointAxes = v;
|
|
616
|
-
if (v && this.jointAxesHelpers.length === 0) {
|
|
617
|
-
for (const joint of this.jointObjects.values()) {
|
|
618
|
-
const h = new THREE4.AxesHelper(this.helperSize);
|
|
619
|
-
h.name = `${joint.jointName}:axes`;
|
|
620
|
-
joint.add(h);
|
|
621
|
-
this.jointAxesHelpers.push(h);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
for (const h of this.jointAxesHelpers) h.visible = v;
|
|
625
|
-
}
|
|
626
|
-
get showLinkFrames() {
|
|
627
|
-
return this._showLinkFrames;
|
|
628
|
-
}
|
|
629
|
-
set showLinkFrames(v) {
|
|
630
|
-
this._showLinkFrames = v;
|
|
631
|
-
if (v && this.linkFrameHelpers.length === 0) {
|
|
632
|
-
for (const link of this.linkObjects.values()) {
|
|
633
|
-
const h = new THREE4.AxesHelper(this.helperSize);
|
|
634
|
-
h.name = `${link.linkName}:frame`;
|
|
635
|
-
link.add(h);
|
|
636
|
-
this.linkFrameHelpers.push(h);
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
for (const h of this.linkFrameHelpers) h.visible = v;
|
|
640
|
-
}
|
|
641
|
-
setKindVisibility(kind, visible) {
|
|
642
|
-
this.traverse((o) => {
|
|
643
|
-
if (o.userData.kind === kind) o.visible = visible;
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
};
|
|
647
|
-
function setMatrix(obj, m) {
|
|
648
|
-
obj.matrixAutoUpdate = false;
|
|
649
|
-
obj.matrix.fromArray(m);
|
|
650
|
-
obj.matrixWorldNeedsUpdate = true;
|
|
651
|
-
}
|
|
652
376
|
|
|
653
377
|
// src/three/ThreeUsdRobotLoader.ts
|
|
654
378
|
var ThreeUsdRobotLoader = class {
|
|
@@ -661,52 +385,76 @@ var ThreeUsdRobotLoader = class {
|
|
|
661
385
|
}
|
|
662
386
|
/**
|
|
663
387
|
* Fetch an asset by URL and build the robot. `.usdz` packages are unzipped and
|
|
664
|
-
* composed from their entries; everything else is sniffed for the crate
|
|
665
|
-
* and parsed as binary USDC or USDA text.
|
|
388
|
+
* composed from their entries; everything else is sniffed for the zip / crate
|
|
389
|
+
* magic and parsed as USDZ, binary USDC, or USDA text.
|
|
666
390
|
*/
|
|
667
391
|
async loadAsync(url) {
|
|
668
392
|
const bytes = await this.fetchRootBytes(url);
|
|
669
393
|
if (/\.usdz$/i.test(url)) return this.parseUsdz(bytes);
|
|
670
|
-
|
|
671
|
-
return this.parse(new TextDecoder().decode(bytes), url);
|
|
394
|
+
return this.parse(bytes, url);
|
|
672
395
|
}
|
|
673
396
|
async fetchRootBytes(url) {
|
|
674
397
|
const resolver = this.resolver;
|
|
675
398
|
if (resolver.fetchBytes) return resolver.fetchBytes(url);
|
|
676
399
|
return new TextEncoder().encode(await resolver.fetchText(url));
|
|
677
400
|
}
|
|
678
|
-
/**
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
401
|
+
/**
|
|
402
|
+
* Build a robot from in-memory content — no fetch involved. Accepts USDA
|
|
403
|
+
* source text, or an `ArrayBuffer` / typed array / `Blob` (e.g. a dropped
|
|
404
|
+
* `File`) holding USDA text, a binary crate, or a `.usdz` package; binary
|
|
405
|
+
* input is sniffed for the zip / crate magic. `baseUrl` anchors relative
|
|
406
|
+
* references, payloads, and texture paths of non-package input.
|
|
407
|
+
*/
|
|
408
|
+
async parse(data, baseUrl = "") {
|
|
409
|
+
return this.buildFromStage(await this.openSource(data, baseUrl));
|
|
685
410
|
}
|
|
686
|
-
/** Build a robot from
|
|
687
|
-
async parseUsdz(
|
|
688
|
-
|
|
689
|
-
const rootBytes = await pkg.resolver.fetchBytes(pkg.rootEntry);
|
|
690
|
-
const stage = await this.composeStageFromBytes(rootBytes, pkg.rootEntry, pkg.resolver);
|
|
691
|
-
return this.buildFromStage(stage, pkg.rootEntry, pkg.resolver);
|
|
411
|
+
/** Build a robot from a `.usdz` package (bytes, `ArrayBuffer`, or `Blob`). */
|
|
412
|
+
async parseUsdz(data) {
|
|
413
|
+
return this.buildFromStage(await this.openUsdzStage(await toBytes(data)));
|
|
692
414
|
}
|
|
693
|
-
/** Build a robot from
|
|
694
|
-
async parseCrate(
|
|
695
|
-
const stage = await this.composeStageFromBytes(
|
|
696
|
-
return this.buildFromStage(stage, baseUrl, this.resolver);
|
|
415
|
+
/** Build a robot from a binary crate (`.usdc` / binary `.usd`). */
|
|
416
|
+
async parseCrate(data, baseUrl = "") {
|
|
417
|
+
const stage = await this.composeStageFromBytes(await toBytes(data), baseUrl, this.resolver);
|
|
418
|
+
return this.buildFromStage({ stage, baseUrl, resolver: this.resolver });
|
|
697
419
|
}
|
|
698
|
-
/**
|
|
699
|
-
|
|
700
|
-
|
|
420
|
+
/**
|
|
421
|
+
* Parse + compose in-memory content (same formats as {@link parse}) into the
|
|
422
|
+
* Three.js-independent robot IR.
|
|
423
|
+
*/
|
|
424
|
+
async parseRobotDescription(data, baseUrl = "") {
|
|
425
|
+
const { stage } = await this.openSource(data, baseUrl);
|
|
701
426
|
return extractRobotDescription(stage, this.extractOptions());
|
|
702
427
|
}
|
|
703
|
-
|
|
428
|
+
/** Sniff in-memory content (text / zip / crate) and compose it into a stage. */
|
|
429
|
+
async openSource(data, baseUrl) {
|
|
430
|
+
const resolver = this.resolver;
|
|
431
|
+
if (typeof data === "string") {
|
|
432
|
+
return { stage: await this.composeStage(data, baseUrl, resolver), baseUrl, resolver };
|
|
433
|
+
}
|
|
434
|
+
const bytes = await toBytes(data);
|
|
435
|
+
if (isZip(bytes)) return this.openUsdzStage(bytes);
|
|
436
|
+
const stage = CrateReader.isCrate(bytes) ? await this.composeStageFromBytes(bytes, baseUrl, resolver) : await this.composeStage(new TextDecoder().decode(bytes), baseUrl, resolver);
|
|
437
|
+
return { stage, baseUrl, resolver };
|
|
438
|
+
}
|
|
439
|
+
async openUsdzStage(bytes) {
|
|
440
|
+
const pkg = openUsdz(bytes);
|
|
441
|
+
const rootBytes = await pkg.resolver.fetchBytes(pkg.rootEntry);
|
|
442
|
+
const stage = await this.composeStageFromBytes(rootBytes, pkg.rootEntry, pkg.resolver);
|
|
443
|
+
return { stage, baseUrl: pkg.rootEntry, resolver: pkg.resolver };
|
|
444
|
+
}
|
|
445
|
+
buildFromStage({ stage, baseUrl, resolver }) {
|
|
704
446
|
const robot = extractRobotDescription(stage, this.extractOptions());
|
|
705
447
|
const tree = buildKinematicTree(robot);
|
|
706
448
|
const robot3d = new ThreeUsdRobot(robot, tree, this.robotOptions());
|
|
707
449
|
const loadVisuals = this.options.loadVisuals ?? true;
|
|
708
450
|
const loadCollisions = this.options.loadCollisions ?? false;
|
|
709
|
-
const
|
|
451
|
+
const isStaticScene = Object.keys(robot.links).length === 0 && Object.keys(robot.joints).length === 0;
|
|
452
|
+
if (isStaticScene && this.options.loadSceneGeometry === void 0) {
|
|
453
|
+
this.options.onWarn?.(
|
|
454
|
+
"no articulation found (0 links / 0 joints); rendering the stage as static scene geometry"
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const loadScene = this.options.loadSceneGeometry ?? isStaticScene;
|
|
710
458
|
if (loadVisuals || loadCollisions || loadScene) {
|
|
711
459
|
const textureProvider = this.options.loadTextures ?? true ? createTextureProvider(resolver, baseUrl) : void 0;
|
|
712
460
|
if (loadVisuals || loadCollisions) {
|
|
@@ -741,7 +489,7 @@ var ThreeUsdRobotLoader = class {
|
|
|
741
489
|
}
|
|
742
490
|
robotOptions() {
|
|
743
491
|
return {
|
|
744
|
-
upAxisConversion: this.options.upAxisConversion ?? "auto",
|
|
492
|
+
...this.options.worldUp ? { worldUp: this.options.worldUp } : { upAxisConversion: this.options.upAxisConversion ?? "auto" },
|
|
745
493
|
...this.options.clampJointLimits !== void 0 ? { clampJointLimits: this.options.clampJointLimits } : {},
|
|
746
494
|
...this.options.unitScale !== void 0 ? { unitScale: this.options.unitScale } : {},
|
|
747
495
|
...this.options.applyDriveTargetsAsInitialPose !== void 0 ? { applyInitialPose: this.options.applyDriveTargetsAsInitialPose } : {}
|
|
@@ -755,6 +503,6 @@ var ThreeUsdRobotLoader = class {
|
|
|
755
503
|
}
|
|
756
504
|
};
|
|
757
505
|
|
|
758
|
-
export {
|
|
759
|
-
//# sourceMappingURL=chunk-
|
|
760
|
-
//# sourceMappingURL=chunk-
|
|
506
|
+
export { ThreeUsdRobotLoader, bindRobotMeshes, bindSceneMeshes, buildGprimGeometry, buildMeshGeometry, buildMeshMaterial, createTextureProvider };
|
|
507
|
+
//# sourceMappingURL=chunk-JHOX3VUW.js.map
|
|
508
|
+
//# sourceMappingURL=chunk-JHOX3VUW.js.map
|