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/__init__.py +7 -7
- pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json +161 -0
- pyjallib/max/__init__.py +31 -20
- pyjallib/max/anim.py +60 -36
- pyjallib/max/autoClavicle.py +122 -122
- pyjallib/max/bip.py +213 -14
- pyjallib/max/bone.py +378 -16
- pyjallib/max/boneChain.py +182 -0
- pyjallib/max/groinBone.py +148 -73
- pyjallib/max/header.py +42 -6
- pyjallib/max/helper.py +3 -21
- pyjallib/max/hip.py +276 -366
- pyjallib/max/kneeBone.py +552 -0
- pyjallib/max/macro/jal_macro_align.py +11 -10
- pyjallib/max/macro/jal_macro_bone.py +3 -2
- pyjallib/max/macro/jal_macro_constraint.py +2 -1
- pyjallib/max/macro/jal_macro_helper.py +2 -1
- pyjallib/max/macro/jal_macro_link.py +2 -1
- pyjallib/max/macro/jal_macro_select.py +2 -1
- pyjallib/max/mirror.py +1 -1
- pyjallib/max/twistBone.py +264 -462
- pyjallib/max/volumeBone.py +324 -0
- pyjallib/namePart.py +16 -16
- pyjallib/nameToPath.py +1 -1
- pyjallib/naming.py +16 -17
- pyjallib/namingConfig.py +3 -3
- pyjallib/perforce.py +127 -9
- {pyjallib-0.1.9.dist-info → pyjallib-0.1.11.dist-info}/METADATA +1 -1
- pyjallib-0.1.11.dist-info/RECORD +43 -0
- pyjallib/max/volumePreserveBone.py +0 -209
- pyjallib/p4module.py +0 -488
- pyjallib-0.1.9.dist-info/RECORD +0 -41
- {pyjallib-0.1.9.dist-info → pyjallib-0.1.11.dist-info}/WHEEL +0 -0
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.
|
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(
|
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
|
-
|
711
|
-
|
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,
|
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"{
|
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.
|
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: 스킨 뼈대 기본 이름 (기본값: "
|
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
|