Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

MhRigCreator

从符合 MetaHuman 官方拓扑的 Mesh 构建 MetaHuman 绑定与 DNA 的 Maya 工具。

日常拿到的只有一套模型(头部 9 个 LOD0 网格 + 身体网格),拿不到 DNA。本工具把 Epic 官方 MetaHumanForMaya 插件里原本只存在于交互式 UI 的 conform 流程串成可脚本化的流水线:

拓扑校验 → 骨骼重定位 → 表情重算 → 输出 DNA → 装配 Maya 绑定(头身骨架合并)

环境要求

依赖 说明
Maya 2024 / 2025 / 2026 / 2027(对应 Python 3.10 / 3.11 / 3.11 / 3.13)
MetaHumanForMaya Epic 官方插件,需已安装。工具从它加载 dna / dnacalib2 / rdf / frt_api / mh_assemble_lib / nls 与六个 .mll
身体模板 DNA 插件不自带 MHB.1 原型;仓库自带 Assets/body.dna,可换成你自己体型的,见下文

mhrigcreator.env.setup() 负责定位插件、解析 env/<platform>-<maya版本>.json、注入 sys.path 与 DLL 目录、加载插件。它按当前解释器的 Python 版本挑选配置,所以在 Maya、 mayapy、以及版本匹配的独立 CPython 里都能工作(独立解释器下只有 frt_api / mh_assemble_lib 不可用,它们依赖 maya.cmds)。

如果插件不在 ~/Documents/maya/modules/MetaHumanForMaya,用环境变量指定:

set MHRIGCREATOR_MH4M_ROOT=<MetaHumanForMaya 根目录>

不要引入 OpenRigLogic。 它的 dna 是 13.2.5,与插件的 10.1.4 冲突 —— 这正是 MetaHumanForMaya.py:dependency_checks() 警告的场景。它只适合作为格式文档与离线交叉验证。

快速开始

图形界面(推荐)

from mhrigcreator import ui
ui.show()

三个按钮对应流水线三步:校验拓扑 → 构建 DNA → 装配绑定。第三步会新建场景,面板会先弹确认框, 且在当前场景有未保存改动时额外警告。

脚本

from mhrigcreator import assemble, character, env

env.setup()

# 步骤 1+2:校验拓扑并从当前场景网格构建头身 DNA
result = character.build_character("D:/out", character_name="my_char")
print(result.format())
assert result.ok

# 步骤 3:装配可动绑定(注意:会新建场景)
asm = assemble.assemble_character(result.head_dna, result.body_dna)
print(asm.format())

只想校验拓扑:

from mhrigcreator import env, topology

env.setup()
head_report, body_report = topology.check_scene()
print(head_report.format())
if not head_report.ok:
    raise RuntimeError("拓扑不匹配,无法构建绑定")

无头批处理

整条流水线不依赖 GUI,可以用 mayapy 跑(tests/check_assemble.py 就是这么验证的):

"C:/Program Files/Autodesk/Maya2024/bin/mayapy.exe" your_batch_script.py

实测耗时

MH.6 头部(870 骨骼 / 1673 表情 / 50 网格 / 8 LOD)+ MHB.1 身体(342 骨骼),Maya 2024:

阶段 耗时
拓扑校验(10 个网格逐面比对) ~1 s
身体:骨骼定位 + 写入 + LOD 重生成 4.9 s
头部:骨骼定位 0.5 s
头部:构造 RigDataHandler 2.0 s
头部:写中性骨骼(含 1673 个表情的 sculpt 物化) 5.2 s
头部:全表情重算 19.6 s
头部:LOD 重生成(ONNX) 0.2 s
头部:导出 + 自检 1.5 s
构建 DNA 合计 ~36 s
装配绑定(头 + 身 + 接缝重建 + RigLogic) ~31 s

JM 关节匹配优化(默认关闭)会额外增加数十分钟,量级完全不同。

输出

产物 说明
<name>_head.dna MH.6 头部 DNA,可直接导入 UE
<name>_body.dna MHB.1 身体 DNA
Maya 场景 1185 根骨骼的单一骨架(342 身体 + 870 头部 − 27 共享)、54 个 skinCluster、blendShape、head_rl4Embedded / body_rl4Embedded 两个 RigLogic 节点、178 个 faceboard 控制器

装配产物已验证可动:把 CTRL_C_jaw.ty 拨到 1.0,头部 24049 个顶点里有 20824 个发生位移,最大 3.65 cm。

