three-usd-robot 0.4.0 → 0.6.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 +136 -35
- package/dist/AssetResolver-CpIJNgWZ.d.ts +29 -0
- package/dist/{ThreeUsdRobot-Deoh7zam.d.ts → ThreeUsdRobot-CajXJqYU.d.ts} +1 -1
- package/dist/ThreeUsdRobotLoader-DrMMLzo-.d.ts +67 -0
- package/dist/{buildKinematicTree-CZjBMA1I.d.ts → buildKinematicTree-wtZPzy0B.d.ts} +53 -16
- package/dist/{chunk-FYVZ7YPW.js → chunk-4MEZM763.js} +3153 -2618
- package/dist/chunk-4MEZM763.js.map +1 -0
- package/dist/chunk-JZMCABQO.js +864 -0
- package/dist/chunk-JZMCABQO.js.map +1 -0
- package/dist/chunk-YVWSO27W.js +709 -0
- package/dist/chunk-YVWSO27W.js.map +1 -0
- package/dist/core.d.ts +297 -134
- package/dist/core.js +2 -1
- package/dist/extras.d.ts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.d.ts +229 -83
- package/dist/index.js +271 -631
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +72 -0
- package/dist/react.js +92 -0
- package/dist/react.js.map +1 -0
- package/package.json +22 -1
- package/dist/chunk-FYVZ7YPW.js.map +0 -1
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
import { buildKinematicTree, multiply, invert, identity4, ARTICULATION_ROOT_API, Quat, COLLISION_API, MESH_COLLISION_API, PHYSICS_MATERIAL_API, AssetPath, toUsdMatrix, driveKindFor, jointValueFromSI, decomposeRigid, resolveBoundMaterial, computeLocalTransform, RIGID_BODY_API, MASS_API } from './chunk-4MEZM763.js';
|
|
2
|
+
import { strToU8, zipSync } from 'fflate';
|
|
3
|
+
|
|
4
|
+
// src/version.ts
|
|
5
|
+
var PACKAGE_NAME = "three-usd-robot";
|
|
6
|
+
var VERSION = "0.0.0";
|
|
7
|
+
|
|
8
|
+
// src/export/exportRobot.ts
|
|
9
|
+
var PHYSX_ARTICULATION_API = "PhysxArticulationAPI";
|
|
10
|
+
var JOINT_SCHEMA_BY_TYPE = {
|
|
11
|
+
fixed: "PhysicsFixedJoint",
|
|
12
|
+
revolute: "PhysicsRevoluteJoint",
|
|
13
|
+
continuous: "PhysicsRevoluteJoint",
|
|
14
|
+
prismatic: "PhysicsPrismaticJoint"
|
|
15
|
+
};
|
|
16
|
+
function exportRobotUsda(desc, options = {}) {
|
|
17
|
+
const warn = (m) => options.onWarn?.(m);
|
|
18
|
+
const tree = buildKinematicTree(desc, { onWarn: warn });
|
|
19
|
+
const robotName = sanitizeName(options.robotName ?? desc.name ?? "robot");
|
|
20
|
+
const alloc = nameAllocator(warn);
|
|
21
|
+
const linkNames = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const key of Object.keys(desc.links)) linkNames.set(key, alloc(key, `link "${key}"`));
|
|
23
|
+
const jointNames = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const key of Object.keys(desc.joints)) jointNames.set(key, alloc(key, `joint "${key}"`));
|
|
25
|
+
const linkPath = (key) => `/${robotName}/${linkNames.get(key)}`;
|
|
26
|
+
const authoredWorld = (key) => desc.links[key]?.worldTransform ?? identity4();
|
|
27
|
+
const worldByLink = /* @__PURE__ */ new Map();
|
|
28
|
+
for (const key of tree.order) {
|
|
29
|
+
const node = tree.nodes[key];
|
|
30
|
+
if (node.parent === null || node.jointToParent === null) {
|
|
31
|
+
const rootJoint = tree.rootJoint ? desc.joints[tree.rootJoint] : void 0;
|
|
32
|
+
worldByLink.set(
|
|
33
|
+
key,
|
|
34
|
+
rootJoint ? multiply(rootJoint.jointFrame0, invert(rootJoint.jointFrame1)) : authoredWorld(key)
|
|
35
|
+
);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const joint = desc.joints[node.jointToParent];
|
|
39
|
+
const parentWorld = worldByLink.get(node.parent) ?? identity4();
|
|
40
|
+
worldByLink.set(
|
|
41
|
+
key,
|
|
42
|
+
multiply(multiply(parentWorld, joint.jointFrame0), invert(joint.jointFrame1))
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
for (const key of tree.isolatedLinks) worldByLink.set(key, authoredWorld(key));
|
|
46
|
+
const articulationLinks = new Set(desc.articulationRoots ?? []);
|
|
47
|
+
const meshesByLink = /* @__PURE__ */ new Map();
|
|
48
|
+
const materials = /* @__PURE__ */ new Map();
|
|
49
|
+
const physicsMaterials = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const [key, link] of Object.entries(desc.links)) {
|
|
51
|
+
const meshes = options.geometry?.(key, link) ?? [];
|
|
52
|
+
meshesByLink.set(key, meshes);
|
|
53
|
+
for (const mesh of meshes) {
|
|
54
|
+
if (mesh.material && !materials.has(mesh.material.name)) {
|
|
55
|
+
materials.set(mesh.material.name, mesh.material);
|
|
56
|
+
}
|
|
57
|
+
if (mesh.physicsMaterial && !physicsMaterials.has(mesh.physicsMaterial.name)) {
|
|
58
|
+
physicsMaterials.set(mesh.physicsMaterial.name, mesh.physicsMaterial);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const looksName = materials.size > 0 ? alloc("Looks") : void 0;
|
|
63
|
+
const materialAlloc = nameAllocator();
|
|
64
|
+
const materialNames = /* @__PURE__ */ new Map();
|
|
65
|
+
for (const name of materials.keys()) materialNames.set(name, materialAlloc(name));
|
|
66
|
+
const materialPath = (name) => looksName ? `/${robotName}/${looksName}/${materialNames.get(name)}` : void 0;
|
|
67
|
+
const physicsScopeName = physicsMaterials.size > 0 ? alloc("PhysicsMaterials") : void 0;
|
|
68
|
+
const physicsMaterialAlloc = nameAllocator();
|
|
69
|
+
const physicsMaterialNames = /* @__PURE__ */ new Map();
|
|
70
|
+
for (const name of physicsMaterials.keys()) {
|
|
71
|
+
physicsMaterialNames.set(name, physicsMaterialAlloc(name));
|
|
72
|
+
}
|
|
73
|
+
const physicsMaterialPath = (name) => physicsScopeName ? `/${robotName}/${physicsScopeName}/${physicsMaterialNames.get(name)}` : void 0;
|
|
74
|
+
const isaac = options.isaacRobotSchema === true;
|
|
75
|
+
const children = [];
|
|
76
|
+
for (const [key, link] of Object.entries(desc.links)) {
|
|
77
|
+
const apiSchemas = [RIGID_BODY_API];
|
|
78
|
+
if (articulationLinks.has(key)) apiSchemas.push(ARTICULATION_ROOT_API);
|
|
79
|
+
if (link.inertial) apiSchemas.push(MASS_API);
|
|
80
|
+
if (isaac) apiSchemas.push("IsaacLinkAPI");
|
|
81
|
+
const selfCollision = articulationLinks.has(key) ? options.enabledSelfCollisions : void 0;
|
|
82
|
+
if (selfCollision !== void 0) apiSchemas.push(PHYSX_ARTICULATION_API);
|
|
83
|
+
children.push(
|
|
84
|
+
buildLinkPrim({
|
|
85
|
+
name: linkNames.get(key),
|
|
86
|
+
world: worldByLink.get(key) ?? identity4(),
|
|
87
|
+
apiSchemas,
|
|
88
|
+
meshes: meshesByLink.get(key) ?? [],
|
|
89
|
+
materialPath,
|
|
90
|
+
physicsMaterialPath,
|
|
91
|
+
warn,
|
|
92
|
+
...link.inertial ? { inertial: link.inertial } : {},
|
|
93
|
+
...selfCollision !== void 0 ? { selfCollision } : {}
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const emittedJoints = /* @__PURE__ */ new Set();
|
|
98
|
+
for (const [key, joint] of Object.entries(desc.joints)) {
|
|
99
|
+
if (!desc.links[joint.child] || joint.parent !== "" && !desc.links[joint.parent]) {
|
|
100
|
+
warn(`joint "${key}" references an unknown link; skipped`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
emittedJoints.add(key);
|
|
104
|
+
children.push(buildJointPrim(jointNames.get(key), key, joint, linkPath, isaac, warn));
|
|
105
|
+
}
|
|
106
|
+
if (looksName) {
|
|
107
|
+
children.push(buildLooksPrim(looksName, materials, materialNames, materialPath));
|
|
108
|
+
}
|
|
109
|
+
if (physicsScopeName) {
|
|
110
|
+
children.push(
|
|
111
|
+
buildPhysicsMaterialsPrim(physicsScopeName, physicsMaterials, physicsMaterialNames)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const rootApiSchemas = [];
|
|
115
|
+
const rootProperties = [];
|
|
116
|
+
if (articulationLinks.size === 0) {
|
|
117
|
+
rootApiSchemas.push(ARTICULATION_ROOT_API);
|
|
118
|
+
if (options.enabledSelfCollisions !== void 0) {
|
|
119
|
+
rootApiSchemas.push(PHYSX_ARTICULATION_API);
|
|
120
|
+
rootProperties.push(
|
|
121
|
+
attr("physxArticulation:enabledSelfCollisions", "bool", options.enabledSelfCollisions)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (isaac) {
|
|
126
|
+
rootApiSchemas.push("IsaacRobotAPI");
|
|
127
|
+
const orderedLinks = [...tree.order, ...tree.isolatedLinks];
|
|
128
|
+
const orderedJoints = [];
|
|
129
|
+
const pushJoint = (key) => {
|
|
130
|
+
if (key && emittedJoints.has(key) && !orderedJoints.includes(key)) orderedJoints.push(key);
|
|
131
|
+
};
|
|
132
|
+
pushJoint(tree.rootJoint);
|
|
133
|
+
for (const linkKey of tree.order) pushJoint(tree.nodes[linkKey]?.jointToParent);
|
|
134
|
+
for (const jointKey of tree.loopJoints) pushJoint(jointKey);
|
|
135
|
+
for (const jointKey of Object.keys(desc.joints)) pushJoint(jointKey);
|
|
136
|
+
rootProperties.push(rel("isaac:physics:robotLinks", orderedLinks.map(linkPath)));
|
|
137
|
+
rootProperties.push(
|
|
138
|
+
rel(
|
|
139
|
+
"isaac:physics:robotJoints",
|
|
140
|
+
orderedJoints.map((key) => `/${robotName}/${jointNames.get(key)}`)
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const root = {
|
|
145
|
+
specifier: "def",
|
|
146
|
+
typeName: "Xform",
|
|
147
|
+
name: robotName,
|
|
148
|
+
metadata: rootApiSchemas.length > 0 ? { apiSchemas: rootApiSchemas } : {},
|
|
149
|
+
properties: rootProperties,
|
|
150
|
+
children,
|
|
151
|
+
line: 0
|
|
152
|
+
};
|
|
153
|
+
const metadata = {
|
|
154
|
+
defaultPrim: robotName,
|
|
155
|
+
metersPerUnit: desc.metersPerUnit,
|
|
156
|
+
upAxis: desc.upAxis,
|
|
157
|
+
...desc.timeCodesPerSecond !== void 0 ? { timeCodesPerSecond: desc.timeCodesPerSecond } : {},
|
|
158
|
+
...desc.startTimeCode !== void 0 ? { startTimeCode: desc.startTimeCode } : {},
|
|
159
|
+
...desc.endTimeCode !== void 0 ? { endTimeCode: desc.endTimeCode } : {}
|
|
160
|
+
};
|
|
161
|
+
return { version: "1.0", metadata, prims: [root] };
|
|
162
|
+
}
|
|
163
|
+
function buildLinkPrim(args) {
|
|
164
|
+
const meshAlloc = nameAllocator();
|
|
165
|
+
const properties = transformProps(args.world);
|
|
166
|
+
if (args.inertial) properties.push(...massProps(args.inertial, args.name, args.warn));
|
|
167
|
+
if (args.selfCollision !== void 0) {
|
|
168
|
+
properties.push(attr("physxArticulation:enabledSelfCollisions", "bool", args.selfCollision));
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
specifier: "def",
|
|
172
|
+
typeName: "Xform",
|
|
173
|
+
name: args.name,
|
|
174
|
+
metadata: { apiSchemas: args.apiSchemas },
|
|
175
|
+
properties,
|
|
176
|
+
children: args.meshes.map(
|
|
177
|
+
(mesh) => buildMeshPrim(mesh, meshAlloc(mesh.name), args.materialPath, args.physicsMaterialPath)
|
|
178
|
+
),
|
|
179
|
+
line: 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function massProps(inertial, linkName, warn) {
|
|
183
|
+
const out = [];
|
|
184
|
+
if (inertial.mass !== void 0) out.push(attr("physics:mass", "float", inertial.mass));
|
|
185
|
+
if (inertial.density !== void 0) out.push(attr("physics:density", "float", inertial.density));
|
|
186
|
+
if (inertial.centerOfMass) {
|
|
187
|
+
out.push(attr("physics:centerOfMass", "point3f", inertial.centerOfMass));
|
|
188
|
+
}
|
|
189
|
+
if (inertial.diagonalInertia) {
|
|
190
|
+
out.push(attr("physics:diagonalInertia", "float3", inertial.diagonalInertia));
|
|
191
|
+
out.push(attr("physics:principalAxes", "quatf", inertial.principalAxes ?? Quat.identity()));
|
|
192
|
+
} else if (inertial.principalAxes) {
|
|
193
|
+
warn(`link "${linkName}": principalAxes without diagonalInertia is meaningless; dropped`);
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
function buildMeshPrim(mesh, name, materialPath, physicsMaterialPath) {
|
|
198
|
+
const properties = [];
|
|
199
|
+
if (mesh.transform) properties.push(...transformProps(mesh.transform));
|
|
200
|
+
properties.push(
|
|
201
|
+
arrayAttr("faceVertexCounts", "int", mesh.faceVertexCounts),
|
|
202
|
+
arrayAttr("faceVertexIndices", "int", mesh.faceVertexIndices),
|
|
203
|
+
arrayAttr("points", "point3f", mesh.points)
|
|
204
|
+
);
|
|
205
|
+
if (mesh.normals) properties.push(arrayAttr("normals", "normal3f", mesh.normals));
|
|
206
|
+
if (mesh.st) {
|
|
207
|
+
properties.push(arrayAttr("primvars:st", "texCoord2f", mesh.st, { interpolation: "vertex" }));
|
|
208
|
+
}
|
|
209
|
+
if (mesh.displayColor) {
|
|
210
|
+
properties.push(arrayAttr("primvars:displayColor", "color3f", [mesh.displayColor]));
|
|
211
|
+
}
|
|
212
|
+
if (mesh.doubleSided) properties.push(attr("doubleSided", "bool", true));
|
|
213
|
+
properties.push(uniformToken("subdivisionScheme", "none"));
|
|
214
|
+
const apiSchemas = [];
|
|
215
|
+
if (mesh.kind === "collision") {
|
|
216
|
+
apiSchemas.push(COLLISION_API);
|
|
217
|
+
properties.push(uniformToken("purpose", "guide"));
|
|
218
|
+
if (mesh.collisionApproximation) {
|
|
219
|
+
apiSchemas.push(MESH_COLLISION_API);
|
|
220
|
+
properties.push(uniformToken("physics:approximation", mesh.collisionApproximation));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const binding = mesh.material ? materialPath(mesh.material.name) : void 0;
|
|
224
|
+
if (binding) properties.push(rel("material:binding", [binding]));
|
|
225
|
+
const physicsBinding = mesh.kind === "collision" && mesh.physicsMaterial ? physicsMaterialPath(mesh.physicsMaterial.name) : void 0;
|
|
226
|
+
if (physicsBinding) properties.push(rel("material:binding:physics", [physicsBinding]));
|
|
227
|
+
if (binding || physicsBinding) apiSchemas.push("MaterialBindingAPI");
|
|
228
|
+
return {
|
|
229
|
+
specifier: "def",
|
|
230
|
+
typeName: "Mesh",
|
|
231
|
+
name,
|
|
232
|
+
metadata: apiSchemas.length > 0 ? { apiSchemas } : {},
|
|
233
|
+
properties,
|
|
234
|
+
children: [],
|
|
235
|
+
line: 0
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function buildPhysicsMaterialsPrim(scopeName, materials, names) {
|
|
239
|
+
const children = [];
|
|
240
|
+
for (const [key, material] of materials) {
|
|
241
|
+
children.push(buildPhysicsMaterialPrim(names.get(key), material));
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
specifier: "def",
|
|
245
|
+
typeName: "Scope",
|
|
246
|
+
name: scopeName,
|
|
247
|
+
metadata: {},
|
|
248
|
+
properties: [],
|
|
249
|
+
children,
|
|
250
|
+
line: 0
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function buildPhysicsMaterialPrim(name, material) {
|
|
254
|
+
const properties = [];
|
|
255
|
+
if (material.staticFriction !== void 0) {
|
|
256
|
+
properties.push(attr("physics:staticFriction", "float", material.staticFriction));
|
|
257
|
+
}
|
|
258
|
+
if (material.dynamicFriction !== void 0) {
|
|
259
|
+
properties.push(attr("physics:dynamicFriction", "float", material.dynamicFriction));
|
|
260
|
+
}
|
|
261
|
+
if (material.restitution !== void 0) {
|
|
262
|
+
properties.push(attr("physics:restitution", "float", material.restitution));
|
|
263
|
+
}
|
|
264
|
+
if (material.density !== void 0) {
|
|
265
|
+
properties.push(attr("physics:density", "float", material.density));
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
specifier: "def",
|
|
269
|
+
typeName: "Material",
|
|
270
|
+
name,
|
|
271
|
+
metadata: { apiSchemas: [PHYSICS_MATERIAL_API] },
|
|
272
|
+
properties,
|
|
273
|
+
children: [],
|
|
274
|
+
line: 0
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function buildLooksPrim(looksName, materials, materialNames, materialPath) {
|
|
278
|
+
const children = [];
|
|
279
|
+
for (const [key, material] of materials) {
|
|
280
|
+
children.push(buildMaterialPrim(materialNames.get(key), material, materialPath(key)));
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
specifier: "def",
|
|
284
|
+
typeName: "Scope",
|
|
285
|
+
name: looksName,
|
|
286
|
+
metadata: {},
|
|
287
|
+
properties: [],
|
|
288
|
+
children,
|
|
289
|
+
line: 0
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
var TEXTURE_WIRING = {
|
|
293
|
+
color: {
|
|
294
|
+
input: "inputs:diffuseColor",
|
|
295
|
+
inputType: "color3f",
|
|
296
|
+
output: "outputs:rgb",
|
|
297
|
+
outputType: "float3",
|
|
298
|
+
colorSpace: "sRGB"
|
|
299
|
+
},
|
|
300
|
+
emissive: {
|
|
301
|
+
input: "inputs:emissiveColor",
|
|
302
|
+
inputType: "color3f",
|
|
303
|
+
output: "outputs:rgb",
|
|
304
|
+
outputType: "float3",
|
|
305
|
+
colorSpace: "sRGB"
|
|
306
|
+
},
|
|
307
|
+
normal: {
|
|
308
|
+
input: "inputs:normal",
|
|
309
|
+
inputType: "normal3f",
|
|
310
|
+
output: "outputs:rgb",
|
|
311
|
+
outputType: "float3",
|
|
312
|
+
colorSpace: "raw"
|
|
313
|
+
},
|
|
314
|
+
opacity: {
|
|
315
|
+
input: "inputs:opacity",
|
|
316
|
+
inputType: "float",
|
|
317
|
+
output: "outputs:a",
|
|
318
|
+
outputType: "float",
|
|
319
|
+
colorSpace: "raw"
|
|
320
|
+
},
|
|
321
|
+
roughness: {
|
|
322
|
+
input: "inputs:roughness",
|
|
323
|
+
inputType: "float",
|
|
324
|
+
output: "outputs:r",
|
|
325
|
+
outputType: "float",
|
|
326
|
+
colorSpace: "raw"
|
|
327
|
+
},
|
|
328
|
+
metalness: {
|
|
329
|
+
input: "inputs:metallic",
|
|
330
|
+
inputType: "float",
|
|
331
|
+
output: "outputs:r",
|
|
332
|
+
outputType: "float",
|
|
333
|
+
colorSpace: "raw"
|
|
334
|
+
},
|
|
335
|
+
occlusion: {
|
|
336
|
+
input: "inputs:occlusion",
|
|
337
|
+
inputType: "float",
|
|
338
|
+
output: "outputs:r",
|
|
339
|
+
outputType: "float",
|
|
340
|
+
colorSpace: "raw"
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
function buildMaterialPrim(name, material, selfPath) {
|
|
344
|
+
const textures = material.textures ?? {};
|
|
345
|
+
const shaderProps = [uniformToken("info:id", "UsdPreviewSurface")];
|
|
346
|
+
const input = (channel, value) => {
|
|
347
|
+
const wiring = TEXTURE_WIRING[channel];
|
|
348
|
+
const texturePath = textures[channel];
|
|
349
|
+
if (value === void 0 && texturePath === void 0) return;
|
|
350
|
+
const spec = attr(wiring.input, wiring.inputType, value);
|
|
351
|
+
if (texturePath !== void 0) {
|
|
352
|
+
spec.connections = [`${selfPath}/${channel}Texture.${wiring.output}`];
|
|
353
|
+
}
|
|
354
|
+
shaderProps.push(spec);
|
|
355
|
+
};
|
|
356
|
+
input("color", material.diffuseColor);
|
|
357
|
+
input("metalness", material.metallic);
|
|
358
|
+
input("roughness", material.roughness);
|
|
359
|
+
input("opacity", material.opacity);
|
|
360
|
+
input("emissive", material.emissiveColor);
|
|
361
|
+
input("normal");
|
|
362
|
+
input("occlusion");
|
|
363
|
+
shaderProps.push(attr("outputs:surface", "token", void 0));
|
|
364
|
+
const children = [shaderPrim("PreviewSurface", shaderProps)];
|
|
365
|
+
const textureEntries = Object.entries(textures);
|
|
366
|
+
if (textureEntries.length > 0) {
|
|
367
|
+
children.push(
|
|
368
|
+
shaderPrim("stReader", [
|
|
369
|
+
uniformToken("info:id", "UsdPrimvarReader_float2"),
|
|
370
|
+
attr("inputs:varname", "string", "st"),
|
|
371
|
+
attr("outputs:result", "float2", void 0)
|
|
372
|
+
])
|
|
373
|
+
);
|
|
374
|
+
for (const [channel, path] of textureEntries) {
|
|
375
|
+
children.push(buildUvTexturePrim(channel, path, selfPath));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const surfaceConnect = attr("outputs:surface", "token", void 0);
|
|
379
|
+
surfaceConnect.connections = [`${selfPath}/PreviewSurface.outputs:surface`];
|
|
380
|
+
return {
|
|
381
|
+
specifier: "def",
|
|
382
|
+
typeName: "Material",
|
|
383
|
+
name,
|
|
384
|
+
metadata: {},
|
|
385
|
+
properties: [surfaceConnect],
|
|
386
|
+
children,
|
|
387
|
+
line: 0
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function buildUvTexturePrim(channel, path, selfPath) {
|
|
391
|
+
const wiring = TEXTURE_WIRING[channel];
|
|
392
|
+
const st = attr("inputs:st", "float2", void 0);
|
|
393
|
+
st.connections = [`${selfPath}/stReader.outputs:result`];
|
|
394
|
+
return shaderPrim(`${channel}Texture`, [
|
|
395
|
+
uniformToken("info:id", "UsdUVTexture"),
|
|
396
|
+
attr("inputs:file", "asset", new AssetPath(path)),
|
|
397
|
+
uniformToken("inputs:sourceColorSpace", wiring.colorSpace),
|
|
398
|
+
st,
|
|
399
|
+
attr(wiring.output, wiring.outputType, void 0)
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
402
|
+
function shaderPrim(name, properties) {
|
|
403
|
+
return {
|
|
404
|
+
specifier: "def",
|
|
405
|
+
typeName: "Shader",
|
|
406
|
+
name,
|
|
407
|
+
metadata: {},
|
|
408
|
+
properties,
|
|
409
|
+
children: [],
|
|
410
|
+
line: 0
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function transformProps(m) {
|
|
414
|
+
return [
|
|
415
|
+
attr("xformOp:transform", "matrix4d", toUsdMatrix(m)),
|
|
416
|
+
arrayAttr("xformOpOrder", "token", ["xformOp:transform"], void 0, "uniform")
|
|
417
|
+
];
|
|
418
|
+
}
|
|
419
|
+
function buildJointPrim(name, key, joint, linkPath, isaac, warn) {
|
|
420
|
+
const kind = driveKindFor(joint.type);
|
|
421
|
+
const angular = kind === "angular";
|
|
422
|
+
const properties = [];
|
|
423
|
+
const apiSchemas = [];
|
|
424
|
+
if (joint.parent !== "") properties.push(rel("physics:body0", [linkPath(joint.parent)]));
|
|
425
|
+
properties.push(rel("physics:body1", [linkPath(joint.child)]));
|
|
426
|
+
if (joint.type !== "fixed") properties.push(uniformToken("physics:axis", joint.axis));
|
|
427
|
+
if (joint.lower !== void 0) {
|
|
428
|
+
properties.push(attr("physics:lowerLimit", "float", jointValueFromSI(angular, joint.lower)));
|
|
429
|
+
}
|
|
430
|
+
if (joint.upper !== void 0) {
|
|
431
|
+
properties.push(attr("physics:upperLimit", "float", jointValueFromSI(angular, joint.upper)));
|
|
432
|
+
}
|
|
433
|
+
for (const index of [0, 1]) {
|
|
434
|
+
const frame = index === 0 ? joint.jointFrame0 : joint.jointFrame1;
|
|
435
|
+
const { position, orientation, rigid } = decomposeRigid(frame);
|
|
436
|
+
if (!rigid) {
|
|
437
|
+
warn(`joint "${key}": jointFrame${index} is not rigid; scale/shear was discarded`);
|
|
438
|
+
}
|
|
439
|
+
properties.push(attr(`physics:localPos${index}`, "point3f", position));
|
|
440
|
+
properties.push(attr(`physics:localRot${index}`, "quatf", orientation));
|
|
441
|
+
}
|
|
442
|
+
if (joint.initialValue !== void 0 || joint.valueSamples) {
|
|
443
|
+
const state = attr(
|
|
444
|
+
`state:${kind}:physics:position`,
|
|
445
|
+
"float",
|
|
446
|
+
joint.initialValue !== void 0 ? jointValueFromSI(angular, joint.initialValue) : void 0
|
|
447
|
+
);
|
|
448
|
+
if (joint.valueSamples) {
|
|
449
|
+
state.timeSamples = new Map(
|
|
450
|
+
joint.valueSamples.times.map((t, i) => [
|
|
451
|
+
t,
|
|
452
|
+
jointValueFromSI(angular, joint.valueSamples.values[i] ?? 0)
|
|
453
|
+
])
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
properties.push(state);
|
|
457
|
+
apiSchemas.push(`PhysicsJointStateAPI:${kind}`);
|
|
458
|
+
}
|
|
459
|
+
if (joint.drive) {
|
|
460
|
+
const d = joint.drive;
|
|
461
|
+
if (d.targetPosition !== void 0) {
|
|
462
|
+
properties.push(
|
|
463
|
+
attr(
|
|
464
|
+
`drive:${kind}:physics:targetPosition`,
|
|
465
|
+
"float",
|
|
466
|
+
jointValueFromSI(angular, d.targetPosition)
|
|
467
|
+
)
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
if (d.stiffness !== void 0) {
|
|
471
|
+
properties.push(attr(`drive:${kind}:physics:stiffness`, "float", d.stiffness));
|
|
472
|
+
}
|
|
473
|
+
if (d.damping !== void 0) {
|
|
474
|
+
properties.push(attr(`drive:${kind}:physics:damping`, "float", d.damping));
|
|
475
|
+
}
|
|
476
|
+
if (d.maxForce !== void 0) {
|
|
477
|
+
properties.push(attr(`drive:${kind}:physics:maxForce`, "float", d.maxForce));
|
|
478
|
+
}
|
|
479
|
+
apiSchemas.push(`PhysicsDriveAPI:${kind}`);
|
|
480
|
+
}
|
|
481
|
+
if (isaac) {
|
|
482
|
+
apiSchemas.push("IsaacJointAPI");
|
|
483
|
+
if (joint.type !== "fixed") {
|
|
484
|
+
const prefix = joint.type === "prismatic" ? "Trans" : "Rot";
|
|
485
|
+
properties.push(
|
|
486
|
+
arrayAttr("isaac:physics:DofOffsetOpOrder", "token", [`${prefix}${joint.axis}`])
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
specifier: "def",
|
|
492
|
+
typeName: JOINT_SCHEMA_BY_TYPE[joint.type],
|
|
493
|
+
name,
|
|
494
|
+
metadata: apiSchemas.length > 0 ? { apiSchemas } : {},
|
|
495
|
+
properties,
|
|
496
|
+
children: [],
|
|
497
|
+
line: 0
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function sanitizeName(raw) {
|
|
501
|
+
const cleaned = raw.replace(/[^A-Za-z0-9_]/g, "_");
|
|
502
|
+
const named = cleaned.length > 0 ? cleaned : "_";
|
|
503
|
+
return /^[0-9]/.test(named) ? `_${named}` : named;
|
|
504
|
+
}
|
|
505
|
+
function nameAllocator(warn) {
|
|
506
|
+
const used = /* @__PURE__ */ new Set();
|
|
507
|
+
return (raw, label) => {
|
|
508
|
+
let name = sanitizeName(raw);
|
|
509
|
+
for (let i = 2; used.has(name); i++) name = `${sanitizeName(raw)}_${i}`;
|
|
510
|
+
used.add(name);
|
|
511
|
+
if (name !== raw && label && warn) warn(`${label} exported as prim "${name}"`);
|
|
512
|
+
return name;
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function attr(name, typeName, value, metadata = {}) {
|
|
516
|
+
return {
|
|
517
|
+
kind: "attribute",
|
|
518
|
+
name,
|
|
519
|
+
typeName,
|
|
520
|
+
isArray: false,
|
|
521
|
+
variability: "varying",
|
|
522
|
+
custom: false,
|
|
523
|
+
...value !== void 0 ? { value } : {},
|
|
524
|
+
metadata,
|
|
525
|
+
line: 0
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function arrayAttr(name, typeName, value, metadata = {}, variability = "varying") {
|
|
529
|
+
return {
|
|
530
|
+
kind: "attribute",
|
|
531
|
+
name,
|
|
532
|
+
typeName,
|
|
533
|
+
isArray: true,
|
|
534
|
+
variability,
|
|
535
|
+
custom: false,
|
|
536
|
+
value,
|
|
537
|
+
metadata,
|
|
538
|
+
line: 0
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function uniformToken(name, value) {
|
|
542
|
+
return {
|
|
543
|
+
kind: "attribute",
|
|
544
|
+
name,
|
|
545
|
+
typeName: "token",
|
|
546
|
+
isArray: false,
|
|
547
|
+
variability: "uniform",
|
|
548
|
+
custom: false,
|
|
549
|
+
value,
|
|
550
|
+
metadata: {},
|
|
551
|
+
line: 0
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function rel(name, targets) {
|
|
555
|
+
return {
|
|
556
|
+
kind: "relationship",
|
|
557
|
+
name,
|
|
558
|
+
custom: false,
|
|
559
|
+
listOp: "explicit",
|
|
560
|
+
targets,
|
|
561
|
+
metadata: {},
|
|
562
|
+
line: 0
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/export/GeometryProvider.ts
|
|
567
|
+
function stageGeometryProvider(stage) {
|
|
568
|
+
return (_linkKey, link) => {
|
|
569
|
+
const linkPrim = stage.GetPrimAtPath(link.primPath);
|
|
570
|
+
if (!linkPrim) return [];
|
|
571
|
+
const collisionSet = new Set(link.collisionPrims ?? []);
|
|
572
|
+
const out = [];
|
|
573
|
+
for (const path of link.visualPrims) {
|
|
574
|
+
if (collisionSet.has(path)) continue;
|
|
575
|
+
const mesh = readMesh(stage, linkPrim, path, "visual");
|
|
576
|
+
if (mesh) out.push(mesh);
|
|
577
|
+
}
|
|
578
|
+
for (const path of link.collisionPrims ?? []) {
|
|
579
|
+
const mesh = readMesh(stage, linkPrim, path, "collision");
|
|
580
|
+
if (mesh) out.push(mesh);
|
|
581
|
+
}
|
|
582
|
+
return out;
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function readMesh(stage, linkPrim, meshPath, kind) {
|
|
586
|
+
const prim = stage.GetPrimAtPath(meshPath);
|
|
587
|
+
if (!prim) return null;
|
|
588
|
+
const points = prim.GetAttribute("points").Get();
|
|
589
|
+
if (!isVec3Array(points) || points.length === 0) return null;
|
|
590
|
+
const counts = prim.GetAttribute("faceVertexCounts").Get();
|
|
591
|
+
const indices = prim.GetAttribute("faceVertexIndices").Get();
|
|
592
|
+
const normals = prim.GetAttribute("normals").Get();
|
|
593
|
+
const st = prim.GetAttribute("primvars:st").Get();
|
|
594
|
+
const displayColor = prim.GetAttribute("primvars:displayColor").Get();
|
|
595
|
+
const mesh = {
|
|
596
|
+
name: prim.GetName(),
|
|
597
|
+
kind,
|
|
598
|
+
points: points.slice(),
|
|
599
|
+
faceVertexCounts: isNumberArray(counts) ? counts.slice() : [],
|
|
600
|
+
faceVertexIndices: isNumberArray(indices) ? indices.slice() : []
|
|
601
|
+
};
|
|
602
|
+
if (isVec3Array(normals) && normals.length === points.length) mesh.normals = normals.slice();
|
|
603
|
+
if (isVec2Array(st) && st.length === points.length) mesh.st = st.slice();
|
|
604
|
+
if (isVec3Array(displayColor) && displayColor[0]) mesh.displayColor = displayColor[0];
|
|
605
|
+
if (prim.GetAttribute("doubleSided").Get() === true) mesh.doubleSided = true;
|
|
606
|
+
const transform = relativeTransform(linkPrim, prim);
|
|
607
|
+
if (!isIdentity(transform)) mesh.transform = transform;
|
|
608
|
+
const material = readBoundMaterial(stage, prim);
|
|
609
|
+
if (material) mesh.material = material;
|
|
610
|
+
if (kind === "collision") {
|
|
611
|
+
const approximation = prim.GetAttribute("physics:approximation").Get();
|
|
612
|
+
if (typeof approximation === "string" && APPROXIMATIONS.has(approximation)) {
|
|
613
|
+
mesh.collisionApproximation = approximation;
|
|
614
|
+
}
|
|
615
|
+
const physicsMaterial = readBoundPhysicsMaterial(stage, prim);
|
|
616
|
+
if (physicsMaterial) mesh.physicsMaterial = physicsMaterial;
|
|
617
|
+
}
|
|
618
|
+
return mesh;
|
|
619
|
+
}
|
|
620
|
+
var APPROXIMATIONS = /* @__PURE__ */ new Set([
|
|
621
|
+
"none",
|
|
622
|
+
"convexHull",
|
|
623
|
+
"convexDecomposition",
|
|
624
|
+
"boundingSphere",
|
|
625
|
+
"boundingCube",
|
|
626
|
+
"meshSimplification"
|
|
627
|
+
]);
|
|
628
|
+
function readBoundPhysicsMaterial(stage, prim) {
|
|
629
|
+
const target = firstBindingTarget(prim, "material:binding:physics");
|
|
630
|
+
if (!target) return void 0;
|
|
631
|
+
const materialPrim = stage.GetPrimAtPath(target);
|
|
632
|
+
if (!materialPrim) return void 0;
|
|
633
|
+
const out = {
|
|
634
|
+
name: target.split("/").filter(Boolean).pop() ?? "PhysicsMaterial"
|
|
635
|
+
};
|
|
636
|
+
const read = (attr2) => {
|
|
637
|
+
const v = materialPrim.GetAttribute(attr2).Get();
|
|
638
|
+
return typeof v === "number" ? v : void 0;
|
|
639
|
+
};
|
|
640
|
+
const staticFriction = read("physics:staticFriction");
|
|
641
|
+
if (staticFriction !== void 0) out.staticFriction = staticFriction;
|
|
642
|
+
const dynamicFriction = read("physics:dynamicFriction");
|
|
643
|
+
if (dynamicFriction !== void 0) out.dynamicFriction = dynamicFriction;
|
|
644
|
+
const restitution = read("physics:restitution");
|
|
645
|
+
if (restitution !== void 0) out.restitution = restitution;
|
|
646
|
+
const density = read("physics:density");
|
|
647
|
+
if (density !== void 0) out.density = density;
|
|
648
|
+
return Object.keys(out).length > 1 ? out : void 0;
|
|
649
|
+
}
|
|
650
|
+
function firstBindingTarget(prim, relName) {
|
|
651
|
+
let p = prim;
|
|
652
|
+
while (p) {
|
|
653
|
+
const targets = p.GetRelationship(relName).GetTargets();
|
|
654
|
+
if (targets.length > 0) return targets[0];
|
|
655
|
+
p = p.GetParent();
|
|
656
|
+
}
|
|
657
|
+
return void 0;
|
|
658
|
+
}
|
|
659
|
+
function readBoundMaterial(stage, prim) {
|
|
660
|
+
const bound = resolveBoundMaterial(stage, prim);
|
|
661
|
+
if (!bound) return void 0;
|
|
662
|
+
const material = { name: boundMaterialName(prim) ?? "Material" };
|
|
663
|
+
if (bound.color) material.diffuseColor = bound.color;
|
|
664
|
+
if (bound.metalness !== void 0) material.metallic = bound.metalness;
|
|
665
|
+
if (bound.roughness !== void 0) material.roughness = bound.roughness;
|
|
666
|
+
if (bound.opacity !== void 0) material.opacity = bound.opacity;
|
|
667
|
+
if (bound.emissiveColor) material.emissiveColor = bound.emissiveColor;
|
|
668
|
+
const textures = {};
|
|
669
|
+
if (bound.colorTexture) textures.color = bound.colorTexture.path;
|
|
670
|
+
if (bound.opacityTexture) textures.opacity = bound.opacityTexture.path;
|
|
671
|
+
if (bound.normalTexture) textures.normal = bound.normalTexture.path;
|
|
672
|
+
if (bound.roughnessTexture) textures.roughness = bound.roughnessTexture.path;
|
|
673
|
+
if (bound.metalnessTexture) textures.metalness = bound.metalnessTexture.path;
|
|
674
|
+
if (bound.occlusionTexture) textures.occlusion = bound.occlusionTexture.path;
|
|
675
|
+
if (bound.emissiveTexture) textures.emissive = bound.emissiveTexture.path;
|
|
676
|
+
if (Object.keys(textures).length > 0) material.textures = textures;
|
|
677
|
+
return Object.keys(material).length > 1 ? material : void 0;
|
|
678
|
+
}
|
|
679
|
+
function boundMaterialName(prim) {
|
|
680
|
+
let p = prim;
|
|
681
|
+
while (p) {
|
|
682
|
+
const targets = p.GetRelationship("material:binding").GetTargets();
|
|
683
|
+
if (targets.length > 0) return targets[0].split("/").filter(Boolean).pop();
|
|
684
|
+
p = p.GetParent();
|
|
685
|
+
}
|
|
686
|
+
return void 0;
|
|
687
|
+
}
|
|
688
|
+
function relativeTransform(linkPrim, meshPrim) {
|
|
689
|
+
const chain = [];
|
|
690
|
+
let p = meshPrim;
|
|
691
|
+
const stop = linkPrim.GetPath();
|
|
692
|
+
while (p && p.GetPath() !== stop) {
|
|
693
|
+
chain.push(p);
|
|
694
|
+
p = p.GetParent();
|
|
695
|
+
}
|
|
696
|
+
chain.reverse();
|
|
697
|
+
let m = identity4();
|
|
698
|
+
for (const prim of chain) {
|
|
699
|
+
m = multiply(m, computeLocalTransform(prim).matrix);
|
|
700
|
+
}
|
|
701
|
+
return m;
|
|
702
|
+
}
|
|
703
|
+
function isIdentity(m) {
|
|
704
|
+
for (let i = 0; i < 16; i++) {
|
|
705
|
+
if (m[i] !== (i % 5 === 0 ? 1 : 0)) return false;
|
|
706
|
+
}
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
function isNumberArray(v) {
|
|
710
|
+
return Array.isArray(v) && v.every((n) => typeof n === "number");
|
|
711
|
+
}
|
|
712
|
+
function isVec3Array(v) {
|
|
713
|
+
return Array.isArray(v) && v.every((e) => Array.isArray(e) && e.length === 3);
|
|
714
|
+
}
|
|
715
|
+
function isVec2Array(v) {
|
|
716
|
+
return Array.isArray(v) && v.every((e) => Array.isArray(e) && e.length === 2);
|
|
717
|
+
}
|
|
718
|
+
var USD_ENTRY = /\.(usda|usdc|usd)$/i;
|
|
719
|
+
var PAD_EXTRA_ID = 12345;
|
|
720
|
+
var LOCAL_HEADER_SIZE = 30;
|
|
721
|
+
var EXTRA_HEADER_SIZE = 4;
|
|
722
|
+
function writeUsdz(entries) {
|
|
723
|
+
const names = Object.keys(entries);
|
|
724
|
+
const root = names.find((n) => USD_ENTRY.test(n));
|
|
725
|
+
if (!root) {
|
|
726
|
+
throw new Error("writeUsdz: entries must include a root USD layer (.usda/.usdc/.usd)");
|
|
727
|
+
}
|
|
728
|
+
const ordered = [root, ...names.filter((n) => n !== root)];
|
|
729
|
+
const files = {};
|
|
730
|
+
let offset = 0;
|
|
731
|
+
for (const name of ordered) {
|
|
732
|
+
const raw = entries[name];
|
|
733
|
+
const data = typeof raw === "string" ? strToU8(raw) : raw;
|
|
734
|
+
const nameLength = strToU8(name).length;
|
|
735
|
+
const dataStart = offset + LOCAL_HEADER_SIZE + nameLength;
|
|
736
|
+
if ((dataStart & 63) === 0) {
|
|
737
|
+
files[name] = data;
|
|
738
|
+
offset = dataStart + data.length;
|
|
739
|
+
} else {
|
|
740
|
+
const padLength = 64 - (dataStart + EXTRA_HEADER_SIZE & 63) & 63;
|
|
741
|
+
files[name] = [data, { extra: { [PAD_EXTRA_ID]: new Uint8Array(padLength) } }];
|
|
742
|
+
offset = dataStart + EXTRA_HEADER_SIZE + padLength + data.length;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return zipSync(files, { level: 0 });
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/robot/RobotValidator.ts
|
|
749
|
+
function validateRobotDescription(desc, options = {}) {
|
|
750
|
+
const issues = [];
|
|
751
|
+
const add = (severity, code, message, subject) => issues.push({ severity, code, message, ...subject !== void 0 ? { subject } : {} });
|
|
752
|
+
if (Object.keys(desc.links).length === 0) {
|
|
753
|
+
add("error", "no-links", "robot has no links");
|
|
754
|
+
}
|
|
755
|
+
for (const [key, joint] of Object.entries(desc.joints)) {
|
|
756
|
+
if (joint.parent !== "" && !desc.links[joint.parent]) {
|
|
757
|
+
add(
|
|
758
|
+
"error",
|
|
759
|
+
"unknown-link",
|
|
760
|
+
`joint "${key}" references unknown parent "${joint.parent}"`,
|
|
761
|
+
key
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
if (!desc.links[joint.child]) {
|
|
765
|
+
add("error", "unknown-link", `joint "${key}" references unknown child "${joint.child}"`, key);
|
|
766
|
+
}
|
|
767
|
+
if (joint.lower !== void 0 && joint.upper !== void 0 && joint.lower > joint.upper) {
|
|
768
|
+
add(
|
|
769
|
+
"error",
|
|
770
|
+
"invalid-limits",
|
|
771
|
+
`joint "${key}" lower limit ${joint.lower} exceeds upper ${joint.upper}`,
|
|
772
|
+
key
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
if (joint.initialValue !== void 0 && (joint.lower !== void 0 && joint.initialValue < joint.lower || joint.upper !== void 0 && joint.initialValue > joint.upper)) {
|
|
776
|
+
add(
|
|
777
|
+
"warning",
|
|
778
|
+
"initial-out-of-limits",
|
|
779
|
+
`joint "${key}" initial value ${joint.initialValue} is outside its limits`,
|
|
780
|
+
key
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
const frames = [
|
|
784
|
+
["jointFrame0", joint.jointFrame0],
|
|
785
|
+
["jointFrame1", joint.jointFrame1]
|
|
786
|
+
];
|
|
787
|
+
for (const [label, frame] of frames) {
|
|
788
|
+
if (!decomposeRigid(frame).rigid) {
|
|
789
|
+
add(
|
|
790
|
+
"warning",
|
|
791
|
+
"non-rigid-frame",
|
|
792
|
+
`joint "${key}" ${label} carries scale/shear/reflection (discarded on export)`,
|
|
793
|
+
key
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
const tree = buildKinematicTree(desc);
|
|
799
|
+
for (const jointKey of tree.loopJoints) {
|
|
800
|
+
add(
|
|
801
|
+
"warning",
|
|
802
|
+
"closed-loop",
|
|
803
|
+
`joint "${jointKey}" closes a kinematic loop (dropped from the spanning tree)`,
|
|
804
|
+
jointKey
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
for (const linkKey of tree.isolatedLinks) {
|
|
808
|
+
add(
|
|
809
|
+
"warning",
|
|
810
|
+
"isolated-link",
|
|
811
|
+
`link "${linkKey}" is unreachable from root "${tree.root}"`,
|
|
812
|
+
linkKey
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
if ((desc.articulationRoots ?? []).length === 0 && !tree.rootJoint) {
|
|
816
|
+
add(
|
|
817
|
+
"warning",
|
|
818
|
+
"no-articulation-root",
|
|
819
|
+
"no ArticulationRootAPI link and no world-fixed joint \u2014 the base will float"
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
for (const [key, link] of Object.entries(desc.links)) {
|
|
823
|
+
const inertial = link.inertial;
|
|
824
|
+
if (!inertial || inertial.mass === void 0 && inertial.density === void 0) {
|
|
825
|
+
add("warning", "no-inertial", `link "${key}" has no mass or density (PhysicsMassAPI)`, key);
|
|
826
|
+
}
|
|
827
|
+
if (inertial?.mass !== void 0 && inertial.mass <= 0) {
|
|
828
|
+
add("error", "bad-mass", `link "${key}" has non-positive mass ${inertial.mass}`, key);
|
|
829
|
+
}
|
|
830
|
+
if (inertial?.diagonalInertia?.some((v) => v < 0)) {
|
|
831
|
+
add("error", "bad-inertia", `link "${key}" has a negative diagonal-inertia component`, key);
|
|
832
|
+
}
|
|
833
|
+
if (inertial?.principalAxes && !inertial.diagonalInertia) {
|
|
834
|
+
add(
|
|
835
|
+
"warning",
|
|
836
|
+
"inertia-pairing",
|
|
837
|
+
`link "${key}" authors principalAxes without diagonalInertia (dropped on export)`,
|
|
838
|
+
key
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
if (options.geometry) {
|
|
842
|
+
const meshes = options.geometry(key, link);
|
|
843
|
+
const collisions = meshes.filter((mesh) => mesh.kind === "collision");
|
|
844
|
+
if (collisions.length === 0) {
|
|
845
|
+
add("warning", "no-collision", `link "${key}" has no collision geometry`, key);
|
|
846
|
+
}
|
|
847
|
+
for (const mesh of collisions) {
|
|
848
|
+
if (!mesh.collisionApproximation) {
|
|
849
|
+
add(
|
|
850
|
+
"warning",
|
|
851
|
+
"collision-approximation",
|
|
852
|
+
`link "${key}" collision mesh "${mesh.name}" has no physics:approximation (PhysX dynamic bodies need a convex or primitive approximation)`,
|
|
853
|
+
key
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return issues.sort((a, b) => a.severity === b.severity ? 0 : a.severity === "error" ? -1 : 1);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
export { PACKAGE_NAME, VERSION, exportRobotUsda, stageGeometryProvider, validateRobotDescription, writeUsdz };
|
|
863
|
+
//# sourceMappingURL=chunk-JZMCABQO.js.map
|
|
864
|
+
//# sourceMappingURL=chunk-JZMCABQO.js.map
|