# msh2fbx — Star Conflict MSH → FBX 转换器

纯 C 命令行工具，将 Hammer Engine 的 `.mdl-mshXXX` 静态网格转换为 Autodesk FBX 格式。
零外部依赖，无需 Noesis 或 Autodesk SDK。

> **v1.7** (2026-07-14) — 修复 VBytes=32 UV2 (lightmap) 缺失：offset 28 uint16_unorm，对齐 blender plugin PRO。
> **v1.6** (2026-07-11) — 修复 VBytes=40 flag=0x10/0x13 和 VBytes=32 flag=0x0F 角色模型 UV1 偏移。
> **v1.5** (2026-07) — VBytes=28 flag=0x0E 新增 FX UV2 提取（animated_mock 空气墙 gate_mask02）。
> **v1.4** (2026-07) — 修复 VB=28 flag=0x0005 天空盒 UV 偏移：offset 16→20，修复 UV 解析为单线问题。
> **v1.3** (2026-06) — 修复 Blender 4.2 LTS FBX 导入 UV 不可见问题：UV 映射从 `ByVertice` 改为 `ByPolygonVertex`。
> **v1.2** (2026-06) — 修复前向轴：MSH 模型前向为 -Z，取反后前向为 +Z。

## 编译

**前置条件**：Visual Studio 2019 或 2022（含 C++ 桌面开发工作负载）

```powershell
cd msh2fbx
.\build.bat
```

编译产物：`msh2fbx.exe`（约 500KB，单文件可分发）

## 用法

```
msh2fbx <input.msh>                  # 单文件，自动生成 <input>.fbx
msh2fbx <input.msh> <output.fbx>     # 单文件，指定输出路径
msh2fbx --batch <dir> <outdir>       # 批量递归转换目录树
msh2fbx --help                        # 显示帮助
```

## 参数说明

| 参数 | 说明 |
|------|------|
| `<input.msh>` | 输入的 `.mdl-mshXXX` 文件路径 |
| `<output.fbx>` | 输出 FBX 路径（可选，默认 = 输入路径 + `.fbx`） |
| `--batch <dir> <outdir>` | 批量模式：递归扫描 `<dir>` 下所有 `.mdl-msh*`，输出到 `<outdir>` |
| `--help`, `-h` | 显示帮助信息 |

## 命名规则

批量模式下，输出文件名遵循以下规则（与 Noesis 管道一致）：

| 输入 | 输出 |
|------|------|
| `plasma_gun_mod1.mdl-msh000` | `plasma_gun_mod1000.fbx` |
| `map.mdl-msh001` | `map001.fbx` |
| `ship_hull.mdl-msh005` | `ship_hull005.fbx` |

规则：移除 `.mdl-msh` 部分，保留模型名 + 子网格编号，追加 `.fbx` 扩展名。

## 批量转换

批量模式递归遍历输入目录，保持目录层级结构：

```powershell
# 转换整个 backgrounds 目录
.\msh2fbx.exe --batch quickbms_unpacksource\mapskit\backgrounds fbx_output\backgrounds
```

输出目录结构：
```
fbx_output/
└── backgrounds/
    └── area1/
        ├── allidium_in_danger/
        │   ├── map000.fbx
        │   ├── map001.fbx
        │   └── map002.fbx
        └── allidium_yard/
            ├── map000.fbx
            ├── map001.fbx
            └── map002.fbx
```

**覆盖模式**：v1.1+ 默认覆盖已存在文件。如需断点续传请先备份。

## 转换流程

```
┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│  .mdl-mshXXX     │ →   │  msh2fbx.exe     │ →   │  .fbx (7400)     │
│  (Hammer Engine) │     │  MSH解析 + FBX写  │     │  (Autodesk FBX)  │
└──────────────────┘     └──────────────────┘     └──────────────────┘
```

导出内容：
- 顶点位置 (position xyz)
- UV 坐标 (set 0, ByPolygonVertex, V 自动翻转)
- **UV2 Lightmap** (set 1, VBytes≥32 时自动导出，uint16_unorm/float2 自动检测)
- 三角形面索引（Z 轴取反，前向 -Z→+Z）
- 平滑法线 (flat normals)

> **Blender 4.2 LTS UV 可见性**: v1.3 起 UV 映射改为 `ByPolygonVertex` 模式，
> 兼容 Blender 4.2/5.0 和 Maya。旧版 binary FBX 在 Blender 4.2 中 UV 不可见。