输入网格不全也能产出完整 DNA —— 缺失的装饰网格会用蒙皮补出来,见输入网格不全时怎么办。

产出 DNA 会比原型小(实测 43.4 MB vs 51.8 MB)。这是正常的:表情重算按 config.json 的剪枝 阈值(corrective blend shape 0.002 cm)丢掉了视觉无关的小增量,实测保留约 79% 的 CBS 增量与 99% 的关节增量。export.verify_dna() 会核对这两个比例,真正塌掉的 behavior 层会被拦下来。

自检脚本

前三个不依赖 pytest,可直接运行;在 Maya 里 import 后调 main() 会额外跑 Maya 相关项。

uv run python tests/check_foundation.py    # 环境注入、模板资源、DNA 读写
uv run python tests/check_joint_math.py    # 纯 Python 矩阵与 Maya 一致性、世界↔局部往返
uv run python tests/check_joint_fit.py     # 骨骼定位精度(头部用真值 DNA,身体用合成仿射变形)

另两个需要 frt_api(依赖 maya.cmds),用 mayapy 跑:

# 缺失网格路径:角色分级、拒绝/补齐行为、补出网格的质量。不碰 Maya 场景
"C:/Program Files/Autodesk/Maya2024/bin/mayapy.exe" tests/check_partial_meshes.py

# 装配自检。注意它会新建场景,别在有未保存改动的 GUI 会话里跑
"C:/Program Files/Autodesk/Maya2024/bin/mayapy.exe" tests/check_assemble.py

Assets/ 是什么

文件 角色
SM_MH_test.ma 测试输入网格:9 个头部 LOD0 网格 + body_lod0_mesh,无骨骼
head.dna MH.6 角色 DNA。头部精度打分的真值
body.dna MHB.1 身体 DNA。身体流水线的默认模板

两份 DNA 是配套的同一角色(27 根共享骨骼世界位置一致到 3.5e-5 cm)。

head.dna 当真值用:它与插件自带的原型 res/MH.6/template.dna 结构完全一致(870 骨骼 / 50 网格 / 8 LOD / 782 BS 通道 / 545 PSD / 38 RBF pose)但几何不同(网格差 2.87 cm、骨骼差 4.01 cm),是一份货真价实的"另一个角色"。于是头部打分可以做到:从原型拟合到它的网格,再逐骨骼 对比它自己的骨骼位置 —— 骨骼位置由 Epic 管线产出,不是我们造的。

body.dna 当模板用:MHB.1 是参数化体型,没有唯一"原型"。这份是身高约 154.7 cm(head 骨骼 高度)、网格高 142.4 cm 的体型。身体精度打分改用合成仿射变形 + 解析真值,见下节。

身体模板:为什么可以随便换,以及换的时候要注意什么

MetaHumanForMaya 不带 MHB.1 身体原型(官方假定身体来自 MetaHuman Creator 导出); mh_pose_editor/resources/reference/{mh6,mhb1}.dna 只是结构校验参考,几何被清零,不能当 模板用。

mhrigcreator.resources.find_body_archetype_dna() 按以下顺序查找:

  1. 环境变量 MHRIGCREATOR_BODY_ARCHETYPE_DNA
  2. resources/body_archetype.dna
  3. Assets/body.dna(默认)

换模板安全,因为流水线会覆盖全部与体型相关的数据:LOD0 全部 30455 个顶点、342 根骨骼的 局部平移、LOD1–3 网格。模板保留下来的部分实测与体型无关或无风险:

保留项 换模板的风险 实测依据
骨骼名 / 层级 / 网格名 / LOD 映射 无 两份 MHB.1 完全一致
中性骨骼旋转 无 旋转矩阵逐元素差 2.6e-06,与体型无关(注意必须比矩阵,比欧拉角会得到假的 360° 差)
behavior 层(176 raw control / 215 RBF pose / 72 solver / 72 joint group) 无 计数与关节增量值数量完全一致
UV 无 拓扑一致
蒙皮权重 有 两份不同体型的 MHB.1 抽样 4351 顶点中 3708 个(85%)权重不同,最大差 0.165

蒙皮那条是唯一要留意的:差异成片分布(pelvis / spine_02-04 / 各 *_twist_* 100% 不同, 脚部 73%),因为 Epic 是按体型分别调的权重,而流水线没有重算蒙皮的手段。 所以请选一份体型接近你日常绑定目标的 MHB.1,不必纠结"是不是官方默认"。

