# 加载外部模型 (Load External Models)

- **Summary**: 提供了一系列独立的加载函数 (`loadGltf`, `loadPly`, `loadFbx` 等)，用于加载各种格式的外部 3D 模型（如 .glb, .ply, .splat, .fbx），并将其转换为 Three.js 对象以便添加到 Five 场景中。
- **Schema**: 涉及 `LoadResultType` 返回结构及各加载器的配置项 (`LoaderOptions`)。
- **Concepts**: 独立加载函数、坐标系转换 (UpAxis)、资源销毁 (Dispose)、Fetcher。
- **Configuration**: `modelUpAxis`, `upAxis`, `fetcher` 等通用及特定配置。
- **Examples**: 加载 GLTF 模型、加载高斯泼溅 (Gaussian Splat)、加载点云。

## Schema

> **Definition**: [LoadResultType](../../five/model/loaders/shared.d.ts)

所有加载函数均返回 `Promise<LoadResultType>`。

```typescript
// 通用返回类型
export type LoadResultType<Scene = THREE.Object3D> = {
  type: 'gltf' | 'obj' | 'ply' | 'fbx' | 'pbm' | 'splat' | 'spz' | 'b3dm' | 'pnts' | 'at3d' | 'dome' | 'domez' | 'x3p'; // 模型类型
  modelUpAxis: 'Y' | 'Z';     // 原始模型的上轴
  upAxis: 'Y' | 'Z';          // 输出模型的上轴 (通常为 Z)
  byteLength: number;         // 文件字节大小
  memoryUsage: number;        // 显存/内存占用估算
  uri: string;                // 模型地址
  scene: Scene;               // Three.js 对象 (Group 或 Object3D)，可直接添加到场景
  textures: THREE.Texture[];  // 加载的纹理列表
  animations: THREE.AnimationClip[]; // 动画剪辑列表
  rtcCenter?: THREE.Vector3;  // 若存在 RTC (Relative To Center) 坐标偏移
  dispose: () => void;        // 销毁资源函数
}

// 通用配置项接口
interface BaseLoaderOptions {
  upAxis?: 'Y' | 'Z';         // 期望输出的上轴，默认为 'Z' (Five 坐标系)
  modelUpAxis?: 'Y' | 'Z';    // (部分加载器支持) 原始模型的上轴，用于自动旋转修正
  fetcher?: Fetcher;          // 自定义网络请求 Fetcher
  light?: boolean;            // 是否使用简化/无光照材质 (取决于加载器实现)
  onDownloadProgress?: (progress: { loaded: number, total: number }) => void; // 下载进度回调
}
```

## Concepts

### 独立加载函数 (Standalone Functions)
这些加载函数（如 `loadGltf`, `loadPly`）是 `@realsee/five` 包导出的**独立函数**，而非 `Five` 实例的方法。你需要单独引入它们使用。

### 坐标系转换 (Up-Axis Conversion)
Five 使用 **Z-up** (Z 轴向上) 的右手坐标系。
许多外部模型（如 standard GLTF, FBX）通常是 **Y-up** 的。
加载器提供了 `upAxis` 和 `modelUpAxis` 选项来自动处理旋转：
- `upAxis`: 你期望得到的结果坐标系上轴（通常保持默认 'Z'）。
- `modelUpAxis`: 原始文件的上轴（如 GLTF 默认为 'Y'，PLY 默认为 'Z'）。
如果不指定，加载器会尝试使用合理的默认值将模型旋转到 Z 轴向上。

### 资源销毁 (Disposal)
加载的模型包含 Geometry、Material 和 Texture 等 WebGL 资源。当你不再需要该模型时（例如切换场景或移除物体），**必须**调用返回结果中的 `dispose()` 方法来释放内存，否则会导致内存泄漏。

### Fetcher
所有加载器支持传入自定义 `fetcher`。这在需要鉴权、自定义 Header 或处理特定 CDN 逻辑时非常有用。如果不传，默认使用内部的 `internalFetcher`。

## Configuration

### loadGltf
加载 `.gltf` 或 `.glb` 文件。支持 Draco 压缩。
支持扩展：
*   `KHR_binary_glTF`
*   `KHR_draco_mesh_compression`
*   `KHR_texture_basisu`
*   `KHR_texture_transform`
*   `KHR_mesh_quantization`
*   `KHR_materials_unlit`
*   `KHR_node_visibility`
*   `KHR_animation_pointer`
*   `EXT_meshopt_compression`
*   `KHR_materials_clearcoat`
*   `KHR_materials_sheen`
*   `KHR_materials_transmission`
*   `KHR_materials_emissive_strength`
*   `EXT_texture_webp`
*   `CESIUM_RTC`
*   `REALSEE_materials_lightmap`
*   `KHR_gaussian_splatting`
*   `KHR_gaussian_splatting_compression_spz`
*   `KHR_gaussian_splatting_compression_spz_2`

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelUpAxis` | `'Y' | 'Z'` | `'Y'` | 原始模型的上轴。GLTF 标准通常为 Y。 |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `undefined` | 是否强制使用 Unlit 材质或移除光照扩展。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadPly
加载 `.ply` 文件。支持网格、线框、点云及 PBM 扩展格式。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `type` | `'geometry' | 'line' | 'pbmMesh' | 'pbmPointCloud'` | 自动推断 | 指定解析类型。 |
| `customPropertyMapping` | `Object` | - | 自定义属性映射（用于解析非标准 PLY 属性）。 |
| `propertyNameMapping` | `Object` | - | 属性名重映射 (e.g. `{'diffuse_red': 'red'}`)。 |
| `modelUpAxis` | `'Y' | 'Z'` | `'Z'` | PLY 通常为 Z-up。 |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `undefined` | 是否使用简化材质。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadSplat
加载 `.splat` 格式的高斯泼溅 (Gaussian Splat) 模型。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelUpAxis` | `'Y' | 'Z'` | `'Y'` | Splat 模型通常为 Y-up。 |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadSpz
加载 `.spz` 格式的高斯泼溅 (Gaussian Splat) 模型。`.spz` 是对 `.splat` 的压缩格式。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelUpAxis` | `'Y' | 'Z'` | `'Y'` | 原始模型的上轴。 |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadFbx
加载 `.fbx` 文件。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出上轴。 |
| `light` | `boolean` | `undefined` | 是否使用简化材质。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadObj
加载 `.obj` 文件。会自动尝试加载同目录下的 `.mtl` 材质文件。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelUpAxis` | `'Y' | 'Z'` | `'Z'` | 原始模型的上轴。 |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `undefined` | 是否使用简化材质。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadPbm
加载 Realsee 私有 PBM 格式。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `false` | 是否使用简化材质。 |
| `textureBaseUri` | `string` | 自动推断 | 纹理的基础路径。 |
| `textureArray` | `string[]` | - | 纹理路径列表。 |
| `textureOptions` | `TextureOptions` | - | 纹理加载配置 (size, quality 等)。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadDome / loadDomez
加载 Dome 全景模型 (`.dome` 或 `.domez` 压缩格式)。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `true` | 是否使用简化材质。 |
| `textureBaseUri` | `string` | - | 纹理基础路径。 |
| `textureOptions` | `TextureOptions` | - | 纹理加载配置。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadAt3d
加载 `.at3d` 格式模型。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `false` | 是否使用简化材质。 |
| `textureBaseUri` | `string` | 自动推断 | 纹理基础路径。 |
| `textureArray` | `string[]` | - | 纹理路径列表。 |
| `textureOptions` | `TextureOptions` | - | 纹理加载配置。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadB3dm
加载 `.b3dm` (Batched 3D Model) 格式。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelUpAxis` | `'Y' | 'Z'` | `'Y'` | 原始模型的上轴。 |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `light` | `boolean` | `undefined` | 是否使用简化材质。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadPnts
加载 `.pnts` (Point Cloud) 格式。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `computeBoundingBox` | `boolean` | `true` | 是否计算包围盒。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

### loadX3p
加载 `.x3p` 格式。

| 参数 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `upAxis` | `'Y' | 'Z'` | `'Z'` | 输出模型的上轴。 |
| `color` | `THREE.Color` | - | 指定颜色。 |
| `light` | `boolean` | `true` | 是否使用简化材质。 |
| `fetcher` | `Fetcher` | - | 自定义 Fetcher。 |

## Examples

### 基础用法：加载 GLTF 模型 (Basic Usage)

```typescript
import { Five, FivePlugin } from '@realsee/five';
import { loadGltf } from '@realsee/five';
import * as THREE from 'three';

const five = new Five({/*...*/});

// 加载 GLB 模型
loadGltf('https://example.com/model.glb').then((result) => {
  // 1. 获取模型对象
  const model = result.scene;

  // 2. 设置位置和大小
  model.position.set(0, 0, 0);
  model.scale.set(1, 1, 1);

  // 3. 添加到 Five 场景
  five.scene.add(model);

  // 4. (可选) 渲染一帧以确保显示
  five.needsRender = true;

  // 保存 result 用于后续销毁
  // result.dispose();
}).catch(err => {
  console.error('加载失败', err);
});
```

### 进阶用法：加载高斯泼溅 (Gaussian Splat)

```typescript
import { loadSplat } from '@realsee/five';

