jasongzy commited on
Commit
b14d5c1
·
1 Parent(s): 0e6cdcd

✨ feat: load from pre-animated files

Browse files
Files changed (4) hide show
  1. .gitmodules +3 -0
  2. apply_animation.py +38 -0
  3. auto_rig_pro +1 -0
  4. utils.py +65 -0
.gitmodules ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ [submodule "auto_rig_pro"]
2
+ path = auto_rig_pro
3
+ url = https://github.com/jasongzy/auto_rig_pro
apply_animation.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from glob import glob
3
+
4
+ from tqdm import tqdm
5
+
6
+ from utils import HiddenPrints, bpy, load_mixamo_anim, remove_all, reset, select_objs, update
7
+
8
+ if __name__ == "__main__":
9
+ character_dir = "./character_vroid_refined"
10
+ animation_dir = "./animation"
11
+ output_dir = "./animated_vroid"
12
+ os.makedirs(output_dir, exist_ok=True)
13
+
14
+ reset()
15
+ character_list = sorted(glob(os.path.join(character_dir, "*.fbx")))
16
+ assert character_list
17
+ animation_list = sorted(glob(os.path.join(animation_dir, "*.fbx")))
18
+ assert animation_list
19
+ for char_file in tqdm(character_list, dynamic_ncols=True, desc="Character"):
20
+ for anim_file in tqdm(animation_list, dynamic_ncols=True, leave=False, desc="Animation"):
21
+ with HiddenPrints():
22
+ remove_all()
23
+ objs = load_mixamo_anim(char_file, anim_file, do_retarget=True, inplace=False)
24
+ update()
25
+ get_base_name = lambda s: os.path.splitext(os.path.basename(s))[0]
26
+ output_filename = f"{get_base_name(char_file)}-{get_base_name(anim_file)}"
27
+ # bpy.ops.wm.save_as_mainfile(filepath=os.path.join(output_dir, f"{output_filename}.blend"))
28
+ select_objs(objs, deselect_first=True)
29
+ bpy.ops.export_scene.fbx(
30
+ filepath=os.path.join(output_dir, f"{output_filename}.fbx"),
31
+ check_existing=False,
32
+ use_selection=True,
33
+ use_triangles=True,
34
+ add_leaf_bones=False,
35
+ bake_anim=True,
36
+ # path_mode="COPY",
37
+ # embed_textures=True,
38
+ )
auto_rig_pro ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 614be17f80cda54f58f378b7bd28f4b4c1ba4ec5
utils.py CHANGED
@@ -242,6 +242,13 @@ def remove_empty_vgroups(mesh_obj_list: "list[Object]"):
242
  vertex_groups.remove(vertex_groups[i])
243
 
244
 
 
 
 
 
 
 
 
245
  def mesh_quads2tris(obj_list: "list[Object]" = None):
246
  if not obj_list:
247
  obj_list = bpy.context.scene.objects
@@ -249,3 +256,61 @@ def mesh_quads2tris(obj_list: "list[Object]" = None):
249
  if obj.type == "MESH":
250
  with Mode("EDIT", obj):
251
  bpy.ops.mesh.quads_convert_to_tris(quad_method="BEAUTY", ngon_method="BEAUTY")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  vertex_groups.remove(vertex_groups[i])
243
 
244
 
245
+ def set_action(armature_obj: Object, action: Action):
246
+ if not armature_obj.animation_data:
247
+ armature_obj.animation_data_create()
248
+ armature_obj.animation_data.action = action
249
+ return armature_obj
250
+
251
+
252
  def mesh_quads2tris(obj_list: "list[Object]" = None):
253
  if not obj_list:
254
  obj_list = bpy.context.scene.objects
 
256
  if obj.type == "MESH":
257
  with Mode("EDIT", obj):
258
  bpy.ops.mesh.quads_convert_to_tris(quad_method="BEAUTY", ngon_method="BEAUTY")
259
+
260
+
261
+ def get_enabled_addons() -> "list[str]":
262
+ return [x.module for x in bpy.context.preferences.addons]
263
+
264
+
265
+ def enable_arp(armature_obj: Object, addon_path=os.path.join(os.path.dirname(__file__), "auto_rig_pro")):
266
+ import sys
267
+
268
+ assert os.path.isfile(os.path.join(addon_path, "__init__.py")), "Auto-Rig Pro not found"
269
+ dirname, addon_name = os.path.split(addon_path)
270
+ if addon_name in get_enabled_addons():
271
+ return
272
+ sys.path.insert(0, dirname)
273
+ with Mode("POSE", armature_obj):
274
+ # import addon_utils
275
+ # addon_utils.enable(addon_name)
276
+ bpy.ops.preferences.addon_enable(module=addon_name)
277
+
278
+
279
+ def retarget(source_armature: Object, target_armature: Object, inplace=False):
280
+ enable_arp(target_armature)
281
+ scn = bpy.context.scene
282
+ scn.source_rig = source_armature.name
283
+ if inplace:
284
+ scn.arp_retarget_in_place = True
285
+ scn.target_rig = target_armature.name
286
+ bpy.ops.arp.auto_scale()
287
+ bpy.ops.arp.build_bones_list()
288
+ hips = scn.bones_map_v2["mixamorig:Hips"]
289
+ scn.bones_map_index = list(scn.bones_map_v2).index(hips)
290
+ hips.set_as_root = True
291
+ bpy.ops.arp.retarget()
292
+ return target_armature
293
+
294
+
295
+ def load_mixamo_anim(char_file: str, anim_file: str, do_retarget=False, inplace=False, to_tris=False):
296
+ char_objs = load_file(char_file) if isinstance(char_file, str) else char_file
297
+ char_armature = get_armature_obj(char_objs)
298
+
299
+ anim_objs = load_file(anim_file)
300
+ anim_armature = get_armature_obj(anim_objs)
301
+ print(anim_armature)
302
+ print(anim_armature.animation_data)
303
+ assert anim_armature.animation_data is not None and len(bpy.data.actions) > 0, f"Animation not found in {anim_file}"
304
+
305
+ set_action(char_armature, anim_armature.animation_data.action)
306
+ if do_retarget:
307
+ retarget(anim_armature, char_armature, inplace=inplace)
308
+ for action in bpy.data.actions:
309
+ if action is not char_armature.animation_data.action:
310
+ bpy.data.actions.remove(action, do_unlink=True)
311
+ for obj in anim_objs:
312
+ bpy.data.objects.remove(obj, do_unlink=True)
313
+
314
+ if to_tris:
315
+ mesh_quads2tris(char_objs)
316
+ return char_objs