输入网格不全时怎么办

只需要 4 个网格就能构建:head_lod0_mesh、teeth_lod0_mesh、eyeLeft_lod0_mesh、 eyeRight_lod0_mesh(身体另需 body_lod0_mesh)。其余 5 个是"装饰网格",缺了照样能建。

网格角色从数据推导(topology.mesh_roles()),不是硬编码 —— REQUIRED 取自官方顶点映射表 实际引用的网格,EYE 取自眼球球心拟合的配置,换 rig 版本会自动跟着变:

角色 网格 缺失后果
REQUIRED head_lod0_mesh、teeth_lod0_mesh 拒绝构建 —— 官方映射表依赖它们定位 812 根骨骼
EYE eyeLeft_lod0_mesh、eyeRight_lod0_mesh 警告。眼球球心拟合跳过,眼球骨骼链退化(整体平均 0.0073 → 0.0092 cm),仍可构建
FILLABLE saliva、eyeshell、eyelashes、eyeEdge、cartilage 警告。对骨骼定位几乎零影响,网格用蒙皮权重自动补出来

为什么装饰网格对骨骼没影响

实测它们的蒙皮骨骼独占数全为 0 —— 每根骨骼的证据都能从别的网格拿到:

网格 顶点 映射表骨骼 有蒙皮骨骼 独占蒙皮
head_lod0_mesh 24049 800 639 584
teeth_lod0_mesh 4246 12 17 15(舌头)
eyeLeft / eyeRight 770 0 2 2(EyeParallel / Pupil)
saliva 660 0 2 0
eyeshell 552 0 21 0
eyelashes 2144 0 21 0
eyeEdge 268 0 21 0
cartilage 386 0 55 0

只给 4 个网格 vs 全 9 个:整体平均 0.053687 vs 0.053715 cm(差 2.8e-5 cm),受影响最大的单根 是 head,偏移 0.24 mm。不是严格零 —— head 与眼周骨骼在装饰网格上也有权重 —— 但比本项目最差 骨骼的误差还小一个数量级。

缺失网格的几何:用蒙皮补出来

不补的后果很实在:输出 DNA 里它们会保留原型几何,睫毛会浮在眼睛外面。因为流水线只改骨骼 平移,线性混合蒙皮退化成一个加权平移和,可以直接算出来:

p'_v = p_v + Σ_j w_vj · (T'_j − T_j) / Σ_j w_vj

实测(缺 5 个装饰网格,与真实网格对比):

网格 保留原型 蒙皮补出 改善
eyelashes_lod0_mesh 1.6902 cm 0.1218 cm 13.9x
eyeEdge_lod0_mesh 1.6584 cm 0.1378 cm 12.0x
eyeshell_lod0_mesh 1.6614 cm 0.1540 cm 10.8x
cartilage_lod0_mesh 1.6694 cm 0.1692 cm 9.9x
saliva_lod0_mesh 1.8310 cm 0.2111 cm 8.7x

默认开启(HeadBuildOptions.fill_missing_meshes=True)。补出来的网格终究是推测而非艺术家做的 模型,所以报告里会明确列出哪些是补的、蒙皮覆盖率多少 —— 不让它悄悄发生。关掉开关则会警告 "输出 DNA 里它们保留原型几何、与新角色对不上"。

骨骼定位(本工具的核心)

DNA 的中性骨骼只存局部平移 + 局部旋转。"网格变了骨骼该去哪"没有单一答案,按证据强度 分级叠加,一根骨骼由第一个能定位它的策略接管:

