def main(moreinfo=True): # prompt PMX name core.MY_PRINT_FUNC("Please enter name of PMX input file:") input_filename_pmx = core.MY_FILEPROMPT_FUNC(".pmx") pmx = pmxlib.read_pmx(input_filename_pmx, moreinfo=moreinfo) # detect whether arm ik exists r = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_r + jp_newik) if r is None: r = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_r + jp_newik2) l = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_l + jp_newik) if l is None: l = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_l + jp_newik2) # decide whether to create or remove arm ik if r is None and l is None: # add IK branch core.MY_PRINT_FUNC(">>>> Adding arm IK <<<") # set output name if input_filename_pmx.lower().endswith(pmx_noik_suffix.lower()): output_filename = input_filename_pmx[0:-( len(pmx_noik_suffix))] + pmx_yesik_suffix else: output_filename = input_filename_pmx[0:-4] + pmx_yesik_suffix for side in [jp_l, jp_r]: # first find all 3 arm bones # even if i insert into the list, this will still be a valid reference i think bones = [] bones: List[pmxstruct.PmxBone] for n in [jp_arm, jp_elbow, jp_wrist]: i = core.my_list_search(pmx.bones, lambda x: x.name_jp == side + n, getitem=True) if i is None: core.MY_PRINT_FUNC( "ERROR1: semistandard bone '%s' is missing from the model, unable to create attached arm IK" % (side + n)) raise RuntimeError() bones.append(i) # get parent of arm bone shoulder_idx = bones[0].parent_idx # then do the "remapping" on all existing bone references, to make space for inserting 4 bones # don't delete any bones, just remap them bone_shiftmap = ([shoulder_idx + 1], [-4]) apply_bone_remapping(pmx, [], bone_shiftmap) # new bones will be inserted AFTER shoulder_idx # newarm_idx = shoulder_idx+1 # newelbow_idx = shoulder_idx+2 # newwrist_idx = shoulder_idx+3 # newik_idx = shoulder_idx+4 # make copies of the 3 armchain bones for i, b in enumerate(bones): b: pmxstruct.PmxBone # newarm = b[0:5] + [shoulder_idx + i] + b[6:8] # copy names/pos, parent, copy deform layer # newarm += [1, 0, 0, 0] # rotateable, not translateable, not visible, not enabled(?) # newarm += [1, [shoulder_idx + 2 + i], 0, 0, [], 0, []] # tail type, no inherit, no fixed axis, # newarm += b[19:21] + [0, [], 0, []] # copy local axis, no ext parent, no ik # newarm[0] += jp_ikchainsuffix # add suffix to jp name # newarm[1] += jp_ikchainsuffix # add suffix to en name newarm = pmxstruct.PmxBone( name_jp=b.name_jp + jp_ikchainsuffix, name_en=b.name_en + jp_ikchainsuffix, pos=b.pos, parent_idx=b.parent_idx, deform_layer=b.deform_layer, deform_after_phys=b.deform_after_phys, has_rotate=True, has_translate=False, has_visible=False, has_enabled=True, tail_type=True, tail=shoulder_idx + 2 + i, inherit_rot=False, inherit_trans=False, has_fixedaxis=False, has_localaxis=b.has_localaxis, localaxis_x=b.localaxis_x, localaxis_z=b.localaxis_z, has_externalparent=False, has_ik=False, ) pmx.bones.insert(shoulder_idx + 1 + i, newarm) # then change the existing arm/elbow (not the wrist) to inherit rot from them if i != 2: b.inherit_rot = True b.inherit_parent_idx = shoulder_idx + 1 + i b.inherit_ratio = 1 # copy the wrist to make the IK bone en_suffix = "_L" if side == jp_l else "_R" # get index of "upperbody" to use as parent of hand IK bone ikpar = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_upperbody) if ikpar is None: core.MY_PRINT_FUNC( "ERROR1: semistandard bone '%s' is missing from the model, unable to create attached arm IK" % jp_upperbody) raise RuntimeError() # newik = [side + jp_newik, en_newik + en_suffix] + bones[2][2:5] + [ikpar] # new names, copy pos, new par # newik += bones[2][6:8] + [1, 1, 1, 1] + [0, [0,1,0]] # copy deform layer, rot/trans/vis/en, tail type # newik += [0, 0, [], 0, [], 0, [], 0, []] # no inherit, no fixed axis, no local axis, no ext parent, yes IK # # add the ik info: [is_ik, [target, loops, anglelimit, [[link_idx, []]], [link_idx, []]]] ] ] # newik += [1, [shoulder_idx+3, newik_loops, newik_angle, [[shoulder_idx+2,[]],[shoulder_idx+1,[]]] ] ] newik = pmxstruct.PmxBone( name_jp=side + jp_newik, name_en=en_newik + en_suffix, pos=bones[2].pos, parent_idx=ikpar, deform_layer=bones[2].deform_layer, deform_after_phys=bones[2].deform_after_phys, has_rotate=True, has_translate=True, has_visible=True, has_enabled=True, tail_type=False, tail=[0, 1, 0], inherit_rot=False, inherit_trans=False, has_fixedaxis=False, has_localaxis=False, has_externalparent=False, has_ik=True, ik_target_idx=shoulder_idx + 3, ik_numloops=newik_loops, ik_angle=newik_angle, ik_links=[ pmxstruct.PmxBoneIkLink(idx=shoulder_idx + 2), pmxstruct.PmxBoneIkLink(idx=shoulder_idx + 1) ]) pmx.bones.insert(shoulder_idx + 4, newik) # then add to dispframe # first, does the frame already exist? f = core.my_list_search(pmx.frames, lambda x: x.name_jp == jp_newik, getitem=True) if f is None: # need to create the new dispframe! easy newframe = pmxstruct.PmxFrame(name_jp=jp_newik, name_en=en_newik, is_special=False, items=[[0, shoulder_idx + 4]]) pmx.frames.append(newframe) else: # frame already exists, also easy f.items.append([0, shoulder_idx + 4]) else: # remove IK branch core.MY_PRINT_FUNC(">>>> Removing arm IK <<<") # set output name if input_filename_pmx.lower().endswith(pmx_yesik_suffix.lower()): output_filename = input_filename_pmx[0:-( len(pmx_yesik_suffix))] + pmx_noik_suffix else: output_filename = input_filename_pmx[0:-4] + pmx_noik_suffix # identify all bones in ik chain of hand ik bones bone_dellist = [] for b in [r, l]: bone_dellist.append(b) # this IK bone bone_dellist.append( pmx.bones[b].ik_target_idx) # the target of the bone for v in pmx.bones[b].ik_links: bone_dellist.append(v.idx) # each link along the bone bone_dellist.sort() # build the remap thing bone_shiftmap = delme_list_to_rangemap(bone_dellist) # do the actual delete & shift apply_bone_remapping(pmx, bone_dellist, bone_shiftmap) # delete dispframe for hand ik # first, does the frame already exist? f = core.my_list_search(pmx.frames, lambda x: x.name_jp == jp_newik) if f is not None: # frame already exists, delete it pmx.frames.pop(f) pass # write out output_filename = core.get_unused_file_name(output_filename) pmxlib.write_pmx(output_filename, pmx, moreinfo=moreinfo) core.MY_PRINT_FUNC("Done!") return None
def main(moreinfo=True): # prompt PMX name core.MY_PRINT_FUNC("Please enter name of PMX input file:") input_filename_pmx = core.MY_FILEPROMPT_FUNC(".pmx") pmx = pmxlib.read_pmx(input_filename_pmx, moreinfo=moreinfo) # detect whether arm ik exists r = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_r + jp_newik) if r is None: r = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_r + jp_newik2) l = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_l + jp_newik) if l is None: l = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_l + jp_newik2) # decide whether to create or remove arm ik if r is None and l is None: # add IK branch core.MY_PRINT_FUNC(">>>> Adding arm IK <<<") # set output name if input_filename_pmx.lower().endswith(pmx_noik_suffix.lower()): output_filename = input_filename_pmx[0:-(len(pmx_noik_suffix))] + pmx_yesik_suffix else: output_filename = input_filename_pmx[0:-4] + pmx_yesik_suffix for side in [jp_l, jp_r]: # first find all 3 arm bones # even if i insert into the list, this will still be a valid reference i think bones = [] bones: List[pmxstruct.PmxBone] for n in [jp_arm, jp_elbow, jp_wrist]: i = core.my_list_search(pmx.bones, lambda x: x.name_jp == side + n, getitem=True) if i is None: core.MY_PRINT_FUNC("ERROR1: semistandard bone '%s' is missing from the model, unable to create attached arm IK" % (side + n)) raise RuntimeError() bones.append(i) # get parent of arm bone (shoulder bone), new bones will be inserted after this shoulder_idx = bones[0].parent_idx # new bones will be inserted AFTER shoulder_idx # newarm_idx = shoulder_idx+1 # newelbow_idx = shoulder_idx+2 # newwrist_idx = shoulder_idx+3 # newik_idx = shoulder_idx+4 # make copies of the 3 armchain bones # arm: parent is shoulder newarm = pmxstruct.PmxBone( name_jp=bones[0].name_jp + jp_ikchainsuffix, name_en=bones[0].name_en + jp_ikchainsuffix, pos=bones[0].pos, parent_idx=shoulder_idx, deform_layer=bones[0].deform_layer, deform_after_phys=bones[0].deform_after_phys, has_rotate=True, has_translate=False, has_visible=False, has_enabled=True, has_ik=False, tail_usebonelink=True, tail=0, # want arm tail to point at the elbow, can't set it until elbow is created inherit_rot=False, inherit_trans=False, has_localaxis=bones[0].has_localaxis, localaxis_x=bones[0].localaxis_x, localaxis_z=bones[0].localaxis_z, has_externalparent=False, has_fixedaxis=False, ) insert_single_bone(pmx, newarm, shoulder_idx + 1) # change existing arm to inherit rot from this bones[0].inherit_rot = True bones[0].inherit_parent_idx = shoulder_idx + 1 bones[0].inherit_ratio = 1 # elbow: parent is newarm newelbow = pmxstruct.PmxBone( name_jp=bones[1].name_jp + jp_ikchainsuffix, name_en=bones[1].name_en + jp_ikchainsuffix, pos=bones[1].pos, parent_idx=shoulder_idx+1, deform_layer=bones[1].deform_layer, deform_after_phys=bones[1].deform_after_phys, has_rotate=True, has_translate=False, has_visible=False, has_enabled=True, has_ik=False, tail_usebonelink=True, tail=0, # want elbow tail to point at the wrist, can't set it until wrist is created inherit_rot=False, inherit_trans=False, has_localaxis=bones[1].has_localaxis, localaxis_x=bones[1].localaxis_x, localaxis_z=bones[1].localaxis_z, has_externalparent=False, has_fixedaxis=False, ) insert_single_bone(pmx, newelbow, shoulder_idx + 2) # change existing elbow to inherit rot from this bones[1].inherit_rot = True bones[1].inherit_parent_idx = shoulder_idx + 2 bones[1].inherit_ratio = 1 # now that newelbow exists, change newarm tail to point to this newarm.tail = shoulder_idx + 2 # wrist: parent is newelbow newwrist = pmxstruct.PmxBone( name_jp=bones[2].name_jp + jp_ikchainsuffix, name_en=bones[2].name_en + jp_ikchainsuffix, pos=bones[2].pos, parent_idx=shoulder_idx+2, deform_layer=bones[2].deform_layer, deform_after_phys=bones[2].deform_after_phys, has_rotate=True, has_translate=False, has_visible=False, has_enabled=True, has_ik=False, tail_usebonelink=True, tail=-1, # newwrist has no tail inherit_rot=False, inherit_trans=False, has_localaxis=bones[2].has_localaxis, localaxis_x=bones[2].localaxis_x, localaxis_z=bones[2].localaxis_z, has_externalparent=False, has_fixedaxis=False, ) insert_single_bone(pmx, newwrist, shoulder_idx + 3) # now that newwrist exists, change newelbow tail to point to this newelbow.tail = shoulder_idx + 3 # copy the wrist to make the IK bone en_suffix = "_L" if side == jp_l else "_R" # get index of "upperbody" to use as parent of hand IK bone ikpar = core.my_list_search(pmx.bones, lambda x: x.name_jp == jp_upperbody) if ikpar is None: core.MY_PRINT_FUNC("ERROR1: semistandard bone '%s' is missing from the model, unable to create attached arm IK" % jp_upperbody) raise RuntimeError() # newik = [side + jp_newik, en_newik + en_suffix] + bones[2][2:5] + [ikpar] # new names, copy pos, new par # newik += bones[2][6:8] + [1, 1, 1, 1] + [0, [0,1,0]] # copy deform layer, rot/trans/vis/en, tail type # newik += [0, 0, [], 0, [], 0, [], 0, []] # no inherit, no fixed axis, no local axis, no ext parent, yes IK # # add the ik info: [is_ik, [target, loops, anglelimit, [[link_idx, []]], [link_idx, []]]] ] ] # newik += [1, [shoulder_idx+3, newik_loops, newik_angle, [[shoulder_idx+2,[]],[shoulder_idx+1,[]]] ] ] newik = pmxstruct.PmxBone( name_jp=side + jp_newik, name_en=en_newik + en_suffix, pos=bones[2].pos, parent_idx=ikpar, deform_layer=bones[2].deform_layer, deform_after_phys=bones[2].deform_after_phys, has_rotate=True, has_translate=True, has_visible=True, has_enabled=True, tail_usebonelink=False, tail=[0,1,0], inherit_rot=False, inherit_trans=False, has_fixedaxis=False, has_localaxis=False, has_externalparent=False, has_ik=True, ik_target_idx=shoulder_idx+3, ik_numloops=newik_loops, ik_angle=newik_angle, ik_links=[pmxstruct.PmxBoneIkLink(idx=shoulder_idx+2), pmxstruct.PmxBoneIkLink(idx=shoulder_idx+1)] ) insert_single_bone(pmx, newik, shoulder_idx + 4) # then add to dispframe # first, does the frame already exist? f = core.my_list_search(pmx.frames, lambda x: x.name_jp == jp_newik, getitem=True) newframeitem = pmxstruct.PmxFrameItem(is_morph=False, idx=shoulder_idx + 4) if f is None: # need to create the new dispframe! easy newframe = pmxstruct.PmxFrame(name_jp=jp_newik, name_en=en_newik, is_special=False, items=[newframeitem]) pmx.frames.append(newframe) else: # frame already exists, also easy f.items.append(newframeitem) else: # remove IK branch core.MY_PRINT_FUNC(">>>> Removing arm IK <<<") # set output name if input_filename_pmx.lower().endswith(pmx_yesik_suffix.lower()): output_filename = input_filename_pmx[0:-(len(pmx_yesik_suffix))] + pmx_noik_suffix else: output_filename = input_filename_pmx[0:-4] + pmx_noik_suffix # identify all bones in ik chain of hand ik bones bone_dellist = [] for b in [r, l]: bone_dellist.append(b) # this IK bone bone_dellist.append(pmx.bones[b].ik_target_idx) # the target of the bone for v in pmx.bones[b].ik_links: bone_dellist.append(v.idx) # each link along the bone bone_dellist.sort() # do the actual delete & shift delete_multiple_bones(pmx, bone_dellist) # delete dispframe for hand ik # first, does the frame already exist? f = core.my_list_search(pmx.frames, lambda x: x.name_jp == jp_newik) if f is not None: # frame already exists, delete it pmx.frames.pop(f) pass # write out output_filename = core.get_unused_file_name(output_filename) pmxlib.write_pmx(output_filename, pmx, moreinfo=moreinfo) core.MY_PRINT_FUNC("Done!") return None
def dispframe_fix(pmx: pmxstruct.Pmx, moreinfo=False): # root group: "Root"/"Root" # facial group: "表情"/"Exp" fix_root = 0 fix_center = 0 hidden_morphs_removed = 0 duplicate_entries_removed = 0 empty_groups_removed = 0 # find the ID# for motherbone... if not found, use whatever is at 0 motherid = core.my_list_search(pmx.bones, lambda x: x.name_jp == "全ての親") if motherid is None: motherid = 0 # ensure that "motherbone" and nothing else is in the root: for d, frame in enumerate(pmx.frames): # only operate on the root group if frame.name_jp == "Root" and frame.name_en == "Root" and frame.is_special: newframelist = [ pmxstruct.PmxFrameItem(is_morph=False, idx=motherid) ] if frame.items != newframelist: # if the itemslist is not exactly only motherbone, make it exactly only motherbone frame.items = newframelist fix_root += 1 break if fix_root and moreinfo: core.MY_PRINT_FUNC("fixing root group") # fix the contents of the "center"/"センター" group # first, find it, or if it does not exist, make it centerid = core.my_list_search(pmx.frames, lambda x: x.name_jp == "センター") if centerid is None: centerid = 2 newframe = pmxstruct.PmxFrame(name_jp="センター", name_en="Center", is_special=False, items=[]) pmx.frames.insert(2, newframe) fix_center += 1 # if i set "motherbone" to be root, then remove it from center if fix_root: removeme = core.my_list_search(pmx.frames[centerid].items, lambda x: x.idx == motherid) if removeme is not None: pmx.frames[centerid].items.pop(removeme) # ensure center contains the proper semistandard contents: view/center/groove/waist # find bone IDs for each of these desired bones centerframeboneids = [ core.my_list_search(pmx.bones, lambda x: x.name_jp == name) for name in CENTER_FRAME_BONES ] for boneid in centerframeboneids: # if this bone does not exist, skip if boneid is None: continue # if this bone already in center, skip if any(item.idx == boneid for item in pmx.frames[centerid].items): continue # add an item for this bone to the group newitem = pmxstruct.PmxFrameItem(is_morph=False, idx=boneid) pmx.frames[centerid].items.append(newitem) # do not count moving a bone from root to center fix_center += 1 if fix_center and moreinfo: core.MY_PRINT_FUNC("fixing center group") displayed_morphs = set() displayed_bones = set() # build sets of all bones/morphs that are in the panels # delete bones that are in the panels more than once # remove all morphs that are group 0 for d, frame in enumerate(pmx.frames): # for each display group, i = 0 while i < len(frame.items): # for each item in that display group, item = frame.items[i] if item.is_morph: # if it is a morph # look up the morph morph = pmx.morphs[item.idx] # figure out what panel of this morph is # if it has an invalid panel #, discard it if morph.panel == pmxstruct.MorphPanel.HIDDEN: frame.items.pop(i) hidden_morphs_removed += 1 # if this is valid but already in the set of used morphs, discard it elif item.idx in displayed_morphs: frame.items.pop(i) duplicate_entries_removed += 1 # otherwise, add it to set of used morphs else: displayed_morphs.add(item.idx) i += 1 else: # if it is a bone # if this is already in the set of used bones, delete it if item.idx in displayed_bones: frame.items.pop(i) duplicate_entries_removed += 1 # otherwise, add it to set of used bones else: displayed_bones.add(item.idx) i += 1 if hidden_morphs_removed: core.MY_PRINT_FUNC("removed %d hidden morphs (cause of crashes)" % hidden_morphs_removed) # core.MY_PRINT_FUNC("!!! Warning: do not add 'hidden' morphs to the display group! MMD will crash!") if duplicate_entries_removed and moreinfo: core.MY_PRINT_FUNC("removed %d duplicate bones or morphs" % duplicate_entries_removed) # have identified which bones/morphs are displayed: now identify which ones are NOT # want all bones not already in 'displayed_bones' that are also visible and enabled undisplayed_bones = [ d for d, bone in enumerate(pmx.bones) if (d not in displayed_bones) and bone.has_visible and bone.has_enabled ] if undisplayed_bones: if moreinfo: core.MY_PRINT_FUNC( "added %d undisplayed bones to new group 'morebones'" % len(undisplayed_bones)) # add a new frame to hold all bones newframelist = [ pmxstruct.PmxFrameItem(is_morph=False, idx=x) for x in undisplayed_bones ] newframe = pmxstruct.PmxFrame(name_jp="morebones", name_en="morebones", is_special=False, items=newframelist) pmx.frames.append(newframe) # build list of which morphs are NOT shown # want all morphs not already in 'displayed_morphs' that are not hidden undisplayed_morphs = [ d for d, morph in enumerate(pmx.morphs) if (d not in displayed_morphs) and ( morph.panel != pmxstruct.MorphPanel.HIDDEN) ] if undisplayed_morphs: if moreinfo: core.MY_PRINT_FUNC("added %d undisplayed morphs to Facials group" % len(undisplayed_morphs)) newframelist = [ pmxstruct.PmxFrameItem(is_morph=True, idx=x) for x in undisplayed_morphs ] # find morphs group and only add to it # should ALWAYS be at index 1 but whatever might as well be extra safe idx = core.my_list_search( pmx.frames, lambda x: (x.name_jp == "表情" and x.is_special)) if idx is not None: # concatenate to end of item list pmx.frames[idx].items += newframelist else: core.MY_PRINT_FUNC( "ERROR: unable to find semistandard 'expressions' display frame" ) # check if there are too many morphs among all frames... if so, trim and remake "displayed morphs" # morphs can theoretically be in any frame, they SHOULD only be in the "expressions" frame but people mess things up total_num_morphs = 0 for frame in pmx.frames: i = 0 while i < len(frame.items): # if this is a bone, skip it if not frame.items[i].is_morph: i += 1 else: # if it is a morph, count it total_num_morphs += 1 # if i have already counted too many morphs, pop it if total_num_morphs > MAX_MORPHS_IN_DISPLAY: frame.items.pop(i) else: i += 1 num_morphs_over_limit = max(total_num_morphs - MAX_MORPHS_IN_DISPLAY, 0) if num_morphs_over_limit: core.MY_PRINT_FUNC( "removed %d morphs to stay under the %d morph limit (cause of crashes)" % (num_morphs_over_limit, MAX_MORPHS_IN_DISPLAY)) core.MY_PRINT_FUNC( "!!! Warning: do not add the remaining morphs to the display group! MMD will crash!" ) # delete any groups that are empty i = 0 while i < len(pmx.frames): frame = pmx.frames[i] # if it is empty AND it is not "special" then delete it if len(frame.items) == 0 and not frame.is_special: pmx.frames.pop(i) empty_groups_removed += 1 else: i += 1 if empty_groups_removed and moreinfo: core.MY_PRINT_FUNC("removed %d empty groups" % empty_groups_removed) overall = num_morphs_over_limit + \ fix_center + \ empty_groups_removed + \ len(undisplayed_bones) + \ len(undisplayed_morphs) + \ duplicate_entries_removed + \ hidden_morphs_removed + \ fix_root if overall == 0: core.MY_PRINT_FUNC("No changes are required") return pmx, False core.MY_PRINT_FUNC("Fixed %d things related to display pane groups" % overall) return pmx, True