pyjallib 0.1.9__py3-none-any.whl → 0.1.10__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 +1 -1
- pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json +161 -0
- pyjallib/max/__init__.py +14 -2
- pyjallib/max/anim.py +60 -36
- pyjallib/max/autoClavicle.py +97 -132
- pyjallib/max/autoClavicleChain.py +173 -0
- pyjallib/max/bip.py +211 -14
- pyjallib/max/bone.py +341 -16
- pyjallib/max/groinBone.py +116 -77
- pyjallib/max/groinBoneChain.py +173 -0
- pyjallib/max/header.py +42 -6
- pyjallib/max/helper.py +3 -21
- pyjallib/max/hip.py +239 -366
- pyjallib/max/kneeBone.py +517 -0
- pyjallib/max/macro/jal_macro_align.py +11 -10
- pyjallib/max/macro/jal_macro_bone.py +2 -1
- pyjallib/max/macro/jal_macro_constraint.py +2 -1
- pyjallib/max/macro/jal_macro_helper.py +2 -1
- pyjallib/max/macro/jal_macro_link.py +2 -1
- pyjallib/max/macro/jal_macro_select.py +2 -1
- pyjallib/max/mirror.py +1 -1
- pyjallib/max/twistBone.py +258 -475
- pyjallib/max/twistBoneChain.py +162 -0
- pyjallib/max/volumeBone.py +273 -0
- pyjallib/max/volumeBoneChain.py +363 -0
- pyjallib/namePart.py +16 -16
- pyjallib/nameToPath.py +1 -1
- pyjallib/naming.py +15 -16
- pyjallib/namingConfig.py +3 -3
- {pyjallib-0.1.9.dist-info → pyjallib-0.1.10.dist-info}/METADATA +1 -1
- pyjallib-0.1.10.dist-info/RECORD +46 -0
- pyjallib/max/volumePreserveBone.py +0 -209
- pyjallib/p4module.py +0 -488
- pyjallib-0.1.9.dist-info/RECORD +0 -41
- {pyjallib-0.1.9.dist-info → pyjallib-0.1.10.dist-info}/WHEEL +0 -0
pyjallib/max/kneeBone.py
ADDED
@@ -0,0 +1,517 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
자동 무릎 본(AutoKnee) 모듈 - 3ds Max용 자동화된 무릎 본 기능 제공
|
6
|
+
원본 MAXScript의 autoKnee.ms를 Python으로 변환하였으며, pymxs 모듈 기반으로 구현됨
|
7
|
+
"""
|
8
|
+
|
9
|
+
from pymxs import runtime as rt
|
10
|
+
|
11
|
+
# Import necessary service classes for default initialization
|
12
|
+
from .name import Name
|
13
|
+
from .anim import Anim
|
14
|
+
from .helper import Helper
|
15
|
+
from .bone import Bone
|
16
|
+
from .constraint import Constraint
|
17
|
+
from .volumeBone import VolumeBone
|
18
|
+
|
19
|
+
class KneeBone:
|
20
|
+
"""
|
21
|
+
자동 무릎 본(AutoKnee) 관련 기능을 제공하는 클래스.
|
22
|
+
MAXScript의 _AutoKneeBone 구조체 개념을 Python으로 재구현한 클래스이며,
|
23
|
+
3ds Max의 기능들을 pymxs API를 통해 제어합니다.
|
24
|
+
|
25
|
+
이 클래스는 IK 시스템 기반의 다리 리깅을 자동화하며, 무릎 관절 회전, 비틀림 본 및
|
26
|
+
중간 본을 생성하여 자연스러운 무릎 움직임을 구현합니다.
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(self, nameService=None, animService=None, helperService=None, boneService=None, constraintService=None, volumeBoneService=None):
|
30
|
+
"""
|
31
|
+
KneeBone 클래스 초기화
|
32
|
+
|
33
|
+
Args:
|
34
|
+
nameService: 이름 처리 서비스 (제공되지 않으면 새로 생성)
|
35
|
+
animService: 애니메이션 서비스 (제공되지 않으면 새로 생성)
|
36
|
+
helperService: 헬퍼 객체 서비스 (제공되지 않으면 새로 생성)
|
37
|
+
boneService: 뼈대 서비스 (제공되지 않으면 새로 생성)
|
38
|
+
constraintService: 제약 서비스 (제공되지 않으면 새로 생성)
|
39
|
+
volumeBoneService: 볼륨 본 서비스 (제공되지 않으면 새로 생성)
|
40
|
+
"""
|
41
|
+
# 서비스 인스턴스 설정 또는 생성
|
42
|
+
self.name = nameService if nameService else Name()
|
43
|
+
self.anim = animService if animService else Anim()
|
44
|
+
# 종속성이 있는 서비스들은 이미 생성된 서비스들을 전달
|
45
|
+
self.helper = helperService if helperService else Helper(nameService=self.name)
|
46
|
+
self.bone = boneService if boneService else Bone(nameService=self.name, animService=self.anim)
|
47
|
+
self.const = constraintService if constraintService else Constraint(nameService=self.name)
|
48
|
+
self.volumeBone = volumeBoneService if volumeBoneService else VolumeBone(nameService=self.name, animService=self.anim, constraintService=self.const, boneService=self.bone, helperService=self.helper)
|
49
|
+
|
50
|
+
self.thigh = None
|
51
|
+
self.calf = None
|
52
|
+
self.foot = None
|
53
|
+
|
54
|
+
self.lookAtHleper = None
|
55
|
+
self.thighRotHelper = None
|
56
|
+
self.calfRotHelper = None
|
57
|
+
|
58
|
+
self.thighRotRootHelper = None
|
59
|
+
self.calfRotRootHelper = None
|
60
|
+
|
61
|
+
self.thighTwistBones = []
|
62
|
+
self.calfTwistBones = []
|
63
|
+
self.thighTwistHelpers = []
|
64
|
+
self.calfTwistHelpers = []
|
65
|
+
|
66
|
+
self.middleBones = []
|
67
|
+
|
68
|
+
self.liftScale = 0.05
|
69
|
+
|
70
|
+
self.thighRotScriptExpression = (
|
71
|
+
"localLimbTm = limb.transform * inverse limbParent.transform\n"
|
72
|
+
"localDeltaTm = localLimbTm * inverse localRotRefTm\n"
|
73
|
+
"\n"
|
74
|
+
"q = localDeltaTm.rotation\n"
|
75
|
+
"\n"
|
76
|
+
"axis = [0,0,1]\n"
|
77
|
+
"\n"
|
78
|
+
"proj = (dot q.axis axis) * axis\n"
|
79
|
+
"twist = quat -q.angle proj\n"
|
80
|
+
"twist = normalize twist\n"
|
81
|
+
"\n"
|
82
|
+
"twist\n"
|
83
|
+
)
|
84
|
+
self.calfRotScriptExpression = (
|
85
|
+
"localLimbTm = limb.transform * inverse limbParent.transform\n"
|
86
|
+
"localDeltaTm = localLimbTm * inverse localRotRefTm\n"
|
87
|
+
"\n"
|
88
|
+
"q = localDeltaTm.rotation\n"
|
89
|
+
"\n"
|
90
|
+
"axis = [0,0,1]\n"
|
91
|
+
"\n"
|
92
|
+
"proj = (dot q.axis axis) * axis\n"
|
93
|
+
"twist = quat q.angle proj\n"
|
94
|
+
"twist = normalize twist\n"
|
95
|
+
"\n"
|
96
|
+
"twist\n"
|
97
|
+
)
|
98
|
+
|
99
|
+
|
100
|
+
def create_lookat_helper(self, inThigh, inFoot):
|
101
|
+
"""
|
102
|
+
무릎 시스템을 위한 LookAt 헬퍼 객체를 생성합니다.
|
103
|
+
|
104
|
+
이 헬퍼는 대퇴골(Thigh)에 위치하면서 발(Foot)을 바라보도록 제약됩니다.
|
105
|
+
무릎 회전의 기반이 되는 방향을 결정하는 역할을 합니다.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
inThigh: 대퇴골 본 객체
|
109
|
+
inFoot: 발 본 객체
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
bool: 헬퍼 생성 성공 여부
|
113
|
+
"""
|
114
|
+
if not rt.isValidNode(inThigh) or not rt.isValidNode(inFoot):
|
115
|
+
return False
|
116
|
+
|
117
|
+
filteringChar = self.name._get_filtering_char(inThigh.name)
|
118
|
+
isLowerName = inThigh.name.islower()
|
119
|
+
|
120
|
+
# 서비스 인스턴스 설정 또는 생성
|
121
|
+
self.thigh = inThigh
|
122
|
+
self.foot = inFoot
|
123
|
+
|
124
|
+
lookAtHelperName = self.name.replace_name_part("Type", inThigh.name, self.name.get_name_part_value_by_description("Type", "LookAt"))
|
125
|
+
lookAtHelperName = self.name.add_suffix_to_real_name(lookAtHelperName, filteringChar + "Lift")
|
126
|
+
if isLowerName:
|
127
|
+
lookAtHelperName = lookAtHelperName.lower()
|
128
|
+
|
129
|
+
lookAtHelper = self.helper.create_point(lookAtHelperName)
|
130
|
+
lookAtHelper.transform = inThigh.transform
|
131
|
+
lookAtHelper.parent = inThigh
|
132
|
+
lookAtConst = self.const.assign_lookat(lookAtHelper, inFoot)
|
133
|
+
lookAtConst.upnode_world = False
|
134
|
+
lookAtConst.pickUpNode = inThigh
|
135
|
+
lookAtConst.lookat_vector_length = 0.0
|
136
|
+
|
137
|
+
self.lookAtHleper = lookAtHelper
|
138
|
+
|
139
|
+
def create_rot_root_heleprs(self, inThigh, inCalf, inFoot):
|
140
|
+
"""
|
141
|
+
무릎 회전의 기준이 되는 루트 헬퍼 객체들을 생성합니다.
|
142
|
+
|
143
|
+
대퇴골과 종아리뼈에 각각 위치하며, 비틀림 계산을 위한 기준점 역할을 합니다.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
inThigh: 대퇴골 본 객체
|
147
|
+
inCalf: 종아리뼈 본 객체
|
148
|
+
inFoot: 발 본 객체
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
bool: 헬퍼 생성 성공 여부
|
152
|
+
"""
|
153
|
+
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf) or not rt.isValidNode(inFoot):
|
154
|
+
return False
|
155
|
+
|
156
|
+
filteringChar = self.name._get_filtering_char(inThigh.name)
|
157
|
+
isLowerName = inThigh.name.islower()
|
158
|
+
|
159
|
+
# 서비스 인스턴스 설정 또는 생성
|
160
|
+
self.thigh = inThigh
|
161
|
+
self.calf = inCalf
|
162
|
+
self.foot = inFoot
|
163
|
+
|
164
|
+
thighRotRootHelperName = self.name.replace_name_part("Type", inThigh.name, self.name.get_name_part_value_by_description("Type", "Dummy"))
|
165
|
+
calfRotRootHelperName = self.name.replace_name_part("Type", inCalf.name, self.name.get_name_part_value_by_description("Type", "Dummy"))
|
166
|
+
thighRotRootHelperName = self.name.add_suffix_to_real_name(thighRotRootHelperName, filteringChar + "Lift")
|
167
|
+
calfRotRootHelperName = self.name.add_suffix_to_real_name(calfRotRootHelperName, filteringChar + "Lift")
|
168
|
+
if isLowerName:
|
169
|
+
thighRotRootHelperName = thighRotRootHelperName.lower()
|
170
|
+
calfRotRootHelperName = calfRotRootHelperName.lower()
|
171
|
+
|
172
|
+
thighRotRootHelper = self.helper.create_point(thighRotRootHelperName, crossToggle=False, boxToggle=True)
|
173
|
+
thighRotRootHelper.transform = inThigh.transform
|
174
|
+
thighRotRootHelper.parent = inThigh
|
175
|
+
|
176
|
+
calfRotRootHelper = self.helper.create_point(calfRotRootHelperName, crossToggle=False, boxToggle=True)
|
177
|
+
calfRotRootHelper.transform = inCalf.transform
|
178
|
+
calfRotRootHelper.position = inFoot.position
|
179
|
+
calfRotRootHelper.parent = inCalf
|
180
|
+
|
181
|
+
self.thighRotRootHelper = thighRotRootHelper
|
182
|
+
self.calfRotRootHelper = calfRotRootHelper
|
183
|
+
|
184
|
+
def create_rot_helper(self, inThigh, inCalf, inFoot):
|
185
|
+
"""
|
186
|
+
대퇴골과 종아리뼈의 회전을 제어하는 헬퍼 객체들을 생성합니다.
|
187
|
+
|
188
|
+
이 헬퍼들은 실제 무릎 움직임에 따른 비틀림 효과를 구현하는 데 사용됩니다.
|
189
|
+
|
190
|
+
Args:
|
191
|
+
inThigh: 대퇴골 본 객체
|
192
|
+
inCalf: 종아리뼈 본 객체
|
193
|
+
inFoot: 발 본 객체
|
194
|
+
|
195
|
+
Returns:
|
196
|
+
bool: 헬퍼 생성 성공 여부
|
197
|
+
"""
|
198
|
+
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf):
|
199
|
+
return False
|
200
|
+
|
201
|
+
filteringChar = self.name._get_filtering_char(inThigh.name)
|
202
|
+
isLowerName = inThigh.name.islower()
|
203
|
+
|
204
|
+
# 서비스 인스턴스 설정 또는 생성
|
205
|
+
self.thigh = inThigh
|
206
|
+
self.calf = inCalf
|
207
|
+
|
208
|
+
thighRotHelperName = self.name.replace_name_part("Type", inThigh.name, self.name.get_name_part_value_by_description("Type", "Rotation"))
|
209
|
+
calfRotHelperName = self.name.replace_name_part("Type", inCalf.name, self.name.get_name_part_value_by_description("Type", "Rotation"))
|
210
|
+
thighRotHelperName = self.name.add_suffix_to_real_name(thighRotHelperName, filteringChar + "Lift")
|
211
|
+
calfRotHelperName = self.name.add_suffix_to_real_name(calfRotHelperName, filteringChar + "Lift")
|
212
|
+
if isLowerName:
|
213
|
+
thighRotHelperName = thighRotHelperName.lower()
|
214
|
+
calfRotHelperName = calfRotHelperName.lower()
|
215
|
+
|
216
|
+
thighRotHelper = self.helper.create_point(thighRotHelperName)
|
217
|
+
thighRotHelper.transform = inThigh.transform
|
218
|
+
thighRotHelper.parent = inThigh
|
219
|
+
|
220
|
+
calfRotHelper = self.helper.create_point(calfRotHelperName)
|
221
|
+
calfRotHelper.transform = inCalf.transform
|
222
|
+
calfRotHelper.position = inFoot.transform.position
|
223
|
+
calfRotHelper.parent = inCalf
|
224
|
+
|
225
|
+
self.thighRotHelper = thighRotHelper
|
226
|
+
self.calfRotHelper = calfRotHelper
|
227
|
+
|
228
|
+
def assign_thigh_rot_constraint(self, inLiftScale=0.1):
|
229
|
+
"""
|
230
|
+
대퇴골 회전 헬퍼에 스크립트 기반 회전 제약을 할당합니다.
|
231
|
+
|
232
|
+
LookAt 헬퍼와 대퇴골 회전 루트 헬퍼 사이의 관계를 기반으로 비틀림 회전을 계산합니다.
|
233
|
+
|
234
|
+
Args:
|
235
|
+
inLiftScale: 회전 영향력 스케일 (0.0~1.0)
|
236
|
+
"""
|
237
|
+
self.liftScale = inLiftScale
|
238
|
+
localRotRefTm = self.lookAtHleper.transform * rt.inverse(self.thighRotRootHelper.transform)
|
239
|
+
|
240
|
+
rotListConst = self.const.assign_rot_list(self.thighRotHelper)
|
241
|
+
rotScriptConst = rt.Rotation_Script()
|
242
|
+
rt.setPropertyController(rotListConst, "Available", rotScriptConst)
|
243
|
+
rotListConst.setActive(rotListConst.count)
|
244
|
+
|
245
|
+
rotScriptConst.addConstant("localRotRefTm", localRotRefTm)
|
246
|
+
rotScriptConst.addNode("limb", self.lookAtHleper)
|
247
|
+
rotScriptConst.addNode("limbParent", self.thighRotRootHelper)
|
248
|
+
rotScriptConst.setExpression(self.thighRotScriptExpression)
|
249
|
+
|
250
|
+
self.const.set_rot_controllers_weight_in_list(self.thighRotHelper, 1, self.liftScale * 100.0)
|
251
|
+
|
252
|
+
def assign_calf_rot_constraint(self, inLiftScale=0.1):
|
253
|
+
"""
|
254
|
+
종아리뼈 회전 헬퍼에 스크립트 기반 회전 제약을 할당합니다.
|
255
|
+
|
256
|
+
LookAt 헬퍼와 대퇴골 회전 루트 헬퍼 사이의 관계를 기반으로 비틀림 회전을 계산합니다.
|
257
|
+
|
258
|
+
Args:
|
259
|
+
inLiftScale: 회전 영향력 스케일 (0.0~1.0)
|
260
|
+
"""
|
261
|
+
self.liftScale = inLiftScale
|
262
|
+
localRotRefTm = self.lookAtHleper.transform * rt.inverse(self.thighRotRootHelper.transform)
|
263
|
+
|
264
|
+
rotListConst = self.const.assign_rot_list(self.calfRotHelper)
|
265
|
+
rotScriptConst = rt.Rotation_Script()
|
266
|
+
rt.setPropertyController(rotListConst, "Available", rotScriptConst)
|
267
|
+
rotListConst.setActive(rotListConst.count)
|
268
|
+
|
269
|
+
rotScriptConst.addConstant("localRotRefTm", localRotRefTm)
|
270
|
+
rotScriptConst.addNode("limb", self.lookAtHleper)
|
271
|
+
rotScriptConst.addNode("limbParent", self.thighRotRootHelper)
|
272
|
+
rotScriptConst.setExpression(self.calfRotScriptExpression)
|
273
|
+
|
274
|
+
self.const.set_rot_controllers_weight_in_list(self.calfRotHelper, 1, self.liftScale * 100.0)
|
275
|
+
|
276
|
+
def create_middle_bone(self, inThigh, inCalf, inKneePopScale=0.1, inKneeBackScale=1.5):
|
277
|
+
"""
|
278
|
+
무릎 중간 본을 생성합니다.
|
279
|
+
|
280
|
+
이 본들은 무릎이 구부러질 때 앞(Pop)과 뒤(Back)로 움직이는 볼륨감 있는 본들입니다.
|
281
|
+
무릎 관절의 시각적 품질을 향상시킵니다.
|
282
|
+
|
283
|
+
Args:
|
284
|
+
inThigh: 대퇴골 본 객체
|
285
|
+
inCalf: 종아리뼈 본 객체
|
286
|
+
inKneePopScale: 무릎 앞쪽 돌출 스케일 (1.0이 기본값)
|
287
|
+
inKneeBackScale: 무릎 뒤쪽 돌출 스케일 (1.0이 기본값)
|
288
|
+
|
289
|
+
Returns:
|
290
|
+
bool: 중간 본 생성 성공 여부
|
291
|
+
"""
|
292
|
+
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf):
|
293
|
+
return False
|
294
|
+
|
295
|
+
facingDirVec = inCalf.transform.position - inThigh.transform.position
|
296
|
+
inObjXAxisVec = inCalf.objectTransform.row1
|
297
|
+
distanceDir = 1.0 if rt.dot(inObjXAxisVec, facingDirVec) > 0 else -1.0
|
298
|
+
|
299
|
+
self.thigh = inThigh
|
300
|
+
self.calf = inCalf
|
301
|
+
|
302
|
+
transScales = []
|
303
|
+
if distanceDir > 0:
|
304
|
+
transScales.append(inKneePopScale)
|
305
|
+
transScales.append(inKneeBackScale)
|
306
|
+
else:
|
307
|
+
transScales.append(inKneeBackScale)
|
308
|
+
transScales.append(inKneePopScale)
|
309
|
+
|
310
|
+
result = self.volumeBone.create_bones(self.calf, self.thigh, inVolumeSize=5.0, inRotAxises=["Z", "Z"], inTransAxises=["PosY", "NegY"], inTransScales=transScales)
|
311
|
+
|
312
|
+
filteringChar = self.name._get_filtering_char(inCalf.name)
|
313
|
+
calfName = self.name.get_name_part("RealName", inCalf.name+ filteringChar + "Vol")
|
314
|
+
isLower = calfName[0].islower()
|
315
|
+
replaceName = "Knee"
|
316
|
+
if isLower:
|
317
|
+
replaceName = replaceName.lower()
|
318
|
+
|
319
|
+
for item in result["Bones"]:
|
320
|
+
item.name.replace(calfName, replaceName)
|
321
|
+
|
322
|
+
result["rootBone"].name.replace(calfName, replaceName)
|
323
|
+
result["RotHelper"].name.replace(calfName, replaceName)
|
324
|
+
|
325
|
+
# 결과 저장
|
326
|
+
if result and "Bones" in result:
|
327
|
+
self.middleBones.extend(result["Bones"])
|
328
|
+
|
329
|
+
return result
|
330
|
+
|
331
|
+
def create_twist_bones(self, inThigh, inCalf):
|
332
|
+
"""
|
333
|
+
대퇴골과 종아리뼈에 연결된 비틀림 본들에 대한 리프팅 본과 헬퍼를 생성합니다.
|
334
|
+
|
335
|
+
기존 비틀림 본들을 찾아 각각에 대응하는 리프팅 본과 헬퍼를 생성하여
|
336
|
+
무릎 구부림에 따라 자연스럽게 회전하도록 제약을 설정합니다.
|
337
|
+
|
338
|
+
Args:
|
339
|
+
inThigh: 대퇴골 본 객체
|
340
|
+
inCalf: 종아리뼈 본 객체
|
341
|
+
|
342
|
+
Returns:
|
343
|
+
bool: 비틀림 본 생성 성공 여부
|
344
|
+
"""
|
345
|
+
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf):
|
346
|
+
return False
|
347
|
+
|
348
|
+
filteringChar = self.name._get_filtering_char(inThigh.name)
|
349
|
+
isLowerName = inThigh.name.islower()
|
350
|
+
|
351
|
+
# 서비스 인스턴스 설정 또는 생성
|
352
|
+
self.thigh = inThigh
|
353
|
+
self.calf = inCalf
|
354
|
+
|
355
|
+
oriThighTwistBones = []
|
356
|
+
oriClafTwistBones = []
|
357
|
+
thighChildren = inThigh.children
|
358
|
+
calfChildren = inCalf.children
|
359
|
+
|
360
|
+
if len(thighChildren) < 1 or len(calfChildren) < 1:
|
361
|
+
return False
|
362
|
+
|
363
|
+
for item in thighChildren:
|
364
|
+
testName = item.name.lower()
|
365
|
+
if testName.find("twist") != -1:
|
366
|
+
oriThighTwistBones.append(item)
|
367
|
+
|
368
|
+
for item in calfChildren:
|
369
|
+
testName = item.name.lower()
|
370
|
+
if testName.find("twist") != -1:
|
371
|
+
oriClafTwistBones.append(item)
|
372
|
+
|
373
|
+
for item in oriThighTwistBones:
|
374
|
+
liftTwistBoneName = self.name.add_suffix_to_real_name(item.name, filteringChar + "Lift")
|
375
|
+
liftTwistHelperName = self.name.add_suffix_to_real_name(item.name, filteringChar + "Lift")
|
376
|
+
if isLowerName:
|
377
|
+
liftTwistBoneName = liftTwistBoneName.lower()
|
378
|
+
liftTwistHelperName = liftTwistHelperName.lower()
|
379
|
+
|
380
|
+
liftTwistBone = self.bone.create_nub_bone(liftTwistBoneName, 2)
|
381
|
+
liftTwistBone.name = self.name.remove_name_part("Nub", liftTwistBone.name)
|
382
|
+
liftTwistBone.name = self.name.replace_name_part("Index", liftTwistBone.name, self.name.get_name("Index", oriThighTwistBones.name))
|
383
|
+
|
384
|
+
rt.setProperty(liftTwistBone, "transform", item.transform)
|
385
|
+
liftTwistBone.parent = item
|
386
|
+
|
387
|
+
liftTwistHelper = self.helper.create_point(liftTwistHelperName)
|
388
|
+
liftTwistHelper.name = self.name.replace_name_part("Type", liftTwistHelper.name, self.name.get_name_part_value_by_description("Type", "Position"))
|
389
|
+
|
390
|
+
rt.setProperty(liftTwistHelper, "transform", item.transform)
|
391
|
+
liftTwistHelper.parent = self.thighRotHelper
|
392
|
+
|
393
|
+
liftTwistBonePosConst = self.const.assign_pos_const(liftTwistBone, liftTwistHelper)
|
394
|
+
|
395
|
+
self.thighTwistBones.append(liftTwistBone)
|
396
|
+
self.thighTwistHelpers.append(liftTwistHelper)
|
397
|
+
|
398
|
+
for item in oriClafTwistBones:
|
399
|
+
liftTwistBoneName = self.name.add_suffix_to_real_name(item.name, filteringChar + "Lift")
|
400
|
+
liftTwistHelperName = self.name.add_suffix_to_real_name(item.name, filteringChar + "Lift")
|
401
|
+
if isLowerName:
|
402
|
+
liftTwistBoneName = liftTwistBoneName.lower()
|
403
|
+
liftTwistHelperName = liftTwistHelperName.lower()
|
404
|
+
|
405
|
+
liftTwistBone = self.bone.create_nub_bone(liftTwistBoneName, 2)
|
406
|
+
liftTwistBone.name = self.name.remove_name_part("Nub", liftTwistBone.name)
|
407
|
+
liftTwistBone.name = self.name.replace_name_part("Index", liftTwistBone.name, self.name.get_name("Index", oriClafTwistBones.name))
|
408
|
+
|
409
|
+
rt.setProperty(liftTwistBone, "transform", item.transform)
|
410
|
+
liftTwistBone.parent = item
|
411
|
+
|
412
|
+
liftTwistHelper = self.helper.create_point(liftTwistHelperName)
|
413
|
+
liftTwistHelper.name = self.name.replace_name_part("Type", liftTwistHelper.name, self.name.get_name_part_value_by_description("Type", "Position"))
|
414
|
+
|
415
|
+
rt.setProperty(liftTwistHelper, "transform", item.transform)
|
416
|
+
liftTwistHelper.parent = self.calfRotHelper
|
417
|
+
|
418
|
+
liftTwistBonePosConst = self.const.assign_pos_const(liftTwistBone, liftTwistHelper)
|
419
|
+
|
420
|
+
self.calfTwistBones.append(liftTwistBone)
|
421
|
+
self.calfTwistHelpers.append(liftTwistHelper)
|
422
|
+
|
423
|
+
def create_bone(self, inThigh, inCalf, inFoot, inLiftScale=0.05, inKneePopScale=1.0, inKneeBackScale=1.0):
|
424
|
+
"""
|
425
|
+
자동 무릎 본 시스템의 모든 요소를 생성하는 주요 메서드입니다.
|
426
|
+
|
427
|
+
이 메서드는 다음 단계들을 순차적으로 실행합니다:
|
428
|
+
1. LookAt 헬퍼 생성
|
429
|
+
2. 회전 루트 헬퍼 생성
|
430
|
+
3. 회전 헬퍼 생성
|
431
|
+
4. 대퇴골과 종아리뼈 회전 제약 설정
|
432
|
+
5. 무릎 중간 본 생성
|
433
|
+
6. 비틀림 본 생성 및 제약 설정
|
434
|
+
|
435
|
+
Args:
|
436
|
+
inThigh: 대퇴골 본 객체
|
437
|
+
inCalf: 종아리뼈 본 객체
|
438
|
+
inFoot: 발 본 객체
|
439
|
+
inLiftScale: 회전 영향력 스케일 (0.0~1.0)
|
440
|
+
inKneePopScale: 무릎 앞쪽 돌출 스케일 (1.0이 기본값)
|
441
|
+
inKneeBackScale: 무릎 뒤쪽 돌출 스케일 (1.0이 기본값)
|
442
|
+
|
443
|
+
Returns:
|
444
|
+
bool: 자동 무릎 본 시스템 생성 성공 여부
|
445
|
+
"""
|
446
|
+
if not rt.isValidNode(inThigh) or not rt.isValidNode(inCalf) or not rt.isValidNode(inFoot):
|
447
|
+
return False
|
448
|
+
|
449
|
+
self.create_lookat_helper(inThigh, inFoot)
|
450
|
+
self.create_rot_root_heleprs(inThigh, inCalf, inFoot)
|
451
|
+
self.create_rot_helper(inThigh, inCalf, inFoot)
|
452
|
+
self.assign_thigh_rot_constraint(inLiftScale=inLiftScale)
|
453
|
+
self.assign_calf_rot_constraint(inLiftScale=inLiftScale)
|
454
|
+
self.create_middle_bone(inThigh, inCalf, inKneePopScale=inKneePopScale, inKneeBackScale=inKneeBackScale)
|
455
|
+
self.create_twist_bones(inThigh, inCalf)
|
456
|
+
|
457
|
+
# 결과를 딕셔너리 형태로 준비
|
458
|
+
result = {
|
459
|
+
"Thigh": inThigh,
|
460
|
+
"Calf": inCalf,
|
461
|
+
"Foot": inFoot,
|
462
|
+
"LookAtHelper": self.lookAtHleper,
|
463
|
+
"ThighRotHelper": self.thighRotHelper,
|
464
|
+
"CalfRotHelper": self.calfRotHelper,
|
465
|
+
"ThighRotRootHelper": self.thighRotRootHelper,
|
466
|
+
"CalfRotRootHelper": self.calfRotRootHelper,
|
467
|
+
"ThighTwistBones": self.thighTwistBones,
|
468
|
+
"CalfTwistBones": self.calfTwistBones,
|
469
|
+
"ThighTwistHelpers": self.thighTwistHelpers,
|
470
|
+
"CalfTwistHelpers": self.calfTwistHelpers,
|
471
|
+
"MiddleBones": self.middleBones,
|
472
|
+
"LiftScale": inLiftScale,
|
473
|
+
"KneePopScale": inKneePopScale,
|
474
|
+
"KneeBackScale": inKneeBackScale
|
475
|
+
}
|
476
|
+
|
477
|
+
# 메소드 호출 후 데이터 초기화
|
478
|
+
self.reset()
|
479
|
+
|
480
|
+
return result
|
481
|
+
|
482
|
+
def reset(self):
|
483
|
+
"""
|
484
|
+
클래스의 주요 컴포넌트들을 초기화합니다.
|
485
|
+
서비스가 아닌 클래스 자체의 작업 데이터를 초기화하는 함수입니다.
|
486
|
+
|
487
|
+
Returns:
|
488
|
+
self: 메소드 체이닝을 위한 자기 자신 반환
|
489
|
+
"""
|
490
|
+
self.thigh = None
|
491
|
+
self.calf = None
|
492
|
+
self.foot = None
|
493
|
+
|
494
|
+
self.lookAtHleper = None
|
495
|
+
self.thighRotHelper = None
|
496
|
+
self.calfRotHelper = None
|
497
|
+
|
498
|
+
self.thighRotRootHelper = None
|
499
|
+
self.calfRotRootHelper = None
|
500
|
+
|
501
|
+
self.thighTwistBones = []
|
502
|
+
self.calfTwistBones = []
|
503
|
+
self.thighTwistHelpers = []
|
504
|
+
self.calfTwistHelpers = []
|
505
|
+
|
506
|
+
self.middleBones = []
|
507
|
+
|
508
|
+
self.liftScale = 0.05
|
509
|
+
|
510
|
+
return self
|
511
|
+
|
512
|
+
|
513
|
+
|
514
|
+
|
515
|
+
|
516
|
+
|
517
|
+
|
@@ -2,7 +2,8 @@
|
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
4
|
from pymxs import runtime as rt
|
5
|
-
from pyjallib.max.header import
|
5
|
+
from pyjallib.max.header import get_pyjallibmaxheader
|
6
|
+
jal = get_pyjallibmaxheader()
|
6
7
|
|
7
8
|
def jal_align_to_last():
|
8
9
|
jal.align.align_to_last_sel()
|
@@ -11,10 +12,10 @@ def jal_align_to_last_center():
|
|
11
12
|
jal.align.align_to_last_sel_center()
|
12
13
|
|
13
14
|
def jal_align_pos_only():
|
14
|
-
jal.align.
|
15
|
+
jal.align.align_to_last_sel_pos()
|
15
16
|
|
16
17
|
def jal_align_rot_only():
|
17
|
-
jal.align.
|
18
|
+
jal.align.align_to_last_sel_rot()
|
18
19
|
|
19
20
|
def jal_align_mirror_x():
|
20
21
|
if rt.selection.count == 0:
|
@@ -42,12 +43,12 @@ def jal_align_mirror_x():
|
|
42
43
|
helperArray.append(item)
|
43
44
|
else:
|
44
45
|
nonBoneArray.append(item)
|
45
|
-
|
46
|
-
if boneArray
|
46
|
+
|
47
|
+
if len(boneArray) != 0:
|
47
48
|
mirroredBoneArray = jal.mirror.mirror_bone(boneArray, mAxis=defMirrorAxis)
|
48
|
-
if helperArray
|
49
|
+
if len(helperArray) != 0:
|
49
50
|
mirroredHelperArray = jal.mirror.mirror_geo(helperArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
|
50
|
-
if nonBoneArray
|
51
|
+
if len(nonBoneArray) != 0:
|
51
52
|
mirroredNonBoneArray = jal.mirror.mirror_geo(nonBoneArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
|
52
53
|
|
53
54
|
mirroredObjArray.extend(mirroredBoneArray)
|
@@ -84,11 +85,11 @@ def jal_align_mirror_y():
|
|
84
85
|
else:
|
85
86
|
nonBoneArray.append(item)
|
86
87
|
|
87
|
-
if boneArray
|
88
|
+
if len(boneArray) != 0:
|
88
89
|
mirroredBoneArray = jal.mirror.mirror_bone(boneArray, mAxis=defMirrorAxis)
|
89
|
-
if helperArray
|
90
|
+
if len(helperArray) != 0:
|
90
91
|
mirroredHelperArray = jal.mirror.mirror_geo(helperArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
|
91
|
-
if nonBoneArray
|
92
|
+
if len(nonBoneArray) != 0:
|
92
93
|
mirroredNonBoneArray = jal.mirror.mirror_geo(nonBoneArray, mAxis=defMirrorAxis, pivotObj=pObj, cloneStatus=2)
|
93
94
|
|
94
95
|
mirroredObjArray.extend(mirroredBoneArray)
|
@@ -2,7 +2,8 @@
|
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
4
|
from pymxs import runtime as rt
|
5
|
-
from pyjallib.max.header import
|
5
|
+
from pyjallib.max.header import get_pyjallibmaxheader
|
6
|
+
jal = get_pyjallibmaxheader()
|
6
7
|
|
7
8
|
from PySide2 import QtWidgets, QtCore, QtGui
|
8
9
|
import gc # Import garbage collector
|
@@ -2,7 +2,8 @@
|
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
4
|
from pymxs import runtime as rt
|
5
|
-
from pyjallib.max.header import
|
5
|
+
from pyjallib.max.header import get_pyjallibmaxheader
|
6
|
+
jal = get_pyjallibmaxheader()
|
6
7
|
|
7
8
|
def jal_collapse_const():
|
8
9
|
if rt.selection.count > 0:
|
@@ -5,7 +5,8 @@ from pymxs import runtime as rt
|
|
5
5
|
from PySide2 import QtWidgets, QtCore, QtGui
|
6
6
|
import gc # Import garbage collector
|
7
7
|
|
8
|
-
from pyjallib.max.header import
|
8
|
+
from pyjallib.max.header import get_pyjallibmaxheader
|
9
|
+
jal = get_pyjallibmaxheader()
|
9
10
|
|
10
11
|
class HelperTypeSelDialog(QtWidgets.QDialog):
|
11
12
|
def __init__(self, parent=QtWidgets.QWidget.find(rt.windows.getMAXHWND())):
|
@@ -2,7 +2,8 @@
|
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
|
4
4
|
from pymxs import runtime as rt
|
5
|
-
from pyjallib.max.header import
|
5
|
+
from pyjallib.max.header import get_pyjallibmaxheader
|
6
|
+
jal = get_pyjallibmaxheader()
|
6
7
|
|
7
8
|
def jal_selFilter_all():
|
8
9
|
jal.sel.set_selectionSet_to_all()
|
pyjallib/max/mirror.py
CHANGED
@@ -297,7 +297,7 @@ class Mirror:
|
|
297
297
|
|
298
298
|
# 이름 생성 (좌우/앞뒤 방향이 있는 경우 미러링된 이름 생성)
|
299
299
|
if self.name.has_Side(original.name) or self.name.has_FrontBack(original.name):
|
300
|
-
reflection.name = self.name.gen_mirroring_name(original.name
|
300
|
+
reflection.name = self.name.gen_mirroring_name(original.name)
|
301
301
|
else:
|
302
302
|
reflection.name = self.name.add_suffix_to_real_name(original.name, "Mirrored")
|
303
303
|
|