优先级 策略 依据
0 外部硬约束 头身共享骨骼,由另一侧拟合结果回灌
1 官方顶点映射 Epic 自带 vertex_joint_mapping/*.csv
2 眼球球心拟合 眼球网格的代数球面最小二乘
3 链上插值 从原型 DNA 自身反解 lerp(A, B, w)
4 蒙皮权重迁移 骨骼跟着它影响的皮肤走
5 子树相似拟合 已定位子孙的相似变换(旋转 + 均匀缩放 + 平移);无子孙则继承最近已定位祖先的变换
6 随父刚性跟随 兜底。实测头身两侧都已归零 —— 342 / 870 根骨骼全部有正向依据

只改平移,不改旋转 —— 与 Epic 一致。DNA behavior 层里每个表情的关节变换都是相对中性 姿态的增量,改动中性旋转会让全部表情失效。

绑定锚点(位于世界原点的根骨骼,如 MHB.1 的 root)被排除在所有策略之外:它是地面/世界 锚点而非解剖关节。不排除的话子树拟合会把整套骨架拖偏 5.09 cm。而 MH.6 头部骨架的根 spine_04 是真会动的解剖关节(原型 y=117.62),必须参与拟合。

子树拟合必须带缩放项。 早期版本只求旋转(纯刚性 Procrustes),在体型缩放场景下系统性偏移。 用受控实验隔离过:把 thigh_l 的全部子孙放到精确位置、只让该策略反推 thigh_l ——

形变 仅旋转 旋转 + 缩放
等比 1.04 2.0553 cm 0.0000 cm(s 正确解出 1.04000)
非等比 1.02/1.09/1.05 4.6391 cm 0.6471 cm

没有子孙的骨骼继承最近已定位祖先的变换。 MHB.1 的 head 是叶子骨骼(面部骨骼在头部 DNA 里),neck_02 唯一子孙就是 head,neck_01 的子孙全是这两根 —— 整条颈链拿不到样本,原先只能 落到"随父跟随"(等于假设该段一点没缩放)。改为继承 spine_05 的 (s, R) 后(链式传递 neck_01←spine_05 → neck_02 → head,实测 s=1.0399):

骨骼 随父跟随 继承祖先变换
head 1.057 cm 0.048 cm
neck_01 0.652 cm 0.041 cm
neck_02 0.848 cm 0.045 cm

实测精度

头部:原型 res/MH.6/template.dna → Assets/head.dna 的网格,以它自己的骨骼位置为真值, 870 根骨骼,逐骨骼欧氏距离(cm):

策略 骨骼数 平均 中位 p95 最大
0 外部约束(头身共享) 27 0.0000 0.0000 0.0000 0.0000
1 官方映射 surface 770 0.0001 0.0000 0.0000 0.0046
1 官方映射 volumetric 42 0.0568 0.0597 0.1077 0.1629
2 眼球球心拟合 2 0.0159 0.0172 0.0172 0.0172
3 链上插值 16 0.1641 0.0520 0.6969 0.6969
4 蒙皮权重迁移 11 0.0709 0.0483 0.3190 0.3190
5 子树相似拟合 2 0.2386 0.3179 0.3179 0.3179
合计 870 0.0073 0.0000 0.0483 0.6969
不拟合的基线 870 1.8045 1.7757 2.2596 4.0144

身体:合成仿射变形 + 解析真值(见 自检脚本),342 根骨骼:

策略 骨骼数 平均 中位 p95 最大
3 链上插值 32 0.0597 0.0545 0.1465 0.1465
4 蒙皮权重迁移 285 0.0631 0.0281 0.2442 0.6613
5 子树相似拟合 24 0.0708 0.0540 0.1480 0.1480
锚点(有意不动) 1 0.0000 0.0000 0.0000 0.0000
合计 342 0.0632 0.0358 0.1856 0.6613
不拟合的基线 342 3.9408 4.2703 5.3981 6.1867

以上身体数据用的是等比 1.04 的形变 —— 对应我手上两份 MHB.1 之间约 4.4% 的真实体型差。 把形变加严到非等比 1.03/1.09/1.05 + 剪切 0.02 时,合计平均 0.1909、最大 3.9615 cm,误差集中在 子树相似拟合那 24 根上(平均 1.2689):非等比形变超出了"均匀缩放"的表达能力,这是该策略的 固有上限。

读这些数字时注意:头部基线是 1.80 cm 而不是更早文档里的 5.18 cm,原因是换了一份更接近 原型的真值角色(网格差 2.87 cm,而非 7.01 cm),不是算法变好了。跨真值比较绝对值没有意义, 要比的是「改善倍数」与「按策略分解」。

头身共享骨骼约束(默认开启)

头部 DNA 与身体 DNA 有 27 根同名骨骼(spine_04~upperarm_* 与颈链)。实测配套的一对 DNA 里它们的世界位置一致到 3.5e-5 cm —— 官方管线本身就满足这个约束。所以流水线先拟合身体、 再把这 27 根位置作为优先级最高的硬约束回灌头部(character.build_character 自动完成)。

头部网格几乎覆盖不到肩背,光靠它推断这批骨骼很不准。收益(同一真值下对比):

指标 无约束 有约束
870 根整体平均 0.0537 cm 0.0073 cm
870 根整体最大 3.3761 cm 0.6969 cm
靠蒙皮迁移兜底的骨骼数 31 根(平均 1.0698 cm) 11 根(平均 0.0709 cm)
靠子树拟合兜底的骨骼数 9 根(平均 0.9342 cm) 2 根(平均 0.2386 cm)

加约束后误差最大的是 FACIAL_C_Jaw / FACIAL_C_LowerLipRotation(0.70 cm)—— 下颌旋转中心 在头骨内部,没有网格直接对应,只能靠链上插值推断。它在官方 user_defined_joints 里,本来就是 要人工摆的。

试过但被数据否掉的方案

  • 用一环邻域极分解旋转 volumetric 偏移。 CSV 里的 delta 实测就是模板空间的 joint_ws - vertex_ws(吻合到平均 0.045 cm),而真值里这个偏移会随局部形状转动 (平均差 0.27 cm),所以"跟着局部帧转"看起来很合理。实测明显更差:平均 0.54 cm、 最大 2.08 cm(额头与脸颊内侧最糟)。密集网格上的局部帧对形状变化过于敏感。 → 保留 Epic 原始规则。

已知限制

  1. 只改骨骼平移,不改朝向。 输入网格与原型比例差异过大时会有轻微蒙皮滑动。
  2. UV 沿用模板。 dnacalib2 没有写纹理坐标的命令;输入网格若改了 UV,拓扑校验只给 warning,输出 DNA 的 UV 仍是原型的。
  3. 身体骨骼定位无官方映射表,靠蒙皮权重迁移 + 链上插值 + 子树相似拟合三级策略。真实体型差 量级下平均 0.06 cm、最大 0.66 cm;形变越偏离"均匀缩放"(非等比、剪切)误差越大,因为子树 相似拟合只能表达均匀缩放。若日后拿到官方身体映射表,替换策略 4/5 即可。
  4. 绑定 DBName 固定 MH.6 / MHB.1。 resources.py 已按 DBName 分目录,扩展 MH.4 需另备 template.dna(插件的 MH.4 目录缺这个文件)。

开发注意

  • Maya 内代码必须兼容 Python 3.10(Maya 2024)。pyproject.toml 的 requires-python 是 >=3.10;venv 只用于离线 DNA 工具与类型检查。

  • dna reader 的生命周期。 SWIG 包装层用 __del__ 调 C++ destroy(),被隐式回收的 reader 会让进程段错误:

    db = dna_io.read(path).getDBName()      # 崩溃:reader 是临时对象

    所以统一走 mhrigcreator.dna_io:它把 (reader, stream) 登记进注册表防止隐式回收,用完 调 dna_io.release(reader),或用 with dna_io.opened(path) as reader:。

  • dna / dnacalib2 / rdf 不抛异常,只设全局 Status 位。用 dna_io.check_status(dna, dnacalib, context="...") 包住调用,否则错误会静默传播。

  • additional assemble script(AAS)有三个版本,只有一个是对的。

    路径 签名 能用吗
    mh_expression_editor/res/<rig>/rig_editing/additional_assemble_script.py 2 个位置参数 ✗ lib 用 3 个参数调用,直接报错
    mh_assemble_lib/resources/<rig>/ass_json.py 3 个位置参数 △ 只摆位,不接 GUI → CTRL_expressions,RigLogic 不会被驱动
    mh_character_assembler/assets/<rig>/ass_json.py 3 个位置参数 ✓ 摆位 + connect_expressions(),这才是装配该用的

    resources.HeadResources.aas_character_json 指向正确的那个。另外 AAS 是尽力执行的: 它失败只降级成警告,不会中断装配 —— mh_assemble_lib 的参考 AAS 在只装头部的场景里会因为 对 GRP_C_eyesAim 调 cmds.parent(..., world=True)(该节点本就在世界层级)而报错,那是它对 场景结构的假设问题,不值得为此让整次装配失败。

  • embeddedRL4 的 cs 参数要写逗号分隔的方向全名("left,up,front")。写缩写会让插件报 Unknown direction 然后静默退回默认值。

  • 批处理下 cmds.refresh() 不推动 DG。 要验证形变必须显式 cmds.dgdirty(allPlugs=True) + cmds.dgeval("<mesh>Shape.outMesh"),否则读到的是缓存,会误判 RigLogic 没生效。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages