Dimitriuses/Roblox-Mesh-Importer

GitHub: Dimitriuses/Roblox-Mesh-Importer

读取 Roblox 未公开的 .mesh v4 二进制格式中的几何体、骨骼及蒙皮数据,并将其作为已绑定的模型导入 Blender。

Stars: 0 | Forks: 0

# Roblox Mesh Importer 读取 Roblox `.mesh` 版本 4 文件 —— 几何体、LOD、骨骼和蒙皮权重 —— 并将其作为绑定了骨骼的对象导入到 Blender 中。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/Dimitriuses/Roblox-Mesh-Importer/actions/workflows/ci.yml) [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/) [![Blender 4.0+](https://img.shields.io/badge/blender-4.0%2B-orange)](https://www.blender.org/) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![status: active](https://img.shields.io/badge/status-active-brightgreen)](ROADMAP.md) Roblox 的 `.mesh` 是一种未公开的二进制格式,而版本 4 是其最有趣的部 分:它包含了骨骼层级和逐顶点蒙皮权重,这是任何公开的导出工具都不会 提供给你的。本项目可以读取它,并且它所使用的布局已经[记录在案](docs/MESH_FORMAT.md) —— 这是 基于 799 个真实的 mesh 测量得出的,而不是凭空猜测的。 | | | | |---|---|---| | ![Imported mesh](https://static.pigsec.cn/wp-content/uploads/repos/cas/9f/9fbb73d1e0e429e36e5cc8946d45f1b0d906c5eb9e4997dc7e1189e8100cbf28.png) | ![Posed](https://static.pigsec.cn/wp-content/uploads/repos/cas/64/6489dd59c38dac387df523f9cfd16f818ac7b87029786c5d64a90f816d1080ac.png) | ![Bone influence](https://static.pigsec.cn/wp-content/uploads/repos/cas/01/01daf64967192960dfb50b6e57da8a7658a12eaa00674f378efbc3822e0e3676.png) | | 导入后,静息姿态 | 摆出姿态 —— 蒙皮跟随骨骼 | 逐顶点骨骼影响 | *该样本是程序化生成的肢体([`tools/make_sample_mesh.py`](tools/make_sample_mesh.py));此处不重新分发任何 Roblox 资源。可以使用 `python tools/capture_screenshots.py` 重新生成这些截图。* ## 它能做什么 - 解析 `version 4.00` / `4.01` 的 mesh:顶点、法线、UV、切线、 顶点颜色、面、LOD 级别、骨骼和蒙皮。 - 正确解析蒙皮权重 —— 骨骼槽位是*子集本地*索引,必须 通过每个子集的骨骼表进行映射才能找到真实的骨骼。 - 导出为 JSON,然后将其作为 mesh + 骨骼导入到 Blender 中,每个 骨骼对应一个顶点组。 - 验证文件的内部一致性(`check`),这是你发现二进制读取器错误的 方法:错误的读取器会返回看似合理的数字,而不是报错。 - 没有第三方运行时依赖。转换器是仅使用标准库的 Python,因此 它可以在你拥有的任何解释器中运行,包括 Blender 自带的解释器。 ## 快速开始 转换器无需安装: ``` git clone https://github.com/Dimitriuses/Roblox-Mesh-Importer.git cd Roblox-Mesh-Importer python -m roblox_mesh info samples/arm.mesh # what's in it python -m roblox_mesh check samples/arm.mesh # is it internally consistent python -m roblox_mesh convert samples/arm.mesh -o arm.json ``` ``` version 4.01 vertices 544 faces 1024 bones 4 subsets 2 LOD levels 2 offsets=[0, 736, 1024] weighted 544 / 544 vertices ``` 然后导入到 Blender: ``` blender --background --python import_to_blender.py -- arm.json ``` 去掉 `--background` 可以观察其执行过程。`python convert.py file.mesh` 仍可 作为 `convert` 子命令的简写形式。 ### 获取要转换的 `.mesh` 本仓库附带了一个生成的样本,以便上述所有操作都能立即运行。要使用 你自己的 mesh,请从 Roblox Studio 导出,或者从本地 Roblox 安装 中获取一个 —— 客户端将 mesh 存储在 `content/` 及其资源缓存下。 仅转换你有权使用的 mesh;参见 [`NOTICE.md`](NOTICE.md)。 ## 用法 ### `convert` —— mesh 转 JSON ``` python -m roblox_mesh convert model.mesh # writes model.json python -m roblox_mesh convert model.mesh -o out.json --compact ``` ### `info` —— 摘要 ``` python -m roblox_mesh info model.mesh -v # -v lists the bone hierarchy ``` ### `check` —— 验证 ``` python -m roblox_mesh check model.mesh python -m roblox_mesh check model.mesh --remap # nearest-bone suggestions ``` 如果文件存在结构性错误,则以非零状态退出。`--remap` 会打印 最近骨骼比较;它仅供诊断使用,绝不进行实际应用 —— 参见 [已知限制](#known-limitations)。 ### `import_to_blender.py` —— JSON 转换为绑定了骨骼的 Blender 对象 ``` blender --background --python import_to_blender.py -- model.json blender --background --python import_to_blender.py -- model.json --lod 1 blender --background --python import_to_blender.py -- model.json --keep-axes --scale 10 ``` | 选项 | 含义 | |---|---| | `--lod N` | 要导入的 LOD 级别;0 为最详细(默认) | | `--keep-axes` | 保留 Roblox 的 Y 轴向上方向,而不是转换为 Z 轴向上 | | `--scale S` | 等比缩放 | | `--name NAME` | 所创建对象的名称 | ### 作为库使用 ``` from roblox_mesh import read_mesh_file, check_structure mesh = read_mesh_file("model.mesh") print(len(mesh.vertices), len(mesh.bones), mesh.lodOffsets) for issue in check_structure(mesh): print(issue) for vertex in mesh.vertices[:3]: print(vertex.position, vertex.weights) # {"UpperArm": 0.56, "Hand": 0.44} ``` ## 组合方式 ``` model.mesh ──> roblox_mesh.reader ──> Mesh ──> roblox_mesh.exporter ──> model.json │ │ └─> roblox_mesh.diagnostics │ v import_to_blender.py (Blender) │ v mesh + armature + weights ``` 这种分离是因为这两个部分无法共享同一个解释器:转换器 应该能在任何地方运行,而导入器只在 Blender 内部运行。JSON 是它们之间的交换格式,由 `schemaVersion` 标记版本,以便导入器 拒绝它无法理解的输入。 | 路径 | 职责 | |---|---| | [`roblox_mesh/reader.py`](roblox_mesh/reader.py) | 二进制解析 | | [`roblox_mesh/writer.py`](roblox_mesh/writer.py) | 序列化器 —— 它的存在是为了能对读取器进行往返测试 | | [`roblox_mesh/model.py`](roblox_mesh/model.py) | Dataclass 和格式的常量 | | [`roblox_mesh/exporter.py`](roblox_mesh/exporter.py) | JSON schema | | [`roblox_mesh/diagnostics.py`](roblox_mesh/diagnostics.py) | 结构验证 | | [`roblox_mesh/cli.py`](roblox_mesh/cli.py) | `convert` / `info` / `check` | | [`import_to_blender.py`](import_to_blender.py) | Blender 端 | | [`docs/MESH_FORMAT.md`](docs/MESH_FORMAT.md) | 格式说明及其相关证据 | ## 扩展 - **另一种输出格式** —— 在 `exporter.py` 旁边编写一个消耗 `Mesh` 的模块。其他任何内容都不需要更改。 - **另一个 mesh 版本** —— 将布局添加到 `reader.py` 并扩充 `SUPPORTED_VERSIONS`。首先向 `tools/make_sample_mesh.py` 添加一个 fixture: 往返测试将告诉你布局是否正确。 - **新的格式知识** —— 在 `diagnostics.check_structure` 中为你发现的 任何不变量添加检查。该函数是项目针对错误读取布局的预警 系统。 ## 测试 ``` pip install pytest pytest # 50 tests ``` 最有趣的测试是往返测试:解析一个文件,将其写回,然后比较 字节。一个存在细微错误的二进制读取器不会抛出异常 —— 它会返回看起来 没问题的数字 —— 而字节相等性检查是捕获字段读取时偏移量或 宽度错误的最廉价的方法。 在默认测试套件之外还有两个层会运行: ``` # 针对本地 Roblox 安装中的真实 meshes(未进行任何分发) ROBLOX_MESH_CORPUS="$LOCALAPPDATA/Roblox" pytest tests/test_real_corpus.py -v -s # 通过 Blender 端到端:导入 sample 并断言 rig 完好无损 python tools/blender_smoke.py # or: blender --background --python tools/blender_smoke.py ``` CI 会在 Linux 和 Windows 上跨 Python 3.10 和 3.13 运行单元测试套件,以及 Blender 导入检查。 ## 已知限制 - **仅限版本 4。** 版本 1–3 和 5+ 具有不同的布局,并且会 被以明确的提示信息拒绝,而不是被错误地解析。版本 5(包含 面部动画数据)是显而易见的下一个目标 —— 参见 [`ROADMAP.md`](ROADMAP.md)。 - **无法写回 Roblox。** `roblox_mesh.writer` 的存在是为了测试读取器。 它能生成有效的文件,但将 mesh 往返转换*回* Roblox 是未经测试的。 - **骨骼朝向被忽略。** 每个骨骼的 3×3 旋转会被解析并导出,但 Blender 导入器仅使用位置,通过其子级来确定每个骨骼的方向。 旋转对蒙皮来说只是表面属性;只有在计划重定向动画时, 它们才重要。 - **`check --remap` 是一种启发式方法,并且被故意不予应用。** 它 将每个骨骼受影响的顶点质心与骨骼原点进行比较,而骨骼的顶点自然位于其自身的原点和子骨骼之间 —— 因此在 正确解析的 R15 手臂上,它仍然会建议 `RightUpperArm → RightLowerArm`。 它能可靠地检测*严重的*破坏,而不是细微的错误。请将其输出视为 一种迹象,而不是修复方案。 - **语料库范围较窄。** 所测量的每个蒙皮文件都有 6–10 个骨骼和 3 个 子集。接近 26 个骨骼子集限制的宽骨骼绑定未经测试。 - **没有材质或纹理。** Mesh 包含 UV 和顶点颜色,它们会被导入; 纹理位于此工具不会触及的独立 Roblox 资源中。 更多细节及重现方法,请参见 [`KNOWNISSUES.md`](KNOWNISSUES.md)。 ## 贡献 欢迎提交 issue 和 pull request。如果你有一个导致此工具失败的 `.mesh`, 那就是你能报告的最有用的东西 —— 请附上 `python -m roblox_mesh check yourfile.mesh` 的输出。请不要附带你不拥有 的 Roblox 资源。 开发说明和不变量在 [`CLAUDE.md`](CLAUDE.md) 中。 ## 许可证 MIT —— 参见 [`LICENSE`](LICENSE)。署名、商标和资产声明在 [`NOTICE.md`](NOTICE.md) 中。“Roblox” 是 Roblox Corporation 的商标;这是一个 非官方、无附属关系的工具。
标签:3D模型导入, Blender插件, Homebrew安装, Python, Roblox, 无后门, 网格解析, 逆向工具, 骨骼绑定