# `@huggingface/usd-web-viewer`

在浏览器中使用 [OpenUSD](https://openusd.org)：基于 WebAssembly 的 USD 组合，构建于 [openusd](https://github.com/mxpv/openusd)（纯 Rust）之上，并使用 [three.js](https://threejs.org) 进行渲染。支持读取 `.usd`、`.usda`、`.usdc` 和 `.usdz`；加载过程在 Web Worker 中运行。

```sh
npm install @huggingface/usd-web-viewer three
```

## 用法

现成的查看器（渲染器、光照、轨道控制）：

```js
import { createViewer } from '@huggingface/usd-web-viewer';

const viewer = createViewer(document.getElementById('viewer')); // a container element
const { info } = await viewer.load(
  'https://huggingface.co/datasets/Robotiq-Official/simready-assets/resolve/main/Robotiq_2F_85/simready_usd/Robotiq_2F_85.usda',
);
```

或者放入你自己的 three.js 场景中：

```js
import { loadUsd } from '@huggingface/usd-web-viewer';

const { root, complete, dispose } = await loadUsd(url);
scene.add(root); // a THREE.Group, Y-up, in meters
await complete; // textures stream in after the geometry
```

## 物理与关节

`info` 还会报告仿真（UsdPhysics）及其 UsdSemantics 标签的创建者所编写的信息：不会进行任何估算。质量单位为千克，密度单位为 kg/m³，惯性单位为 kg·m²，关节限位和位置单位为度或米；位置和矩阵位于 stage 空间中，与 `root` 下的网格相同。

```js
const { info, joints, setJointPositions, resetJointPositions, getJointPositions, getWorldMatrix } = await viewer.load(url);

info.physics.bodies; // rigid bodies: mass, center of mass, inertia, colliders, bodies fixed to them
info.physics.colliders; // shape, approximation (convexHull, sdf, ...), physics material
info.physics.materials; // density, friction, restitution
info.physics.joints; // revolute, prismatic, spherical, distance, fixed and generic joints with their limits and drives
info.physics.articulationRoots; // where articulations (bodies solved together, like a robot) start
info.physics.frames; // Xforms under a body, often points with no mesh, like a gripper's tip
info.labels; // e.g. { path: '/RootNode', taxonomy: 'wikidata_class', values: ['cabinet'] }
info.variantSelections; // e.g. { prim: '/Robotiq_2F_85', set: 'Physics', selection: 'Physx_parallel_grip' }
info.dependencies; // every layer and texture the stage read, with its size

joints; // the joints setJointPositions can move, e.g. to build sliders
setJointPositions({ '/Robotiq_2F_85/Robotiq_2F_85/Joints/finger_joint': 40 }); // revolute and prismatic joints, any number at once; what hangs from them and mimic joints follow
getJointPositions(); // where every revolute and prismatic joint is now
getWorldMatrix('/SO101/gripper/gripper_frame_link'); // where a body, frame or mesh under a body is now and which way it faces, as a THREE.Matrix4 in the scene; null if it is none of those
resetJointPositions(); // back to the authored pose
```

姿态调整仅涉及运动学（无碰撞或重力），且开销足够低，适用于滑块拖拽或录制的每一帧。

## 选项

`loadUsd(url, options)` 和 `viewer.load(url, options)` 接受以下参数：

| 选项                         | 默认             |                                                                                 |
| ------------------------------ | ------------------- | ------------------------------------------------------------------------------- |
| `textures`                     | `'preview'`         | `'none'`、`'preview'`（基础颜色、小型数据贴图、无法线贴图）或 `'full'` |
| `maxTextureSize`               | `1024`              | 解码后纹理的长边，以像素为单位                                        |
| `signal`                       |                     | `AbortSignal`；加载会以代码为 `aborted` 的 `UsdLoadError` 被拒绝      |
| `onProgress`                   |                     | 调用时传入 stage 以及一个从 0 到 1 的总`fraction`                     |
| `files`                        |                     | 加载已丢弃或已拾取的文件，而不是通过网络加载（详见下文）                 |
| `wasmUrl`, `workerUrl`         | 在包旁边 | WASM 二进制文件和 worker 脚本的提供位置                     |
| `maxConcurrentFetches`         | `16`                | 同时处理中的请求                                                      |
| `maxLayerBytes`, `maxLayers`   | 768 MiB，1024       | 层下载限制                                                           |
| `maxTextureBytes`              | 512 MiB             | 纹理下载限制                                                          |
| `maxTriangles`, `maxInstances` | 20M, 100,000        | 最多绘制                                                           |

`createViewer(container, options)` 接受 `background`（默认透明）、`onContextLost` 和 `onContextRestored`（在重新加载最后显示的模型时调用）。

失败是带有 `code`（`aborted`、`fetch`、`compose`、`worker`、`webgl`）的 `UsdLoadError`；任何无法忠实显示的内容都会列在 `info.warnings` 中。每个选项、错误和警告都有类型。

## 加载不受信任的文件

- 请求的发送方式与 `<model-viewer>` 相同：Cookie 仅发送到页面自身的源（`credentials: 'same-origin'`），不发送自定义请求头，也不包含 Referer。
- 图层和纹理仅通过 http(s) 获取。
- 解析在编译为 WebAssembly 的 Rust 中运行，位于 Web Worker 内。
- 每次加载均有上限：`maxLayerBytes`（768 MiB）、`maxLayers`（1024）、`maxTextureBytes`（512 MiB）、`maxTriangles`（2000 万）以及 `maxInstances`（100,000）；图像每边最大 16384 像素，面积最大 8192 x 8192 像素；图元嵌套深度最多 256 层；`.usdz` 条目单个最大 512 MiB，总计最大 1 GiB，且以未压缩形式存储。
- 格式错误的文件在加载时会抛出 `UsdLoadError`。

## 本地文件

要显示拖放到页面上或通过 `<input type="file" webkitdirectory>` 选取的文件，请将它们作为 `files` 传入，并以它们在包中的路径作为键，然后加载根层的键。文件中的相对路径会相互解析；不会为它们获取任何内容。

```js
const files = new Map([...input.files].map((file) => [file.webkitRelativePath || file.name, file]));
await viewer.load('chair/simready_usd/sm_chair.usd', { files });
```

## 打包器

Worker 和 WASM 二进制文件通过 `new URL(..., import.meta.url)` 查找，Vite、webpack 和其他打包工具会解析并输出该路径。在使用 Vite 开发服务器时，请将此包从依赖预打包中排除：

```js
// vite.config.js
export default { optimizeDeps: { exclude: ['@huggingface/usd-web-viewer'] } };
```

采用 MIT 许可证。编译到 WASM 二进制文件中的 Rust 软件包的许可证位于 `THIRD_PARTY_LICENSES` 中。

