pyjallib 0.1.4__py3-none-any.whl → 0.1.6__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/3DSMaxNamingConfig.json +43 -3
- pyjallib/max/__init__.py +5 -1
- pyjallib/max/autoClavicle.py +122 -0
- pyjallib/max/bip.py +2 -4
- pyjallib/max/constraint.py +4 -1
- pyjallib/max/groinBone.py +90 -0
- pyjallib/max/header.py +5 -1
- pyjallib/max/helper.py +54 -0
- pyjallib/max/macro/jal_macro_helper.py +33 -5
- pyjallib/max/twistBone.py +128 -2
- pyjallib/naming.py +5 -4
- {pyjallib-0.1.4.dist-info → pyjallib-0.1.6.dist-info}/METADATA +1 -1
- {pyjallib-0.1.4.dist-info → pyjallib-0.1.6.dist-info}/RECORD +15 -13
- {pyjallib-0.1.4.dist-info → pyjallib-0.1.6.dist-info}/WHEEL +0 -0
pyjallib/__init__.py
CHANGED
@@ -25,6 +25,10 @@
|
|
25
25
|
"SkinBone",
|
26
26
|
"Biped"
|
27
27
|
],
|
28
|
+
"koreanDescriptions": [
|
29
|
+
"",
|
30
|
+
""
|
31
|
+
],
|
28
32
|
"isDirection": false
|
29
33
|
},
|
30
34
|
{
|
@@ -34,14 +38,22 @@
|
|
34
38
|
"Dum",
|
35
39
|
"Exp",
|
36
40
|
"IK",
|
37
|
-
"T"
|
41
|
+
"T",
|
42
|
+
"Rot",
|
43
|
+
"Pos",
|
44
|
+
"Lat",
|
45
|
+
"UpN"
|
38
46
|
],
|
39
47
|
"weights": [
|
40
48
|
5,
|
41
49
|
10,
|
42
50
|
15,
|
43
51
|
20,
|
44
|
-
25
|
52
|
+
25,
|
53
|
+
30,
|
54
|
+
35,
|
55
|
+
40,
|
56
|
+
45
|
45
57
|
],
|
46
58
|
"type": "PREFIX",
|
47
59
|
"descriptions": [
|
@@ -49,7 +61,22 @@
|
|
49
61
|
"Dummy",
|
50
62
|
"ExposeTM",
|
51
63
|
"IK",
|
52
|
-
"Target"
|
64
|
+
"Target",
|
65
|
+
"Rotation",
|
66
|
+
"Position",
|
67
|
+
"LookAt",
|
68
|
+
"UpNode"
|
69
|
+
],
|
70
|
+
"koreanDescriptions": [
|
71
|
+
"",
|
72
|
+
"",
|
73
|
+
"",
|
74
|
+
"",
|
75
|
+
"",
|
76
|
+
"",
|
77
|
+
"",
|
78
|
+
"",
|
79
|
+
""
|
53
80
|
],
|
54
81
|
"isDirection": false
|
55
82
|
},
|
@@ -68,6 +95,10 @@
|
|
68
95
|
"Left",
|
69
96
|
"Right"
|
70
97
|
],
|
98
|
+
"koreanDescriptions": [
|
99
|
+
"",
|
100
|
+
""
|
101
|
+
],
|
71
102
|
"isDirection": true
|
72
103
|
},
|
73
104
|
{
|
@@ -85,6 +116,10 @@
|
|
85
116
|
"Front",
|
86
117
|
"Back"
|
87
118
|
],
|
119
|
+
"koreanDescriptions": [
|
120
|
+
"",
|
121
|
+
""
|
122
|
+
],
|
88
123
|
"isDirection": true
|
89
124
|
},
|
90
125
|
{
|
@@ -93,6 +128,7 @@
|
|
93
128
|
"weights": [],
|
94
129
|
"type": "REALNAME",
|
95
130
|
"descriptions": [],
|
131
|
+
"koreanDescriptions": [],
|
96
132
|
"isDirection": false
|
97
133
|
},
|
98
134
|
{
|
@@ -101,6 +137,7 @@
|
|
101
137
|
"weights": [],
|
102
138
|
"type": "INDEX",
|
103
139
|
"descriptions": [],
|
140
|
+
"koreanDescriptions": [],
|
104
141
|
"isDirection": false
|
105
142
|
},
|
106
143
|
{
|
@@ -115,6 +152,9 @@
|
|
115
152
|
"descriptions": [
|
116
153
|
"Nub"
|
117
154
|
],
|
155
|
+
"koreanDescriptions": [
|
156
|
+
""
|
157
|
+
],
|
118
158
|
"isDirection": false
|
119
159
|
}
|
120
160
|
]
|
pyjallib/max/__init__.py
CHANGED
@@ -26,6 +26,8 @@ from .bip import Bip
|
|
26
26
|
from .skin import Skin
|
27
27
|
|
28
28
|
from .twistBone import TwistBone
|
29
|
+
from .groinBone import GroinBone
|
30
|
+
from .autoClavicle import AutoClavicle
|
29
31
|
|
30
32
|
# 모듈 내보내기
|
31
33
|
__all__ = [
|
@@ -42,5 +44,7 @@ __all__ = [
|
|
42
44
|
'Link',
|
43
45
|
'Bip',
|
44
46
|
'Skin',
|
45
|
-
'TwistBone'
|
47
|
+
'TwistBone',
|
48
|
+
'GroinBone',
|
49
|
+
'AutoClavicle'
|
46
50
|
]
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
자동 쇄골(AutoClavicle) 모듈 - 3ds Max용 자동화된 쇄골 기능 제공
|
6
|
+
원본 MAXScript의 autoclavicle.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 .bip import Bip
|
18
|
+
|
19
|
+
|
20
|
+
class AutoClavicle:
|
21
|
+
"""
|
22
|
+
자동 쇄골(AutoClavicle) 관련 기능을 제공하는 클래스.
|
23
|
+
MAXScript의 _AutoClavicleBone 구조체 개념을 Python으로 재구현한 클래스이며,
|
24
|
+
3ds Max의 기능들을 pymxs API를 통해 제어합니다.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(self, nameService=None, animService=None, helperService=None, boneService=None, constraintService=None, bipService=None):
|
28
|
+
"""
|
29
|
+
클래스 초기화
|
30
|
+
|
31
|
+
Args:
|
32
|
+
nameService: 이름 처리 서비스 (제공되지 않으면 새로 생성)
|
33
|
+
animService: 애니메이션 서비스 (제공되지 않으면 새로 생성)
|
34
|
+
helperService: 헬퍼 객체 서비스 (제공되지 않으면 새로 생성)
|
35
|
+
boneService: 뼈대 서비스 (제공되지 않으면 새로 생성)
|
36
|
+
constraintService: 제약 서비스 (제공되지 않으면 새로 생성)
|
37
|
+
bipService: Biped 서비스 (제공되지 않으면 새로 생성)
|
38
|
+
"""
|
39
|
+
self.name = nameService if nameService else Name()
|
40
|
+
self.anim = animService if animService else Anim()
|
41
|
+
self.helper = helperService if helperService else Helper(nameService=self.name)
|
42
|
+
self.bone = boneService if boneService else Bone(nameService=self.name, animService=self.anim, helperService=self.helper)
|
43
|
+
self.const = constraintService if constraintService else Constraint(nameService=self.name, helperService=self.helper)
|
44
|
+
self.bip = bipService if bipService else Bip(animService=self.anim, nameService=self.name, boneService=self.bone)
|
45
|
+
|
46
|
+
self.bone_size = 2.0
|
47
|
+
|
48
|
+
def create_bones(self, inClavicle, inUpperArm, liftScale=0.8):
|
49
|
+
"""
|
50
|
+
자동 쇄골 뼈를 생성하고 설정합니다.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
inClavicle: 쇄골 뼈 객체
|
54
|
+
inUpperArm: 상완 뼈 객체
|
55
|
+
liftScale: 들어올림 스케일 (기본값: 0.8)
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
생성된 자동 쇄골 뼈대 배열
|
59
|
+
"""
|
60
|
+
# 쇄골과 상완 사이의 거리 계산
|
61
|
+
clavicleLength = rt.distance(inClavicle, inUpperArm)
|
62
|
+
|
63
|
+
# 임시 헬퍼 포인트 생성
|
64
|
+
tempHelperA = rt.Point()
|
65
|
+
tempHelperB = rt.Point()
|
66
|
+
tempHelperA.transform = inClavicle.transform
|
67
|
+
tempHelperB.transform = inClavicle.transform
|
68
|
+
self.anim.move_local(tempHelperB, clavicleLength/2.0, 0.0, 0.0)
|
69
|
+
|
70
|
+
# 자동 쇄골 이름 생성 및 뼈대 생성
|
71
|
+
autoClavicleName = self.name.replace_name_part("RealName", inClavicle.name, "AutoClavicle")
|
72
|
+
autoClavicleBones = self.bone.create_bone(
|
73
|
+
[tempHelperA, tempHelperB],
|
74
|
+
autoClavicleName,
|
75
|
+
end=True,
|
76
|
+
delPoint=True,
|
77
|
+
parent=False,
|
78
|
+
size=self.bone_size
|
79
|
+
)
|
80
|
+
autoClavicleBones[0].transform = inClavicle.transform
|
81
|
+
self.anim.move_local(autoClavicleBones[0], clavicleLength/2.0, 0.0, 0.0)
|
82
|
+
autoClavicleBones[0].parent = inClavicle
|
83
|
+
|
84
|
+
# LookAt 설정
|
85
|
+
ikGoal = self.helper.create_point(autoClavicleName, boxToggle=False, crossToggle=True)
|
86
|
+
ikGoal.transform = autoClavicleBones[1].transform
|
87
|
+
ikGoal.name = self.name.replace_name_part("Type", autoClavicleName, "T")
|
88
|
+
autClavicleLookAtConst = self.const.assign_lookat(autoClavicleBones[0], ikGoal)
|
89
|
+
autClavicleLookAtConst.upnode_world = False
|
90
|
+
autClavicleLookAtConst.pickUpNode = inClavicle
|
91
|
+
autClavicleLookAtConst.lookat_vector_length = 0.0
|
92
|
+
|
93
|
+
# 회전 헬퍼 포인트 생성
|
94
|
+
autoClavicleRotHelper = self.helper.create_point(self.name.replace_name_part("Type", autoClavicleName, "Rot"))
|
95
|
+
autoClavicleRotHelper.transform = autoClavicleBones[0].transform
|
96
|
+
autoClavicleRotHelper.parent = inClavicle
|
97
|
+
|
98
|
+
# 타겟 헬퍼 포인트 생성 (쇄골과 상완용)
|
99
|
+
rotTargetClavicle = self.helper.create_point(self.name.replace_name_part("Type", autoClavicleName, "T"))
|
100
|
+
rotTargetClavicle.transform = inClavicle.transform
|
101
|
+
self.anim.move_local(rotTargetClavicle, clavicleLength, 0.0, 0.0)
|
102
|
+
|
103
|
+
rotTargetUpperArm = self.helper.create_point(self.name.replace_name_part("Type", autoClavicleName, "T"))
|
104
|
+
rotTargetUpperArm.name = self.name.add_suffix_to_real_name(rotTargetUpperArm.name, "UArm")
|
105
|
+
rotTargetUpperArm.transform = inUpperArm.transform
|
106
|
+
self.anim.move_local(rotTargetUpperArm, (clavicleLength/2.0)*liftScale, 0.0, 0.0)
|
107
|
+
|
108
|
+
# 부모 설정
|
109
|
+
rotTargetClavicle.parent = inClavicle
|
110
|
+
rotTargetUpperArm.parent = inUpperArm
|
111
|
+
|
112
|
+
# LookAt 제약 설정
|
113
|
+
# self.const.assign_lookat_multi(autoClavicleRotHelper, [rotTargetClavicle, rotTargetUpperArm])
|
114
|
+
lookAtConst = self.const.assign_scripted_lookat(autoClavicleRotHelper, [rotTargetClavicle, rotTargetUpperArm])["lookAt"]
|
115
|
+
|
116
|
+
lookAtConst.upnode_world = False
|
117
|
+
lookAtConst.pickUpNode = inClavicle
|
118
|
+
lookAtConst.lookat_vector_length = 0.0
|
119
|
+
|
120
|
+
ikGoal.parent = autoClavicleRotHelper
|
121
|
+
|
122
|
+
return autoClavicleBones
|
pyjallib/max/bip.py
CHANGED
@@ -436,12 +436,11 @@ class Bip:
|
|
436
436
|
if colNum > 0:
|
437
437
|
rt.biped.deleteAllCopyCollections(inBipRoot.controller)
|
438
438
|
|
439
|
-
def link_base_skeleton(self):
|
439
|
+
def link_base_skeleton(self, skinBoneBaseName="b"):
|
440
440
|
"""
|
441
441
|
기본 스켈레톤 링크 (Biped와 일반 뼈대 연결)
|
442
442
|
"""
|
443
443
|
rt.setWaitCursor()
|
444
|
-
skinBoneBaseName = "b"
|
445
444
|
|
446
445
|
bipSkel = self.get_bips()
|
447
446
|
baseSkel = [None] * len(bipSkel)
|
@@ -473,12 +472,11 @@ class Bip:
|
|
473
472
|
|
474
473
|
rt.setArrowCursor()
|
475
474
|
|
476
|
-
def unlink_base_skeleton(self):
|
475
|
+
def unlink_base_skeleton(self, skinBoneBaseName="b"):
|
477
476
|
"""
|
478
477
|
기본 스켈레톤 링크 해제
|
479
478
|
"""
|
480
479
|
rt.setWaitCursor()
|
481
|
-
skinBoneBaseName = "b"
|
482
480
|
|
483
481
|
bipSkel = self.get_bips()
|
484
482
|
baseSkel = [None] * len(bipSkel)
|
pyjallib/max/constraint.py
CHANGED
@@ -675,7 +675,8 @@ class Constraint:
|
|
675
675
|
# 객체 이름 생성
|
676
676
|
if self.name:
|
677
677
|
objName = self.name.get_string(oriObj.name)
|
678
|
-
|
678
|
+
indexVal = self.name.get_index_as_digit(oriObj.name)
|
679
|
+
indexNum = 0 if indexVal is False else indexVal
|
679
680
|
dummyName = self.name.add_prefix_to_real_name(objName, self.name.get_dummy_value())
|
680
681
|
|
681
682
|
lookAtPointName = self.name.replace_Index(dummyName, str(indexNum))
|
@@ -752,6 +753,8 @@ class Constraint:
|
|
752
753
|
x_controller.Update()
|
753
754
|
y_controller.Update()
|
754
755
|
z_controller.Update()
|
756
|
+
|
757
|
+
return {"lookAt":lookAtPoint_rot_controller, "x":x_controller, "y":y_controller, "z":z_controller}
|
755
758
|
|
756
759
|
def assign_attachment(self, inPlacedObj, inSurfObj, bAlign=False, shiftAxis=(0, 0, 1), shiftAmount=3.0):
|
757
760
|
"""
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
고간 부 본 모듈 - 3ds Max용 트위스트 뼈대 생성 관련 기능 제공
|
6
|
+
"""
|
7
|
+
|
8
|
+
from pymxs import runtime as rt
|
9
|
+
|
10
|
+
# Import necessary service classes for default initialization
|
11
|
+
from .name import Name
|
12
|
+
from .anim import Anim
|
13
|
+
from .constraint import Constraint
|
14
|
+
from .bip import Bip
|
15
|
+
from .bone import Bone
|
16
|
+
from .helper import Helper
|
17
|
+
from .twistBone import TwistBone
|
18
|
+
|
19
|
+
class GroinBone:
|
20
|
+
"""
|
21
|
+
고간 부 본 관련 기능을 위한 클래스
|
22
|
+
3DS Max에서 고간 부 본을 생성하고 관리하는 기능을 제공합니다.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, nameService=None, animService=None, helperService=None, constService=None, bipService=None, boneService=None, twistBoneService=None):
|
26
|
+
"""
|
27
|
+
클래스 초기화.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
nameService: 이름 처리 서비스 (제공되지 않으면 새로 생성)
|
31
|
+
animService: 애니메이션 서비스 (제공되지 않으면 새로 생성)
|
32
|
+
constService: 제약 서비스 (제공되지 않으면 새로 생성)
|
33
|
+
bipService: 바이페드 서비스 (제공되지 않으면 새로 생성)
|
34
|
+
"""
|
35
|
+
self.name = nameService if nameService else Name()
|
36
|
+
self.anim = animService if animService else Anim()
|
37
|
+
# Ensure dependent services use the potentially newly created instances
|
38
|
+
self.const = constService if constService else Constraint(nameService=self.name)
|
39
|
+
self.bip = bipService if bipService else Bip(animService=self.anim, nameService=self.name)
|
40
|
+
self.bone = boneService if boneService else Bone(nameService=self.name, animService=self.anim)
|
41
|
+
self.twistBone = twistBoneService if twistBoneService else TwistBone(nameService=self.name, animService=self.anim, constService=self.const, bipService=self.bip, boneService=self.bone)
|
42
|
+
self.helper = helperService if helperService else Helper(nameService=self.name)
|
43
|
+
|
44
|
+
def create_bone(self, inObj):
|
45
|
+
"""
|
46
|
+
고간 부 본을 생성하는 메소드.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
name: 생성할 본의 이름
|
50
|
+
parent: 부모 본 (제공되지 않으면 None)
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
생성된 본 객체
|
54
|
+
"""
|
55
|
+
if self.bip.is_biped_object(inObj) == False:
|
56
|
+
rt.messageBox("This is not a biped object.")
|
57
|
+
return False
|
58
|
+
|
59
|
+
bipObj = self.bip.get_com(inObj)
|
60
|
+
|
61
|
+
lThigh = self.bip.get_grouped_nodes(inObj, "lLeg")[0]
|
62
|
+
rThigh = self.bip.get_grouped_nodes(inObj, "rLeg")[0]
|
63
|
+
pelvis = self.bip.get_grouped_nodes(inObj, "pelvis")[0]
|
64
|
+
|
65
|
+
lThighTwists = self.twistBone.get_thigh_type(lThigh)
|
66
|
+
rThighTwists = self.twistBone.get_thigh_type(rThigh)
|
67
|
+
|
68
|
+
if len(lThighTwists) == 0 or len(rThighTwists) == 0:
|
69
|
+
rt.messageBox("There is no twist bone.")
|
70
|
+
return False
|
71
|
+
|
72
|
+
pelvisHelper = self.helper.create_point(bipObj.name + " Dum Groin 00")
|
73
|
+
pelvisHelper.transform = bipObj.transform
|
74
|
+
self.anim.rotate_local(pelvisHelper, 90, 0, 0)
|
75
|
+
self.anim.rotate_local(pelvisHelper, 0, 0, -90)
|
76
|
+
pelvisHelper.parent = pelvis
|
77
|
+
self.helper.set_shape_to_box(pelvisHelper)
|
78
|
+
|
79
|
+
groinBones = self.bone.create_simple_bone(3.0, bipObj.name +" Groin 00", size=2)
|
80
|
+
groinBones[0].transform = pelvisHelper.transform
|
81
|
+
groinBones[0].parent = pelvis
|
82
|
+
|
83
|
+
self.const.assign_rot_const_multi(groinBones[0], [pelvisHelper, lThigh, rThigh])
|
84
|
+
rotConst = self.const.get_rot_list_controller(groinBones[0])[1]
|
85
|
+
rotConst.setWeight(1, 40.0)
|
86
|
+
rotConst.setWeight(2, 30.0)
|
87
|
+
rotConst.setWeight(3, 30.0)
|
88
|
+
|
89
|
+
|
90
|
+
|
pyjallib/max/header.py
CHANGED
@@ -25,6 +25,8 @@ from .bip import Bip
|
|
25
25
|
from .skin import Skin
|
26
26
|
|
27
27
|
from .twistBone import TwistBone
|
28
|
+
from .groinBone import GroinBone
|
29
|
+
from .autoClavicle import AutoClavicle
|
28
30
|
|
29
31
|
class Header:
|
30
32
|
"""
|
@@ -63,7 +65,9 @@ class Header:
|
|
63
65
|
self.bip = Bip(animService=self.anim, nameService=self.name, boneService=self.bone)
|
64
66
|
self.skin = Skin()
|
65
67
|
|
66
|
-
self.twistBone = TwistBone(nameService=self.name, animService=self.anim, constService=self.constraint, bipService=self.bip)
|
68
|
+
self.twistBone = TwistBone(nameService=self.name, animService=self.anim, constService=self.constraint, bipService=self.bip, boneService=self.bone)
|
69
|
+
self.groinBone = GroinBone(nameService=self.name, animService=self.anim, helperService=self.helper, constService=self.constraint, bipService=self.bip, boneService=self.bone, twistBoneService=self.twistBone)
|
70
|
+
self.autoClavicle = AutoClavicle(nameService=self.name, animService=self.anim, helperService=self.helper, boneService=self.bone, constraintService=self.constraint, bipService=self.bip)
|
67
71
|
|
68
72
|
# 모듈 레벨에서 전역 인스턴스 생성
|
69
73
|
jal = Header.get_instance()
|
pyjallib/max/helper.py
CHANGED
@@ -431,3 +431,57 @@ class Helper:
|
|
431
431
|
inObj.centermarker = False
|
432
432
|
inObj.axistripod = False
|
433
433
|
inObj.cross = False
|
434
|
+
|
435
|
+
def get_shape(self, inObj):
|
436
|
+
"""
|
437
|
+
헬퍼 객체의 시각적 형태 속성을 가져옵니다.
|
438
|
+
inObj (object): 형태 정보를 가져올 대상 3ds Max 헬퍼 객체.
|
439
|
+
dict: 헬퍼의 형태 속성을 나타내는 딕셔너리.
|
440
|
+
- "size" (float): 크기
|
441
|
+
- "centermarker" (bool): 센터 마커 활성화 여부
|
442
|
+
- "axistripod" (bool): 축 삼각대 활성화 여부
|
443
|
+
- "cross" (bool): 십자 표시 활성화 여부
|
444
|
+
- "box" (bool): 박스 표시 활성화 여부
|
445
|
+
`inObj`가 `rt.ExposeTm` 또는 `rt.Point` 타입의 객체인 경우 해당 객체의
|
446
|
+
속성값을 반영하며, 그렇지 않은 경우 미리 정의된 기본값을 반환합니다.
|
447
|
+
"""
|
448
|
+
returnDict = {
|
449
|
+
"size": 2.0,
|
450
|
+
"centermarker": False,
|
451
|
+
"axistripod": False,
|
452
|
+
"cross": True,
|
453
|
+
"box": False
|
454
|
+
}
|
455
|
+
if rt.classOf(inObj) == rt.ExposeTm or rt.classOf(inObj) == rt.Point:
|
456
|
+
returnDict["size"] = inObj.size
|
457
|
+
returnDict["centermarker"] = inObj.centermarker
|
458
|
+
returnDict["axistripod"] = inObj.axistripod
|
459
|
+
returnDict["cross"] = inObj.cross
|
460
|
+
returnDict["box"] = inObj.box
|
461
|
+
|
462
|
+
return returnDict
|
463
|
+
|
464
|
+
def set_shape(self, inObj, inShapeDict):
|
465
|
+
"""
|
466
|
+
헬퍼 객체의 표시 형태를 설정합니다.
|
467
|
+
`rt.ExposeTm` 또는 `rt.Point` 타입의 헬퍼 객체에 대해 크기, 센터 마커, 축 삼각대, 십자, 박스 표시 여부를 설정합니다.
|
468
|
+
inObj (rt.ExposeTm | rt.Point): 설정을 적용할 헬퍼 객체입니다.
|
469
|
+
inShapeDict (dict): 헬퍼의 형태를 정의하는 딕셔너리입니다.
|
470
|
+
다음 키와 값을 포함해야 합니다:
|
471
|
+
- "size" (float | int): 헬퍼의 크기.
|
472
|
+
- "centermarker" (bool): 센터 마커 표시 여부 (True/False).
|
473
|
+
- "axistripod" (bool): 축 삼각대(axis tripod) 표시 여부 (True/False).
|
474
|
+
- "cross" (bool): 십자(cross) 표시 여부 (True/False).
|
475
|
+
- "box" (bool): 박스(box) 표시 여부 (True/False).
|
476
|
+
rt.ExposeTm | rt.Point | None: 형태가 설정된 객체를 반환합니다.
|
477
|
+
만약 `inObj`가 `rt.ExposeTm` 또는 `rt.Point` 타입이 아닐 경우,
|
478
|
+
아무 작업도 수행하지 않고 `None`을 반환합니다.
|
479
|
+
"""
|
480
|
+
if rt.classOf(inObj) == rt.ExposeTm or rt.classOf(inObj) == rt.Point:
|
481
|
+
inObj.size = inShapeDict["size"]
|
482
|
+
inObj.centermarker = inShapeDict["centermarker"]
|
483
|
+
inObj.axistripod = inShapeDict["axistripod"]
|
484
|
+
inObj.cross = inShapeDict["cross"]
|
485
|
+
inObj.box = inShapeDict["box"]
|
486
|
+
|
487
|
+
return inObj
|
@@ -15,7 +15,7 @@ class HelperTypeSelDialog(QtWidgets.QDialog):
|
|
15
15
|
self.changeHelperType = False
|
16
16
|
|
17
17
|
self.setWindowTitle("Helper Type")
|
18
|
-
self.setMinimumWidth(
|
18
|
+
self.setMinimumWidth(150)
|
19
19
|
|
20
20
|
self.layout = QtWidgets.QVBoxLayout(self)
|
21
21
|
|
@@ -41,6 +41,7 @@ class HelperTypeSelDialog(QtWidgets.QDialog):
|
|
41
41
|
class ModifyHelperShapeDialog(QtWidgets.QDialog):
|
42
42
|
def __init__(self, parent=QtWidgets.QWidget.find(rt.windows.getMAXHWND())):
|
43
43
|
super(ModifyHelperShapeDialog, self).__init__(parent)
|
44
|
+
self.defaultHelperShapes = []
|
44
45
|
self.helperArray = []
|
45
46
|
|
46
47
|
self.setWindowTitle("Modify Helper Shape")
|
@@ -55,6 +56,8 @@ class ModifyHelperShapeDialog(QtWidgets.QDialog):
|
|
55
56
|
self.size_spinbox = QtWidgets.QDoubleSpinBox()
|
56
57
|
self.size_spinbox.setValue(1.0) # Default value
|
57
58
|
self.size_spinbox.setSingleStep(0.1)
|
59
|
+
# Install event filter for mouse tracking
|
60
|
+
self.size_spinbox.installEventFilter(self)
|
58
61
|
sizeLayout.addWidget(sizeLabel)
|
59
62
|
sizeLayout.addWidget(self.size_spinbox)
|
60
63
|
|
@@ -62,6 +65,8 @@ class ModifyHelperShapeDialog(QtWidgets.QDialog):
|
|
62
65
|
self.add_spinbox = QtWidgets.QDoubleSpinBox()
|
63
66
|
self.add_spinbox.setValue(0.0) # Default value
|
64
67
|
self.add_spinbox.setSingleStep(0.1)
|
68
|
+
# Install event filter for mouse tracking
|
69
|
+
self.add_spinbox.installEventFilter(self)
|
65
70
|
addLayout.addWidget(addLabel)
|
66
71
|
addLayout.addWidget(self.add_spinbox)
|
67
72
|
|
@@ -91,6 +96,7 @@ class ModifyHelperShapeDialog(QtWidgets.QDialog):
|
|
91
96
|
self.layout.addWidget(shapeGroup) # Add the group box to the layout instead of the raw radioLayout
|
92
97
|
self.layout.addLayout(buttonLayout)
|
93
98
|
|
99
|
+
# Replace incorrect stepUp/stepDown connections with proper valueChanged connections
|
94
100
|
self.size_spinbox.valueChanged.connect(self.change_helper_size)
|
95
101
|
self.add_spinbox.valueChanged.connect(self.add_helper_size)
|
96
102
|
self.add_spinbox.editingFinished.connect(self.reset_add_spinbox)
|
@@ -100,8 +106,10 @@ class ModifyHelperShapeDialog(QtWidgets.QDialog):
|
|
100
106
|
self.radio_axis.toggled.connect(self.change_helper_shape)
|
101
107
|
self.radio_center.toggled.connect(self.change_helper_shape)
|
102
108
|
|
103
|
-
|
104
|
-
|
109
|
+
# Make the dialog apply changes immediately without needing to click OK
|
110
|
+
# by removing the requirement for explicit acceptance
|
111
|
+
self.ok_button.clicked.connect(self.close) # Just close instead of accept
|
112
|
+
self.cancel_button.clicked.connect(self.restore_helper_shape)
|
105
113
|
|
106
114
|
def change_helper_size(self):
|
107
115
|
if len(self.helperArray) == 0:
|
@@ -131,6 +139,21 @@ class ModifyHelperShapeDialog(QtWidgets.QDialog):
|
|
131
139
|
elif self.radio_center.isChecked():
|
132
140
|
jal.helper.set_shape_to_center(obj)
|
133
141
|
|
142
|
+
def save_helper_shape(self):
|
143
|
+
if len(self.helperArray) == 0:
|
144
|
+
return
|
145
|
+
for obj in self.helperArray:
|
146
|
+
shape = jal.helper.get_shape(obj)
|
147
|
+
self.defaultHelperShapes.append(shape)
|
148
|
+
|
149
|
+
def restore_helper_shape(self):
|
150
|
+
if len(self.helperArray) == 0 or len(self.defaultHelperShapes) == 0:
|
151
|
+
return
|
152
|
+
for i, obj in enumerate(self.helperArray):
|
153
|
+
jal.helper.set_shape(obj, self.defaultHelperShapes[i])
|
154
|
+
|
155
|
+
self.reject() # Close the dialog without accepting
|
156
|
+
|
134
157
|
def jal_create_parentHelper():
|
135
158
|
jal.helper.create_parent_helper()
|
136
159
|
|
@@ -229,17 +252,22 @@ def jal_modify_helperShape():
|
|
229
252
|
|
230
253
|
# Assuming the first selected object is the one to modify
|
231
254
|
helperObj = helperArray[0]
|
255
|
+
helperObjShape = jal.helper.get_shape(helperObj)
|
232
256
|
|
233
257
|
modDialog = ModifyHelperShapeDialog()
|
234
258
|
|
235
259
|
# Set initial values from the selected helper (if possible)
|
236
260
|
modDialog.size_spinbox.setValue(helperObj.size)
|
261
|
+
modDialog.radio_box.setChecked(helperObjShape["box"])
|
262
|
+
modDialog.radio_cross.setChecked(helperObjShape["cross"])
|
263
|
+
modDialog.radio_axis.setChecked(helperObjShape["axistripod"])
|
264
|
+
modDialog.radio_center.setChecked(helperObjShape["centermarker"])
|
237
265
|
|
238
266
|
modDialog.helperArray = helperArray
|
267
|
+
modDialog.save_helper_shape()
|
239
268
|
|
240
|
-
|
269
|
+
modDialog.show()
|
241
270
|
|
242
|
-
modDialog.deleteLater()
|
243
271
|
modDialog = None
|
244
272
|
gc.collect()
|
245
273
|
|
pyjallib/max/twistBone.py
CHANGED
@@ -13,6 +13,7 @@ from .name import Name
|
|
13
13
|
from .anim import Anim
|
14
14
|
from .constraint import Constraint
|
15
15
|
from .bip import Bip
|
16
|
+
from .bone import Bone
|
16
17
|
|
17
18
|
|
18
19
|
class TwistBone:
|
@@ -22,7 +23,7 @@ class TwistBone:
|
|
22
23
|
3ds Max의 기능들을 pymxs API를 통해 제어합니다.
|
23
24
|
"""
|
24
25
|
|
25
|
-
def __init__(self, nameService=None, animService=None, constService=None, bipService=None):
|
26
|
+
def __init__(self, nameService=None, animService=None, constService=None, bipService=None, boneService=None):
|
26
27
|
"""
|
27
28
|
클래스 초기화.
|
28
29
|
|
@@ -37,6 +38,7 @@ class TwistBone:
|
|
37
38
|
# Ensure dependent services use the potentially newly created instances
|
38
39
|
self.const = constService if constService else Constraint(nameService=self.name)
|
39
40
|
self.bip = bipService if bipService else Bip(animService=self.anim, nameService=self.name) # Pass potentially new instances
|
41
|
+
self.bone = boneService if boneService else Bone(nameService=self.name, animService=self.anim)
|
40
42
|
|
41
43
|
# 표현식 초기화
|
42
44
|
self._init_expressions()
|
@@ -415,4 +417,128 @@ class TwistBone:
|
|
415
417
|
굽힘(Bend) 타입의 트위스트 뼈대 생성
|
416
418
|
(아직 구현되지 않음)
|
417
419
|
"""
|
418
|
-
pass
|
420
|
+
pass
|
421
|
+
|
422
|
+
def get_upperArm_type(self, inObj):
|
423
|
+
"""
|
424
|
+
상완(Upper Arm) 타입의 트위스트 뼈대 가져오기
|
425
|
+
|
426
|
+
Args:
|
427
|
+
inObj: 상완 뼈대 객체
|
428
|
+
|
429
|
+
Returns:
|
430
|
+
상완 타입의 트위스트 뼈대 또는 False(실패 시)
|
431
|
+
"""
|
432
|
+
returnVal = []
|
433
|
+
|
434
|
+
parentBipObj = None
|
435
|
+
|
436
|
+
if not self.bip.is_biped_object(inObj):
|
437
|
+
return returnVal
|
438
|
+
|
439
|
+
if self.bip.is_left_node(inObj):
|
440
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lArm"), link=2)
|
441
|
+
else:
|
442
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rArm"), link=2)
|
443
|
+
|
444
|
+
children = self.bone.get_every_children(parentBipObj)
|
445
|
+
for child in children:
|
446
|
+
if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
|
447
|
+
returnVal.append(child)
|
448
|
+
|
449
|
+
returnVal = self.name.sort_by_name(returnVal)
|
450
|
+
|
451
|
+
return returnVal
|
452
|
+
|
453
|
+
def get_foreArm_type(self, inObj):
|
454
|
+
"""
|
455
|
+
전완(Forearm) 타입의 트위스트 뼈대 가져오기
|
456
|
+
|
457
|
+
Args:
|
458
|
+
inObj: 전완 뼈대 객체
|
459
|
+
|
460
|
+
Returns:
|
461
|
+
전완 타입의 트위스트 뼈대 또는 False(실패 시)
|
462
|
+
"""
|
463
|
+
returnVal = []
|
464
|
+
|
465
|
+
parentBipObj = None
|
466
|
+
|
467
|
+
if not self.bip.is_biped_object(inObj):
|
468
|
+
return returnVal
|
469
|
+
|
470
|
+
if self.bip.is_left_node(inObj):
|
471
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lArm"), link=3)
|
472
|
+
else:
|
473
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rArm"), link=3)
|
474
|
+
|
475
|
+
children = self.bone.get_every_children(parentBipObj)
|
476
|
+
for child in children:
|
477
|
+
if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
|
478
|
+
returnVal.append(child)
|
479
|
+
|
480
|
+
returnVal = self.name.sort_by_name(returnVal)
|
481
|
+
|
482
|
+
return returnVal
|
483
|
+
|
484
|
+
def get_thigh_type(self, inObj):
|
485
|
+
"""
|
486
|
+
허벅지(Thigh) 타입의 트위스트 뼈대 가져오기
|
487
|
+
|
488
|
+
Args:
|
489
|
+
inObj: 허벅지 뼈대 객체
|
490
|
+
|
491
|
+
Returns:
|
492
|
+
허벅지 타입의 트위스트 뼈대 또는 False(실패 시)
|
493
|
+
"""
|
494
|
+
returnVal = []
|
495
|
+
|
496
|
+
parentBipObj = None
|
497
|
+
|
498
|
+
if not self.bip.is_biped_object(inObj):
|
499
|
+
return returnVal
|
500
|
+
|
501
|
+
if self.bip.is_left_node(inObj):
|
502
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lLeg"), link=1)
|
503
|
+
else:
|
504
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rLeg"), link=1)
|
505
|
+
|
506
|
+
children = self.bone.get_every_children(parentBipObj)
|
507
|
+
for child in children:
|
508
|
+
if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
|
509
|
+
returnVal.append(child)
|
510
|
+
|
511
|
+
returnVal = self.name.sort_by_name(returnVal)
|
512
|
+
|
513
|
+
return returnVal
|
514
|
+
|
515
|
+
def get_calf_type(self, inObj):
|
516
|
+
"""
|
517
|
+
종아리(Calf) 타입의 트위스트 뼈대 가져오기
|
518
|
+
|
519
|
+
Args:
|
520
|
+
inObj: 종아리 뼈대 객체
|
521
|
+
|
522
|
+
Returns:
|
523
|
+
종아리 타입의 트위스트 뼈대 또는 False(실패 시)
|
524
|
+
"""
|
525
|
+
returnVal = []
|
526
|
+
|
527
|
+
parentBipObj = None
|
528
|
+
|
529
|
+
if not self.bip.is_biped_object(inObj):
|
530
|
+
return returnVal
|
531
|
+
|
532
|
+
if self.bip.is_left_node(inObj):
|
533
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("lLeg"), link=2)
|
534
|
+
else:
|
535
|
+
parentBipObj = rt.biped.getNode(inObj.controller.rootNode, rt.Name("rLeg"), link=2)
|
536
|
+
|
537
|
+
children = self.bone.get_every_children(parentBipObj)
|
538
|
+
for child in children:
|
539
|
+
if rt.matchPattern(child.name, pattern="*Twist*") and rt.classOf(child) == rt.BoneGeometry:
|
540
|
+
returnVal.append(child)
|
541
|
+
|
542
|
+
returnVal = self.name.sort_by_name(returnVal)
|
543
|
+
|
544
|
+
return returnVal
|
pyjallib/naming.py
CHANGED
@@ -352,11 +352,12 @@ class Naming:
|
|
352
352
|
partValues = partObj.get_predefined_values()
|
353
353
|
|
354
354
|
if partType == NamePartType.PREFIX or partType == NamePartType.SUFFIX:
|
355
|
-
|
356
|
-
|
355
|
+
try:
|
356
|
+
foundIndex = partObj._descriptions.index(inDescription)
|
357
357
|
return partValues[foundIndex]
|
358
|
-
|
359
|
-
|
358
|
+
except ValueError:
|
359
|
+
# Description not found in the list
|
360
|
+
return ""
|
360
361
|
|
361
362
|
def pick_name(self, inNamePartName, inStr):
|
362
363
|
nameArray = self._split_to_array(inStr)
|
@@ -1,34 +1,36 @@
|
|
1
|
-
pyjallib/__init__.py,sha256=
|
1
|
+
pyjallib/__init__.py,sha256=Vn47aG6w0P4VzzOm7lnSp7G38YVsI40nTwg5CClgdR4,460
|
2
2
|
pyjallib/namePart.py,sha256=D1hnFNnZbrNicAiW2ZUaIT0LU5pro3uFYrFYOEjt7_Y,24001
|
3
3
|
pyjallib/nameToPath.py,sha256=61EWrc0Wc1K1Qsc4G6jewIccI0IHbiZWroRcU_lX1Wc,4664
|
4
|
-
pyjallib/naming.py,sha256=
|
4
|
+
pyjallib/naming.py,sha256=jJ6w0n_nnUE2uZy_I4KFsMx95Ij3_KvSjhVvQQplxpw,36621
|
5
5
|
pyjallib/namingConfig.py,sha256=6Wyk3h1dFrzOslSoqjLftSUqt7U00tnR2WcgxYWVx5I,33969
|
6
6
|
pyjallib/perforce.py,sha256=YnKAKlbBss4cl66R4nQbr-bsYjTneLzGYaZ187kOSpA,29872
|
7
7
|
pyjallib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
pyjallib/reloadModules.py,sha256=RAEG3IxzJ0TlsjvnZwJt56JOkc2j8voqAnRbfQuZ44g,1151
|
9
9
|
pyjallib/ConfigFiles/namingConfig.json,sha256=Ov4bbVJb6qodPaooU63e11YUMGXXPWFAA4AQq1sLBYU,1486
|
10
|
-
pyjallib/max/__init__.py,sha256=
|
10
|
+
pyjallib/max/__init__.py,sha256=JofFmmSDMs8hWSAnS3yKw9y_o0IniQiKfzLtVUI9v8w,888
|
11
11
|
pyjallib/max/align.py,sha256=HKjCViQCuicGmtvHB6xxVv4BEGEBGtV2gO3NvR_6R2A,5183
|
12
12
|
pyjallib/max/anim.py,sha256=-shQeE0WeAeCefc8FoI63dNDLHhz0uzOJ4shp5AL_Cs,25135
|
13
|
-
pyjallib/max/
|
13
|
+
pyjallib/max/autoClavicle.py,sha256=rJtXVcTEksstaXF4s1i-0FwhVkPWJFRxlVq8Gyw45Rg,5792
|
14
|
+
pyjallib/max/bip.py,sha256=m6eA-rg-MghYSxbzj-YXa0KJFPm1wiOsOqyJu76_hlY,16967
|
14
15
|
pyjallib/max/bone.py,sha256=GYs3Uohc0AMkLWZAqZTc1DET-WDgZlvlkhSUaBeszvk,33161
|
15
|
-
pyjallib/max/constraint.py,sha256=
|
16
|
-
pyjallib/max/
|
17
|
-
pyjallib/max/
|
16
|
+
pyjallib/max/constraint.py,sha256=WXB6fJ0er4EIZBZ7voQK_bFeqtgjopCKCpwiBn3QyiI,40055
|
17
|
+
pyjallib/max/groinBone.py,sha256=rEZQ7tAXgcZ3NedFacIyvfG07nI8rgF_pVufy-hnKuo,3830
|
18
|
+
pyjallib/max/header.py,sha256=06rzzaOGDtxQGD4SwAFX8nlxNn8lP_gQ_qY0FGZYBFk,2736
|
19
|
+
pyjallib/max/helper.py,sha256=s_rEhY1wNUrwyEN6osuUuOsnqFAQo8_X0Dcf3R89NY0,18526
|
18
20
|
pyjallib/max/layer.py,sha256=e9Mn8h7xf0oBYST3QIpyBpLMl8qpWTExO9Y6yH6rKc0,8795
|
19
21
|
pyjallib/max/link.py,sha256=J3z9nkP8ZxAh9yYhR16tjQFCJTCYZMSB0MGbSHfA7uI,2592
|
20
22
|
pyjallib/max/mirror.py,sha256=j8LnsXowyTINzvtWsvCNaDsQ6v7u2RjlY50R8v5JCuc,14517
|
21
23
|
pyjallib/max/name.py,sha256=z_fclfutesFxhk2hVliqZGnRQauKcB8LJpRbKQ5cYAc,16703
|
22
24
|
pyjallib/max/select.py,sha256=HMJD2WNX3zVBEeYrj0UX2YXM3fHNItfw6UtQSItNsoU,9487
|
23
25
|
pyjallib/max/skin.py,sha256=5mBzG2wSUxoGlkFeb9Ys8uUxOwuZRGeqUMTI9LiWWZU,41937
|
24
|
-
pyjallib/max/twistBone.py,sha256=
|
25
|
-
pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json,sha256=
|
26
|
+
pyjallib/max/twistBone.py,sha256=0Z-Z-OiTPM0fXAnzgol0tiY7Kms6-qrEuVCHsCVVbUc,20338
|
27
|
+
pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json,sha256=_gIsm80fGazA6jUsoBAVxukGNydj6MDgBgGmzb0WQa4,3717
|
26
28
|
pyjallib/max/macro/jal_macro_align.py,sha256=t0gQjybkHYAvPtjT5uWjUQd1vsXIsxOkdYfhFWduxJU,4270
|
27
29
|
pyjallib/max/macro/jal_macro_bone.py,sha256=QS492tskLrARGIFDmbKEwJw7122xJga0ZP5z8WVVRn8,12422
|
28
30
|
pyjallib/max/macro/jal_macro_constraint.py,sha256=0gpfan7hXES9jhnpZVlxWtCbqUCyGxjtSmVMbp7nUh8,4160
|
29
|
-
pyjallib/max/macro/jal_macro_helper.py,sha256=
|
31
|
+
pyjallib/max/macro/jal_macro_helper.py,sha256=ODwAl4C6WK2UE45dqpfw90TgN5vDjLJvODvYeO8tj4o,12906
|
30
32
|
pyjallib/max/macro/jal_macro_link.py,sha256=xkgcCX0fJw4vLfMYybtfUklT3dgcO0tHfpt2X9BfWLw,1193
|
31
33
|
pyjallib/max/macro/jal_macro_select.py,sha256=-r24l84XmDEM4W6H0r1jOBErp3q0UxNrr0m9oAHSgkE,2289
|
32
|
-
pyjallib-0.1.
|
33
|
-
pyjallib-0.1.
|
34
|
-
pyjallib-0.1.
|
34
|
+
pyjallib-0.1.6.dist-info/METADATA,sha256=CcIpikrV2_YUnr8eiNH2Aggz2DKCsqWx_d81e5N9ORM,869
|
35
|
+
pyjallib-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
36
|
+
pyjallib-0.1.6.dist-info/RECORD,,
|
File without changes
|