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.
pyjallib/max/bone.py CHANGED
@@ -239,7 +239,8 @@ 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.replace_name_part("Nub", nubBone.name, "Nub")
242
+ nubBone.name = self.name.remove_name_part("Nub", nubBone.name)
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
  # 화면 갱신 재개
245
246
  rt.enableSceneRedraw()
@@ -679,18 +680,18 @@ class Bone:
679
680
  inOriBone: 원본 뼈대
680
681
  """
681
682
  self.anim.save_xform(inSkinBone)
682
- self.anim.set_xform(inSkinBone)
683
+ self.anim.set_xform(inSkinBone, space="World")
683
684
 
684
685
  self.anim.save_xform(inOriBone)
685
- self.anim.set_xform(inOriBone)
686
686
 
687
687
  rt.setPropertyController(inSkinBone.controller, "Scale", rt.scaleXYZ())
688
688
 
689
689
  linkConst = rt.link_constraint()
690
+ linkConst.addTarget(inOriBone, 0)
691
+
690
692
  inSkinBone.controller = linkConst
691
693
 
692
- self.anim.set_xform([inSkinBone], space="world")
693
- linkConst.addTarget(inOriBone, 0)
694
+ self.anim.set_xform(inSkinBone, space="World")
694
695
 
695
696
  def link_skin_bones(self, inSkinBoneArray, inOriBoneArray):
696
697
  """
@@ -705,10 +706,48 @@ class Bone:
705
706
  False: 실패
