前言

  MetaHuman 已经在数字人领域里面相当成熟的解决方案。
  并且 UE 官方开发了源码工程。
  目前 github 上有不少人演示自己套用 MetaHuman 动画的效果。
  于是我自己也尝试着想将它 UE 里面的控制器动画导出来。
  然而却发现行不通。

image.png

  它的控制器关键帧是在 sequencer 里面。
  最初是尝试将 sequencer 的资源全部导出成 FBX。
  然而控制器的关键帧并没有跟随导入到 FBX 当中。

  于是我想到可以用 unreal python 读取关键帧数据导出 json
   Maya 再读取数据设置关键帧到控制器上。

  有思路之后就好办。
  之前我也写过脚本来获取 sequencer 关键帧的。
  需要注意如果想要使用 unreal python 的 API 需要开启相应的 C++ 插件。

image.png

  否则 python 会获取不到相应的 API 报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

from collections import defaultdict
import json
import os


import unreal

DIR = os.path.dirname(os.path.abspath(__file__))

def unreal_progress(tasks, label="进度", total=None):
total = total if total else len(tasks)
with unreal.ScopedSlowTask(total, label) as task:
task.make_dialog(True)
for i, item in enumerate(tasks):
if task.should_cancel():
break
task.enter_progress_frame(1, "%s %s/%s" % (label, i, total))
yield item


def main():

sequence = unreal.load_asset('/Game/Sequencer/MetaHumanSample_Sequence.MetaHumanSample_Sequence')

binding_dict = defaultdict(list)
for binding in sequence.get_bindings():
binding_dict[binding.get_name()].append(binding)


for binding in unreal_progress(binding_dict.get("Face", []), "导出 Face 数据"):

keys_dict = {}
for track in binding.get_tracks():
for section in track.get_sections():
for channel in unreal_progress(section.get_channels(), "导出关键帧"):
if not channel.get_num_keys():
continue
keys = []
for key in channel.get_keys():
frame_time = key.get_time()
frame = frame_time.frame_number.value + frame_time.sub_frame
keys.append({"frame": frame, "value": key.get_value()})

keys_dict[channel.get_name()] = keys


name = binding.get_parent().get_name()
export_path = os.path.join(DIR, "{0}.json".format(name))
with open(export_path, "w") as wf:
json.dump(keys_dict, wf, indent=4)

  上面的脚本会定位 MetaHuman 的 sequence 资源,然后导出关键帧的信息为 json

  导出会在脚本目录输出两个 json 文件。
  Maya 可以解析这个这两个 json 将关键帧设置到 控制器上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

import json
import os
import traceback


import pymel.core as pm

DIR = os.path.dirname(os.path.abspath(__file__))


def progress(seq, status="", title=""):
pm.progressWindow(status=status, title=title, progress=0.0, isInterruptable=True)
total = len(seq)
for i, item in enumerate(seq):
try:
if pm.progressWindow(query=True, isCancelled=True):
break
pm.progressWindow(e=True, progress=float(i) / total * 100)
yield item
except:
traceback.print_exc()
pm.progressWindow(ep=1)
pm.progressWindow(ep=1)


def main():


with open(os.path.join(DIR, "BP_metahuman_001.json"), "r") as rf:
data = json.load(rf)

attr_map = {"location": "t", "rotation": "r"}
status = "Import Keyframe to metahuman controller"


pm.undoInfo(ock=1)
for channel, frame_list in progress(data.items(), status=status):

has_attr = channel.count(".")

if not has_attr:

ctrl_name = channel.rsplit("_", 1)[0]
attr = "ty"
else:
parts = iter(channel.split("."))
ctrl_name = next(parts, "")
param = next(parts, "")
axis = next(parts, "")
if not axis:

attr = "t"
axis = param
else:

attr = attr_map.get(param.lower())
attr += axis.split("_")[0].lower()


attribute = pm.PyNode(".".join([ctrl_name, attr]))
for frame_data in frame_list:
frame = frame_data.get("frame")
value = frame_data.get("value")
attribute.setKey(t=frame, v=value)

pm.undoInfo(cck=1)

  加载 unreal 导出的数据。

总结

  其实整个流程不复杂,有思路就很好处理。

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 智伤帝的个人博客

打赏

  • 微信

    微信

  • 支付宝

    支付宝