# Ctrl+O 本地 .spz 文件上传功能说明

## 功能概述

在 3D 高斯泼溅查看器（SparkSplatViewer）页面中，按下 **Ctrl+O**（macOS 为 ⌘+O）即可打开本地文件选择框，选择一个 `.spz` 模型文件并立即加载替换当前模型。

- **上传成功**：顶部显示绿色提示「上传成功：文件名」，新模型自动适配相机视角并恢复自动旋转。
- **上传失败**：顶部显示红色提示「上传失败：原因」，旧模型保留，不会白屏。

## 使用方式

| 操作 | 效果 |
| --- | --- |
| `Ctrl + O` / `⌘ + O` | 弹出本地文件选择框（仅允许 `.spz`） |
| 点击右下角提示条 | 等同于按快捷键，同样弹出文件选择框 |
| 选择 `.spz` 文件 | 显示加载进度，完成后替换当前模型 |
| 选择非 `.spz` 文件 | 直接提示「上传失败：仅支持 .spz 文件」 |

页面右下角常驻一个不突兀的快捷键提示条（半透明小字 `Ctrl + O 打开本地模型`），鼠标悬停时变清晰，点击也可触发上传。

## 支持的文件格式

| 格式 | 说明 |
| --- | --- |
| SPZ v1 ~ v3（gzip 封装） | 直接解码加载 |
| SPZ v4（NGSP / ZSTD） | 前端自动转码为 v3 后加载，无需后端改动 |

> 选择文件时仅校验 `.spz` 后缀；gzip 与 NGSP v4 由文件头魔数自动识别，两种格式均可直接上传。

## 交互反馈（Toast）

页面顶部居中显示状态提示，2.5 秒后自动消失：

- **加载中**：`正在加载：xxx.spz … 45%`（含进度百分比）
- **成功**：绿色 `上传成功：xxx.spz`
- **失败**：红色 `上传失败：具体原因`（如文件为空、解析出错、HTTP 错误等）

## 技术实现

### 1. 新增 API：`viewer.loadModelFromFile(file, options)`

位于 `SparkSplatViewer.js`，供任何需要"从本地 File 对象加载模型"的场景调用：

```js
const mesh = await viewer.loadModelFromFile(file, {
    autoFit: true,          // 加载后自动适配相机（默认 true）
    zoomDuration: 500,      // 缩放动画时长 ms
    onBeforeLoad: () => {}, // 读取文件前触发（可显示 loading 遮罩）
    onComplete: (mesh) => {}, // 加载成功回调
    onError: (err) => {},   // 加载失败回调
    onProgress: (e) => {},  // 进度回调
});
// 成功返回 SplatMesh，失败返回 null
```

设计要点：

1. **直接读取 File 字节**（`file.arrayBuffer()`），不经过 fetch / blob URL。原 `loadModel(url)` 靠 URL 后缀判断是否走 v4→v3 转码，blob URL 没有 `.spz` 后缀会绕过适配器导致 v4 文件报 "Invalid gzip header"；新路径按文件名判断，转码必定生效。
2. **先读取并转码，成功后才移除旧模型**。读取或转码失败时旧模型保留在场景中，避免白屏。
3. 返回 Promise，内部把 SplatMesh 的回调式 `onLoad`/`onError` 包装为异步返回。

### 2. 快捷键与提示（index.html）

- 监听 `keydown`：`Ctrl+O` / `⌘+O`（不带 Shift/Alt）时 `preventDefault()` 拦截浏览器默认行为，触发隐藏的 `<input type="file" accept=".spz">`。
- 每次选择后清空 input 的 value，保证重复选择同一文件也能触发 `change`。
- 右下角 `#open-file-hint` 提示条：加载完成后淡入，低透明度常驻，悬停高亮，点击等效于快捷键。
- `#upload-toast` 顶部提示条：`showToast(msg, type, duration)` 统一管理，支持 `loading`（不自动消失）、`success`、`error` 三种状态。

## 文件变更清单

| 文件 | 变更 |
| --- | --- |
| `SparkSplatViewer.js` | 新增 `loadModelFromFile(file, options)` 方法 |
| `index.html` | 新增 Toast 样式与节点、快捷键提示条、隐藏文件输入框、Ctrl+O 监听与上传逻辑 |

其余文件（`spz-v4-adapter.js`、`js/` 目录下的引擎文件等）无需改动，直接替换上述两个文件即可使用。