706
707
  """
707
708
  if len(inSkinBoneArray) != len(inOriBoneArray):
709
+ print("Error: Skin bone array and original bone array must have the same length.")
708
710
  return False
709
711
 
710
- for i in range(len(inSkinBoneArray)):
711
- self.link_skin_bone(inSkinBoneArray[i], inOriBoneArray[i])
712
+ skinBoneDict = {}
713
+ oriBoneDict = {}
714
+
715
+ # 스킨 뼈대 딕셔너리 생성 (이름과 패턴화된 이름을 함께 저장)
716
+ for item in inSkinBoneArray:
717
+ # 아이템 저장
718
+ skinBoneDict[item.name] = item
719
+ # 언더스코어를 별표로 변환한 패턴 생성
720
+ namePattern = self.name.remove_name_part("Base", item.name)
721
+ namePattern = namePattern.replace("_", "*")
722
+ skinBoneDict[item.name + "_Pattern"] = namePattern
723
+
724
+ # 원본 뼈대 딕셔너리 생성 (이름과 패턴화된 이름을 함께 저장)
725
+ for item in inOriBoneArray:
726
+ # 아이템 저장
727
+ oriBoneDict[item.name] = item
728
+ # 공백을 별표로 변환한 패턴 생성
729
+ namePattern = self.name.remove_name_part("Base", item.name)
730
+ namePattern = namePattern.replace(" ", "*")
731
+ oriBoneDict[item.name + "_Pattern"] = namePattern
732
+
733
+ # 정렬된 배열 생성
734
+ sortedSkinBoneArray = []
735
+ sortedOriBoneArray = []
736
+
737
+ # 같은 패턴을 가진 뼈대들을 찾아 매칭
738
+ for skinName, skinBone in [(k, v) for k, v in skinBoneDict.items() if not k.endswith("_Pattern")]:
739
+ skinPattern = skinBoneDict[skinName + "_Pattern"]
740
+
741
+ for oriName, oriBone in [(k, v) for k, v in oriBoneDict.items() if not k.endswith("_Pattern")]:
742
+ oriPattern = oriBoneDict[oriName + "_Pattern"]
743
+
744
+ if rt.matchPattern(skinName, pattern=oriPattern):
745
+ sortedSkinBoneArray.append(skinBone)
746
+ sortedOriBoneArray.append(oriBone)
747
+ break
748
+ # 링크 연결 수행
749
+ for i in range(len(sortedSkinBoneArray)):
750
+ self.link_skin_bone(sortedSkinBoneArray[i], sortedOriBoneArray[i])
712
751
 
713
752
  return True
714
753
 
@@ -732,17 +771,12 @@ class Bone:
732
771
  returnBones = []
733
772
 
734
773
  definedSkinBoneBaseName = self.name.get_name_part_value_by_description("Base", "SkinBone")
735
- if skinBoneBaseName == "":
736
- if definedSkinBoneBaseName == "":
737
- skinBoneBaseName = "b"
738
- else:
739
- skinBoneBaseName = definedSkinBoneBaseName
740
774
 
741
775
  for i in range(len(inBoneArray)):
742
- skinBoneName = self.name.replace_name_part("Base", inBoneArray[i].name, skinBoneBaseName)
776
+ skinBoneName = self.name.replace_name_part("Base", inBoneArray[i].name, definedSkinBoneBaseName)
743
777
  skinBoneName = self.name.replace_filtering_char(skinBoneName, skinBoneFilteringChar)
744
778
 
745
- skinBone = self.create_nub_bone(f"{skinBoneBaseName}_TempSkin", 2)
779
+ skinBone = self.create_nub_bone(f"{definedSkinBoneBaseName}_TempSkin", 2)
746
780
  skinBone.name = skinBoneName
747
781
  skinBone.wireColor = rt.Color(255, 88, 199)
748
782
  skinBone.transform = inBoneArray[i].transform
@@ -767,12 +801,19 @@ class Bone:
767
801
  for i in range(len(inBoneArray)):
768
802
  oriParentObj = inBoneArray[i].parent
769
803
  if oriParentObj is not None:
770
- skinBoneParentObjName = self.name.replace_base(oriParentObj.name, skinBoneBaseName)
804
+ skinBoneParentObjName = self.name.replace_name_part("Base", oriParentObj.name, definedSkinBoneBaseName)
771
805
  skinBoneParentObjName = self.name.replace_filtering_char(skinBoneParentObjName, skinBoneFilteringChar)
772
806
  bones[i].parent = rt.getNodeByName(skinBoneParentObjName)
773
807
  else:
774
808
  bones[i].parent = None
775
809
 
810
+ for item in bones:
811
+ item.showLinks = True
812
+ item.showLinksOnly = True
813
+
814
+ for item in bones:
815
+ item.name = self.name.replace_name_part("Base", item.name, skinBoneBaseName)
816
+
776
817
  if link:
777
818
  self.link_skin_bones(bones, inBoneArray)
778
819
 
@@ -798,7 +839,7 @@ class Bone:
798
839
  skipNub: Nub 뼈대 건너뛰기 (기본값: True)
799
840
  mesh: 메시 스냅샷 사용 (기본값: False)
800
841
  link: 원본 뼈대에 연결 (기본값: True)
801
- skinBoneBaseName: 스킨 뼈대 기본 이름 (기본값: "b")
842
+ skinBoneBaseName: 스킨 뼈대 기본 이름 (기본값: "")
802
843
 
803
844
  Returns:
804
845
  생성된 스킨 뼈대 배열
@@ -844,6 +885,327 @@ class Bone:
844
885
 
845
886
  return genBones
846
887
 
888
+ def gen_missing_bip_bones_for_ue5manny(self, inBoneArray):
889
+ returnBones = []
890
+ spine3 = None
891
+ neck = None
892
+ head = None
893
+
894
+ handL = None
895
+ handR = None
896
+
897
+ fingerNames = ["index", "middle", "ring", "pinky"]
898
+ knuckleName = "metacarpal"
899
+ lKnuckleDistance = []
900
+ rKnuckleDistance = []
901
+
902
+ lFingers = []
903
+ rFingers = []
904
+
905
+ for item in inBoneArray:
906
+ if rt.matchPattern(item.name, pattern="*spine 03"):
907
+ spine3 = item
908
+ if rt.matchPattern(item.name, pattern="*neck 01"):
909
+ neck = item
910
+ if rt.matchPattern(item.name, pattern="*head"):
911
+ head = item
912
+ if rt.matchPattern(item.name, pattern="*hand*l"):
913
+ handL = item
914
+ if rt.matchPattern(item.name, pattern="*hand*r"):
915
+ handR = item
916
+
917
+ for fingerName in fingerNames:
918
+ if rt.matchPattern(item.name, pattern="*"+fingerName+"*01*l"):
919
+ lFingers.append(item)
920
+ if rt.matchPattern(item.name, pattern="*"+fingerName+"*01*r"):
921
+ rFingers.append(item)
922
+ for finger in lFingers:
923
+ fingerDistance = rt.distance(finger, handL)
924
+ lKnuckleDistance.append(fingerDistance)
925
+ for finger in rFingers:
926
+ fingerDistance = rt.distance(finger, handR)
927
+ rKnuckleDistance.append(fingerDistance)
928
+
929
+ filteringChar = self.name._get_filtering_char(inBoneArray[-1].name)
930
+ isLower = inBoneArray[-1].name[0].islower()
931
+
932
+ # Spine 4,5 생성
933
+ spineName = self.name.get_name_part_value_by_description("Base", "Biped") + filteringChar + "Spine"
934
+
935
+ spine4 = self.create_nub_bone(spineName, 2)
936
+ spine5 = self.create_nub_bone(spineName, 2)
937
+
938
+ spine4.name = self.name.replace_name_part("Index", spine4.name, "4")
939
+ spine4.name = self.name.remove_name_part("Nub", spine4.name)
940
+ spine5.name = self.name.replace_name_part("Index", spine5.name, "5")
941
+ spine5.name = self.name.remove_name_part("Nub", spine5.name)
942
+ if isLower:
943
+ spine4.name = spine4.name.lower()
944
+ spine5.name = spine5.name.lower()
945
+
946
+ spineDistance = rt.distance(spine3, neck)/3.0
947
+ rt.setProperty(spine4, "transform", spine3.transform)
948
+ rt.setProperty(spine5, "transform", spine3.transform)
949
+ self.anim.move_local(spine4, spineDistance, 0, 0)
950
+ self.anim.move_local(spine5, spineDistance * 2, 0, 0)
951
+
952
+ returnBones.append(spine4)
953
+ returnBones.append(spine5)
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
+ # 손가락용 메타카팔 생성
972
+ for i, finger in enumerate(lFingers):
973
+ knuckleBoneName = self.name.add_suffix_to_real_name(finger.name, filteringChar+knuckleName)
974
+ knuckleBoneName = self.name.remove_name_part("Index", knuckleBoneName)
975
+
976
+ knuckleBone = self.create_nub_bone(knuckleBoneName, 2)
977
+ knuckleBone.name = self.name.remove_name_part("Nub", knuckleBone.name)
978
+ if isLower:
979
+ knuckleBone.name = knuckleBone.name.lower()
980
+
981
+ knuckleBone.transform = finger.transform
982
+ lookAtConst = self.const.assign_lookat(knuckleBone, handL)
983
+ lookAtConst.upnode_world = False
984
+ lookAtConst.pickUpNode = handL
985
+ lookAtConst.lookat_vector_length = 0.0
986
+ lookAtConst.target_axisFlip = True
987
+ self.const.collapse(knuckleBone)
988
+ self.anim.move_local(knuckleBone, -lKnuckleDistance[i]*0.8, 0, 0)
989
+
990
+ returnBones.append(knuckleBone)
991
+
992
+ for i, finger in enumerate(rFingers):
993
+ knuckleBoneName = self.name.add_suffix_to_real_name(finger.name, filteringChar+knuckleName)
994
+ knuckleBoneName = self.name.remove_name_part("Index", knuckleBoneName)
995
+
996
+ knuckleBone = self.create_nub_bone(knuckleBoneName, 2)
997
+ knuckleBone.name = self.name.remove_name_part("Nub", knuckleBone.name)
998
+ if isLower:
999
+ knuckleBone.name = knuckleBone.name.lower()
1000
+
1001
+ knuckleBone.transform = finger.transform
1002
+ lookAtConst = self.const.assign_lookat(knuckleBone, handR)
1003
+ lookAtConst.upnode_world = False
1004
+ lookAtConst.pickUpNode = handR
1005
+ lookAtConst.lookat_vector_length = 0.0
1006
+ lookAtConst.target_axisFlip = True
1007
+ self.const.collapse(knuckleBone)
1008
+ self.anim.move_local(knuckleBone, -rKnuckleDistance[i]*0.8, 0, 0)
1009
+
1010
+ returnBones.append(knuckleBone)
1011
+
1012
+ return returnBones
1013
+
1014
+ def relink_missing_bip_bones_for_ue5manny(self, inBipArray, inMissingBoneArray):
1015
+ returnBones = []
1016
+
1017
+ spine3 = None
1018
+ neck = None
1019
+
1020
+ handL = None
1021
+ handR = None
1022
+
1023
+ knuckleName = "metacarpal"
1024
+
1025
+ for item in inBipArray:
1026
+ if rt.matchPattern(item.name, pattern="*spine 03"):
1027
+ spine3 = item
1028
+ if rt.matchPattern(item.name, pattern="*neck 01"):
1029
+ neck = item
1030
+ if rt.matchPattern(item.name, pattern="*hand*l"):
1031
+ handL = item
1032
+ if rt.matchPattern(item.name, pattern="*hand*r"):
1033
+ handR = item
1034
+
1035
+ for item in inMissingBoneArray:
1036
+ if rt.matchPattern(item.name, pattern="*spine*"):
1037
+ item.parent = spine3
1038
+ if rt.matchPattern(item.name, pattern="*neck*"):
1039
+ item.parent = neck
1040
+ if rt.matchPattern(item.name, pattern=f"*{knuckleName}*l"):
1041
+ item.parent = handL
1042
+ if rt.matchPattern(item.name, pattern=f"*{knuckleName}*r"):
1043
+ item.parent = handR
1044
+
1045
+ returnBones.append(inBipArray)
1046
+ returnBones.append(inMissingBoneArray)
1047
+ return returnBones
1048
+
1049
+ def relink_missing_skin_bones_for_ue5manny(self, inSkinArray):
1050
+ returnBones = []
1051
+ spine3 = None
1052
+ spine4 = None
1053
+ spine5 = None
1054
+
1055
+ neck = None
1056
+ neck2 = None
1057
+ head = None
1058
+ clavicleL = None
1059
+ clavicleR = None
1060
+
1061
+ handL = None
1062
+ handR = None
1063
+
1064
+ fingerNames = ["index", "middle", "ring", "pinky"]
1065
+ knuckleName = "metacarpal"
1066
+
1067
+ lFingers = []
1068
+ rFingers = []
1069
+
1070
+ for item in inSkinArray:
1071
+ if rt.matchPattern(item.name, pattern="*spine*03"):
1072
+ spine3 = item
1073
+ if rt.matchPattern(item.name, pattern="*neck*01"):
1074
+ neck = item
1075
+ if rt.matchPattern(item.name, pattern="*head*"):
1076
+ head = item
1077
+ if rt.matchPattern(item.name, pattern="*clavicle*l"):
1078
+ clavicleL = item
1079
+ if rt.matchPattern(item.name, pattern="*clavicle*r"):
1080
+ clavicleR = item
1081
+
1082
+ if rt.matchPattern(item.name, pattern="*hand*l"):
1083
+ handL = item
1084
+ if rt.matchPattern(item.name, pattern="*hand*r"):
1085
+ handR = item
1086
+
1087
+ for fingerName in fingerNames:
1088
+ if rt.matchPattern(item.name, pattern="*"+fingerName+"*01*l"):
1089
+ lFingers.append(item)
1090
+ if rt.matchPattern(item.name, pattern="*"+fingerName+"*01*r"):
1091
+ rFingers.append(item)
1092
+
1093
+ for item in inSkinArray:
1094
+ if rt.matchPattern(item.name, pattern="*spine*04"):
1095
+ spine4 = item
1096
+ item.parent = spine3
1097
+
1098
+ if rt.matchPattern(item.name, pattern="*spine*05"):
1099
+ spine5 = item
1100
+ item.parent = spine4
1101
+ neck.parent = spine5
1102
+ clavicleL.parent = spine5
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
1109
+
1110
+ if rt.matchPattern(item.name, pattern=f"*{knuckleName}*l"):
1111
+ item.parent = handL
1112
+ if rt.matchPattern(item.name, pattern=f"*{knuckleName}*r"):
1113
+ item.parent = handR
1114
+
1115
+ filteringChar = self.name._get_filtering_char(inSkinArray[-1].name)
1116
+
1117
+ for item in lFingers:
1118
+ fingerNamePattern = self.name.add_suffix_to_real_name(item.name, filteringChar+knuckleName)
1119
+ fingerNamePattern = self.name.remove_name_part("Index", fingerNamePattern)
1120
+ for knuckle in inSkinArray:
1121
+ if rt.matchPattern(knuckle.name, pattern=fingerNamePattern):
1122
+ item.parent = knuckle
1123
+ break
1124
+
1125
+ for item in rFingers:
1126
+ fingerNamePattern = self.name.add_suffix_to_real_name(item.name, filteringChar+knuckleName)
1127
+ fingerNamePattern = self.name.remove_name_part("Index", fingerNamePattern)
1128
+ for knuckle in inSkinArray:
1129
+ if rt.matchPattern(knuckle.name, pattern=fingerNamePattern):
1130
+ item.parent = knuckle
1131
+ break
1132
+
1133
+ return returnBones
1134
+
1135
+ def create_skin_bone_from_bip_for_ue5manny(self, inBoneArray, skipNub=True, mesh=False, link=True, isHuman=False, skinBoneBaseName=""):
1136
+ targetBones = [item for item in inBoneArray
1137
+ if (rt.classOf(item) == rt.Biped_Object)
1138
+ and (not rt.matchPattern(item.name, pattern="*Twist*"))
1139
+ and (item != item.controller.rootNode)]
1140
+
1141
+ missingBipBones = []
1142
+
1143
+ if isHuman:
1144
+ missingBipBones = self.gen_missing_bip_bones_for_ue5manny(targetBones)
1145
+ self.relink_missing_bip_bones_for_ue5manny(targetBones, missingBipBones)
1146
+
1147
+ for item in missingBipBones:
1148
+ targetBones.append(item)
1149
+
1150
+ sortedBipBones = self.sort_bones_as_hierarchy(targetBones)
1151
+
1152
+ skinBones = self.create_skin_bone(sortedBipBones, skipNub=skipNub, mesh=mesh, link=False, skinBoneBaseName=skinBoneBaseName)
1153
+ if len(skinBones) == 0:
1154
+ return False
1155
+
1156
+ for item in skinBones:
1157
+ if rt.matchPattern(item.name, pattern="*pelvis*"):
1158
+ self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
1159
+ if rt.matchPattern(item.name, pattern="*spine*"):
1160
+ self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
1161
+ if rt.matchPattern(item.name, pattern="*neck*"):
1162
+ self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
1163
+ if rt.matchPattern(item.name, pattern="*head*"):
1164
+ self.anim.rotate_local(item, 180, 0, 0, dontAffectChildren=True)
1165
+ if rt.matchPattern(item.name, pattern="*thigh*l"):
1166
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1167
+ if rt.matchPattern(item.name, pattern="*calf*l"):
1168
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1169
+ if rt.matchPattern(item.name, pattern="*foot*l"):
1170
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1171
+ if rt.matchPattern(item.name, pattern="*ball*r"):
1172
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1173
+
1174
+ if rt.matchPattern(item.name, pattern="*clavicle*r"):
1175
+ self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
1176
+ if rt.matchPattern(item.name, pattern="*upperarm*r"):
1177
+ self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
1178
+ if rt.matchPattern(item.name, pattern="*lowerarm*r"):
1179
+ self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
1180
+ if rt.matchPattern(item.name, pattern="*hand*r"):
1181
+ self.anim.rotate_local(item, 0, 0, -180, dontAffectChildren=True)
1182
+
1183
+ if rt.matchPattern(item.name, pattern="*thumb*r"):
1184
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1185
+ if rt.matchPattern(item.name, pattern="*index*r"):
1186
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1187
+ if rt.matchPattern(item.name, pattern="*middle*r"):
1188
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1189
+ if rt.matchPattern(item.name, pattern="*ring*r"):
1190
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1191
+ if rt.matchPattern(item.name, pattern="*pinky*r"):
1192
+ self.anim.rotate_local(item, 0, 0, 180, dontAffectChildren=True)
1193
+
1194
+ if rt.matchPattern(item.name, pattern="*metacarpal*"):
1195
+ tempArray = self.name._split_to_array(item.name)
1196
+ item.name = self.name._combine(tempArray, inFilChar="_")
1197
+ item.name = self.name.remove_name_part("Base", item.name)
1198
+
1199
+ self.anim.save_xform(item)
1200
+
1201
+ self.relink_missing_skin_bones_for_ue5manny(skinBones)
1202
+
1203
+ self.link_skin_bones(skinBones, sortedBipBones)
1204
+ for item in skinBones:
1205
+ self.anim.save_xform(item)
1206
+
1207
+ return skinBones
1208
+
847
1209
  def set_bone_on(self, inBone):
848
1210
  """
849
1211
  뼈대 활성화.
@@ -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