Repository navigation
Expand file tree
/
Copy pathmaya_export_blenshape_objs.py
More file actions
executable file
·388 lines (299 loc) · 14 KB
/
Copy pathmaya_export_blenshape_objs.py
File metadata and controls
executable file
·388 lines (299 loc) · 14 KB
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# coding=utf-8
# """
# Export .obj files for target shapes for a blendshape node in Maya.
# Author: Zhao Yafei (zhaoyafei0210@gmail.com)
# """
import os
import os.path as osp
import json
import maya.cmds as cmds
import maya.mel as mel
from pprint import pprint
def get_current_scene_name():
"""
Get current scene name.
Args:
None.
Returns:
str
Scene name.
"""
scene_name = cmds.file(query=True, sceneName=True, shortName=True)
scene_name = osp.splitext(scene_name)[0]
return scene_name
def get_blendshape_keys_list(blendshape_node_name, sort_keys=False):
"""
Get name list (blendshape keys) of target-shapes/morphing-targets of blendshape.
Args:
blendshape_node_name: str
Name of blend shape deformer (blendShape Node) in Maya.
sort_keys: bool
Whether to sort the keys by name.
Returns:
list of str
Name list of target shapes of the input blendshape node.
"""
# Maya Mel cmd
# cmd = 'listAttr -k -m -st "weight" ' + blendshape_node_name
# blendshape_keys_list = mel.eval(cmd)
# Maya python cmd
blendshape_keys_list = cmds.listAttr(
blendshape_node_name, st='weight', multi=True, keyable=True)
if sort_keys:
blendshape_keys_list.sort()
return blendshape_keys_list
def get_blendshape_geometry_name(blendshape_node_name):
"""
Get the geometry name binded with the specified blendshape.
Args:
blendshape_node_name: str
Name of blend shape deformer (blendShape Node) in Maya.
Returns:
list of str
Name list of geometry of the input blendshape node.
"""
# Maya python cmd
geometry_list = cmds.blendShape(
blendshape_node_name, query=True, geometry=True)
geometry_list.sort()
return geometry_list
def make_blendshape_keys_settable(blendshape_node_name, save_dir, blendshape_keys_list=None, forced=False):
"""
Make keyable/settable a list of blendshape keys (name of target-shapes/morphing-targets)
by unlocking/makeing settable/breaking connections.
Args:
blendshape_node_name: str
Name of blend shape deformer (blendShape Node) in Maya.
save_dir: str
Where to save the restore info file
blendshape_keys_list: list of str
List of blendshape keys (weight names).
forced: bool
If True, will not ask user to check.
Returns: tuple of (int, str)
str: file path to restore info of all the modifications to make blendshape settable.
"""
need_restore = -1
info_str = ""
if forced:
input_words = 'y'
else:
pprint("""\n===> Are you sure to make blendshape attributes settable?
This will try to make attributes settable by making them keyable, unlocking them,
breaking their connection. Although this script will try to save the modification info,
and use these info to restore all the attribute state back,
there still is risk not able to restore everything.
Please make a copy of your Maya Project (.ma,.mb), and operates in the project copy.
If you are aware of what you are doing, input 'yes' or 'y' to continue;
Otherwise, this script will exit.""")
input_words = raw_input('yes or no:')
input_words = input_words.lower()
if not(input_words == 'yes' or input_words == 'y'):
need_restore = -1
info_str = "make_blendshape_keys_settable() is canceled"
else:
scene_name = get_current_scene_name()
need_restore = True
settable_restore_info_list = []
if blendshape_keys_list is None:
blendshape_keys_list = get_blendshape_keys_list(
blendshape_node_name)
for k in blendshape_keys_list:
key_name = "{}.{}".format(blendshape_node_name, k)
restore_info_dict = {'key_name': key_name}
if not cmds.getAttr(key_name, settable=True):
pprint('---> Attribute {} is not settable'.format(key_name))
pprint(' Try to make it keyable and unlocked')
keyable = cmds.getAttr(key_name, keyable=True)
lock = cmds.getAttr(key_name, lock=True)
restore_info_dict["keyable"] = keyable
restore_info_dict["lock"] = lock
# set attr keyable and unlock attr
cmds.setAttr(key_name, keyable=True, lock=False)
if not cmds.getAttr(key_name, settable=True):
pprint('---> Attribute {} is still not settable'.format(key_name))
pprint(' Try to break all the connections upon it.')
# break all connections:
# cmd = 'CBdeleteConnection ' + key_name
# mel.eval(cmd)
restore_info_dict["connections_from"] = cmds.listConnections(
key_name, d=False, s=True, p=True)
restore_info_dict["connections_to"] = cmds.listConnections(
key_name, d=True, s=False, p=True)
if isinstance(restore_info_dict["connections_from"], list) and len(restore_info_dict["connections_from"]) > 0:
for attr in restore_info_dict["connections_from"]:
cmds.disconnectAttr(attr, key_name)
if isinstance(restore_info_dict["connections_to"], list) and len(restore_info_dict["connections_to"]) > 0:
for attr in restore_info_dict["connections_to"]:
cmds.disconnectAttr(key_name, attr)
settable_restore_info_list.append(restore_info_dict)
if len(settable_restore_info_list) > 0:
restore_filename = osp.join(
save_dir, '{}.blendshape.restore_info.txt'.format(scene_name))
pprint('\n===> save blendshape restore info into file: ')
pprint(restore_filename)
fp = open(restore_filename, 'w')
fp.write(json.dumps(settable_restore_info_list, indent=2) + '\n')
fp.close()
need_restore = 1
info_str = restore_filename
else:
need_restore = 0
info_str = "Nothing to restore"
return (need_restore, info_str)
def restore_settable_modification(restore_info):
"""
Restore all the modifications to make keyable/settable a list of
blendshape keys (name of target-shapes/morphing-targets)
by unlocking/makeing settable/breaking connections.
Args:
restore_info: list of dict or str
list of dict: Restore info of all the modifications to make blendshape settable.
str: path to the restore info file
Returns:
None.
"""
if isinstance(restore_info, list):
settable_restore_info_list = restore_info
elif isinstance(restore_info, str) and osp.isfile(restore_info):
with open(restore_info, 'r') as fp:
settable_restore_info_list = json.load(fp)
fp.close()
else:
pprint("===> restore_settable_modification(): valid input ")
return
for restore_info_dict in settable_restore_info_list[::-1]:
key_name = restore_info_dict['key_name']
if "connections_to" in restore_info_dict and isinstance(restore_info_dict["connections_to"], list) and len(restore_info_dict["connections_to"]) > 0:
for attr in restore_info_dict["connections_to"]:
cmds.connectAttr(key_name, attr)
if "connections_from" in restore_info_dict and isinstance(restore_info_dict["connections_from"], list) and len(restore_info_dict["connections_from"]) > 0:
for attr in restore_info_dict["connections_from"]:
cmds.connectAttr(attr, key_name)
# restore attr keyable info and lock info
cmds.setAttr(
key_name, keyable=restore_info_dict["keyable"], lock=restore_info_dict["lock"])
def export_blendshape_target_shapes(blendshape_node_name,
mesh_node_name,
save_dir='./',
blendshape_keys_list=None,
force_triangulate=False,
skip_existing_files=True):
"""
Export target shapes into .obj files for a blendshape node (name of target-shapes/morphing-targets).
Args:
blendshape_node_name: str
Name of blend shape deformer (blendShape Node) in Maya.
mesh_node_name: str or list of str
Name of mesh/shape in Maya;
save_dir: str
Directory to save .obj files for all target shapes of a blendshape node.
blendshape_keys_list: list of str or None
List of blendshape keys (names of target shapes) to export. If =None, export all target shapes.
Returns:
None.
"""
pprint("===> blendshape_node_name: {}".format(blendshape_node_name))
pprint("===> mesh_node_name: {}".format(mesh_node_name))
if not osp.exists(save_dir):
os.makedirs(save_dir)
scene_name = get_current_scene_name()
blendshape_keys_filename = osp.join(
save_dir, '{}.blendshape.{}.txt'.format(scene_name, blendshape_node_name))
if blendshape_keys_list is None:
blendshape_keys_list = get_blendshape_keys_list(blendshape_node_name, sort_keys=True)
for idx, blendshape in enumerate(blendshape_keys_list):
pprint('---> {}: {}'.format(idx+1, blendshape))
pprint('\n===> {} blendshape keys in total'.format(len(blendshape_keys_list)))
pprint('\n===> save blendshape keys into file: ')
pprint(blendshape_keys_filename)
fp = open(blendshape_keys_filename, 'w')
fp.write('\n'.join(blendshape_keys_list) + '\n')
fp.close()
# 0. make all blendshape keys/attributes settable
need_restore, restore_info = make_blendshape_keys_settable(
blendshape_node_name, save_dir, blendshape_keys_list, forced=False)
if need_restore < 0:
exit()
# 1. Export the neutral pose.
obj_filename = osp.join(save_dir, '00_neutral.obj')
pprint('===> Export the neutral pose into {}.'.format(obj_filename))
curr_time = 0
cmds.currentTime(curr_time)
if osp.isfile(obj_filename) and skip_existing_files:
pprint('===> skip existing file: {}'.format(obj_filename))
else:
for k in blendshape_keys_list:
v = 0.
key_name = "{}.{}".format(blendshape_node_name, k)
cmds.setAttr(key_name, v)
cmds.setKeyframe(key_name)
# Select Mesh before export
cmds.select(mesh_node_name)
if force_triangulate:
pprint('===> Force to export triangulated mesh')
if isinstance(mesh_node_name, list):
cmd = "polyTriangulate -ch 1 " + ' '.join(mesh_node_name)
else:
cmd = "polyTriangulate -ch 1 " + mesh_node_name
pprint('===> run MEL command: ')
pprint(cmd)
mel.eval(cmd)
cmd = """file -force -options "groups=1;ptgroups=1;materials=1;smoothing=1;normals=1"
-typ "OBJexport" -pr -es " {}";""".format(obj_filename)
# cmd = """file -force -options "groups=1;ptgroups=1;materials=1;smoothing=1;normals=1"
# -typ "OBJexport" -pr -es " {}/{}.obj";""".format(save_dir, "00_neutral")
pprint('===> run MEL command: ')
pprint(cmd)
mel.eval(cmd)
# 2. Export blendshape target shapes.
# for curr_k in blendshape_keys_list[:5]:
for curr_k in blendshape_keys_list:
obj_filename = osp.join(save_dir, curr_k+'.obj')
# if not curr_k.startswith('jaw'):
# continue
pprint('===> Export the blendshape key: {}'.format(curr_k))
curr_time += 1
cmds.currentTime(curr_time)
if osp.isfile(obj_filename) and skip_existing_files:
pprint('===> skip existing file: {}'.format(obj_filename))
else:
for k in blendshape_keys_list:
v = 1.0 if curr_k == k else 0.
key_name = "{}.{}".format(blendshape_node_name, k)
cmds.setAttr(key_name, v)
cmds.setKeyframe(key_name)
# Select Mesh before export
cmds.select(mesh_node_name)
if force_triangulate:
pprint('===> Force to export triangulated mesh')
if isinstance(mesh_node_name, list):
cmd = "polyTriangulate -ch 1 " + ' '.join(mesh_node_name)
else:
cmd = "polyTriangulate -ch 1 " + mesh_node_name
pprint('===> run MEL command: ')
pprint(cmd)
mel.eval(cmd)
cmd = """file -force -options "groups=1;ptgroups=1;materials=1;smoothing=1;normals=1"
-typ "OBJexport" -pr -es " {}";""".format(obj_filename)
# cmd = """file -force -options "groups=1;ptgroups=1;materials=1;smoothing=1;normals=1"
# -typ "OBJexport" -pr -es " {}/{}.obj";""".format(save_dir, curr_k)
pprint('===> run MEL command: ')
pprint(cmd)
mel.eval(cmd)
if need_restore==1:
restore_settable_modification(restore_info)
if __name__ == '__main__':
save_dir = r'/Users/zhaoyafei/Downloads/bs_definition_3D_face/bs_objs_bs_vert_tri'
# save_dir = r'/Users/zhaoyafei/Downloads/bs_definition_3D_face/bs_objs_bs_vert_quad'
blendshape_node_name = r'Head01_blendShape'
pprint("===> blendshape_node_name: {}".format(blendshape_node_name))
# mesh_node_name = r'Head01Shape'
mesh_node_name = get_blendshape_geometry_name(blendshape_node_name)
pprint("===> mesh_node_name: {}".format(mesh_node_name))
force_triangulate = True
skip_existing_files = True
export_blendshape_target_shapes(
blendshape_node_name, mesh_node_name, save_dir,
force_triangulate=force_triangulate,
skip_existing_files=skip_existing_files)