pyjallib 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyjallib/__init__.py +17 -0
- pyjallib/max/__init__.py +46 -0
- pyjallib/max/align.py +112 -0
- pyjallib/max/anim.py +594 -0
- pyjallib/max/bip.py +508 -0
- pyjallib/max/bone.py +910 -0
- pyjallib/max/constraint.py +973 -0
- pyjallib/max/header.py +57 -0
- pyjallib/max/helper.py +433 -0
- pyjallib/max/layer.py +262 -0
- pyjallib/max/link.py +78 -0
- pyjallib/max/macro/jal_macro_align.py +155 -0
- pyjallib/max/macro/jal_macro_bone.py +358 -0
- pyjallib/max/macro/jal_macro_constraint.py +140 -0
- pyjallib/max/macro/jal_macro_helper.py +321 -0
- pyjallib/max/macro/jal_macro_link.py +55 -0
- pyjallib/max/macro/jal_macro_select.py +91 -0
- pyjallib/max/mirror.py +388 -0
- pyjallib/max/name.py +521 -0
- pyjallib/max/select.py +278 -0
- pyjallib/max/skin.py +996 -0
- pyjallib/max/twistBone.py +418 -0
- pyjallib/namePart.py +633 -0
- pyjallib/nameToPath.py +113 -0
- pyjallib/naming.py +1066 -0
- pyjallib/namingConfig.py +844 -0
- pyjallib/perforce.py +735 -0
- pyjallib/reloadModules.py +33 -0
- pyjallib-0.1.0.dist-info/METADATA +28 -0
- pyjallib-0.1.0.dist-info/RECORD +32 -0
- pyjallib-0.1.0.dist-info/WHEEL +5 -0
- pyjallib-0.1.0.dist-info/top_level.txt +1 -0
pyjallib/max/skin.py
ADDED
@@ -0,0 +1,996 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
스킨 모듈 - 3ds Max용 고급 스킨 관련 기능 제공
|
6
|
+
원본 MAXScript의 skin2.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현됨
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
from enum import IntEnum
|
11
|
+
import textwrap
|
12
|
+
from pymxs import runtime as rt
|
13
|
+
|
14
|
+
class VertexMode(IntEnum):
|
15
|
+
"""
|
16
|
+
버텍스 모드 열거형
|
17
|
+
"""
|
18
|
+
Edges = 1
|
19
|
+
Attach = 2
|
20
|
+
All = 3
|
21
|
+
Stiff = 4
|
22
|
+
|
23
|
+
class Skin:
|
24
|
+
"""
|
25
|
+
고급 스킨 관련 기능을 제공하는 클래스.
|
26
|
+
MAXScript의 ODC_Char_Skin 구조체 개념을 Python으로 재구현한 클래스이며,
|
27
|
+
3ds Max의 기능들을 pymxs API를 통해 제어합니다.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self):
|
31
|
+
"""
|
32
|
+
클래스 초기화
|
33
|
+
"""
|
34
|
+
self.skin_match_list = []
|
35
|
+
|
36
|
+
def has_skin(self, obj=None):
|
37
|
+
"""
|
38
|
+
객체에 스킨 모디파이어가 있는지 확인
|
39
|
+
|
40
|
+
Args:
|
41
|
+
obj: 확인할 객체 (기본값: 현재 선택된 객체)
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
True: 스킨 모디파이어가 있는 경우
|
45
|
+
False: 없는 경우
|
46
|
+
"""
|
47
|
+
if obj is None:
|
48
|
+
if len(rt.selection) > 0:
|
49
|
+
obj = rt.selection[0]
|
50
|
+
else:
|
51
|
+
return False
|
52
|
+
|
53
|
+
# 객체의 모든 모디파이어를 검사하여 Skin 모디파이어가 있는지 확인
|
54
|
+
for mod in obj.modifiers:
|
55
|
+
if rt.classOf(mod) == rt.Skin:
|
56
|
+
return True
|
57
|
+
return False
|
58
|
+
|
59
|
+
def is_valid_bone(self, inNode):
|
60
|
+
"""
|
61
|
+
노드가 유효한 스킨 본인지 확인
|
62
|
+
|
63
|
+
Args:
|
64
|
+
inNode: 확인할 노드
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
True: 유효한 본인 경우
|
68
|
+
False: 아닌 경우
|
69
|
+
"""
|
70
|
+
return (rt.superClassOf(inNode) == rt.GeometryClass or
|
71
|
+
rt.classOf(inNode) == rt.BoneGeometry or
|
72
|
+
rt.superClassOf(inNode) == rt.Helper)
|
73
|
+
|
74
|
+
def get_skin_mod(self, obj=None):
|
75
|
+
"""
|
76
|
+
객체의 스킨 모디파이어 배열 반환
|
77
|
+
|
78
|
+
Args:
|
79
|
+
obj: 모디파이어를 가져올 객체 (기본값: 현재 선택된 객체)
|
80
|
+
|
81
|
+
Returns:
|
82
|
+
스킨 모디파이어 배열
|
83
|
+
"""
|
84
|
+
if obj is None:
|
85
|
+
if len(rt.selection) > 0:
|
86
|
+
obj = rt.selection[0]
|
87
|
+
else:
|
88
|
+
return []
|
89
|
+
|
90
|
+
return [mod for mod in obj.modifiers if rt.classOf(mod) == rt.Skin]
|
91
|
+
|
92
|
+
def bind_skin(self, obj, bone_array):
|
93
|
+
"""
|
94
|
+
객체에 스킨 모디파이어 바인딩
|
95
|
+
|
96
|
+
Args:
|
97
|
+
obj: 바인딩할 객체
|
98
|
+
bone_array: 바인딩할 본 배열
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
True: 성공한 경우
|
102
|
+
False: 실패한 경우
|
103
|
+
"""
|
104
|
+
if obj is None or len(bone_array) < 1:
|
105
|
+
print("Select at least 1 influence and an object.")
|
106
|
+
return False
|
107
|
+
|
108
|
+
# Switch to modify mode
|
109
|
+
rt.execute("max modify mode")
|
110
|
+
|
111
|
+
# Check if the object is valid for skinning
|
112
|
+
if rt.superClassOf(obj) != rt.GeometryClass:
|
113
|
+
print(f"{obj.name} must be 'Edit_Mesh' or 'Edit_Poly'.")
|
114
|
+
return False
|
115
|
+
|
116
|
+
# Add skin modifier
|
117
|
+
objmod = rt.Skin()
|
118
|
+
rt.addModifier(obj, objmod)
|
119
|
+
rt.select(obj)
|
120
|
+
|
121
|
+
# Add bones to skin modifier
|
122
|
+
wgt = 1.0
|
123
|
+
for each in bone_array:
|
124
|
+
rt.skinOps.addBone(objmod, each, wgt)
|
125
|
+
|
126
|
+
# Set skin modifier options
|
127
|
+
objmod.filter_vertices = True
|
128
|
+
objmod.filter_envelopes = False
|
129
|
+
objmod.filter_cross_sections = True
|
130
|
+
objmod.enableDQ = False
|
131
|
+
objmod.bone_Limit = 8
|
132
|
+
objmod.colorAllWeights = True
|
133
|
+
objmod.showNoEnvelopes = True
|
134
|
+
|
135
|
+
return True
|
136
|
+
|
137
|
+
def optimize_skin(self, skin_mod, bone_limit=8, skin_tolerance=0.01):
|
138
|
+
"""
|
139
|
+
스킨 모디파이어 최적화
|
140
|
+
|
141
|
+
Args:
|
142
|
+
skin_mod: 스킨 모디파이어
|
143
|
+
bone_limit: 본 제한 수 (기본값: 8)
|
144
|
+
skin_tolerance: 스킨 가중치 허용 오차 (기본값: 0.01)
|
145
|
+
"""
|
146
|
+
# 스킨 모디파이어 설정
|
147
|
+
skin_mod.enableDQ = False
|
148
|
+
skin_mod.bone_Limit = bone_limit
|
149
|
+
skin_mod.clearZeroLimit = skin_tolerance
|
150
|
+
rt.skinOps.RemoveZeroWeights(skin_mod)
|
151
|
+
skin_mod.clearZeroLimit = 0
|
152
|
+
|
153
|
+
skin_mod.filter_vertices = True
|
154
|
+
skin_mod.showNoEnvelopes = True
|
155
|
+
|
156
|
+
rt.skinOps.closeWeightTable(skin_mod)
|
157
|
+
rt.skinOps.closeWeightTool(skin_mod)
|
158
|
+
|
159
|
+
if rt.skinOps.getNumberBones(skin_mod) > 1:
|
160
|
+
list_of_bones = [i for i in range(1, rt.skinOps.GetNumberBones(skin_mod) + 1)]
|
161
|
+
|
162
|
+
for v in range(1, rt.skinOps.GetNumberVertices(skin_mod) + 1):
|
163
|
+
for b in range(1, rt.skinOps.GetVertexWeightCount(skin_mod, v) + 1):
|
164
|
+
bone_id = rt.skinOps.GetVertexWeightBoneID(skin_mod, v, b)
|
165
|
+
if bone_id in list_of_bones:
|
166
|
+
list_of_bones.remove(bone_id)
|
167
|
+
|
168
|
+
# 역순으로 본 제거 (인덱스 변경 문제 방지)
|
169
|
+
for i in range(len(list_of_bones) - 1, -1, -1):
|
170
|
+
bone_id = list_of_bones[i]
|
171
|
+
rt.skinOps.SelectBone(skin_mod, bone_id)
|
172
|
+
rt.skinOps.removebone(skin_mod, bone_id)
|
173
|
+
|
174
|
+
if rt.skinOps.getNumberBones(skin_mod) > 1:
|
175
|
+
rt.skinOps.SelectBone(skin_mod, 1)
|
176
|
+
|
177
|
+
skin_mod_obj = rt.getCurrentSelection()[0]
|
178
|
+
|
179
|
+
print(f"Obj:{skin_mod_obj.name} Removed:{len(list_of_bones)} Left:{rt.skinOps.GetNumberBones(skin_mod)}")
|
180
|
+
|
181
|
+
def optimize_skin_process(self, objs=None, optim_all_skin_mod=False, bone_limit=8, skin_tolerance=0.01):
|
182
|
+
"""
|
183
|
+
여러 객체의 스킨 최적화 프로세스
|
184
|
+
|
185
|
+
Args:
|
186
|
+
objs: 최적화할 객체 배열 (기본값: 현재 선택된 객체들)
|
187
|
+
optim_all_skin_mod: 모든 스킨 모디파이어 최적화 여부 (기본값: False)
|
188
|
+
bone_limit: 본 제한 수 (기본값: 8)
|
189
|
+
skin_tolerance: 스킨 가중치 허용 오차 (기본값: 0.01)
|
190
|
+
"""
|
191
|
+
if objs is None:
|
192
|
+
objs = rt.selection
|
193
|
+
|
194
|
+
if not objs:
|
195
|
+
return
|
196
|
+
|
197
|
+
rt.execute("max modify mode")
|
198
|
+
|
199
|
+
for obj in objs:
|
200
|
+
if self.has_skin(obj):
|
201
|
+
mod_id = [i+1 for i in range(len(obj.modifiers)) if rt.classOf(obj.modifiers[i]) == rt.Skin]
|
202
|
+
|
203
|
+
if not optim_all_skin_mod:
|
204
|
+
mod_id = [mod_id[0]]
|
205
|
+
|
206
|
+
for each in mod_id:
|
207
|
+
rt.modPanel.setCurrentObject(obj.modifiers[each-1])
|
208
|
+
self.optimize_skin(obj.modifiers[each-1], bone_limit=bone_limit, skin_tolerance=skin_tolerance)
|
209
|
+
|
210
|
+
rt.select(objs)
|
211
|
+
|
212
|
+
def load_skin(self, obj, file_path, load_bind_pose=False, keep_skin=False):
|
213
|
+
"""
|
214
|
+
스킨 데이터 로드
|
215
|
+
|
216
|
+
Args:
|
217
|
+
obj: 로드할 객체
|
218
|
+
file_path: 스킨 파일 경로
|
219
|
+
load_bind_pose: 바인드 포즈 로드 여부
|
220
|
+
keep_skin: 기존 스킨 유지 여부
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
누락된 본 배열
|
224
|
+
"""
|
225
|
+
# 기본값 설정
|
226
|
+
if keep_skin != True:
|
227
|
+
keep_skin = False
|
228
|
+
|
229
|
+
# 객체 선택
|
230
|
+
rt.select(obj)
|
231
|
+
data = []
|
232
|
+
missing_bones = []
|
233
|
+
|
234
|
+
# 파일 열기
|
235
|
+
try:
|
236
|
+
with open(file_path, 'r') as f:
|
237
|
+
for line in f:
|
238
|
+
data.append(line.strip())
|
239
|
+
except:
|
240
|
+
return []
|
241
|
+
|
242
|
+
# 버텍스 수 확인
|
243
|
+
if len(data) - 1 != obj.verts.count or obj.verts.count == 0:
|
244
|
+
print("Bad number of verts")
|
245
|
+
return []
|
246
|
+
|
247
|
+
# 기존 스킨 모디파이어 처리
|
248
|
+
if not keep_skin:
|
249
|
+
for i in range(len(obj.modifiers) - 1, -1, -1):
|
250
|
+
if rt.classOf(obj.modifiers[i]) == rt.Skin:
|
251
|
+
rt.deleteModifier(obj, i+1)
|
252
|
+
|
253
|
+
# 모디파이 모드 설정
|
254
|
+
rt.setCommandPanelTaskMode(rt.Name('modify'))
|
255
|
+
|
256
|
+
# 새 스킨 모디파이어 생성
|
257
|
+
new_skin = rt.Skin()
|
258
|
+
rt.addModifier(obj, new_skin, before=1 if keep_skin else 0)
|
259
|
+
|
260
|
+
# 스킨 이름 설정
|
261
|
+
if keep_skin:
|
262
|
+
new_skin.name = "Skin_" + os.path.splitext(os.path.basename(file_path))[0]
|
263
|
+
|
264
|
+
# 현재 모디파이어 설정
|
265
|
+
rt.modPanel.setCurrentObject(new_skin)
|
266
|
+
|
267
|
+
tempData = [rt.execute(item) for item in data]
|
268
|
+
|
269
|
+
# 본 데이터 처리
|
270
|
+
bones_data = rt.execute(tempData[0])
|
271
|
+
hierarchy = []
|
272
|
+
|
273
|
+
for i in range(len(bones_data)):
|
274
|
+
# 본 이름으로 노드 찾기
|
275
|
+
my_bone = [node for node in rt.objects if node.name == bones_data[i]]
|
276
|
+
|
277
|
+
# 없는 본인 경우 더미 생성
|
278
|
+
if len(my_bone) == 0:
|
279
|
+
print(f"Missing bone: {bones_data[i]}")
|
280
|
+
tmp = rt.Dummy(name=bones_data[i])
|
281
|
+
my_bone = [tmp]
|
282
|
+
missing_bones.append(tmp)
|
283
|
+
|
284
|
+
# 계층 구조 확인
|
285
|
+
if len(my_bone) > 1 and len(hierarchy) != 0:
|
286
|
+
print(f"Multiple bones are named: {my_bone[0].name} ({len(my_bone)})")
|
287
|
+
good_bone = None
|
288
|
+
for o in my_bone:
|
289
|
+
if o in hierarchy:
|
290
|
+
good_bone = o
|
291
|
+
break
|
292
|
+
if good_bone is not None:
|
293
|
+
my_bone = [good_bone]
|
294
|
+
|
295
|
+
# 사용할 본 결정
|
296
|
+
my_bone = my_bone[0]
|
297
|
+
|
298
|
+
# 계층에 추가
|
299
|
+
if my_bone not in hierarchy:
|
300
|
+
hierarchy.append(my_bone)
|
301
|
+
all_nodes = list(hierarchy)
|
302
|
+
|
303
|
+
for node in all_nodes:
|
304
|
+
# 자식 노드 추가
|
305
|
+
for child in node.children:
|
306
|
+
if child not in all_nodes:
|
307
|
+
all_nodes.append(child)
|
308
|
+
# 부모 노드 추가
|
309
|
+
if node.parent is not None and node.parent not in all_nodes:
|
310
|
+
all_nodes.append(node.parent)
|
311
|
+
|
312
|
+
# 계층에 추가
|
313
|
+
for node in all_nodes:
|
314
|
+
if self.is_valid_bone(node) and node not in hierarchy:
|
315
|
+
hierarchy.append(node)
|
316
|
+
|
317
|
+
# 본 추가
|
318
|
+
rt.skinOps.addBone(new_skin, my_bone, 1.0)
|
319
|
+
|
320
|
+
# 바인드 포즈 로드
|
321
|
+
if load_bind_pose:
|
322
|
+
bind_pose_file = os.path.splitext(file_path)[0] + "bp"
|
323
|
+
bind_poses = []
|
324
|
+
|
325
|
+
if os.path.exists(bind_pose_file):
|
326
|
+
try:
|
327
|
+
with open(bind_pose_file, 'r') as f:
|
328
|
+
for line in f:
|
329
|
+
bind_poses.append(rt.execute(line.strip()))
|
330
|
+
except:
|
331
|
+
pass
|
332
|
+
|
333
|
+
if i < len(bind_poses) and bind_poses[i] is not None:
|
334
|
+
rt.skinUtils.SetBoneBindTM(obj, my_bone, bind_poses[i])
|
335
|
+
|
336
|
+
# 가중치 데이터 처리
|
337
|
+
for i in range(1, obj.verts.count + 1):
|
338
|
+
bone_id = []
|
339
|
+
bone_weight = []
|
340
|
+
good_bones = []
|
341
|
+
all_bone_weight = [0] * len(bones_data)
|
342
|
+
|
343
|
+
# 가중치 합산
|
344
|
+
for b in range(len(tempData[i][0])):
|
345
|
+
bone_index = tempData[i][0][b]
|
346
|
+
weight = tempData[i][1][b]
|
347
|
+
all_bone_weight[bone_index-1] += weight
|
348
|
+
good_bones.append(bone_index)
|
349
|
+
|
350
|
+
# 가중치 적용
|
351
|
+
for b in good_bones:
|
352
|
+
bone_id.append(b)
|
353
|
+
bone_weight.append(all_bone_weight[b-1])
|
354
|
+
|
355
|
+
# 가중치 설정
|
356
|
+
if len(bone_id) != 0:
|
357
|
+
rt.skinOps.SetVertexWeights(new_skin, i, bone_id[0], 1.0) # Max 2014 sp5 hack
|
358
|
+
rt.skinOps.ReplaceVertexWeights(new_skin, i, bone_id, bone_weight)
|
359
|
+
|
360
|
+
return missing_bones
|
361
|
+
|
362
|
+
def save_skin(self, obj=None, file_path=None, save_bind_pose=False):
|
363
|
+
"""
|
364
|
+
스킨 데이터 저장
|
365
|
+
MAXScript의 saveskin.ms 를 Python으로 변환한 함수
|
366
|
+
|
367
|
+
Args:
|
368
|
+
obj: 저장할 객체 (기본값: 현재 선택된 객체)
|
369
|
+
file_path: 저장할 파일 경로 (기본값: None, 자동 생성)
|
370
|
+
|
371
|
+
Returns:
|
372
|
+
저장된 파일 경로
|
373
|
+
"""
|
374
|
+
# 현재 선택된 객체가 없는 경우 선택된 객체 사용
|
375
|
+
if obj is None:
|
376
|
+
if len(rt.selection) > 0:
|
377
|
+
obj = rt.selection[0]
|
378
|
+
else:
|
379
|
+
print("No object selected")
|
380
|
+
return None
|
381
|
+
|
382
|
+
# 현재 스킨 모디파이어 가져오기
|
383
|
+
skin_mod = rt.modPanel.getCurrentObject()
|
384
|
+
|
385
|
+
# 스킨 모디파이어가 아니거나 본이 없는 경우 종료
|
386
|
+
if rt.classOf(skin_mod) != rt.Skin or rt.skinOps.GetNumberBones(skin_mod) <= 0:
|
387
|
+
print("Current modifier is not a Skin modifier or has no bones")
|
388
|
+
return None
|
389
|
+
|
390
|
+
# 본 리스트 생성
|
391
|
+
bones_list = []
|
392
|
+
for i in range(1, rt.skinOps.GetNumberBones(skin_mod) + 1):
|
393
|
+
bones_list.append(rt.skinOps.GetBoneName(skin_mod, i, 1))
|
394
|
+
|
395
|
+
# 스킨 데이터 생성
|
396
|
+
skin_data = "\"#(\\\"" + "\\\",\\\"".join(str(x) for x in bones_list) + "\\\")\"\n"
|
397
|
+
|
398
|
+
# 버텍스별 가중치 데이터 수집
|
399
|
+
for v in range(1, rt.skinOps.GetNumberVertices(skin_mod) + 1):
|
400
|
+
bone_array = []
|
401
|
+
weight_array = []
|
402
|
+
|
403
|
+
for b in range(1, rt.skinOps.GetVertexWeightCount(skin_mod, v) + 1):
|
404
|
+
bone_array.append(rt.skinOps.GetVertexWeightBoneID(skin_mod, v, b))
|
405
|
+
weight_array.append(rt.skinOps.GetVertexWeight(skin_mod, v, b))
|
406
|
+
|
407
|
+
stringBoneArray = "#(" + ",".join(str(x) for x in bone_array) + ")"
|
408
|
+
stringWeightArray = "#(" + ",".join(str(w) for w in weight_array) + ")"
|
409
|
+
skin_data += ("#(" + stringBoneArray + ", " + stringWeightArray + ")\n")
|
410
|
+
|
411
|
+
# 파일 경로가 지정되지 않은 경우 자동 생성
|
412
|
+
if file_path is None:
|
413
|
+
# animations 폴더 내 skindata 폴더 생성
|
414
|
+
animations_dir = rt.getDir(rt.Name('animations'))
|
415
|
+
skin_data_dir = os.path.join(animations_dir, "skindata")
|
416
|
+
|
417
|
+
if not os.path.exists(skin_data_dir):
|
418
|
+
os.makedirs(skin_data_dir)
|
419
|
+
|
420
|
+
# 파일명 생성 (객체명 + 버텍스수 + 면수)
|
421
|
+
file_name = f"{obj.name} [v{obj.mesh.verts.count}] [t{obj.mesh.faces.count}].skin"
|
422
|
+
file_path = os.path.join(skin_data_dir, file_name)
|
423
|
+
|
424
|
+
print(f"Saving to: {file_path}")
|
425
|
+
|
426
|
+
# 스킨 데이터 파일 저장
|
427
|
+
try:
|
428
|
+
with open(file_path, 'w') as f:
|
429
|
+
for data in skin_data:
|
430
|
+
f.write(data)
|
431
|
+
except Exception as e:
|
432
|
+
print(f"Error saving skin data: {e}")
|
433
|
+
return None
|
434
|
+
|
435
|
+
if save_bind_pose:
|
436
|
+
# 바인드 포즈 데이터 수집 및 저장
|
437
|
+
bind_poses = []
|
438
|
+
for i in range(1, rt.skinOps.GetNumberBones(skin_mod) + 1):
|
439
|
+
bone_name = rt.skinOps.GetBoneName(skin_mod, i, 1)
|
440
|
+
bone_node = rt.getNodeByName(bone_name)
|
441
|
+
bind_pose = rt.skinUtils.GetBoneBindTM(obj, bone_node)
|
442
|
+
bind_poses.append(bind_pose)
|
443
|
+
|
444
|
+
# 바인드 포즈 파일 저장
|
445
|
+
bind_pose_file = file_path[:-4] + "bp" # .skin -> .bp
|
446
|
+
try:
|
447
|
+
with open(bind_pose_file, 'w') as f:
|
448
|
+
for pose in bind_poses:
|
449
|
+
f.write(str(pose) + '\n')
|
450
|
+
except Exception as e:
|
451
|
+
print(f"Error saving bind pose data: {e}")
|
452
|
+
|
453
|
+
return file_path
|
454
|
+
|
455
|
+
def get_bone_id(self, skin_mod, b_array, type=1, refresh=True):
|
456
|
+
"""
|
457
|
+
스킨 모디파이어에서 본 ID 가져오기
|
458
|
+
|
459
|
+
Args:
|
460
|
+
skin_mod: 스킨 모디파이어
|
461
|
+
b_array: 본 배열
|
462
|
+
type: 0=객체, 1=객체 이름
|
463
|
+
refresh: 인터페이스 업데이트 여부
|
464
|
+
|
465
|
+
Returns:
|
466
|
+
본 ID 배열
|
467
|
+
"""
|
468
|
+
bone_id = []
|
469
|
+
|
470
|
+
if refresh:
|
471
|
+
rt.modPanel.setCurrentObject(skin_mod)
|
472
|
+
|
473
|
+
for i in range(1, rt.skinOps.GetNumberBones(skin_mod) + 1):
|
474
|
+
if type == 0:
|
475
|
+
bone_name = rt.skinOps.GetBoneName(skin_mod, i, 1)
|
476
|
+
id = b_array.index(bone_name) + 1 if bone_name in b_array else 0
|
477
|
+
elif type == 1:
|
478
|
+
bone = rt.getNodeByName(rt.skinOps.GetBoneName(skin_mod, i, 1))
|
479
|
+
id = b_array.index(bone) + 1 if bone in b_array else 0
|
480
|
+
|
481
|
+
if id != 0:
|
482
|
+
bone_id.append(i)
|
483
|
+
|
484
|
+
return bone_id
|
485
|
+
|
486
|
+
def get_bone_id_from_name(self, in_skin_mod, bone_name):
|
487
|
+
"""
|
488
|
+
본 이름으로 본 ID 가져오기
|
489
|
+
|
490
|
+
Args:
|
491
|
+
in_skin_mod: 스킨 모디파이어를 가진 객체
|
492
|
+
bone_name: 본 이름
|
493
|
+
|
494
|
+
Returns:
|
495
|
+
본 ID
|
496
|
+
"""
|
497
|
+
for i in range(1, rt.skinOps.GetNumberBones(in_skin_mod) + 1):
|
498
|
+
if rt.skinOps.GetBoneName(in_skin_mod, i, 1) == bone_name:
|
499
|
+
return i
|
500
|
+
return None
|
501
|
+
|
502
|
+
def get_bones_from_skin(self, objs, skin_mod_index):
|
503
|
+
"""
|
504
|
+
스킨 모디파이어에서 사용된 본 배열 가져오기
|
505
|
+
|
506
|
+
Args:
|
507
|
+
objs: 객체 배열
|
508
|
+
skin_mod_index: 스킨 모디파이어 인덱스
|
509
|
+
|
510
|
+
Returns:
|
511
|
+
본 배열
|
512
|
+
"""
|
513
|
+
inf_list = []
|
514
|
+
|
515
|
+
for obj in objs:
|
516
|
+
if rt.isValidNode(obj):
|
517
|
+
deps = rt.refs.dependsOn(obj.modifiers[skin_mod_index])
|
518
|
+
for n in deps:
|
519
|
+
if rt.isValidNode(n) and self.is_valid_bone(n):
|
520
|
+
if n not in inf_list:
|
521
|
+
inf_list.append(n)
|
522
|
+
|
523
|
+
return inf_list
|
524
|
+
|
525
|
+
def find_skin_mod_id(self, obj):
|
526
|
+
"""
|
527
|
+
객체에서 스킨 모디파이어 인덱스 찾기
|
528
|
+
|
529
|
+
Args:
|
530
|
+
obj: 대상 객체
|
531
|
+
|
532
|
+
Returns:
|
533
|
+
스킨 모디파이어 인덱스 배열
|
534
|
+
"""
|
535
|
+
return [i+1 for i in range(len(obj.modifiers)) if rt.classOf(obj.modifiers[i]) == rt.Skin]
|
536
|
+
|
537
|
+
def sel_vert_from_bones(self, skin_mod, threshold=0.01):
|
538
|
+
"""
|
539
|
+
선택된 본에 영향 받는 버텍스 선택
|
540
|
+
|
541
|
+
Args:
|
542
|
+
skin_mod: 스킨 모디파이어
|
543
|
+
threshold: 가중치 임계값 (기본값: 0.01)
|
544
|
+
|
545
|
+
Returns:
|
546
|
+
선택된 버텍스 배열
|
547
|
+
"""
|
548
|
+
verts_to_sel = []
|
549
|
+
|
550
|
+
if skin_mod is not None:
|
551
|
+
le_bone = rt.skinOps.getSelectedBone(skin_mod)
|
552
|
+
svc = rt.skinOps.GetNumberVertices(skin_mod)
|
553
|
+
|
554
|
+
for o in range(1, svc + 1):
|
555
|
+
lv = rt.skinOps.GetVertexWeightCount(skin_mod, o)
|
556
|
+
|
557
|
+
for k in range(1, lv + 1):
|
558
|
+
if rt.skinOps.GetVertexWeightBoneID(skin_mod, o, k) == le_bone:
|
559
|
+
if rt.skinOps.GetVertexWeight(skin_mod, o, k) >= threshold:
|
560
|
+
if o not in verts_to_sel:
|
561
|
+
verts_to_sel.append(o)
|
562
|
+
|
563
|
+
rt.skinOps.SelectVertices(skin_mod, verts_to_sel)
|
564
|
+
|
565
|
+
else:
|
566
|
+
print("You must have a skinned object selected")
|
567
|
+
|
568
|
+
return verts_to_sel
|
569
|
+
|
570
|
+
def sel_all_verts(self, skin_mod):
|
571
|
+
"""
|
572
|
+
스킨 모디파이어의 모든 버텍스 선택
|
573
|
+
|
574
|
+
Args:
|
575
|
+
skin_mod: 스킨 모디파이어
|
576
|
+
|
577
|
+
Returns:
|
578
|
+
선택된 버텍스 배열
|
579
|
+
"""
|
580
|
+
verts_to_sel = []
|
581
|
+
|
582
|
+
if skin_mod is not None:
|
583
|
+
svc = rt.skinOps.GetNumberVertices(skin_mod)
|
584
|
+
|
585
|
+
for o in range(1, svc + 1):
|
586
|
+
verts_to_sel.append(o)
|
587
|
+
|
588
|
+
rt.skinOps.SelectVertices(skin_mod, verts_to_sel)
|
589
|
+
|
590
|
+
return verts_to_sel
|
591
|
+
|
592
|
+
def make_rigid_skin(self, skin_mod, vert_list):
|
593
|
+
"""
|
594
|
+
버텍스 가중치를 경직화(rigid) 처리
|
595
|
+
|
596
|
+
Args:
|
597
|
+
skin_mod: 스킨 모디파이어
|
598
|
+
vert_list: 버텍스 리스트
|
599
|
+
|
600
|
+
Returns:
|
601
|
+
[본 ID 배열, 가중치 배열]
|
602
|
+
"""
|
603
|
+
weight_array = {}
|
604
|
+
vert_count = 0
|
605
|
+
bone_array = []
|
606
|
+
final_weight = []
|
607
|
+
|
608
|
+
# 가중치 수집
|
609
|
+
for v in vert_list:
|
610
|
+
for cur_bone in range(1, rt.skinOps.GetVertexWeightCount(skin_mod, v) + 1):
|
611
|
+
cur_id = rt.skinOps.GetVertexWeightBoneID(skin_mod, v, cur_bone)
|
612
|
+
|
613
|
+
if cur_id not in weight_array:
|
614
|
+
weight_array[cur_id] = 0
|
615
|
+
|
616
|
+
cur_weight = rt.skinOps.GetVertexWeight(skin_mod, v, cur_bone)
|
617
|
+
weight_array[cur_id] += cur_weight
|
618
|
+
vert_count += cur_weight
|
619
|
+
|
620
|
+
# 최종 가중치 계산
|
621
|
+
for i in weight_array:
|
622
|
+
if weight_array[i] > 0:
|
623
|
+
new_val = weight_array[i] / vert_count
|
624
|
+
if new_val > 0.01:
|
625
|
+
bone_array.append(i)
|
626
|
+
final_weight.append(new_val)
|
627
|
+
|
628
|
+
return [bone_array, final_weight]
|
629
|
+
|
630
|
+
def transfert_skin_data(self, obj, source_bones, target_bones, vtx_list):
|
631
|
+
"""
|
632
|
+
스킨 가중치 데이터 이전
|
633
|
+
|
634
|
+
Args:
|
635
|
+
obj: 대상 객체
|
636
|
+
source_bones: 원본 본 배열
|
637
|
+
target_bones: 대상 본
|
638
|
+
vtx_list: 버텍스 리스트
|
639
|
+
"""
|
640
|
+
skin_data = []
|
641
|
+
new_skin_data = []
|
642
|
+
|
643
|
+
# 본 ID 가져오기
|
644
|
+
source_bones_id = [self.get_bone_id_from_name(obj, b.name) for b in source_bones]
|
645
|
+
target_bone_id = self.get_bone_id_from_name(obj, target_bones.name)
|
646
|
+
|
647
|
+
bone_list = [n for n in rt.refs.dependsOn(obj.skin) if rt.isValidNode(n) and self.is_valid_bone(n)]
|
648
|
+
bone_id_map = {self.get_bone_id_from_name(obj, b.name): i for i, b in enumerate(bone_list)}
|
649
|
+
|
650
|
+
# 스킨 데이터 수집
|
651
|
+
for vtx in vtx_list:
|
652
|
+
bone_array = []
|
653
|
+
weight_array = []
|
654
|
+
bone_weight = [0] * len(bone_list)
|
655
|
+
|
656
|
+
for b in range(1, rt.skinOps.GetVertexWeightCount(obj.skin, vtx) + 1):
|
657
|
+
bone_idx = rt.skinOps.GetVertexWeightBoneID(obj.skin, vtx, b)
|
658
|
+
bone_weight[bone_id_map[bone_idx]] += rt.skinOps.GetVertexWeight(obj.skin, vtx, b)
|
659
|
+
|
660
|
+
for b in range(len(bone_weight)):
|
661
|
+
if bone_weight[b] > 0:
|
662
|
+
bone_array.append(b+1)
|
663
|
+
weight_array.append(bone_weight[b])
|
664
|
+
|
665
|
+
skin_data.append([bone_array, weight_array])
|
666
|
+
new_skin_data.append([bone_array[:], weight_array[:]])
|
667
|
+
|
668
|
+
# 스킨 데이터 이전
|
669
|
+
for b, source_bone_id in enumerate(source_bones_id):
|
670
|
+
vtx_id = []
|
671
|
+
vtx_weight = []
|
672
|
+
|
673
|
+
# 원본 본의 가중치 추출
|
674
|
+
for vtx in range(len(skin_data)):
|
675
|
+
for i in range(len(skin_data[vtx][0])):
|
676
|
+
if skin_data[vtx][0][i] == source_bone_id:
|
677
|
+
vtx_id.append(vtx)
|
678
|
+
vtx_weight.append(skin_data[vtx][1][i])
|
679
|
+
|
680
|
+
# 원본 본 영향력 제거
|
681
|
+
for vtx in range(len(vtx_id)):
|
682
|
+
for i in range(len(new_skin_data[vtx_id[vtx]][0])):
|
683
|
+
if new_skin_data[vtx_id[vtx]][0][i] == source_bone_id:
|
684
|
+
new_skin_data[vtx_id[vtx]][1][i] = 0.0
|
685
|
+
|
686
|
+
# 타겟 본에 영향력 추가
|
687
|
+
for vtx in range(len(vtx_id)):
|
688
|
+
id = new_skin_data[vtx_id[vtx]][0].index(target_bone_id) if target_bone_id in new_skin_data[vtx_id[vtx]][0] else -1
|
689
|
+
|
690
|
+
if id == -1:
|
691
|
+
new_skin_data[vtx_id[vtx]][0].append(target_bone_id)
|
692
|
+
new_skin_data[vtx_id[vtx]][1].append(vtx_weight[vtx])
|
693
|
+
else:
|
694
|
+
new_skin_data[vtx_id[vtx]][1][id] += vtx_weight[vtx]
|
695
|
+
|
696
|
+
# 스킨 데이터 적용
|
697
|
+
for i in range(len(vtx_list)):
|
698
|
+
rt.skinOps.ReplaceVertexWeights(obj.skin, vtx_list[i],
|
699
|
+
skin_data[i][0], new_skin_data[i][1])
|
700
|
+
|
701
|
+
def smooth_skin(self, inObj, inVertMode=VertexMode.Edges, inRadius=5.0, inIterNum=3, inKeepMax=False):
|
702
|
+
"""
|
703
|
+
스킨 가중치 부드럽게 하기
|
704
|
+
|
705
|
+
Args:
|
706
|
+
inObj: 대상 객체
|
707
|
+
inVertMode: 버텍스 모드 (기본값: 1)
|
708
|
+
inRadius: 반경 (기본값: 5.0)
|
709
|
+
inIterNum: 반복 횟수 (기본값: 3)
|
710
|
+
inKeepMax: 최대 가중치 유지 여부 (기본값: False)
|
711
|
+
|
712
|
+
Returns:
|
713
|
+
None
|
714
|
+
"""
|
715
|
+
maxScriptCode = textwrap.dedent(r'''
|
716
|
+
struct _SmoothSkin (
|
717
|
+
SmoothSkinMaxUndo = 10,
|
718
|
+
UndoWeights = #(),
|
719
|
+
SmoothSkinData = #(#(), #(), #(), #(), #(), #(), #()),
|
720
|
+
smoothRadius = 5.0,
|
721
|
+
iterNum = 1,
|
722
|
+
keepMax = false,
|
723
|
+
|
724
|
+
-- vertGroupMode: Edges, Attach, All, Stiff
|
725
|
+
vertGroupMode = 1,
|
726
|
+
|
727
|
+
fn make_rigid_skin skin_mod vert_list =
|
728
|
+
(
|
729
|
+
/*
|
730
|
+
Rigidify vertices weights in skin modifier
|
731
|
+
*/
|
732
|
+
WeightArray = #()
|
733
|
+
VertCount = 0
|
734
|
+
BoneArray = #()
|
735
|
+
FinalWeight = #()
|
736
|
+
|
737
|
+
for v in vert_list do
|
738
|
+
(
|
739
|
+
for CurBone = 1 to (skinOps.GetVertexWeightCount skin_mod v) do
|
740
|
+
(
|
741
|
+
CurID = (skinOps.GetVertexWeightBoneID skin_mod v CurBone)
|
742
|
+
if WeightArray[CurID] == undefined do WeightArray[CurID] = 0
|
743
|
+
|
744
|
+
CurWeight = (skinOps.GetVertexWeight skin_mod v CurBone)
|
745
|
+
WeightArray[CurID] += CurWeight
|
746
|
+
VertCount += CurWeight
|
747
|
+
)
|
748
|
+
|
749
|
+
for i = 1 to WeightArray.count where WeightArray[i] != undefined and WeightArray[i] > 0 do
|
750
|
+
(
|
751
|
+
NewVal = (WeightArray[i] / VertCount)
|
752
|
+
if NewVal > 0.01 do (append BoneArray i; append FinalWeight NewVal)
|
753
|
+
)
|
754
|
+
)
|
755
|
+
return #(BoneArray, FinalWeight)
|
756
|
+
),
|
757
|
+
|
758
|
+
fn smooth_skin =
|
759
|
+
(
|
760
|
+
if $selection.count != 1 then return false
|
761
|
+
|
762
|
+
p = 0
|
763
|
+
for iter = 1 to iterNum do
|
764
|
+
(
|
765
|
+
p += 1
|
766
|
+
if classOf (modPanel.getCurrentObject()) != Skin then return false
|
767
|
+
|
768
|
+
obj = $; skinMod = modPanel.getCurrentObject()
|
769
|
+
FinalBoneArray = #(); FinalWeightArray = #(); o = 1
|
770
|
+
|
771
|
+
UseOldData = (obj == SmoothSkinData[1][1]) and (obj.verts.count == SmoothSkinData[1][2])
|
772
|
+
if not UseOldData do SmoothSkinData = #(#(), #(), #(), #(), #(), #(), #())
|
773
|
+
SmoothSkinData[1][1] = obj; SmoothSkinData[1][2] = obj.verts.count
|
774
|
+
|
775
|
+
tmpObj = copy Obj
|
776
|
+
tmpObj.modifiers[skinMod.name].enabled = false
|
777
|
+
|
778
|
+
fn DoNormalizeWeight Weight =
|
779
|
+
(
|
780
|
+
WeightLength = 0; NormalizeWeight = #()
|
781
|
+
for w = 1 to Weight.count do WeightLength += Weight[w]
|
782
|
+
if WeightLength != 0 then
|
783
|
+
for w = 1 to Weight.count do NormalizeWeight[w] = Weight[w] * (1 / WeightLength)
|
784
|
+
else
|
785
|
+
NormalizeWeight[1] = 1.0
|
786
|
+
return NormalizeWeight
|
787
|
+
)
|
788
|
+
|
789
|
+
skinMod.clearZeroLimit = 0.00
|
790
|
+
skinOps.RemoveZeroWeights skinMod
|
791
|
+
|
792
|
+
posarray = for a in tmpObj.verts collect a.pos
|
793
|
+
|
794
|
+
if (SmoothSkinData[8] != smoothRadius) do (SmoothSkinData[6] = #(); SmoothSkinData[7] = #())
|
795
|
+
|
796
|
+
for v = 1 to obj.verts.count where (skinOps.IsVertexSelected skinMod v == 1) and (not keepMax or (skinOps.GetVertexWeightCount skinmod v != 1)) do
|
797
|
+
(
|
798
|
+
VertBros = #{}; VertBrosRatio = #()
|
799
|
+
Weightarray = #(); BoneArray = #(); FinalWeight = #()
|
800
|
+
WeightArray.count = skinOps.GetNumberBones skinMod
|
801
|
+
|
802
|
+
if vertGroupMode == 1 and (SmoothSkinData[2][v] == undefined) do
|
803
|
+
(
|
804
|
+
if (classof tmpObj == Editable_Poly) or (classof tmpObj == PolyMeshObject) then
|
805
|
+
(
|
806
|
+
CurEdges = polyop.GetEdgesUsingVert tmpObj v
|
807
|
+
for CE in CurEdges do VertBros += (polyop.getEdgeVerts tmpObj CE) as bitArray
|
808
|
+
)
|
809
|
+
else
|
810
|
+
(
|
811
|
+
CurEdges = meshop.GetEdgesUsingvert tmpObj v
|
812
|
+
for i in CurEdges do CurEdges[i] = (getEdgeVis tmpObj (1+(i-1)/3)(1+mod (i-1) 3))
|
813
|
+
for CE in CurEdges do VertBros += (meshop.getVertsUsingEdge tmpObj CE) as bitArray
|
814
|
+
)
|
815
|
+
|
816
|
+
VertBros = VertBros as array
|
817
|
+
SmoothSkinData[2][v] = #()
|
818
|
+
SmoothSkinData[3][v] = #()
|
819
|
+
|
820
|
+
if VertBros.count > 0 do
|
821
|
+
(
|
822
|
+
for vb in VertBros do
|
823
|
+
(
|
824
|
+
CurDist = distance posarray[v] posarray[vb]
|
825
|
+
if CurDist == 0 then
|
826
|
+
append VertBrosRatio 0
|
827
|
+
else
|
828
|
+
append VertBrosRatio (1 / CurDist)
|
829
|
+
)
|
830
|
+
|
831
|
+
VertBrosRatio = DoNormalizeWeight VertBrosRatio
|
832
|
+
VertBrosRatio[finditem VertBros v] = 1
|
833
|
+
SmoothSkinData[2][v] = VertBros
|
834
|
+
SmoothSkinData[3][v] = VertBrosRatio
|
835
|
+
)
|
836
|
+
)
|
837
|
+
|
838
|
+
if vertGroupMode == 2 do
|
839
|
+
(
|
840
|
+
SmoothSkinData[4][v] = for vb = 1 to posarray.count where (skinOps.IsVertexSelected skinMod vb == 0) and (distance posarray[v] posarray[vb]) < smoothRadius collect vb
|
841
|
+
SmoothSkinData[5][v] = for vb in SmoothSkinData[4][v] collect
|
842
|
+
(CurDist = distance posarray[v] posarray[vb]; if CurDist == 0 then 0 else (1 / CurDist))
|
843
|
+
SmoothSkinData[5][v] = DoNormalizeWeight SmoothSkinData[5][v]
|
844
|
+
for i = 1 to SmoothSkinData[5][v].count do SmoothSkinData[5][v][i] *= 2
|
845
|
+
)
|
846
|
+
|
847
|
+
if vertGroupMode == 3 and (SmoothSkinData[6][v] == undefined) do
|
848
|
+
(
|
849
|
+
SmoothSkinData[6][v] = for vb = 1 to posarray.count where (distance posarray[v] posarray[vb]) < smoothRadius collect vb
|
850
|
+
SmoothSkinData[7][v] = for vb in SmoothSkinData[6][v] collect
|
851
|
+
(CurDist = distance posarray[v] posarray[vb]; if CurDist == 0 then 0 else (1 / CurDist))
|
852
|
+
SmoothSkinData[7][v] = DoNormalizeWeight SmoothSkinData[7][v]
|
853
|
+
for i = 1 to SmoothSkinData[7][v].count do SmoothSkinData[7][v][i] *= 2
|
854
|
+
)
|
855
|
+
|
856
|
+
if vertGroupMode != 4 do
|
857
|
+
(
|
858
|
+
VertBros = SmoothSkinData[vertGroupMode * 2][v]
|
859
|
+
VertBrosRatio = SmoothSkinData[(vertGroupMode * 2) + 1][v]
|
860
|
+
|
861
|
+
for z = 1 to VertBros.count do
|
862
|
+
for CurBone = 1 to (skinOps.GetVertexWeightCount skinMod VertBros[z]) do
|
863
|
+
(
|
864
|
+
CurID = (skinOps.GetVertexWeightBoneID skinMod VertBros[z] CurBone)
|
865
|
+
if WeightArray[CurID] == undefined do WeightArray[CurID] = 0
|
866
|
+
WeightArray[CurID] += (skinOps.GetVertexWeight skinMod VertBros[z] CurBone) * VertBrosRatio[z]
|
867
|
+
)
|
868
|
+
|
869
|
+
for i = 1 to WeightArray.count where WeightArray[i] != undefined and WeightArray[i] > 0 do
|
870
|
+
(
|
871
|
+
NewVal = (WeightArray[i] / 2)
|
872
|
+
if NewVal > 0.01 do (append BoneArray i; append FinalWeight NewVal)
|
873
|
+
)
|
874
|
+
FinalBoneArray[v] = BoneArray
|
875
|
+
FinalWeightArray[v] = FinalWeight
|
876
|
+
)
|
877
|
+
)
|
878
|
+
|
879
|
+
if vertGroupMode == 4 then
|
880
|
+
(
|
881
|
+
convertTopoly tmpObj
|
882
|
+
polyObj = tmpObj
|
883
|
+
|
884
|
+
-- Only test selected
|
885
|
+
VertSelection = for v = 1 to obj.verts.count where (skinOps.IsVertexSelected skinMod v == 1) collect v
|
886
|
+
DoneEdge = (polyobj.edges as bitarray) - polyop.getEdgesUsingVert polyObj VertSelection
|
887
|
+
DoneFace = (polyobj.faces as bitarray) - polyop.getFacesUsingVert polyObj VertSelection
|
888
|
+
|
889
|
+
-- Elements
|
890
|
+
SmallElements = #()
|
891
|
+
for f = 1 to polyobj.faces.count where not DoneFace[f] do
|
892
|
+
(
|
893
|
+
CurElement = polyop.getElementsUsingFace polyObj #{f}
|
894
|
+
|
895
|
+
CurVerts = polyop.getVertsUsingFace polyobj CurElement; MaxDist = 0
|
896
|
+
for v1 in CurVerts do
|
897
|
+
for v2 in CurVerts where MaxDist < (smoothRadius * 2) do
|
898
|
+
(
|
899
|
+
dist = distance polyobj.verts[v1].pos polyobj.verts[v2].pos
|
900
|
+
if dist > MaxDist do MaxDist = dist
|
901
|
+
)
|
902
|
+
if MaxDist < (smoothRadius * 2) do append SmallElements CurVerts
|
903
|
+
DoneFace += CurElement
|
904
|
+
)
|
905
|
+
|
906
|
+
-- Loops
|
907
|
+
EdgeLoops = #()
|
908
|
+
for ed in SmallElements do DoneEdge += polyop.getEdgesUsingVert polyobj ed
|
909
|
+
for ed = 1 to polyobj.edges.count where not DoneEdge[ed] do
|
910
|
+
(
|
911
|
+
polyobj.selectedEdges = #{ed}
|
912
|
+
polyobj.ButtonOp #SelectEdgeLoop
|
913
|
+
CurEdgeLoop = (polyobj.selectedEdges as bitarray)
|
914
|
+
if CurEdgeLoop.numberSet > 2 do
|
915
|
+
(
|
916
|
+
CurVerts = (polyop.getvertsusingedge polyobj CurEdgeLoop); MaxDist = 0
|
917
|
+
for v1 in CurVerts do
|
918
|
+
for v2 in CurVerts where MaxDist < (smoothRadius * 2) do
|
919
|
+
(
|
920
|
+
dist = distance polyobj.verts[v1].pos polyobj.verts[v2].pos
|
921
|
+
if dist > MaxDist do MaxDist = dist
|
922
|
+
)
|
923
|
+
if MaxDist < (smoothRadius * 2) do append EdgeLoops CurVerts
|
924
|
+
)
|
925
|
+
DoneEdge += CurEdgeLoop
|
926
|
+
)
|
927
|
+
|
928
|
+
modPanel.setCurrentObject SkinMod; subobjectLevel = 1
|
929
|
+
for z in #(SmallElements, EdgeLoops) do
|
930
|
+
for i in z do
|
931
|
+
(
|
932
|
+
VertList = for v3 in i where (skinOps.IsVertexSelected skinMod v3 == 1) collect v3
|
933
|
+
NewWeights = self.make_rigid_skin SkinMod VertList
|
934
|
+
for v3 in VertList do (FinalBoneArray[v3] = NewWeights[1]; FinalWeightArray[v3] = NewWeights[2])
|
935
|
+
)
|
936
|
+
)
|
937
|
+
|
938
|
+
SmoothSkinData[8] = smoothRadius
|
939
|
+
|
940
|
+
delete tmpObj
|
941
|
+
OldWeightArray = #(); OldBoneArray = #(); LastWeights = #()
|
942
|
+
for sv = 1 to FinalBoneArray.count where FinalBonearray[sv] != undefined and FinalBoneArray[sv].count != 0 do
|
943
|
+
(
|
944
|
+
-- Home-Made undo
|
945
|
+
NumItem = skinOps.GetVertexWeightCount skinMod sv
|
946
|
+
OldWeightArray.count = OldBoneArray.count = NumItem
|
947
|
+
for CurBone = 1 to NumItem do
|
948
|
+
(
|
949
|
+
OldBoneArray[CurBone] = (skinOps.GetVertexWeightBoneID skinMod sv CurBone)
|
950
|
+
OldWeightArray[CurBone] = (skinOps.GetVertexWeight skinMod sv CurBone)
|
951
|
+
)
|
952
|
+
|
953
|
+
append LastWeights #(skinMod, sv, deepcopy OldBoneArray, deepcopy OldWeightArray)
|
954
|
+
if UndoWeights.count >= SmoothSkinMaxUndo do deleteItem UndoWeights 1
|
955
|
+
|
956
|
+
skinOps.ReplaceVertexWeights skinMod sv FinalBoneArray[sv] FinalWeightArray[sv]
|
957
|
+
)
|
958
|
+
|
959
|
+
append UndoWeights LastWeights
|
960
|
+
|
961
|
+
prog = ((p as float / iterNum as float) * 100.0)
|
962
|
+
format "Smoothing Progress:%\n" prog
|
963
|
+
)
|
964
|
+
),
|
965
|
+
|
966
|
+
fn undo_smooth_skin = (
|
967
|
+
CurUndo = UndoWeights[UndoWeights.count]
|
968
|
+
try(
|
969
|
+
if modPanel.GetCurrentObject() != CurUndo[1][1] do (modPanel.setCurrentObject CurUndo[1][1]; subobjectLevel = 1)
|
970
|
+
for i in CurUndo do skinOps.ReplaceVertexWeights i[1] i[2] i[3] i[4]
|
971
|
+
)
|
972
|
+
catch( print "Undo fail")
|
973
|
+
deleteitem UndoWeights UndoWeights.count
|
974
|
+
if UndoWeights.count == 0 then return false
|
975
|
+
),
|
976
|
+
|
977
|
+
fn setting inVertMode inRadius inIterNum inKeepMax = (
|
978
|
+
vertGroupMode = inVertMode
|
979
|
+
smoothRadius = inRadius
|
980
|
+
iterNum = inIterNum
|
981
|
+
keepMax = inKeepMax
|
982
|
+
)
|
983
|
+
)
|
984
|
+
''')
|
985
|
+
|
986
|
+
if rt.isValidNode(inObj):
|
987
|
+
rt.select(inObj)
|
988
|
+
rt.execute("max modify mode")
|
989
|
+
|
990
|
+
targetSkinMod = self.get_skin_mod(inObj)
|
991
|
+
rt.modPanel.setCurrentObject(targetSkinMod[0])
|
992
|
+
|
993
|
+
rt.execute(maxScriptCode)
|
994
|
+
smooth_skin = rt._SmoothSkin()
|
995
|
+
smooth_skin.setting(inVertMode.value, inRadius, inIterNum, inKeepMax)
|
996
|
+
smooth_skin.smooth_skin()
|