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.
Files changed (69) hide show
  1. {pyjallib-0.1.10 → pyjallib-0.1.11}/PKG-INFO +1 -1
  2. {pyjallib-0.1.10 → pyjallib-0.1.11}/pyproject.toml +1 -1
  3. pyjallib-0.1.11/src/pyjallib/__init__.py +17 -0
  4. pyjallib-0.1.11/src/pyjallib/max/__init__.py +68 -0
  5. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/autoClavicle.py +39 -4
  6. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/bip.py +2 -0
  7. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/bone.py +37 -0
  8. pyjallib-0.1.11/src/pyjallib/max/boneChain.py +182 -0
  9. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/groinBone.py +65 -29
  10. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/hip.py +44 -7
  11. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/kneeBone.py +61 -26
  12. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_bone.py +1 -1
  13. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/twistBone.py +59 -40
  14. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/volumeBone.py +77 -26
  15. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/naming.py +1 -1
  16. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/perforce.py +127 -9
  17. {pyjallib-0.1.10 → pyjallib-0.1.11}/uv.lock +1 -1
  18. pyjallib-0.1.10/src/pyjallib/__init__.py +0 -17
  19. pyjallib-0.1.10/src/pyjallib/max/__init__.py +0 -69
  20. pyjallib-0.1.10/src/pyjallib/max/autoClavicleChain.py +0 -173
  21. pyjallib-0.1.10/src/pyjallib/max/groinBoneChain.py +0 -173
  22. pyjallib-0.1.10/src/pyjallib/max/twistBoneChain.py +0 -162
  23. pyjallib-0.1.10/src/pyjallib/max/volumeBoneChain.py +0 -363
  24. {pyjallib-0.1.10 → pyjallib-0.1.11}/.gitignore +0 -0
  25. {pyjallib-0.1.10 → pyjallib-0.1.11}/.python-version +0 -0
  26. {pyjallib-0.1.10 → pyjallib-0.1.11}/README.md +0 -0
  27. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/index.html +0 -0
  28. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/max.html +0 -0
  29. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/namePart.html +0 -0
  30. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/nameToPath.html +0 -0
  31. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/naming.html +0 -0
  32. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/namingConfig.html +0 -0
  33. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/p4module.html +0 -0
  34. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/perforce.html +0 -0
  35. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib/reloadModules.html +0 -0
  36. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/pyjallib.html +0 -0
  37. {pyjallib-0.1.10 → pyjallib-0.1.11}/docs/search.js +0 -0
  38. {pyjallib-0.1.10 → pyjallib-0.1.11}/generate_docs.ps1 +0 -0
  39. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/ConfigFiles/namingConfig.json +0 -0
  40. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json +0 -0
  41. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json +0 -0
  42. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/align.py +0 -0
  43. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/anim.py +0 -0
  44. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/constraint.py +0 -0
  45. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/header.py +0 -0
  46. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/helper.py +0 -0
  47. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/layer.py +0 -0
  48. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/link.py +0 -0
  49. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_align.py +0 -0
  50. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_constraint.py +0 -0
  51. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_helper.py +0 -0
  52. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_link.py +0 -0
  53. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/macro/jal_macro_select.py +0 -0
  54. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/mirror.py +0 -0
  55. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/morph.py +0 -0
  56. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/name.py +0 -0
  57. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/select.py +0 -0
  58. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/skin.py +0 -0
  59. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/max/ui/Container.py +0 -0
  60. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/namePart.py +0 -0
  61. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/nameToPath.py +0 -0
  62. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/namingConfig.py +0 -0
  63. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/py.typed +0 -0
  64. {pyjallib-0.1.10 → pyjallib-0.1.11}/src/pyjallib/reloadModules.py +0 -0
  65. {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/autoclavicleTest.py +0 -0
  66. {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/globalVarTest.py +0 -0
  67. {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/moduleImportTest.py +0 -0
  68. {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/p4Test.py +0 -0
  69. {pyjallib-0.1.10 → pyjallib-0.1.11}/tests/volumePreserveBoneTest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyjallib
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: A utility library for 3D game character development pipelines.
5
5
  Author-email: Dongseok Kim <jalnagakds@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyjallib"
3
- version = "0.1.10"
3
+ version = "0.1.11"
4
4
  description = "A utility library for 3D game character development pipelines."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -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
- "Clavicle": inClavicle,
170
- "UpperArm": inUpperArm,
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
- 성공 여부 (Boolean)
86
+ BoneChain: 생성된 고간 부 본 체인 객체 또는 실패 시 False
83
87
  """
84
- returnVal = {
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", inPelvis.name, groinBaseName)
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, "00")
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, "00")
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, "00")
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, "00")
130
- groinBones = self.bone.create_simple_bone(3.0, groinBoneName, size=2)
131
- groinBones[0].transform = pelvisHelper.transform
132
- groinBones[0].parent = inPelvis
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(groinBones[0], [pelvisHelper, lThighTwistHelper, rThighTwistHelper])
135
- rotConst = self.const.get_rot_list_controller(groinBones[0])[1]
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 = groinBones
143
+ self.bones = [groinBone]
145
144
  self.helpers = [pelvisHelper, lThighTwistHelper, rThighTwistHelper]
146
145
  self.pelvisWeight = inPelvisWeight
147
146
  self.thighWeight = inThighWeight
148
147
 
149
- returnVal["Pelvis"] = inPelvis
150
- returnVal["LThighTwist"] = inLThighTwist
151
- returnVal["RThighTwist"] = inRThighTwist
152
- returnVal["Bones"] = groinBones
153
- returnVal["Helpers"] = [pelvisHelper, lThighTwistHelper, rThighTwistHelper]
154
- returnVal["PelvisWeight"] = inPelvisWeight
155
- returnVal["ThighWeight"] = inThighWeight
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
- return returnVal
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)