loadSplat('https://example.com/scene.splat', {
  modelUpAxis: 'Y', // 假设原始数据是 Y-up
  upAxis: 'Z',      // 转换到 Five 的 Z-up
}).then((result) => {
  const splatMesh = result.scene;
  five.scene.add(splatMesh);
  five.needsRender = true;
});
```

### 进阶用法：自定义 PLY 属性映射 (Custom PLY Properties)

```typescript
import { loadPly } from '@realsee/five';

loadPly('https://example.com/data.ply', {
  type: 'pbmPointCloud',
  customPropertyMapping: {
    intensity: {
      itemType: 'float32',
      itemNames: ['intensity'],
      normalized: true
    }
  }
}).then((result) => {
  // result.scene 是一个 PBMPointCloud 对象
  five.scene.add(result.scene);
});
```

### 基础用法：加载 OBJ 模型 (Basic Usage - OBJ)

```typescript
import { loadObj } from '@realsee/five';

loadObj('https://example.com/model.obj', {
  modelUpAxis: 'Z', // OBJ 默认通常是 Z-up (或根据实际情况调整)
}).then((result) => {
  five.scene.add(result.scene);
  // OBJ 文件中定义的 .mtl 材质会自动加载
});
```

### 基础用法：加载 FBX 模型

```typescript
import { loadFbx } from '@realsee/five';

loadFbx('https://example.com/model.fbx').then((result) => {
  five.scene.add(result.scene);
  // 动画会自动解析到 result.animations
});
```

### 进阶用法：加载 Dome 全景 (Panorama)

```typescript
import { loadDome } from '@realsee/five';

// 加载 .dome 或 .domez 格式
loadDome('https://example.com/panorama.domez', {
  light: false, // 全景模型通常不需要光照计算
  textureOptions: {
    size: 4096, // 限制最大纹理尺寸
  }
}).then((result) => {
  result.scene.position.set(0, 0, 0);
  five.scene.add(result.scene);
});
```

### 进阶用法：加载压缩模型 (AT3D)

```typescript
import { loadAt3d } from '@realsee/five';

loadAt3d('https://example.com/model.at3d', {
  light: false,
}).then((result) => {
  five.scene.add(result.scene);
});
```

## Debugging

- **查看网络请求**: 检查 Network 面板，确认模型文件是否成功下载 (200 OK)。
- **检查模型位置**: 如果加载成功但看不到，尝试 `console.log(result.scene)` 检查其 `position` 和 `scale`。模型可能太小、太大或位置偏离相机视野。
- **坐标轴辅助**: 在模型上添加 `new THREE.AxesHelper(1)` 来确认其自身的坐标轴方向。

## Common Pitfalls

1.  **坐标轴方向错误**: 模型“躺着”或“倒立”。
    - **解决**: 调整 `modelUpAxis` 参数。如果是 GLTF 试着改为 'Z' 或 'Y'；如果是 PLY 试着改为 'Y'。
2.  **内存泄漏**: 频繁加载/卸载模型但未调用 `dispose()`。
    - **解决**: 务必保存 `load` 返回的 result，并在移除模型时调用 `result.dispose()`。单纯 `scene.remove(object)` 不会释放 GPU 内存。
3.  **纹理路径错误**: 加载 OBJ/PBM 等带外部纹理的文件时，纹理 404。
    - **解决**: 确保模型文件内的纹理引用是相对路径，或者通过 `resourcePath` / `textureBaseUri` (视具体 loader 而定) 指定纹理目录。
4.  **Draco 解码器缺失**: 加载压缩的 GLTF 失败。
    - **解决**: `loadGltf` 内部集成了 DracoLoader，通常不需要额外配置。如果报错，检查构建配置或网络是否拦截了 wasm 文件。

## Related

*   [Model](./model.md): 关于 Five 内部模型加载机制的说明。
*   [Coordinate System](./coordinate-system.md): 坐标系定义，理解 Y-up 与 Z-up 的转换。
*   [Request Proxy](./request-proxy.md): 自定义请求拦截（鉴权、CDN），与 fetcher 配合使用。

---

```yaml
tags: [加载模型, 导入模型, 点云, 内存泄漏, loader, gltf, ply, splat, fbx, external-model, glb, obj, 3d-model, gaussian-splatting, point-cloud, draco, dispose]
```
