pyjallib 0.1.10__tar.gz → 0.1.11__tar.gz
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-0.1.10 → pyjallib-0.1.11}/PKG-INFO +1 -1
- {pyjallib-0.1.10 → pyjallib-0.1.11}/pyproject.toml +1 -1
- pyjallib-0.1.11/src/pyjallib/__init__.py +17 -0
- pyjallib-0.1.11/src/pyjallib/max/__init__.py +68 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/autoClavicle.py +39 -4
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/bip.py +2 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/bone.py +37 -0
- pyjallib-0.1.11/src/pyjallib/max/boneChain.py +182 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/groinBone.py +65 -29
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/hip.py +44 -7
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/kneeBone.py +61 -26
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_bone.py +1 -1
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/twistBone.py +59 -40
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/volumeBone.py +77 -26
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/naming.py +1 -1
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/perforce.py +127 -9
- {pyjallib-0.1.10 → pyjallib-0.1.11}/uv.lock +1 -1
- pyjallib-0.1.10/src/pyjallib/__init__.py +0 -17
- pyjallib-0.1.10/src/pyjallib/max/__init__.py +0 -69
- pyjallib-0.1.10/src/pyjallib/max/autoClavicleChain.py +0 -173
- pyjallib-0.1.10/src/pyjallib/max/groinBoneChain.py +0 -173
- pyjallib-0.1.10/src/pyjallib/max/twistBoneChain.py +0 -162
- pyjallib-0.1.10/src/pyjallib/max/volumeBoneChain.py +0 -363
- {pyjallib-0.1.10 → pyjallib-0.1.11}/.gitignore +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/.python-version +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/README.md +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/index.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/max.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/namePart.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/nameToPath.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/naming.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/namingConfig.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/p4module.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/perforce.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/reloadModules.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib.html +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/search.js +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/generate_docs.ps1 +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/ConfigFiles/namingConfig.json +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/align.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/anim.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/constraint.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/header.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/helper.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/layer.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/link.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_align.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_constraint.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_helper.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_link.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_select.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/mirror.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/morph.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/name.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/select.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/skin.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/ui/Container.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/namePart.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/nameToPath.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/namingConfig.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/py.typed +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/reloadModules.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/autoclavicleTest.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/globalVarTest.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/moduleImportTest.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/p4Test.py +0 -0
- {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/volumePreserveBoneTest.py +0 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
pyjallib Package
|
6
|
+
Python library for game character development pipeline.
|
7
|
+
"""
|
8
|
+
|
9
|
+
__version__ = '0.1.11'
|
10
|
+
|
11
|
+
# reload_modules 함수를 패키지 레벨에서 사용 가능하게 함
|
12
|
+
from pyjallib.namePart import NamePart, NamePartType
|
13
|
+
from pyjallib.naming import Naming
|
14
|
+
from pyjallib.namingConfig import NamingConfig
|
15
|
+
from pyjallib.nameToPath import NameToPath
|
16
|
+
from pyjallib.perforce import Perforce
|
17
|
+
from pyjallib.reloadModules import reload_modules
|
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
JalTools 3DS 패키지
|
6
|
+
3DS Max 작업을 위한 모듈 모음
|
7
|
+
"""
|
8
|
+
|
9
|
+
# 모듈 임포트
|
10
|
+
from pyjallib.max.header import Header
|
11
|
+
|
12
|
+
from pyjallib.max.name import Name
|
13
|
+
from pyjallib.max.anim import Anim
|
14
|
+
|
15
|
+
from pyjallib.max.helper import Helper
|
16
|
+
from pyjallib.max.constraint import Constraint
|
17
|
+
from pyjallib.max.bone import Bone
|
18
|
+
|
19
|
+
from pyjallib.max.mirror import Mirror
|
20
|
+
from pyjallib.max.layer import Layer
|
21
|
+
from pyjallib.max.align import Align
|
22
|
+
from pyjallib.max.select import Select
|
23
|
+
from pyjallib.max.link import Link
|
24
|
+
|
25
|
+
from pyjallib.max.bip import Bip
|
26
|
+
from pyjallib.max.skin import Skin
|
27
|
+
from pyjallib.max.morph import Morph
|
28
|
+
|
29
|
+
from pyjallib.max.boneChain import BoneChain
|
30
|
+
|
31
|
+
from pyjallib.max.twistBone import TwistBone
|
32
|
+
from pyjallib.max.groinBone import GroinBone
|
33
|
+
from pyjallib.max.autoClavicle import AutoClavicle
|
34
|
+
from pyjallib.max.volumeBone import VolumeBone
|
35
|
+
from pyjallib.max.kneeBone import KneeBone
|
36
|
+
from pyjallib.max.hip import Hip
|
37
|
+
|
38
|
+
from pyjallib.max.ui.Container import Container
|
39
|
+
|
40
|
+
# 모듈 내보내기
|
41
|
+
__all__ = [
|
42
|
+
'Header',
|
43
|
+
'Name',
|
44
|
+
'Anim',
|
45
|
+
'Helper',
|
46
|
+
'Constraint',
|
47
|
+
'Bone',
|
48
|
+
'Mirror',
|
49
|
+
'Layer',
|
50
|
+
'Align',
|
51
|
+
'Select',
|
52
|
+
'Link',
|
53
|
+
'Bip',
|
54
|
+
'Skin',
|
55
|
+
'Morph',
|
56
|
+
'BoneChain',
|
57
|
+
'TwistBone',
|
58
|
+
'TwistBoneChain',
|
59
|
+
'GroinBone',
|
60
|
+
'GroinBoneChain',
|
61
|
+
'AutoClavicle',
|
62
|
+
'AutoClavicleChain',
|
63
|
+
'VolumeBone',
|
64
|
+
'VolumeBoneChain',
|
65
|
+
'KneeBone',
|
66
|
+
'Hip',
|
67
|
+
'Container'
|
68
|
+
]
|
@@ -16,6 +16,8 @@ from .bone import Bone
|
|
16
16
|
from .constraint import Constraint
|
17
17
|
from .bip import Bip
|
18
18
|
|
19
|
+
from .boneChain import BoneChain
|
20
|
+
|
19
21
|
|
20
22
|
class AutoClavicle:
|
21
23
|
"""
|
@@ -166,12 +168,45 @@ class AutoClavicle:
|
|
166
168
|
result = {
|
167
169
|
"Bones": genBones,
|
168
170
|
"Helpers": genHelpers,
|
169
|
-
"
|
170
|
-
"
|
171
|
-
"LiftScale": liftScale
|
171
|
+
"SourceBones": [inClavicle, inUpperArm],
|
172
|
+
"Parameters": [liftScale]
|
172
173
|
}
|
173
174
|
|
174
175
|
# 메소드 호출 후 데이터 초기화
|
175
176
|
self.reset()
|
176
177
|
|
177
|
-
return result
|
178
|
+
return BoneChain.from_result(result)
|
179
|
+
|
180
|
+
def create_bones_from_chain(self, inBoneChain: BoneChain):
|
181
|
+
"""
|
182
|
+
기존 BoneChain 객체에서 자동 쇄골 뼈를 생성합니다.
|
183
|
+
기존 설정을 복원하거나 저장된 데이터에서 쇄골 셋업을 재생성할 때 사용합니다.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
inBoneChain (BoneChain): 자동 쇄골 정보를 포함한 BoneChain 객체
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
BoneChain: 업데이트된 BoneChain 객체 또는 실패 시 None
|
190
|
+
"""
|
191
|
+
if not inBoneChain or inBoneChain.is_empty():
|
192
|
+
return None
|
193
|
+
|
194
|
+
inBoneChain.delete()
|
195
|
+
|
196
|
+
# BoneChain에서 필요한 정보 추출
|
197
|
+
sourceBones = inBoneChain.sourceBones
|
198
|
+
parameters = inBoneChain.parameters
|
199
|
+
|
200
|
+
# 필수 소스 본 확인
|
201
|
+
if len(sourceBones) < 2 or not rt.isValidNode(sourceBones[0]) or not rt.isValidNode(sourceBones[1]):
|
202
|
+
return None
|
203
|
+
|
204
|
+
# 파라미터 가져오기 (또는 기본값 사용)
|
205
|
+
liftScale = parameters[0] if len(parameters) > 0 else 0.8
|
206
|
+
|
207
|
+
# 쇄골 생성
|
208
|
+
inClavicle = sourceBones[0]
|
209
|
+
inUpperArm = sourceBones[1]
|
210
|
+
|
211
|
+
# 새로운 쇄골 생성
|
212
|
+
return self.create_bones(inClavicle, inUpperArm, liftScale)
|
@@ -608,6 +608,7 @@ class Bip:
|
|
608
608
|
for i, fingers in enumerate(lFingersList):
|
609
609
|
for j, item in enumerate(fingers):
|
610
610
|
item.name = self.name.replace_name_part("RealName", item.name, fingerName[i])
|
611
|
+
item.name = self.name.replace_name_part("Side", item.name, self.name.get_name_part_value_by_description("Side", "Left"))
|
611
612
|
item.name = self.name.replace_name_part("Index", item.name, str(j+1))
|
612
613
|
|
613
614
|
fingerNub = self.bone.get_every_children(fingers[-1])[0]
|
@@ -618,6 +619,7 @@ class Bip:
|
|
618
619
|
for i, fingers in enumerate(rFingersList):
|
619
620
|
for j, item in enumerate(fingers):
|
620
621
|
item.name = self.name.replace_name_part("RealName", item.name, fingerName[i])
|
622
|
+
item.name = self.name.replace_name_part("Side", item.name, self.name.get_name_part_value_by_description("Side", "Right"))
|
621
623
|
item.name = self.name.replace_name_part("Index", item.name, str(j+1))
|
622
624
|
|
623
625
|
fingerNub = self.bone.get_every_children(fingers[-1])[0]
|
@@ -239,6 +239,7 @@ class Bone:
|
|
239
239
|
nubBone.backfin = False
|
240
240
|
nubBone.sidefins = False
|
241
241
|
nubBone.name = self.name.remove_name_part("Index", inName)
|
242
|
+
nubBone.name = self.name.remove_name_part("Nub", nubBone.name)
|
242
243
|
nubBone.name = self.name.replace_name_part("Nub", nubBone.name, self.name.get_name_part_value_by_description("Nub", "Nub"))
|
243
244
|
|
244
245
|
# 화면 갱신 재개
|
@@ -888,6 +889,7 @@ class Bone:
|
|
888
889
|
returnBones = []
|
889
890
|
spine3 = None
|
890
891
|
neck = None
|
892
|
+
head = None
|
891
893
|
|
892
894
|
handL = None
|
893
895
|
handR = None
|
@@ -905,6 +907,8 @@ class Bone:
|
|
905
907
|
spine3 = item
|
906
908
|
if rt.matchPattern(item.name, pattern="*neck 01"):
|
907
909
|
neck = item
|
910
|
+
if rt.matchPattern(item.name, pattern="*head"):
|
911
|
+
head = item
|
908
912
|
if rt.matchPattern(item.name, pattern="*hand*l"):
|
909
913
|
handL = item
|
910
914
|
if rt.matchPattern(item.name, pattern="*hand*r"):
|
@@ -924,6 +928,8 @@ class Bone:
|
|
924
928
|
|
925
929
|
filteringChar = self.name._get_filtering_char(inBoneArray[-1].name)
|
926
930
|
isLower = inBoneArray[-1].name[0].islower()
|
931
|
+
|
932
|
+
# Spine 4,5 생성
|
927
933
|
spineName = self.name.get_name_part_value_by_description("Base", "Biped") + filteringChar + "Spine"
|
928
934
|
|
929
935
|
spine4 = self.create_nub_bone(spineName, 2)
|
@@ -946,6 +952,23 @@ class Bone:
|
|
946
952
|
returnBones.append(spine4)
|
947
953
|
returnBones.append(spine5)
|
948
954
|
|
955
|
+
# 목 생성
|
956
|
+
neckName = self.name.get_name_part_value_by_description("Base", "Biped") + filteringChar + "Neck"
|
957
|
+
|
958
|
+
nekc2 = self.create_nub_bone(neckName, 2)
|
959
|
+
|
960
|
+
nekc2.name = self.name.replace_name_part("Index", nekc2.name, "2")
|
961
|
+
nekc2.name = self.name.remove_name_part("Nub", nekc2.name)
|
962
|
+
if isLower:
|
963
|
+
nekc2.name = nekc2.name.lower()
|
964
|
+
|
965
|
+
neckDistance = rt.distance(neck, head)/2.0
|
966
|
+
rt.setProperty(nekc2, "transform", neck.transform)
|
967
|
+
self.anim.move_local(nekc2, neckDistance, 0, 0)
|
968
|
+
|
969
|
+
returnBones.append(nekc2)
|
970
|
+
|
971
|
+
# 손가락용 메타카팔 생성
|
949
972
|
for i, finger in enumerate(lFingers):
|
950
973
|
knuckleBoneName = self.name.add_suffix_to_real_name(finger.name, filteringChar+knuckleName)
|
951
974
|
knuckleBoneName = self.name.remove_name_part("Index", knuckleBoneName)
|
@@ -992,6 +1015,7 @@ class Bone:
|
|
992
1015
|
returnBones = []
|
993
1016
|
|
994
1017
|
spine3 = None
|
1018
|
+
neck = None
|
995
1019
|
|
996
1020
|
handL = None
|
997
1021
|
handR = None
|
@@ -1001,6 +1025,8 @@ class Bone:
|
|
1001
1025
|
for item in inBipArray:
|
1002
1026
|
if rt.matchPattern(item.name, pattern="*spine 03"):
|
1003
1027
|
spine3 = item
|
1028
|
+
if rt.matchPattern(item.name, pattern="*neck 01"):
|
1029
|
+
neck = item
|
1004
1030
|
if rt.matchPattern(item.name, pattern="*hand*l"):
|
1005
1031
|
handL = item
|
1006
1032
|
if rt.matchPattern(item.name, pattern="*hand*r"):
|
@@ -1009,6 +1035,8 @@ class Bone:
|
|
1009
1035
|
for item in inMissingBoneArray:
|
1010
1036
|
if rt.matchPattern(item.name, pattern="*spine*"):
|
1011
1037
|
item.parent = spine3
|
1038
|
+
if rt.matchPattern(item.name, pattern="*neck*"):
|
1039
|
+
item.parent = neck
|
1012
1040
|
if rt.matchPattern(item.name, pattern=f"*{knuckleName}*l"):
|
1013
1041
|
item.parent = handL
|
1014
1042
|
if rt.matchPattern(item.name, pattern=f"*{knuckleName}*r"):
|
@@ -1025,6 +1053,8 @@ class Bone:
|
|
1025
1053
|
spine5 = None
|
1026
1054
|
|
1027
1055
|
neck = None
|
1056
|
+
neck2 = None
|
1057
|
+
head = None
|
1028
1058
|
clavicleL = None
|
1029
1059
|
clavicleR = None
|
1030
1060
|
|
@@ -1042,6 +1072,8 @@ class Bone:
|
|
1042
1072
|
spine3 = item
|
1043
1073
|
if rt.matchPattern(item.name, pattern="*neck*01"):
|
1044
1074
|
neck = item
|
1075
|
+
if rt.matchPattern(item.name, pattern="*head*"):
|
1076
|
+
head = item
|
1045
1077
|
if rt.matchPattern(item.name, pattern="*clavicle*l"):
|
1046
1078
|
clavicleL = item
|
1047
1079
|
if rt.matchPattern(item.name, pattern="*clavicle*r"):
|
@@ -1069,6 +1101,11 @@ class Bone:
|
|
1069
1101
|
neck.parent = spine5
|
1070
1102
|
clavicleL.parent = spine5
|
1071
1103
|
clavicleR.parent = spine5
|
1104
|
+
|
1105
|
+
if rt.matchPattern(item.name, pattern="*neck*02"):
|
1106
|
+
neck2 = item
|
1107
|
+
item.parent = neck
|
1108
|
+
head.parent = neck2
|
1072
1109
|
|
1073
1110
|
if rt.matchPattern(item.name, pattern=f"*{knuckleName}*l"):
|
1074
1111
|
item.parent = handL
|
@@ -0,0 +1,182 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
뼈대 체인(Bone Chain) 기본 클래스 - 뼈대 체인 관리를 위한 공통 기능 제공
|
6
|
+
|
7
|
+
이 모듈은 다양한 뼈대 체인 클래스의 기본 부모 클래스로 사용됩니다.
|
8
|
+
AutoClavicleChain, GroinBoneChain, VolumeBoneChain, TwistBoneChain 등의
|
9
|
+
특수 목적 뼈대 체인들이 상속받아 사용하며, 공통된 관리 기능을 제공합니다.
|
10
|
+
|
11
|
+
기본 클래스는 다음과 같은 공통 기능을 제공합니다:
|
12
|
+
- 체인의 뼈대 및 헬퍼 관리
|
13
|
+
- 체인 비우기/삭제 기능
|
14
|
+
- 체인 상태 확인 기능
|
15
|
+
"""
|
16
|
+
|
17
|
+
from pymxs import runtime as rt
|
18
|
+
|
19
|
+
|
20
|
+
class BoneChain:
|
21
|
+
"""
|
22
|
+
뼈대 체인을 관리하는 기본 클래스
|
23
|
+
|
24
|
+
다양한 뼈대 체인의 공통 기능을 담당하는 부모 클래스입니다.
|
25
|
+
뼈대와 헬퍼를 저장하고 기본적인 조작 기능을 제공합니다.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, inResult=None):
|
29
|
+
"""
|
30
|
+
클래스 초기화
|
31
|
+
|
32
|
+
Args:
|
33
|
+
inResult (dict, optional): 뼈대 생성 결과 데이터를 담은 딕셔너리. 기본값은 None
|
34
|
+
"""
|
35
|
+
# 기본 속성 초기화
|
36
|
+
if inResult is None:
|
37
|
+
# inResult가 None이면 속성들을 빈 리스트로 초기화
|
38
|
+
self.bones = []
|
39
|
+
self.helpers = []
|
40
|
+
self.result = {} # 빈 딕셔너리로 초기화
|
41
|
+
self.sourceBones = []
|
42
|
+
self.parameters = []
|
43
|
+
else:
|
44
|
+
self.bones = inResult.get("Bones", [])
|
45
|
+
self.helpers = inResult.get("Helpers", [])
|
46
|
+
self.result = inResult # 원본 결과 보존
|
47
|
+
self.sourceBones = inResult.get("SourceBones", [])
|
48
|
+
self.parameters = inResult.get("Parameters", [])
|
49
|
+
|
50
|
+
def is_empty(self):
|
51
|
+
"""
|
52
|
+
체인이 비어있는지 확인
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
bool: 체인이 비어있으면 True, 아니면 False
|
56
|
+
"""
|
57
|
+
return len(self.bones) == 0
|
58
|
+
|
59
|
+
def clear(self):
|
60
|
+
"""체인의 모든 뼈대와 헬퍼 참조 제거"""
|
61
|
+
self.bones = []
|
62
|
+
self.helpers = []
|
63
|
+
self.sourceBones = []
|
64
|
+
self.parameters = []
|
65
|
+
|
66
|
+
def delete(self):
|
67
|
+
"""
|
68
|
+
체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서 삭제
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
bool: 삭제 성공 여부
|
72
|
+
"""
|
73
|
+
if self.is_empty() and not self.helpers:
|
74
|
+
return False
|
75
|
+
|
76
|
+
try:
|
77
|
+
# 뼈대 삭제
|
78
|
+
if self.bones:
|
79
|
+
for bone in self.bones:
|
80
|
+
if rt.isValidNode(bone):
|
81
|
+
rt.delete(bone)
|
82
|
+
|
83
|
+
# 헬퍼 삭제
|
84
|
+
if self.helpers:
|
85
|
+
for helper in self.helpers:
|
86
|
+
if rt.isValidNode(helper):
|
87
|
+
rt.delete(helper)
|
88
|
+
|
89
|
+
self.bones = []
|
90
|
+
self.helpers = []
|
91
|
+
|
92
|
+
return True
|
93
|
+
except:
|
94
|
+
return False
|
95
|
+
|
96
|
+
def delete_all(self):
|
97
|
+
"""
|
98
|
+
체인의 모든 뼈대와 헬퍼를 3ds Max 씬에서 삭제
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
bool: 삭제 성공 여부
|
102
|
+
"""
|
103
|
+
if self.is_empty() and not self.helpers:
|
104
|
+
return False
|
105
|
+
|
106
|
+
try:
|
107
|
+
# 뼈대 삭제
|
108
|
+
if self.bones:
|
109
|
+
for bone in self.bones:
|
110
|
+
if rt.isValidNode(bone):
|
111
|
+
rt.delete(bone)
|
112
|
+
|
113
|
+
# 헬퍼 삭제
|
114
|
+
if self.helpers:
|
115
|
+
for helper in self.helpers:
|
116
|
+
if rt.isValidNode(helper):
|
117
|
+
rt.delete(helper)
|
118
|
+
|
119
|
+
self.clear()
|
120
|
+
return True
|
121
|
+
except:
|
122
|
+
return False
|
123
|
+
|
124
|
+
def get_bones(self):
|
125
|
+
"""
|
126
|
+
체인의 모든 뼈대 가져오기
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
list: 모든 뼈대 객체의 배열
|
130
|
+
"""
|
131
|
+
if self.is_empty():
|
132
|
+
return []
|
133
|
+
|
134
|
+
return self.bones
|
135
|
+
|
136
|
+
def get_helpers(self):
|
137
|
+
"""
|
138
|
+
체인의 모든 헬퍼 가져오기
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
list: 모든 헬퍼 객체의 배열
|
142
|
+
"""
|
143
|
+
if not self.helpers:
|
144
|
+
return []
|
145
|
+
|
146
|
+
return self.helpers
|
147
|
+
|
148
|
+
@classmethod
|
149
|
+
def from_result(cls, inResult):
|
150
|
+
"""
|
151
|
+
결과 딕셔너리로부터 체인 인스턴스 생성
|
152
|
+
|
153
|
+
Args:
|
154
|
+
inResult (dict): 뼈대 생성 결과를 담은 딕셔너리
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
BoneChain: 생성된 체인 인스턴스
|
158
|
+
"""
|
159
|
+
return cls(inResult)
|
160
|
+
|
161
|
+
def update_from_result(self, inResult):
|
162
|
+
"""
|
163
|
+
기존 체인 인스턴스를 결과 딕셔너리로부터 업데이트
|
164
|
+
|
165
|
+
이미 생성된 체인 객체의 내용을 새로운 결과 데이터로 갱신합니다.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
inResult (dict): 뼈대 생성 결과를 담은 딕셔너리
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
self: 메서드 체이닝을 위한 자기 자신 반환
|
172
|
+
"""
|
173
|
+
if inResult is None:
|
174
|
+
return self
|
175
|
+
|
176
|
+
self.bones = inResult.get("Bones", [])
|
177
|
+
self.helpers = inResult.get("Helpers", [])
|
178
|
+
self.result = inResult # 원본 결과 보존
|
179
|
+
self.sourceBones = inResult.get("SourceBones", [])
|
180
|
+
self.parameters = inResult.get("Parameters", [])
|
181
|
+
|
182
|
+
return self
|
@@ -14,6 +14,8 @@ from .helper import Helper
|
|
14
14
|
from .bone import Bone
|
15
15
|
from .constraint import Constraint
|
16
16
|
|
17
|
+
from .boneChain import BoneChain
|
18
|
+
|
17
19
|
class GroinBone:
|
18
20
|
"""
|
19
21
|
고간 부 본 관련 기능을 위한 클래스
|
@@ -75,21 +77,15 @@ class GroinBone:
|
|
75
77
|
|
76
78
|
Args:
|
77
79
|
inPelvis: Biped 객체
|
80
|
+
inLThighTwist: 왼쪽 허벅지 트위스트 본
|
81
|
+
inRThighTwist: 오른쪽 허벅지 트위스트 본
|
78
82
|
inPelvisWeight: 골반 가중치 (기본값: 40.0)
|
79
83
|
inThighWeight: 허벅지 가중치 (기본값: 60.0)
|
80
84
|
|
81
85
|
Returns:
|
82
|
-
|
86
|
+
BoneChain: 생성된 고간 부 본 체인 객체 또는 실패 시 False
|
83
87
|
"""
|
84
|
-
|
85
|
-
"Pelvis": None,
|
86
|
-
"LThighTwist": None,
|
87
|
-
"RThighTwist": None,
|
88
|
-
"Bones": [],
|
89
|
-
"Helpers": [],
|
90
|
-
"PelvisWeight": inPelvisWeight,
|
91
|
-
"ThighWeight": inThighWeight
|
92
|
-
}
|
88
|
+
|
93
89
|
if rt.isValidNode(inPelvis) == False or rt.isValidNode(inLThighTwist) == False or rt.isValidNode(inRThighTwist) == False:
|
94
90
|
rt.messageBox("There is no valid node.")
|
95
91
|
return False
|
@@ -98,10 +94,11 @@ class GroinBone:
|
|
98
94
|
if inPelvis.name[0].islower():
|
99
95
|
groinName = groinName.lower()
|
100
96
|
|
101
|
-
groinBaseName = self.name.replace_name_part("RealName",
|
97
|
+
groinBaseName = self.name.replace_name_part("RealName", inLThighTwist.name, groinName)
|
102
98
|
|
103
99
|
pelvisHelperName = self.name.replace_name_part("Type", groinBaseName, self.name.get_name_part_value_by_description("Type", "Dummy"))
|
104
|
-
pelvisHelperName = self.name.replace_name_part("Index", pelvisHelperName, "
|
100
|
+
pelvisHelperName = self.name.replace_name_part("Index", pelvisHelperName, "0")
|
101
|
+
pelvisHelperName = self.name.remove_name_part("Side", pelvisHelperName)
|
105
102
|
pelvisHelper = self.helper.create_point(pelvisHelperName)
|
106
103
|
pelvisHelper.transform = inPelvis.transform
|
107
104
|
self.anim.rotate_local(pelvisHelper, 0.0, 0.0, -180.0)
|
@@ -110,7 +107,7 @@ class GroinBone:
|
|
110
107
|
|
111
108
|
lThighTwistHelperName = self.name.replace_name_part("Type", groinBaseName, self.name.get_name_part_value_by_description("Type", "Dummy"))
|
112
109
|
lThighTwistHelperName = self.name.replace_name_part("Side", lThighTwistHelperName, self.name.get_name_part_value_by_description("Side", "Left"))
|
113
|
-
lThighTwistHelperName = self.name.replace_name_part("Index", lThighTwistHelperName, "
|
110
|
+
lThighTwistHelperName = self.name.replace_name_part("Index", lThighTwistHelperName, "0")
|
114
111
|
lThighTwistHelper = self.helper.create_point(lThighTwistHelperName)
|
115
112
|
lThighTwistHelper.transform = pelvisHelper.transform
|
116
113
|
lThighTwistHelper.position = inLThighTwist.position
|
@@ -119,20 +116,22 @@ class GroinBone:
|
|
119
116
|
|
120
117
|
rThighTwistHelperName = self.name.replace_name_part("Type", groinBaseName, self.name.get_name_part_value_by_description("Type", "Dummy"))
|
121
118
|
rThighTwistHelperName = self.name.replace_name_part("Side", rThighTwistHelperName, self.name.get_name_part_value_by_description("Side", "Right"))
|
122
|
-
rThighTwistHelperName = self.name.replace_name_part("Index", rThighTwistHelperName, "
|
119
|
+
rThighTwistHelperName = self.name.replace_name_part("Index", rThighTwistHelperName, "0")
|
123
120
|
rThighTwistHelper = self.helper.create_point(rThighTwistHelperName)
|
124
121
|
rThighTwistHelper.transform = pelvisHelper.transform
|
125
122
|
rThighTwistHelper.position = inRThighTwist.position
|
126
123
|
rThighTwistHelper.parent = inRThighTwist
|
127
124
|
self.helper.set_shape_to_box(rThighTwistHelper)
|
128
125
|
|
129
|
-
groinBoneName = self.name.replace_name_part("Index", groinBaseName, "
|
130
|
-
|
131
|
-
|
132
|
-
|
126
|
+
groinBoneName = self.name.replace_name_part("Index", groinBaseName, "0")
|
127
|
+
groinBoneName = self.name.remove_name_part("Side", groinBoneName)
|
128
|
+
groinBone = self.bone.create_nub_bone(groinBoneName, 2)
|
129
|
+
groinBone.name = groinBoneName
|
130
|
+
groinBone.transform = pelvisHelper.transform
|
131
|
+
groinBone.parent = inPelvis
|
133
132
|
|
134
|
-
self.const.assign_rot_const_multi(
|
135
|
-
rotConst = self.const.get_rot_list_controller(
|
133
|
+
self.const.assign_rot_const_multi(groinBone, [pelvisHelper, lThighTwistHelper, rThighTwistHelper])
|
134
|
+
rotConst = self.const.get_rot_list_controller(groinBone)[1]
|
136
135
|
rotConst.setWeight(1, inPelvisWeight)
|
137
136
|
rotConst.setWeight(2, inThighWeight/2.0)
|
138
137
|
rotConst.setWeight(3, inThighWeight/2.0)
|
@@ -141,20 +140,57 @@ class GroinBone:
|
|
141
140
|
self.pelvis = inPelvis
|
142
141
|
self.lThighTwist = inLThighTwist
|
143
142
|
self.rThighTwist = inRThighTwist
|
144
|
-
self.bones =
|
143
|
+
self.bones = [groinBone]
|
145
144
|
self.helpers = [pelvisHelper, lThighTwistHelper, rThighTwistHelper]
|
146
145
|
self.pelvisWeight = inPelvisWeight
|
147
146
|
self.thighWeight = inThighWeight
|
148
147
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
148
|
+
# BoneChain 구조에 맞는 결과 딕셔너리 생성
|
149
|
+
result = {
|
150
|
+
"Bones": [groinBone],
|
151
|
+
"Helpers": [pelvisHelper, lThighTwistHelper, rThighTwistHelper],
|
152
|
+
"SourceBones": [inPelvis, inLThighTwist, inRThighTwist],
|
153
|
+
"Parameters": [inPelvisWeight, inThighWeight]
|
154
|
+
}
|
156
155
|
|
157
156
|
# 메소드 호출 후 데이터 초기화
|
158
157
|
self.reset()
|
159
158
|
|
160
|
-
|
159
|
+
# BoneChain 객체 반환
|
160
|
+
return BoneChain.from_result(result)
|
161
|
+
|
162
|
+
def create_bones_from_chain(self, inBoneChain: BoneChain):
|
163
|
+
"""
|
164
|
+
기존 BoneChain 객체에서 고간 부 본을 생성합니다.
|
165
|
+
기존 설정을 복원하거나 저장된 데이터에서 고간 부 본 셋업을 재생성할 때 사용합니다.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
inBoneChain (BoneChain): 고간 부 본 정보를 포함한 BoneChain 객체
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
BoneChain: 업데이트된 BoneChain 객체 또는 실패 시 None
|
172
|
+
"""
|
173
|
+
if not inBoneChain or inBoneChain.is_empty():
|
174
|
+
return None
|
175
|
+
|
176
|
+
# 기존 객체 삭제
|
177
|
+
inBoneChain.delete()
|
178
|
+
|
179
|
+
# BoneChain에서 필요한 정보 추출
|
180
|
+
sourceBones = inBoneChain.sourceBones
|
181
|
+
parameters = inBoneChain.parameters
|
182
|
+
|
183
|
+
# 필수 소스 본 확인 (최소 3개: 골반, 좌허벅지트위스트, 우허벅지트위스트)
|
184
|
+
if len(sourceBones) < 3 or not rt.isValidNode(sourceBones[0]) or not rt.isValidNode(sourceBones[1]) or not rt.isValidNode(sourceBones[2]):
|
185
|
+
return None
|
186
|
+
|
187
|
+
# 파라미터 가져오기 (또는 기본값 사용)
|
188
|
+
pelvisWeight = parameters[0] if len(parameters) > 0 else 40.0
|
189
|
+
thighWeight = parameters[1] if len(parameters) > 1 else 60.0
|
190
|
+
|
191
|
+
# 새로운 고간 부 본 생성
|
192
|
+
inPelvis = sourceBones[0]
|
193
|
+
inLThighTwist = sourceBones[1]
|
194
|
+
inRThighTwist = sourceBones[2]
|
195
|
+
|
196
|
+
return self.create_bone(inPelvis, inLThighTwist, inRThighTwist, pelvisWeight, thighWeight)
|