Fixes
This commit is contained in:
+245
-65
@@ -5,72 +5,252 @@
|
|||||||
# Note from @989onan: Please make sure to make your names are lowercase in this array. I banged my head metaphorically till I figured that out...
|
# Note from @989onan: Please make sure to make your names are lowercase in this array. I banged my head metaphorically till I figured that out...
|
||||||
# Taken from Tuxedo/Cats
|
# Taken from Tuxedo/Cats
|
||||||
bone_names = {
|
bone_names = {
|
||||||
"right_shoulder": ["rightshoulder", "shoulderr", "rshoulder", "valvebipedbip01rclavicle", "右肩"],
|
# Right side bones
|
||||||
"right_arm": ["rightarm", "armr", "rarm", "upperarmr", "rupperarm", "rightupperarm", "uparmr", "ruparm", "valvebipedbip01rupperarm", "右腕"],
|
"right_shoulder": [
|
||||||
"right_elbow": ["rightelbow", "elbowr", "relbow", "lowerarmr", "rightlowerarm", "lowerarmr", "rlowerarm", "lowarmr", "rlowarm", "forearmr", "rforearm", "valvebipedbip01rforearm", "右ひじ"],
|
"rightshoulder", "shoulderr", "rshoulder", "valvebipedbip01rclavicle",
|
||||||
"right_wrist": ["rightwrist", "wristr", "rwrist", "handr", "righthand", "rhand", "valvebipedbip01rhand", "右手首"],
|
"右肩", "肩.r", "肩+.r", "右肩+", "右肩", "右肩+", "肩+r", "肩+右", "ik_肩.r"
|
||||||
"pinkie_0_r": ["littlefinger0r", "pinkie0r", "rpinkie0", "pinkiemetacarpalr", "右小指0"],
|
],
|
||||||
"pinkie_1_r": ["littlefinger1r", "pinkie1r", "rpinkie1", "pinkieproximalr", "valvebipedbip01rfinger4", "右小指1"],
|
"right_arm": [
|
||||||
"pinkie_2_r": ["littlefinger2r", "pinkie2r", "rpinkie2", "pinkieintermediater", "valvebipedbip01rfinger41", "右小指2"],
|
"rightarm", "armr", "rarm", "upperarmr", "rupperarm", "rightupperarm",
|
||||||
"pinkie_3_r": ["littlefinger3r", "pinkie3r", "rpinkie3", "pinkiedistalr", "valvebipedbip01rfinger42", "右小指3"],
|
"uparmr", "ruparm", "valvebipedbip01rupperarm", "右腕", "腕.r", "右腕", "ik_腕.r"
|
||||||
"ring_0_r": ["ringfinger0r", "ring0r", "rring0", "ringmetacarpalr", "右薬指0"],
|
],
|
||||||
"ring_1_r": ["ringfinger1r", "ring1r", "rring1", "ringproximalr", "valvebipedbip01rfinger3", "右薬指1"],
|
"right_elbow": [
|
||||||
"ring_2_r": ["ringfinger2r", "ring2r", "rring2", "ringintermediater", "valvebipedbip01rfinger31", "右薬指2"],
|
"rightelbow", "elbowr", "relbow", "lowerarmr", "rightlowerarm",
|
||||||
"ring_3_r": ["ringfinger3r", "ring3r", "rring3", "ringdistalr", "valvebipedbip01rfinger32", "右薬指3"],
|
"rlowerarm", "lowarmr", "rlowarm", "forearmr", "rforearm",
|
||||||
"middle_0_r": ["middlefinger0r", "middle0r", "rmiddle0", "middlemetacarpalr", "右中指0"],
|
"valvebipedbip01rforearm", "右ひじ", "ひじ.r", "ik_ひじ.r"
|
||||||
"middle_1_r": ["middlefinger1r", "middle1r", "rmiddle1", "middleproximalr", "valvebipedbip01rfinger2", "右中指1"],
|
],
|
||||||
"middle_2_r": ["middlefinger2r", "middle2r", "rmiddle2", "middleintermediater", "valvebipedbip01rfinger21", "右中指2"],
|
"right_wrist": [
|
||||||
"middle_3_r": ["middlefinger3r", "middle3r", "rmiddle3", "middledistalr", "valvebipedbip01rfinger22", "右中指3"],
|
"rightwrist", "wristr", "rwrist", "handr", "righthand", "rhand",
|
||||||
"index_0_r": ["indexfinger0r", "index0r", "rindex0", "indexmetacarpalr", "右人差指0"],
|
"valvebipedbip01rhand", "右手首", "手首.r", "ik_手首.r"
|
||||||
"index_1_r": ["indexfinger1r", "index1r", "rindex1", "indexproximalr", "valvebipedbip01rfinger1", "右人差指1"],
|
],
|
||||||
"index_2_r": ["indexfinger2r", "index2r", "rindex2", "indexintermediater", "valvebipedbip01rfinger11", "右人差指2"],
|
"pinkie_0_r": [
|
||||||
"index_3_r": ["indexfinger3r", "index3r", "rindex3", "indexdistalr", "valvebipedbip01rfinger12", "右人差指3"],
|
"littlefinger0r", "pinkie0r", "rpinkie0", "pinkiemetacarpalr", "右小指0"
|
||||||
"thumb_0_r": ["thumb0r", "rthumb0", "thumbmetacarpalr", "右親指0"],
|
],
|
||||||
"thumb_1_r": ["thumb1r", "rthumb1", "thumbproximalr", "valvebipedbip01rfinger0", "右親指1"],
|
"pinkie_1_r": [
|
||||||
"thumb_2_r": ["thumb2r", "rthumb2", "thumbintermediater", "valvebipedbip01rfinger01", "右親指2"],
|
"littlefinger1r", "pinkie1r", "rpinkie1", "pinkieproximalr",
|
||||||
"thumb_3_r": ["thumb3r", "rthumb3", "thumbdistalr", "valvebipedbip01rfinger02", "右親指3"],
|
"valvebipedbip01rfinger4", "右小指1"
|
||||||
"right_leg": ["rightleg", "legr", "rleg", "upperlegr", "rupperleg", "thighr", "rightupperleg", "uplegr", "rupleg", "valvebipedbip01rthigh", "右足"],
|
],
|
||||||
"right_knee": ["rightknee", "kneer", "rknee", "lowerlegr", "calfr", "rlowerleg", "rcalf", "rightlowerleg", "lowlegr", "rlowleg", "valvebipedbip01rcalf", "右ひざ"],
|
"pinkie_2_r": [
|
||||||
"right_ankle": ["rightankle", "ankler", "rankle", "rightfoot", "footr", "rfoot", "rightfoot", "rightfeet", "feetright", "rfeet", "feetr", "valvebipedbip01rfoot", "右足首"],
|
"littlefinger2r", "pinkie2r", "rpinkie2", "pinkieintermediater",
|
||||||
"right_toe": ["righttoe", "toeright", "toer", "rtoe", "toesr", "rtoes", "valvebipedbip01rtoe0", "右つま先"],
|
"valvebipedbip01rfinger41", "右小指2"
|
||||||
"left_shoulder": ["leftshoulder", "shoulderl", "lshoulder", "valvebipedbip01lclavicle", "左肩"],
|
],
|
||||||
"left_arm": ["leftarm", "arml", "larm", "upperarml", "lupperarm", "leftupperarm", "uparml", "luparm", "valvebipedbip01lupperarm", "左腕"],
|
"pinkie_3_r": [
|
||||||
"left_elbow": ["leftelbow", "elbowl", "lelbow", "lowerarml", "leftlowerarm", "lowerarml", "llowerarm", "lowarml", "llowarm", "forearml", "lforearm", "valvebipedbip01lforearm", "左ひじ"],
|
"littlefinger3r", "pinkie3r", "rpinkie3", "pinkiedistalr",
|
||||||
"left_wrist": ["leftwrist", "wristl", "lwrist", "handl", "lefthand", "lhand", "valvebipedbip01lhand", "左手首"],
|
"valvebipedbip01rfinger42", "右小指3"
|
||||||
"pinkie_0_l": ["pinkiefinger0l", "pinkie0l", "lpinkie0", "pinkiemetacarpall", "左小指0"],
|
],
|
||||||
"pinkie_1_l": ["littlefinger1l", "pinkie1l", "lpinkie1", "pinkieproximall", "valvebipedbip01lfinger4", "左小指1"],
|
"ring_0_r": [
|
||||||
"pinkie_2_l": ["littlefinger2l", "pinkie2l", "lpinkie2", "pinkieintermediatel", "valvebipedbip01lfinger41", "左小指2"],
|
"ringfinger0r", "ring0r", "rring0", "ringmetacarpalr", "右薬指0"
|
||||||
"pinkie_3_l": ["littlefinger3l", "pinkie3l", "lpinkie3", "pinkiedistall", "valvebipedbip01lfinger42", "左小指3"],
|
],
|
||||||
"ring_0_l": ["ringfinger0l", "ring0l", "lring0", "ringmetacarpall", "左薬指0"],
|
"ring_1_r": [
|
||||||
"ring_1_l": ["ringfinger1l", "ring1l", "lring1", "ringproximall", "valvebipedbip01lfinger3", "左薬指1"],
|
"ringfinger1r", "ring1r", "rring1", "ringproximalr",
|
||||||
"ring_2_l": ["ringfinger2l", "ring2l", "lring2", "ringintermediatel", "valvebipedbip01lfinger31", "左薬指2"],
|
"valvebipedbip01rfinger3", "右薬指1"
|
||||||
"ring_3_l": ["ringfinger3l", "ring3l", "lring3", "ringdistall", "valvebipedbip01lfinger32", "左薬指3"],
|
],
|
||||||
"middle_0_l": ["middlefinger0l", "middle_0l", "lmiddle0", "middlemetacarpall", "左中指0"],
|
"ring_2_r": [
|
||||||
"middle_1_l": ["middlefinger1l", "middle_1l", "lmiddle1", "middleproximall", "valvebipedbip01lfinger2", "左中指1"],
|
"ringfinger2r", "ring2r", "rring2", "ringintermediater",
|
||||||
"middle_2_l": ["middlefinger2l", "middle_2l", "lmiddle2", "middleintermediatel", "valvebipedbip01lfinger21", "左中指2"],
|
"valvebipedbip01rfinger31", "右薬指2"
|
||||||
"middle_3_l": ["middlefinger3l", "middle_3l", "lmiddle3", "middledistall", "valvebipedbip01lfinger22", "左中指3"],
|
],
|
||||||
"index_0_l": ["indexfinger0l", "index0l", "lindex0", "indexmetacarpall", "左人差指0"],
|
"ring_3_r": [
|
||||||
"index_1_l": ["indexfinger1l", "index1l", "lindex1", "indexproximall", "valvebipedbip01lfinger1", "左人差指1"],
|
"ringfinger3r", "ring3r", "rring3", "ringdistalr",
|
||||||
"index_2_l": ["indexfinger2l", "index2l", "lindex2", "indexintermediatel", "valvebipedbip01lfinger11", "左人差指2"],
|
"valvebipedbip01rfinger32", "右薬指3"
|
||||||
"index_3_l": ["indexfinger3l", "index3l", "lindex3", "indexdistall", "valvebipedbip01lfinger12", "左人差指3"],
|
],
|
||||||
"thumb_0_l": ["thumb0l", "lthumb0", "thumbmetacarpall", "左親指0"],
|
"middle_0_r": [
|
||||||
"thumb_1_l": ["thumb1l", "lthumb1", "thumbproximall", "valvebipedbip01lfinger0", "左親指1"],
|
"middlefinger0r", "middle0r", "rmiddle0", "middlemetacarpalr", "右中指0"
|
||||||
"thumb_2_l": ["thumb2l", "lthumb2", "thumbintermediatel", "valvebipedbip01lfinger01", "左親指2"],
|
],
|
||||||
"thumb_3_l": ["thumb3l", "lthumb3", "thumbdistall", "valvebipedbip01lfinger02", "左親指3"],
|
"middle_1_r": [
|
||||||
"left_leg": ["leftleg", "legl", "lleg", "upperlegl", "lupperleg", "thighl", "leftupperleg", "uplegl", "lupleg", "valvebipedbip01lthigh", "左足"],
|
"middlefinger1r", "middle1r", "rmiddle1", "middleproximalr",
|
||||||
"left_knee": ["leftknee", "kneel", "lknee", "lowerlegl", "llowerleg", "calfl", "lcalf", "leftlowerleg", "lowlegl", "llowleg", "valvebipedbip01lcalf", "左ひざ"],
|
"valvebipedbip01rfinger2", "右中指1"
|
||||||
"left_ankle": ["leftankle", "anklel", "lankle", "leftfoot", "footl", "lfoot", "leftfoot", "leftfeet", "feetleft", "lfeet", "feetl", "valvebipedbip01lfoot", "左足首"],
|
],
|
||||||
"left_toe": ["lefttoe", "toeleft", "toel", "ltoe", "toesl", "ltoes", "valvebipedbip01ltoe0", "左つま先"],
|
"middle_2_r": [
|
||||||
"hips": ["pelvis", "hips", "hip", "valvebipedbip01pelvis", "腰"],
|
"middlefinger2r", "middle2r", "rmiddle2", "middleintermediater",
|
||||||
"spine": ["torso", "spine", "valvebipedbip01spine", "脊椎"],
|
"valvebipedbip01rfinger21", "右中指2"
|
||||||
"chest": ["chest", "valvebipedbip01spine1", "胸"],
|
],
|
||||||
"upper_chest": ["upperchest", "valvebipedbip01spine4", "上胸"],
|
"middle_3_r": [
|
||||||
"neck": ["neck", "valvebipedbip01neck1", "首"],
|
"middlefinger3r", "middle3r", "rmiddle3", "middledistalr",
|
||||||
"head": ["head", "valvebipedbip01head1", "頭"],
|
"valvebipedbip01rfinger22", "右中指3"
|
||||||
"left_eye": ["eyeleft", "lefteye", "eyel", "leye", "左目"],
|
],
|
||||||
"right_eye": ["eyeright", "righteye", "eyer", "reye", "右目"],
|
"index_0_r": [
|
||||||
}
|
"indexfinger0r", "index0r", "rindex0", "indexmetacarpalr", "右人差指0"
|
||||||
|
],
|
||||||
|
"index_1_r": [
|
||||||
|
"indexfinger1r", "index1r", "rindex1", "indexproximalr",
|
||||||
|
"valvebipedbip01rfinger1", "右人差指1"
|
||||||
|
],
|
||||||
|
"index_2_r": [
|
||||||
|
"indexfinger2r", "index2r", "rindex2", "indexintermediater",
|
||||||
|
"valvebipedbip01rfinger11", "右人差指2"
|
||||||
|
],
|
||||||
|
"index_3_r": [
|
||||||
|
"indexfinger3r", "index3r", "rindex3", "indexdistalr",
|
||||||
|
"valvebipedbip01rfinger12", "右人差指3"
|
||||||
|
],
|
||||||
|
"thumb_0_r": [
|
||||||
|
"thumb0r", "rthumb0", "thumbmetacarpalr", "右親指0"
|
||||||
|
],
|
||||||
|
"thumb_1_r": [
|
||||||
|
"thumb1r", "rthumb1", "thumbproximalr", "valvebipedbip01rfinger0", "右親指1"
|
||||||
|
],
|
||||||
|
"thumb_2_r": [
|
||||||
|
"thumb2r", "rthumb2", "thumbintermediater", "valvebipedbip01rfinger01", "右親指2"
|
||||||
|
],
|
||||||
|
"thumb_3_r": [
|
||||||
|
"thumb3r", "rthumb3", "thumbdistalr", "valvebipedbip01rfinger02", "右親指3"
|
||||||
|
],
|
||||||
|
"right_leg": [
|
||||||
|
"rightleg", "legr", "rleg", "upperlegr", "rupperleg", "thighr",
|
||||||
|
"rightupperleg", "uplegr", "rupleg", "valvebipedbip01rthigh",
|
||||||
|
"右足", "足.r", "ik_足.r"
|
||||||
|
],
|
||||||
|
"right_knee": [
|
||||||
|
"rightknee", "kneer", "rknee", "lowerlegr", "rightlowerleg",
|
||||||
|
"rlowerleg", "lowlegr", "rlowleg", "calfr", "rcalf",
|
||||||
|
"valvebipedbip01rcalf", "右ひざ", "ひざ.r", "すね.r", "ik_ひざ.r"
|
||||||
|
],
|
||||||
|
"right_ankle": [
|
||||||
|
"rightankle", "ankler", "rankle", "rightfoot", "footr", "rfoot",
|
||||||
|
"rightfeet", "feetright", "rfeet", "feetr", "valvebipedbip01rfoot",
|
||||||
|
"右足首", "足首.r", "ik_足首.r"
|
||||||
|
],
|
||||||
|
"right_toe": [
|
||||||
|
"righttoe", "toeright", "toer", "rtoe", "toesr", "rtoes",
|
||||||
|
"valvebipedbip01rtoe0", "右つま先", "つま先.r", "ik_つま先.r"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Left side bones
|
||||||
|
"left_shoulder": [
|
||||||
|
"leftshoulder", "shoulderl", "lshoulder", "valvebipedbip01lclavicle",
|
||||||
|
"左肩", "肩.l", "肩+.l", "左肩+", "左肩", "左肩+", "肩+l", "肩+左", "ik_肩.l"
|
||||||
|
],
|
||||||
|
"left_arm": [
|
||||||
|
"leftarm", "arml", "larm", "upperarml", "lupperarm", "leftupperarm",
|
||||||
|
"uparml", "luparm", "valvebipedbip01lupperarm", "左腕", "腕.l", "左腕", "ik_腕.l"
|
||||||
|
],
|
||||||
|
"left_elbow": [
|
||||||
|
"leftelbow", "elbowl", "lelbow", "lowerarml", "leftlowerarm",
|
||||||
|
"llowerarm", "lowarml", "llowarm", "forearml", "lforearm",
|
||||||
|
"valvebipedbip01lforearm", "左ひじ", "ひじ.l", "すね.l", "ik_ひじ.l"
|
||||||
|
],
|
||||||
|
"left_wrist": [
|
||||||
|
"leftwrist", "wristl", "lwrist", "handl", "lefthand", "lhand",
|
||||||
|
"valvebipedbip01lhand", "左手首", "手首.l", "ik_手首.l"
|
||||||
|
],
|
||||||
|
"pinkie_0_l": [
|
||||||
|
"pinkiefinger0l", "pinkie0l", "lpinkie0", "pinkiemetacarpall", "左小指0"
|
||||||
|
],
|
||||||
|
"pinkie_1_l": [
|
||||||
|
"littlefinger1l", "pinkie1l", "lpinkie1", "pinkieproximall",
|
||||||
|
"valvebipedbip01lfinger4", "左小指1"
|
||||||
|
],
|
||||||
|
"pinkie_2_l": [
|
||||||
|
"littlefinger2l", "pinkie2l", "lpinkie2", "pinkieintermediatel",
|
||||||
|
"valvebipedbip01lfinger41", "左小指2"
|
||||||
|
],
|
||||||
|
"pinkie_3_l": [
|
||||||
|
"littlefinger3l", "pinkie3l", "lpinkie3", "pinkiedistall",
|
||||||
|
"valvebipedbip01lfinger42", "左小指3"
|
||||||
|
],
|
||||||
|
"ring_0_l": [
|
||||||
|
"ringfinger0l", "ring0l", "lring0", "ringmetacarpall", "左薬指0"
|
||||||
|
],
|
||||||
|
"ring_1_l": [
|
||||||
|
"ringfinger1l", "ring1l", "lring1", "ringproximall",
|
||||||
|
"valvebipedbip01lfinger3", "左薬指1"
|
||||||
|
],
|
||||||
|
"ring_2_l": [
|
||||||
|
"ringfinger2l", "ring2l", "lring2", "ringintermediatel",
|
||||||
|
"valvebipedbip01lfinger31", "左薬指2"
|
||||||
|
],
|
||||||
|
"ring_3_l": [
|
||||||
|
"ringfinger3l", "ring3l", "lring3", "ringdistall",
|
||||||
|
"valvebipedbip01lfinger32", "左薬指3"
|
||||||
|
],
|
||||||
|
"middle_0_l": [
|
||||||
|
"middlefinger0l", "middle_0l", "lmiddle0", "middlemetacarpall", "左中指0"
|
||||||
|
],
|
||||||
|
"middle_1_l": [
|
||||||
|
"middlefinger1l", "middle_1l", "lmiddle1", "middleproximall",
|
||||||
|
"valvebipedbip01lfinger2", "左中指1"
|
||||||
|
],
|
||||||
|
"middle_2_l": [
|
||||||
|
"middlefinger2l", "middle_2l", "lmiddle2", "middleintermediatel",
|
||||||
|
"valvebipedbip01lfinger21", "左中指2"
|
||||||
|
],
|
||||||
|
"middle_3_l": [
|
||||||
|
"middlefinger3l", "middle_3l", "lmiddle3", "middledistall",
|
||||||
|
"valvebipedbip01lfinger22", "左中指3"
|
||||||
|
],
|
||||||
|
"index_0_l": [
|
||||||
|
"indexfinger0l", "index0l", "lindex0", "indexmetacarpall", "左人差指0"
|
||||||
|
],
|
||||||
|
"index_1_l": [
|
||||||
|
"indexfinger1l", "index1l", "lindex1", "indexproximall",
|
||||||
|
"valvebipedbip01lfinger1", "左人差指1"
|
||||||
|
],
|
||||||
|
"index_2_l": [
|
||||||
|
"indexfinger2l", "index2l", "lindex2", "indexintermediatel",
|
||||||
|
"valvebipedbip01lfinger11", "左人差指2"
|
||||||
|
],
|
||||||
|
"index_3_l": [
|
||||||
|
"indexfinger3l", "index3l", "lindex3", "indexdistall",
|
||||||
|
"valvebipedbip01lfinger12", "左人差指3"
|
||||||
|
],
|
||||||
|
"thumb_0_l": [
|
||||||
|
"thumb0l", "lthumb0", "thumbmetacarpall", "左親指0"
|
||||||
|
],
|
||||||
|
"thumb_1_l": [
|
||||||
|
"thumb1l", "lthumb1", "thumbproximall", "valvebipedbip01lfinger0", "左親指1"
|
||||||
|
],
|
||||||
|
"thumb_2_l": [
|
||||||
|
"thumb2l", "lthumb2", "thumbintermediatel", "valvebipedbip01lfinger01", "左親指2"
|
||||||
|
],
|
||||||
|
"thumb_3_l": [
|
||||||
|
"thumb3l", "lthumb3", "thumbdistall", "valvebipedbip01lfinger02", "左親指3"
|
||||||
|
],
|
||||||
|
"left_leg": [
|
||||||
|
"leftleg", "legl", "lleg", "upperlegl", "lupperleg", "thighl",
|
||||||
|
"leftupperleg", "uplegl", "lupleg", "valvebipedbip01lthigh",
|
||||||
|
"左足", "足.l", "ik_足.l"
|
||||||
|
],
|
||||||
|
"left_knee": [
|
||||||
|
"leftknee", "kneel", "lknee", "lowerlegl", "leftlowerleg",
|
||||||
|
"llowerleg", "lowlegl", "llowleg", "calfl", "lcalf",
|
||||||
|
"valvebipedbip01lcalf", "左ひざ", "ひざ.l", "すね.l", "ik_ひざ.l"
|
||||||
|
],
|
||||||
|
"left_ankle": [
|
||||||
|
"leftankle", "anklel", "lankle", "leftfoot", "footl", "lfoot",
|
||||||
|
"leftfeet", "feetleft", "lfeet", "feetl", "valvebipedbip01lfoot",
|
||||||
|
"左足首", "足首.l", "ik_足首.l"
|
||||||
|
],
|
||||||
|
"left_toe": [
|
||||||
|
"lefttoe", "toeleft", "toel", "ltoe", "toesl", "ltoes",
|
||||||
|
"valvebipedbip01ltoe0", "左つま先", "つま先.l", "ik_つま先.l"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Central bones
|
||||||
|
"hips": [
|
||||||
|
"pelvis", "hips", "hip", "valvebipedbip01pelvis", "腰", "ik_腰"
|
||||||
|
],
|
||||||
|
"spine": [
|
||||||
|
"torso", "spine", "valvebipedbip01spine", "脊椎", "ik_脊椎"
|
||||||
|
],
|
||||||
|
"chest": [
|
||||||
|
"chest", "valvebipedbip01spine1", "胸", "ik_胸"
|
||||||
|
],
|
||||||
|
"upper_chest": [
|
||||||
|
"upperchest", "valvebipedbip01spine4", "上胸", "ik_上胸"
|
||||||
|
],
|
||||||
|
"neck": [
|
||||||
|
"neck", "valvebipedbip01neck1", "首", "ik_首"
|
||||||
|
],
|
||||||
|
"head": [
|
||||||
|
"head", "valvebipedbip01head1", "頭", "ik_頭"
|
||||||
|
],
|
||||||
|
"left_eye": [
|
||||||
|
"eyeleft", "lefteye", "eyel", "leye", "左目", "ik_左目"
|
||||||
|
],
|
||||||
|
"right_eye": [
|
||||||
|
"eyeright", "righteye", "eyer", "reye", "右目", "ik_右目"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
# Add VRM bone name variations
|
# Add VRM bone name variations
|
||||||
bone_names.update({
|
bone_names.update({
|
||||||
|
|||||||
+439
-148
@@ -8,10 +8,11 @@ from ..core.common import (
|
|||||||
get_active_armature,
|
get_active_armature,
|
||||||
validate_armature,
|
validate_armature,
|
||||||
get_vertex_weights,
|
get_vertex_weights,
|
||||||
transfer_vertex_weights
|
transfer_vertex_weights,
|
||||||
|
get_all_meshes
|
||||||
)
|
)
|
||||||
from ..core.translations import t
|
from ..core.translations import t
|
||||||
from ..core.dictionaries import bone_names
|
from ..core.dictionaries import bone_names, dont_delete_these_main_bones
|
||||||
|
|
||||||
class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
||||||
"""MMD Bone standardization system"""
|
"""MMD Bone standardization system"""
|
||||||
@@ -60,45 +61,21 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
self.report({'ERROR'}, str(e))
|
self.report({'ERROR'}, str(e))
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
def standardize_armature(self) -> Tuple[bool, str]:
|
|
||||||
"""Main standardization process"""
|
|
||||||
if not self.armature:
|
|
||||||
return False, t("MMD.no_armature")
|
|
||||||
|
|
||||||
try:
|
|
||||||
with ProgressTracker(self.context, 5, "MMD Standardization") as progress:
|
|
||||||
# Step 1: Process bone names
|
|
||||||
self.process_bone_names()
|
|
||||||
progress.step("Processed bone names")
|
|
||||||
|
|
||||||
# Step 2: Fix bone structure
|
|
||||||
self.fix_bone_structure()
|
|
||||||
progress.step("Fixed bone structure")
|
|
||||||
|
|
||||||
# Step 3: Process weights
|
|
||||||
self.process_weights()
|
|
||||||
progress.step("Processed weights")
|
|
||||||
|
|
||||||
# Step 4: Clean up
|
|
||||||
self.cleanup_armature()
|
|
||||||
progress.step("Cleaned up armature")
|
|
||||||
|
|
||||||
# Step 5: Final validation
|
|
||||||
self.validate_results()
|
|
||||||
progress.step("Validated results")
|
|
||||||
|
|
||||||
return True, t("MMD.standardization_complete")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"MMD Standardization failed: {str(e)}")
|
|
||||||
return False, str(e)
|
|
||||||
|
|
||||||
def process_bone_names(self, context: Context) -> None:
|
def process_bone_names(self, context: Context) -> None:
|
||||||
"""Process and standardize bone names"""
|
"""Process and standardize bone names"""
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
edit_bones = self.armature.data.edit_bones
|
edit_bones = self.armature.data.edit_bones
|
||||||
|
|
||||||
|
# First pass - handle IK bones
|
||||||
|
ik_bones = [bone for bone in edit_bones if 'IK' in bone.name or 'IK' in bone.name]
|
||||||
|
for bone in ik_bones:
|
||||||
|
new_name = f"ik_{self.standardize_bone_name(bone.name.replace('IK', '').replace('IK', ''))}"
|
||||||
|
self.bone_mapping[bone.name] = new_name
|
||||||
|
bone.name = new_name
|
||||||
|
|
||||||
|
# Second pass - standard bones
|
||||||
for bone in edit_bones:
|
for bone in edit_bones:
|
||||||
|
if bone not in ik_bones:
|
||||||
new_name = self.standardize_bone_name(bone.name)
|
new_name = self.standardize_bone_name(bone.name)
|
||||||
if new_name != bone.name:
|
if new_name != bone.name:
|
||||||
self.bone_mapping[bone.name] = new_name
|
self.bone_mapping[bone.name] = new_name
|
||||||
@@ -106,52 +83,39 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
|
|
||||||
def translate_japanese_bone_name(self, name: str) -> str:
|
def translate_japanese_bone_name(self, name: str) -> str:
|
||||||
"""Translate Japanese bone names to English standardized names"""
|
"""Translate Japanese bone names to English standardized names"""
|
||||||
from ..core.dictionaries import bone_names
|
|
||||||
|
|
||||||
# Convert to lowercase for matching
|
|
||||||
name_lower = name.lower()
|
name_lower = name.lower()
|
||||||
|
|
||||||
# Check each bone category for Japanese character matches
|
|
||||||
for bone_category, variations in bone_names.items():
|
for bone_category, variations in bone_names.items():
|
||||||
for variation in variations:
|
for variation in variations:
|
||||||
if variation in name_lower:
|
if variation in name_lower:
|
||||||
# If Japanese characters are found, return the standardized name
|
|
||||||
return bone_category
|
return bone_category
|
||||||
|
|
||||||
# If no match found, return original name
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def standardize_bone_name(self, name: str) -> str:
|
def standardize_bone_name(self, name: str) -> str:
|
||||||
"""Standardize individual bone names"""
|
"""Standardize individual bone names"""
|
||||||
# First translate Japanese names
|
|
||||||
result = self.translate_japanese_bone_name(name)
|
result = self.translate_japanese_bone_name(name)
|
||||||
|
|
||||||
# Remove common prefixes
|
|
||||||
prefixes = ['ValveBiped_', 'Bip01_', 'MMD_', 'Armature|']
|
prefixes = ['ValveBiped_', 'Bip01_', 'MMD_', 'Armature|']
|
||||||
for prefix in prefixes:
|
for prefix in prefixes:
|
||||||
if result.lower().startswith(prefix.lower()):
|
if result.lower().startswith(prefix.lower()):
|
||||||
result = result[len(prefix):]
|
result = result[len(prefix):]
|
||||||
|
|
||||||
# Handle left/right conventions
|
|
||||||
if result.endswith('_L') or result.endswith('.L'):
|
if result.endswith('_L') or result.endswith('.L'):
|
||||||
result = f"{result[:-2]}.L"
|
result = f"{result[:-2]}.L"
|
||||||
elif result.endswith('_R') or result.endswith('.R'):
|
elif result.endswith('_R') or result.endswith('.R'):
|
||||||
result = f"{result[:-2]}.R"
|
result = f"{result[:-2]}.R"
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
return result
|
||||||
|
|
||||||
def fix_bone_structure(self, context: Context) -> None:
|
def fix_bone_structure(self, context: Context) -> None:
|
||||||
"""Fix bone hierarchy and orientations"""
|
"""Fix bone hierarchy and orientations"""
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
edit_bones = self.armature.data.edit_bones
|
edit_bones = self.armature.data.edit_bones
|
||||||
|
|
||||||
# Process spine hierarchy
|
|
||||||
self.process_spine_chain(context)
|
self.process_spine_chain(context)
|
||||||
|
|
||||||
# Fix bone orientations
|
|
||||||
self.fix_bone_orientations(context)
|
self.fix_bone_orientations(context)
|
||||||
|
|
||||||
# Connect appropriate bones
|
|
||||||
self.connect_bones(context)
|
self.connect_bones(context)
|
||||||
|
|
||||||
def process_weights(self, context: Context) -> None:
|
def process_weights(self, context: Context) -> None:
|
||||||
@@ -167,13 +131,8 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
|
|
||||||
def cleanup_armature(self, context: Context) -> None:
|
def cleanup_armature(self, context: Context) -> None:
|
||||||
"""Perform final cleanup operations"""
|
"""Perform final cleanup operations"""
|
||||||
# Remove unused bones
|
|
||||||
self.remove_unused_bones(context)
|
self.remove_unused_bones(context)
|
||||||
|
|
||||||
# Clean up constraints
|
|
||||||
self.cleanup_constraints(context)
|
self.cleanup_constraints(context)
|
||||||
|
|
||||||
# Fix zero-length bones
|
|
||||||
self.fix_zero_length_bones(context)
|
self.fix_zero_length_bones(context)
|
||||||
|
|
||||||
def get_associated_meshes(self, context: Context) -> List[Object]:
|
def get_associated_meshes(self, context: Context) -> List[Object]:
|
||||||
@@ -184,6 +143,7 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
|
|
||||||
def process_spine_chain(self, context: Context) -> None:
|
def process_spine_chain(self, context: Context) -> None:
|
||||||
"""Process and fix spine bone chain hierarchy"""
|
"""Process and fix spine bone chain hierarchy"""
|
||||||
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
edit_bones = self.armature.data.edit_bones
|
edit_bones = self.armature.data.edit_bones
|
||||||
spine_bones = {
|
spine_bones = {
|
||||||
'hips': None,
|
'hips': None,
|
||||||
@@ -220,7 +180,29 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
"""Fix bone orientations for standard pose compatibility"""
|
"""Fix bone orientations for standard pose compatibility"""
|
||||||
edit_bones = self.armature.data.edit_bones
|
edit_bones = self.armature.data.edit_bones
|
||||||
|
|
||||||
# Process arm bones
|
# Define standardized roll values for key bones
|
||||||
|
roll_values = {
|
||||||
|
'upper_arm.L': -0.1,
|
||||||
|
'upper_arm.R': 0.1,
|
||||||
|
'forearm.L': -0.1,
|
||||||
|
'forearm.R': 0.1,
|
||||||
|
'thigh.L': 0.0,
|
||||||
|
'thigh.R': 0.0,
|
||||||
|
'shin.L': 0.0,
|
||||||
|
'shin.R': 0.0,
|
||||||
|
'foot.L': 0.0,
|
||||||
|
'foot.R': 0.0,
|
||||||
|
'spine': 0.0,
|
||||||
|
'chest': 0.0,
|
||||||
|
'neck': 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Apply roll corrections
|
||||||
|
for bone in edit_bones:
|
||||||
|
if bone.name.lower() in roll_values:
|
||||||
|
bone.roll = roll_values[bone.name.lower()]
|
||||||
|
|
||||||
|
# Process arm chains
|
||||||
arm_pairs = [
|
arm_pairs = [
|
||||||
('upper_arm', 'forearm'),
|
('upper_arm', 'forearm'),
|
||||||
('forearm', 'hand')
|
('forearm', 'hand')
|
||||||
@@ -235,7 +217,7 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
child_bone.use_connect = True
|
child_bone.use_connect = True
|
||||||
child_bone.use_inherit_rotation = True
|
child_bone.use_inherit_rotation = True
|
||||||
|
|
||||||
# Process leg bones
|
# Process leg chains
|
||||||
leg_pairs = [
|
leg_pairs = [
|
||||||
('thigh', 'shin'),
|
('thigh', 'shin'),
|
||||||
('shin', 'foot')
|
('shin', 'foot')
|
||||||
@@ -250,6 +232,12 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
child_bone.use_connect = True
|
child_bone.use_connect = True
|
||||||
child_bone.use_inherit_rotation = True
|
child_bone.use_inherit_rotation = True
|
||||||
|
|
||||||
|
# Align twist bones if present
|
||||||
|
twist_bones = [b for b in edit_bones if 'twist' in b.name.lower()]
|
||||||
|
for twist_bone in twist_bones:
|
||||||
|
if twist_bone.parent:
|
||||||
|
twist_bone.roll = twist_bone.parent.roll
|
||||||
|
|
||||||
def remove_unused_bones(self, context: Context) -> None:
|
def remove_unused_bones(self, context: Context) -> None:
|
||||||
"""Remove unused and unnecessary bones from the armature"""
|
"""Remove unused and unnecessary bones from the armature"""
|
||||||
bpy.ops.object.mode_set(mode='EDIT')
|
bpy.ops.object.mode_set(mode='EDIT')
|
||||||
@@ -261,28 +249,30 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
for group in mesh.vertex_groups:
|
for group in mesh.vertex_groups:
|
||||||
used_bones.add(group.name)
|
used_bones.add(group.name)
|
||||||
|
|
||||||
# Get list of bones to keep based on settings
|
# Get list of essential bones to always keep
|
||||||
toolkit = context.scene.avatar_toolkit
|
essential_bones = {
|
||||||
keep_upper_chest = toolkit.keep_upper_chest
|
'hips', 'spine', 'chest', 'upper_chest', 'neck', 'head',
|
||||||
keep_twist = toolkit.keep_twist_bones
|
'left_leg', 'right_leg', 'left_knee', 'right_knee',
|
||||||
|
'left_ankle', 'right_ankle', 'left_toe', 'right_toe'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add any additional bones you want to preserve
|
||||||
|
essential_bones.update(dont_delete_these_main_bones)
|
||||||
|
|
||||||
# Remove unused bones
|
# Remove unused bones
|
||||||
for bone in edit_bones:
|
for bone in edit_bones:
|
||||||
|
# Skip if bone is essential
|
||||||
|
if bone.name.lower() in essential_bones:
|
||||||
|
continue
|
||||||
|
|
||||||
# Skip if bone has weights
|
# Skip if bone has weights
|
||||||
if bone.name in used_bones:
|
if bone.name in used_bones:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip if bone is upper chest and we want to keep it
|
|
||||||
if 'upper_chest' in bone.name.lower() and keep_upper_chest:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Skip if bone is twist bone and we want to keep them
|
|
||||||
if 'twist' in bone.name.lower() and keep_twist:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Remove the bone
|
# Remove the bone
|
||||||
edit_bones.remove(bone)
|
edit_bones.remove(bone)
|
||||||
|
|
||||||
|
|
||||||
def connect_bones(self, context: Context) -> None:
|
def connect_bones(self, context: Context) -> None:
|
||||||
"""Connect bones that should be connected in the hierarchy"""
|
"""Connect bones that should be connected in the hierarchy"""
|
||||||
edit_bones = self.armature.data.edit_bones
|
edit_bones = self.armature.data.edit_bones
|
||||||
@@ -308,21 +298,16 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
"""Clean up vertex groups by removing zero weights and merging similar groups"""
|
"""Clean up vertex groups by removing zero weights and merging similar groups"""
|
||||||
threshold = context.scene.avatar_toolkit.merge_weights_threshold
|
threshold = context.scene.avatar_toolkit.merge_weights_threshold
|
||||||
|
|
||||||
# Get list of vertex groups
|
|
||||||
vertex_groups = mesh_obj.vertex_groups
|
vertex_groups = mesh_obj.vertex_groups
|
||||||
|
|
||||||
# Track groups to remove
|
|
||||||
groups_to_remove = set()
|
groups_to_remove = set()
|
||||||
|
|
||||||
# Check each vertex group
|
|
||||||
for group in vertex_groups:
|
for group in vertex_groups:
|
||||||
weights = get_vertex_weights(mesh_obj, group.name)
|
weights = get_vertex_weights(mesh_obj, group.name)
|
||||||
|
|
||||||
# If no weights above threshold, mark for removal
|
|
||||||
if not any(weight > threshold for weight in weights.values()):
|
if not any(weight > threshold for weight in weights.values()):
|
||||||
groups_to_remove.add(group.name)
|
groups_to_remove.add(group.name)
|
||||||
|
|
||||||
# Remove empty groups
|
|
||||||
for group_name in groups_to_remove:
|
for group_name in groups_to_remove:
|
||||||
group = vertex_groups.get(group_name)
|
group = vertex_groups.get(group_name)
|
||||||
if group:
|
if group:
|
||||||
@@ -335,41 +320,11 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
raise ValueError("\n".join(messages))
|
raise ValueError("\n".join(messages))
|
||||||
|
|
||||||
def cleanup_constraints(self, context: Context) -> None:
|
def cleanup_constraints(self, context: Context) -> None:
|
||||||
"""Clean up and fix bone constraints"""
|
"""Remove all constraints from the armature."""
|
||||||
bpy.ops.object.mode_set(mode='POSE')
|
bpy.ops.object.mode_set(mode='POSE')
|
||||||
|
|
||||||
# Process each pose bone
|
|
||||||
for pose_bone in self.armature.pose.bones:
|
for pose_bone in self.armature.pose.bones:
|
||||||
constraints_to_remove = []
|
constraints_to_remove = [constraint for constraint in pose_bone.constraints]
|
||||||
|
|
||||||
for constraint in pose_bone.constraints:
|
|
||||||
should_remove = False
|
|
||||||
|
|
||||||
# Handle IK constraints
|
|
||||||
if constraint.type == 'IK':
|
|
||||||
if not constraint.target or constraint.target != self.armature:
|
|
||||||
should_remove = True
|
|
||||||
elif not constraint.subtarget or constraint.subtarget not in self.armature.data.bones:
|
|
||||||
should_remove = True
|
|
||||||
|
|
||||||
# Handle MMD additional rotation constraints
|
|
||||||
elif constraint.name == 'mmd_additional_rotation':
|
|
||||||
if not constraint.target or constraint.target != self.armature:
|
|
||||||
should_remove = True
|
|
||||||
elif not constraint.subtarget or constraint.subtarget not in self.armature.data.bones:
|
|
||||||
should_remove = True
|
|
||||||
|
|
||||||
# Handle transformation constraints
|
|
||||||
elif constraint.type in {'COPY_ROTATION', 'COPY_LOCATION', 'COPY_TRANSFORMS'}:
|
|
||||||
if not constraint.target or constraint.target != self.armature:
|
|
||||||
should_remove = True
|
|
||||||
elif not constraint.subtarget or constraint.subtarget not in self.armature.data.bones:
|
|
||||||
should_remove = True
|
|
||||||
|
|
||||||
if should_remove:
|
|
||||||
constraints_to_remove.append(constraint)
|
|
||||||
|
|
||||||
# Remove invalid constraints
|
|
||||||
for constraint in constraints_to_remove:
|
for constraint in constraints_to_remove:
|
||||||
pose_bone.constraints.remove(constraint)
|
pose_bone.constraints.remove(constraint)
|
||||||
|
|
||||||
@@ -381,59 +336,17 @@ class AVATAR_TOOLKIT_OT_StandardizeMmd(Operator):
|
|||||||
min_length = 0.01 # Minimum bone length in Blender units
|
min_length = 0.01 # Minimum bone length in Blender units
|
||||||
|
|
||||||
for bone in edit_bones:
|
for bone in edit_bones:
|
||||||
# Calculate bone length
|
|
||||||
bone_length = (bone.tail - bone.head).length
|
bone_length = (bone.tail - bone.head).length
|
||||||
|
|
||||||
if bone_length < min_length:
|
if bone_length < min_length:
|
||||||
# Set minimal length while preserving direction
|
|
||||||
if bone.parent:
|
if bone.parent:
|
||||||
# Use parent's orientation as reference
|
|
||||||
direction = bone.parent.tail - bone.parent.head
|
direction = bone.parent.tail - bone.parent.head
|
||||||
direction.normalize()
|
direction.normalize()
|
||||||
else:
|
else:
|
||||||
# Default to Z-axis if no parent
|
direction = Vector((0, 0, 1))
|
||||||
direction = mathutils.Vector((0, 0, 1))
|
|
||||||
|
|
||||||
bone.tail = bone.head + (direction * min_length)
|
bone.tail = bone.head + (direction * min_length)
|
||||||
|
|
||||||
class FixUnmovableBonesOperator(bpy.types.Operator):
|
|
||||||
bl_idname = "avatar_toolkit.fix_unmovable_bones"
|
|
||||||
bl_label = t("MMD.fix_unmovable_bones")
|
|
||||||
bl_description = t("MMD.fix_unmovable_bones_desc")
|
|
||||||
bl_options = {'REGISTER', 'UNDO'}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
armature = get_active_armature(context)
|
|
||||||
return armature is not None and armature.type == 'ARMATURE'
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
armature = get_active_armature(context)
|
|
||||||
if not armature:
|
|
||||||
self.report({'ERROR'}, t("MMD.no_armature"))
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
try:
|
|
||||||
with ProgressTracker(context, 2, "Unlocking Transforms") as progress:
|
|
||||||
# Unlock armature transforms
|
|
||||||
progress.step("Unlocking armature transforms")
|
|
||||||
for attr in ('location', 'rotation', 'scale'):
|
|
||||||
for i in range(3):
|
|
||||||
setattr(armature, f"lock_{attr}", [False] * 3)
|
|
||||||
|
|
||||||
# Unlock bone transforms
|
|
||||||
progress.step("Unlocking bone transforms")
|
|
||||||
for bone in armature.pose.bones:
|
|
||||||
for attr in ('location', 'rotation', 'scale'):
|
|
||||||
setattr(bone, f"lock_{attr}", [False] * 3)
|
|
||||||
|
|
||||||
self.report({'INFO'}, t("MMD.transforms_unlocked"))
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error unlocking transforms: {str(e)}")
|
|
||||||
self.report({'ERROR'}, str(e))
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
class ReparentMeshesOperator(bpy.types.Operator):
|
class ReparentMeshesOperator(bpy.types.Operator):
|
||||||
bl_idname = "avatar_toolkit.reparent_meshes"
|
bl_idname = "avatar_toolkit.reparent_meshes"
|
||||||
@@ -499,3 +412,381 @@ class ReparentMeshesOperator(bpy.types.Operator):
|
|||||||
mesh.parent = armature
|
mesh.parent = armature
|
||||||
if not mesh.parent_type == 'ARMATURE':
|
if not mesh.parent_type == 'ARMATURE':
|
||||||
mesh.parent_type = 'ARMATURE'
|
mesh.parent_type = 'ARMATURE'
|
||||||
|
|
||||||
|
class AVATAR_TOOLKIT_OT_ConvertMmdMorphs(Operator):
|
||||||
|
"""Convert MMD morph data to shape keys"""
|
||||||
|
bl_idname = "avatar_toolkit.convert_mmd_morphs"
|
||||||
|
bl_label = t("MMD.convert_morphs")
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
armature = get_active_armature(context)
|
||||||
|
return armature is not None and get_all_meshes(context)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
armature = get_active_armature(context)
|
||||||
|
if not armature:
|
||||||
|
self.report({'ERROR'}, t("MMD.no_armature"))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with ProgressTracker(context, 3, "Converting MMD Morphs") as progress:
|
||||||
|
# Convert bone morphs to shape keys
|
||||||
|
if hasattr(armature, 'mmd_root') and armature.mmd_root.bone_morphs:
|
||||||
|
self.process_bone_morphs(context, armature, progress)
|
||||||
|
|
||||||
|
progress.step("Processed bone morphs")
|
||||||
|
|
||||||
|
# Clean up unused data
|
||||||
|
self.cleanup_unused_data(context)
|
||||||
|
progress.step("Cleaned up data")
|
||||||
|
|
||||||
|
# Validate results
|
||||||
|
self.validate_results(context)
|
||||||
|
progress.step("Validated results")
|
||||||
|
|
||||||
|
self.report({'INFO'}, t("MMD.conversion_complete"))
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error converting MMD morphs: {str(e)}")
|
||||||
|
self.report({'ERROR'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
def process_bone_morphs(self, context, armature, progress):
|
||||||
|
"""Process bone morphs into shape keys"""
|
||||||
|
for morph in armature.mmd_root.bone_morphs:
|
||||||
|
for mesh in get_all_meshes(context):
|
||||||
|
# Create armature modifier
|
||||||
|
mod = mesh.modifiers.new(morph.name, 'ARMATURE')
|
||||||
|
mod.object = armature
|
||||||
|
|
||||||
|
# Apply as shape key
|
||||||
|
with context.temp_override(object=mesh):
|
||||||
|
bpy.ops.object.modifier_apply(modifier=mod.name)
|
||||||
|
|
||||||
|
class AVATAR_TOOLKIT_OT_CleanupMmdModel(Operator):
|
||||||
|
"""Clean up MMD model by removing unused data and fixing display settings"""
|
||||||
|
bl_idname = "avatar_toolkit.cleanup_mmd"
|
||||||
|
bl_label = t("MMD.cleanup")
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
armature = get_active_armature(context)
|
||||||
|
if not armature:
|
||||||
|
self.report({'ERROR'}, t("MMD.no_armature"))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with ProgressTracker(context, 4, "Cleaning MMD Model") as progress:
|
||||||
|
# Remove rigid bodies and joints
|
||||||
|
self.remove_physics_objects(armature)
|
||||||
|
progress.step("Removed physics objects")
|
||||||
|
|
||||||
|
# Clean up collections and hierarchy
|
||||||
|
self.cleanup_hierarchy(context, armature)
|
||||||
|
progress.step("Cleaned hierarchy")
|
||||||
|
|
||||||
|
# Fix viewport settings
|
||||||
|
self.fix_viewport_settings(context)
|
||||||
|
progress.step("Fixed viewport")
|
||||||
|
|
||||||
|
# Final cleanup
|
||||||
|
clear_unused_data_blocks()
|
||||||
|
progress.step("Cleared unused data")
|
||||||
|
|
||||||
|
self.report({'INFO'}, t("MMD.cleanup_complete"))
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error cleaning MMD model: {str(e)}")
|
||||||
|
self.report({'ERROR'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
def remove_physics_objects(self, armature):
|
||||||
|
"""Remove physics-related objects"""
|
||||||
|
to_delete = []
|
||||||
|
for child in armature.children:
|
||||||
|
if any(x in child.name.lower() for x in ['rigidbodies', 'joints', 'physics']):
|
||||||
|
to_delete.append(child)
|
||||||
|
|
||||||
|
for obj in to_delete:
|
||||||
|
bpy.data.objects.remove(obj, do_unlink=True)
|
||||||
|
|
||||||
|
def cleanup_hierarchy(self, context, armature):
|
||||||
|
"""Clean up object hierarchy and collections"""
|
||||||
|
meshes = get_all_meshes(context)
|
||||||
|
for mesh in meshes:
|
||||||
|
# Ensure proper parenting
|
||||||
|
mesh.parent = armature
|
||||||
|
mesh.parent_type = 'ARMATURE'
|
||||||
|
|
||||||
|
# Clean up collections
|
||||||
|
for col in mesh.users_collection:
|
||||||
|
if col != context.scene.collection:
|
||||||
|
col.objects.unlink(mesh)
|
||||||
|
|
||||||
|
if mesh.name not in context.scene.collection.objects:
|
||||||
|
context.scene.collection.objects.link(mesh)
|
||||||
|
|
||||||
|
def fix_viewport_settings(self, context):
|
||||||
|
"""Fix viewport display settings"""
|
||||||
|
# Set armature display
|
||||||
|
armature = get_active_armature(context)
|
||||||
|
armature.data.display_type = 'OCTAHEDRAL'
|
||||||
|
armature.show_in_front = True
|
||||||
|
|
||||||
|
# Set viewport shading
|
||||||
|
for area in context.screen.areas:
|
||||||
|
if area.type == 'VIEW_3D':
|
||||||
|
space = area.spaces[0]
|
||||||
|
space.shading.type = 'MATERIAL'
|
||||||
|
space.clip_start = 0.01
|
||||||
|
space.clip_end = 300
|
||||||
|
|
||||||
|
class AVATAR_TOOLKIT_OT_FixMeshes(Operator):
|
||||||
|
"""Clean up and optimize mesh materials, shading, and shape keys"""
|
||||||
|
bl_idname = "avatar_toolkit.fix_meshes"
|
||||||
|
bl_label = t("Optimization.fix_meshes")
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
armature = get_active_armature(context)
|
||||||
|
return armature is not None and get_all_meshes(context)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
try:
|
||||||
|
meshes = get_all_meshes(context)
|
||||||
|
if not meshes:
|
||||||
|
self.report({'ERROR'}, t("Optimization.no_meshes"))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
with ProgressTracker(context, len(meshes), "Fixing Meshes") as progress:
|
||||||
|
for mesh in meshes:
|
||||||
|
self.process_mesh(context, mesh)
|
||||||
|
progress.step(f"Processed {mesh.name}")
|
||||||
|
|
||||||
|
self.report({'INFO'}, t("Optimization.meshes_fixed"))
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fixing meshes: {str(e)}")
|
||||||
|
self.report({'ERROR'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
def process_mesh(self, context: Context, mesh: Object) -> None:
|
||||||
|
"""Process and fix individual mesh"""
|
||||||
|
# Unlock transforms
|
||||||
|
for i in range(3):
|
||||||
|
mesh.lock_location[i] = False
|
||||||
|
mesh.lock_rotation[i] = False
|
||||||
|
mesh.lock_scale[i] = False
|
||||||
|
|
||||||
|
# Process shape keys
|
||||||
|
if mesh.data.shape_keys:
|
||||||
|
self.fix_shape_keys(mesh)
|
||||||
|
|
||||||
|
# Process materials
|
||||||
|
self.fix_materials(context, mesh)
|
||||||
|
|
||||||
|
def fix_shape_keys(self, mesh: Object) -> None:
|
||||||
|
"""Fix and clean up shape keys"""
|
||||||
|
if not mesh.data.shape_keys:
|
||||||
|
return
|
||||||
|
|
||||||
|
shape_keys = mesh.data.shape_keys.key_blocks
|
||||||
|
|
||||||
|
# Rename basis
|
||||||
|
if shape_keys[0].name != "Basis":
|
||||||
|
shape_keys[0].name = "Basis"
|
||||||
|
|
||||||
|
# Clean up names
|
||||||
|
for key in shape_keys:
|
||||||
|
# Remove common prefixes/suffixes
|
||||||
|
clean_name = key.name
|
||||||
|
for prefix in ['Face.M F00 000 Fcl ', 'Face.M F00 000 00 Fcl ']:
|
||||||
|
clean_name = clean_name.replace(prefix, '')
|
||||||
|
|
||||||
|
# Replace underscores with spaces
|
||||||
|
clean_name = clean_name.replace('_', ' ')
|
||||||
|
key.name = clean_name
|
||||||
|
|
||||||
|
# Sort shape keys by category
|
||||||
|
categories = ['MTH', 'EYE', 'BRW', 'ALL']
|
||||||
|
|
||||||
|
# Create sorted list of shape key names
|
||||||
|
ordered_names = []
|
||||||
|
|
||||||
|
# Add categorized keys first
|
||||||
|
for category in categories:
|
||||||
|
category_keys = [key.name for key in shape_keys if key.name.startswith(category)]
|
||||||
|
ordered_names.extend(sorted(category_keys))
|
||||||
|
|
||||||
|
# Add remaining keys
|
||||||
|
remaining = [key.name for key in shape_keys if not any(key.name.startswith(c) for c in categories)]
|
||||||
|
ordered_names.extend(sorted(remaining))
|
||||||
|
|
||||||
|
# Reorder using context override
|
||||||
|
with bpy.context.temp_override(active_object=mesh, selected_objects=[mesh]):
|
||||||
|
for idx, name in enumerate(ordered_names):
|
||||||
|
mesh.active_shape_key_index = shape_keys.find(name)
|
||||||
|
while mesh.active_shape_key_index > idx:
|
||||||
|
bpy.ops.object.shape_key_move(type='UP')
|
||||||
|
|
||||||
|
|
||||||
|
def fix_materials(self, context: Context, mesh: Object) -> None:
|
||||||
|
"""Fix and optimize materials"""
|
||||||
|
for slot in mesh.material_slots:
|
||||||
|
if not slot.material:
|
||||||
|
continue
|
||||||
|
|
||||||
|
material = slot.material
|
||||||
|
|
||||||
|
# Set up basic material properties
|
||||||
|
material.use_backface_culling = True
|
||||||
|
material.blend_method = 'HASHED'
|
||||||
|
material.shadow_method = 'HASHED'
|
||||||
|
|
||||||
|
# Clean up material name
|
||||||
|
material.name = self.clean_material_name(material.name)
|
||||||
|
|
||||||
|
# Consolidate similar materials
|
||||||
|
for other_slot in mesh.material_slots:
|
||||||
|
if other_slot.material and other_slot.material != material:
|
||||||
|
if materials_match(material, other_slot.material):
|
||||||
|
other_slot.material = material
|
||||||
|
|
||||||
|
def clean_material_name(self, name: str) -> str:
|
||||||
|
"""Clean up material name"""
|
||||||
|
# Remove common prefixes/suffixes
|
||||||
|
prefixes = ['material', 'mat', 'mtl', 'material.']
|
||||||
|
for prefix in prefixes:
|
||||||
|
if name.lower().startswith(prefix):
|
||||||
|
name = name[len(prefix):]
|
||||||
|
|
||||||
|
# Remove numbers at end
|
||||||
|
while name and name[-1].isdigit():
|
||||||
|
name = name[:-1]
|
||||||
|
|
||||||
|
return name.strip()
|
||||||
|
|
||||||
|
class AVATAR_TOOLKIT_OT_ValidateMeshes(Operator):
|
||||||
|
"""Validate meshes and UV maps for common issues"""
|
||||||
|
bl_idname = "avatar_toolkit.validate_meshes"
|
||||||
|
bl_label = t("Validation.check_meshes")
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
armature = get_active_armature(context)
|
||||||
|
if not armature:
|
||||||
|
self.report({'ERROR'}, t("Validation.no_armature"))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
try:
|
||||||
|
with ProgressTracker(context, 3, "Validating Meshes") as progress:
|
||||||
|
# Check bone hierarchy
|
||||||
|
hierarchy_issues = self.validate_bone_hierarchy(armature)
|
||||||
|
progress.step("Checked bone hierarchy")
|
||||||
|
|
||||||
|
# Check UV coordinates
|
||||||
|
uv_issues = self.validate_uv_maps(context)
|
||||||
|
progress.step("Checked UV maps")
|
||||||
|
|
||||||
|
# Generate report
|
||||||
|
self.generate_validation_report(context, hierarchy_issues, uv_issues)
|
||||||
|
progress.step("Generated report")
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error validating meshes: {str(e)}")
|
||||||
|
self.report({'ERROR'}, str(e))
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
def validate_bone_hierarchy(self, armature: Object) -> List[str]:
|
||||||
|
"""Validate bone hierarchy against standard structure"""
|
||||||
|
issues = []
|
||||||
|
|
||||||
|
# Define expected hierarchy
|
||||||
|
hierarchy = [
|
||||||
|
['hips', 'spine', 'chest', 'neck', 'head'],
|
||||||
|
['hips', 'left_leg', 'left_knee', 'left_ankle'],
|
||||||
|
['hips', 'right_leg', 'right_knee', 'right_ankle'],
|
||||||
|
['chest', 'left_shoulder', 'left_arm', 'left_elbow', 'left_wrist'],
|
||||||
|
['chest', 'right_shoulder', 'right_arm', 'right_elbow', 'right_wrist']
|
||||||
|
]
|
||||||
|
|
||||||
|
for chain in hierarchy:
|
||||||
|
previous = None
|
||||||
|
for bone_name in chain:
|
||||||
|
# Check if bone exists
|
||||||
|
bone = None
|
||||||
|
for alt_name in bone_names[bone_name]:
|
||||||
|
if alt_name in armature.data.bones:
|
||||||
|
bone = armature.data.bones[alt_name]
|
||||||
|
break
|
||||||
|
|
||||||
|
if not bone:
|
||||||
|
issues.append(t("Validation.missing_bone", bone=bone_name))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check parent relationship
|
||||||
|
if previous:
|
||||||
|
if not bone.parent:
|
||||||
|
issues.append(t("Validation.no_parent", bone=bone.name))
|
||||||
|
elif bone.parent.name != previous.name:
|
||||||
|
issues.append(t("Validation.wrong_parent",
|
||||||
|
bone=bone.name,
|
||||||
|
expected=previous.name,
|
||||||
|
actual=bone.parent.name))
|
||||||
|
previous = bone
|
||||||
|
|
||||||
|
return issues
|
||||||
|
|
||||||
|
def validate_uv_maps(self, context: Context) -> Dict[str, int]:
|
||||||
|
"""Check UV maps for issues"""
|
||||||
|
issues = {'nan_coords': 0, 'missing_uvs': 0}
|
||||||
|
|
||||||
|
for mesh in get_all_meshes(context):
|
||||||
|
if not mesh.data.uv_layers:
|
||||||
|
issues['missing_uvs'] += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
for uv_layer in mesh.data.uv_layers:
|
||||||
|
for uv in uv_layer.data:
|
||||||
|
if math.isnan(uv.uv.x):
|
||||||
|
uv.uv.x = 0
|
||||||
|
issues['nan_coords'] += 1
|
||||||
|
if math.isnan(uv.uv.y):
|
||||||
|
uv.uv.y = 0
|
||||||
|
issues['nan_coords'] += 1
|
||||||
|
|
||||||
|
return issues
|
||||||
|
|
||||||
|
def generate_validation_report(self, context: Context,
|
||||||
|
hierarchy_issues: List[str],
|
||||||
|
uv_issues: Dict[str, int]) -> None:
|
||||||
|
"""Generate and display validation report"""
|
||||||
|
report_lines = []
|
||||||
|
|
||||||
|
# Add hierarchy issues
|
||||||
|
if hierarchy_issues:
|
||||||
|
report_lines.append(t("Validation.hierarchy_issues"))
|
||||||
|
report_lines.extend(hierarchy_issues)
|
||||||
|
|
||||||
|
# Add UV issues
|
||||||
|
if uv_issues['nan_coords'] > 0:
|
||||||
|
report_lines.append(t("Validation.uv_nan_coords",
|
||||||
|
count=uv_issues['nan_coords']))
|
||||||
|
|
||||||
|
if uv_issues['missing_uvs'] > 0:
|
||||||
|
report_lines.append(t("Validation.missing_uvs",
|
||||||
|
count=uv_issues['missing_uvs']))
|
||||||
|
|
||||||
|
# Show report
|
||||||
|
if report_lines:
|
||||||
|
self.report({'WARNING'}, "\n".join(report_lines))
|
||||||
|
else:
|
||||||
|
self.report({'INFO'}, t("Validation.no_issues"))
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ class AvatarToolKit_OT_RemoveZeroWeightBones(Operator):
|
|||||||
mesh_data: Mesh = mesh.data
|
mesh_data: Mesh = mesh.data
|
||||||
for vertex in mesh_data.vertices:
|
for vertex in mesh_data.vertices:
|
||||||
for group in vertex.groups:
|
for group in vertex.groups:
|
||||||
if group.weight > context.scene.avatar_toolkit.clean_weights_threshold:
|
if group.weight > context.scene.avatar_toolkit.merge_weights_threshold:
|
||||||
weighted_bones.append(mesh.vertex_groups[group.group].name)
|
weighted_bones.append(mesh.vertex_groups[group.group].name)
|
||||||
|
|
||||||
# Process bone removal
|
# Process bone removal
|
||||||
|
|||||||
+27
-4
@@ -5,7 +5,7 @@ from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
|
|||||||
from ..core.translations import t
|
from ..core.translations import t
|
||||||
|
|
||||||
class AvatarToolKit_PT_MMDPanel(Panel):
|
class AvatarToolKit_PT_MMDPanel(Panel):
|
||||||
"""Panel containing MMD bone standardization tools"""
|
"""Panel containing MMD bone standardization and cleanup tools"""
|
||||||
bl_label = t("MMD.label")
|
bl_label = t("MMD.label")
|
||||||
bl_idname = "OBJECT_PT_avatar_toolkit_mmd"
|
bl_idname = "OBJECT_PT_avatar_toolkit_mmd"
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
@@ -18,6 +18,29 @@ class AvatarToolKit_PT_MMDPanel(Panel):
|
|||||||
layout: UILayout = self.layout
|
layout: UILayout = self.layout
|
||||||
toolkit = context.scene.avatar_toolkit
|
toolkit = context.scene.avatar_toolkit
|
||||||
|
|
||||||
# Add merge twist bones option
|
# Bone Settings Box
|
||||||
layout.prop(toolkit, "keep_twist_bones")
|
bone_box: UILayout = layout.box()
|
||||||
layout.operator("avatar_toolkit.standardize_mmd", icon='BONE_DATA')
|
col: UILayout = bone_box.column(align=True)
|
||||||
|
col.label(text=t("MMD.bone_settings"), icon='BONE_DATA')
|
||||||
|
col.separator(factor=0.5)
|
||||||
|
col.prop(toolkit, "keep_twist_bones")
|
||||||
|
col.prop(toolkit, "keep_upper_chest")
|
||||||
|
col.operator("avatar_toolkit.standardize_mmd", icon='BONE_DATA')
|
||||||
|
|
||||||
|
# Mesh Tools Box
|
||||||
|
mesh_box: UILayout = layout.box()
|
||||||
|
col = mesh_box.column(align=True)
|
||||||
|
col.label(text=t("MMD.mesh_tools"), icon='MESH_DATA')
|
||||||
|
col.separator(factor=0.5)
|
||||||
|
row: UILayout = col.row(align=True)
|
||||||
|
row.operator("avatar_toolkit.fix_meshes", icon='MODIFIER')
|
||||||
|
row.operator("avatar_toolkit.validate_meshes", icon='CHECKMARK')
|
||||||
|
|
||||||
|
# Cleanup Box
|
||||||
|
cleanup_box: UILayout = layout.box()
|
||||||
|
col = cleanup_box.column(align=True)
|
||||||
|
col.label(text=t("MMD.cleanup"), icon='BRUSH_DATA')
|
||||||
|
col.separator(factor=0.5)
|
||||||
|
col.operator("avatar_toolkit.cleanup_mmd", icon='SHADERFX')
|
||||||
|
col.operator("avatar_toolkit.convert_mmd_morphs", icon='SHAPEKEY_DATA')
|
||||||
|
col.operator("avatar_toolkit.reparent_meshes", icon='OUTLINER_OB_ARMATURE')
|
||||||
|
|||||||
Reference in New Issue
Block a user