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