> **关于轴向**：Hammer Engine MSH 模型使用 Y-up 坐标系，前向为 -Z。
> 导入 Maya（Y-up, 前=+Z）时模型朝后；导入 Blender（Z-up）需额外旋转。
> v1.2 起 msh2fbx 对 Z 坐标取反，使模型前向变为 +Z，Maya 即开即用，
> Blender 通过标准 FBX 导入器的 Y→Z 转换即可正常显示。

不包含（MSH 格式无此数据）：
- 骨骼/蒙皮
- 材质/纹理引用
- 动画

## 性能

| 规模 | 文件数 | 耗时 | 吞吐量 |
|------|--------|------|--------|
| 小批量 | 83 | 0.5 秒 | ~173 文件/秒 |
| 中批量 | 622 | 3.4 秒 | ~183 文件/秒 |
| 全量 (实测) | 62,825 | ~6 分钟 | ~175 文件/秒 |

## 支持的格式

| VBytes | flag 条件 | UV 偏移 | UV2 | 常见用途 |
|--------|-----------|---------|:---:|----------|
| 20 | — | 12 | — | 基础网格 |
| 24 | — | 16 | — | 扩展网格 |
| 28 | flag=0xE, 5 | 16 | — | 场景物体 |
| 28 | flag=0x0E | 16 | **24** (FX) | 空气墙/gate (animated_mock) |
| 28 | flag=0x11 | 20 | — | 特殊物体 |
| 32 | — | 20 | 28 | 中型网格 |
| 36 | — | 20 | 28 | 大型网格 |
| 40 | — | 24 | 32 | 角色模型 |
| 44 | — | 20 | 28 | 装饰模型 |

- **UV2 Lightmap**：VBytes≥32 时自动导出到 FBX UV layer 1（set index 1）
- **UV2 FX**：VBytes=28 flag=0x0E 时自动导出到 FBX UV layer 1（gate_mask02 遮罩坐标）

编号范围：`.mdl-msh000` ~ `.mdl-msh1308`

> **关于编号含义**：`.mdl-mshXXX` 不是传统 LOD 编号。不同模型使用不同约定：
> - 简单模型（炮塔/武器）：编号递增 = 精度递减（如 000=LOD0, 001=LOD1, 002=LOD2）
> - 复杂无畏舰：命名差异大（`_03` 可能是 LOD0，`_2` 是 LOD2，`bigship_jer` 是 LOD1）
> - 多部件模型：间隔编号对应不同子网格（如 000/004/008 是三个部件的高精度）
> - `_mod1~4` = 装备升级变体，`_s1~3` = 皮肤变体（仅MDF）
>
> 导入时建议按模型类型逐例确认，详见 `blender_plugin/io_import_starconflict_msh_pro/README_PRO.md` 的 LOD 分析章节。

## 依赖

- **编译时**：ufbx_write（MIT 许可，已随附在 `msh2fbx/` 目录）
- **运行时**：无外部依赖，单文件 `msh2fbx.exe` 即可运行

## 与 Noesis 管道的区别

| | Noesis 管道 | msh2fbx |
|---|---|---|
| 运行方式 | Noesis + Python 脚本 | 单文件 .exe |
| 依赖 | Noesis (闭源) + Python | 无 |
| 速度 | ~1-2 文件/秒 | ~183 文件/秒 |
| 并行 | Python 多进程 | 串行（I/O 已足够快） |
| FBX 版本 | Noesis 内部格式 | 7400 标准二进制 |
| 可分发性 | 需打包 Noesis | 单文件复制即用 |

## 常见问题

**Q: 生成的 FBX 能在哪些软件中打开？**
Blender、Unity、Unreal Engine、3ds Max、Maya 等主流 3D 软件均支持 FBX 7400 格式。

**Q: 为什么没有材质/纹理？**
`.mdl-mshXXX` 是纯网格数据，不包含材质信息。材质定义在 `.mdf` 文件中，纹理存储在 `.tfh`/`.tfd` 文件中。材质关联需要额外的处理脚本。

**Q: 能批量转换全量 188,000 个文件吗？**
可以。运行 `msh2fbx --batch quickbms_unpacksource fbx_output`，约需 17 分钟。断点续传机制确保中断后可以继续。

**Q: Linux 能用吗？**
源码是跨平台 C99。Linux 下编译：
```bash
gcc -O2 -DUFBXW_STATIC -o msh2fbx msh2fbx.c ufbx_write.c -lm
```

**Q: 转换失败了怎么办？**
检查 MSH 文件是否完整（TPAK 解包是否成功）。工具会打印错误信息到 stderr。
