예제 #1
0
def get_idx_in_pmxsublist(s: str, pmxlist: List):
    if s == "": return -1
    # then get the morph index from this
    # search JP names first
    t = core.my_list_search(pmxlist, lambda x: x.name_jp.lower() == s.lower())
    if t is not None: return t
    # search EN names next
    t = core.my_list_search(pmxlist, lambda x: x.name_en.lower() == s.lower())
    if t is not None: return t
    # try to cast to int next
    try:
        t = int(s)
        if 0 <= t < len(pmxlist):
            return t
        else:
            core.MY_PRINT_FUNC("valid indexes are [0-'%d']" %
                               (len(pmxlist) - 1))
            return None
    except ValueError:
        core.MY_PRINT_FUNC("unable to find matching item for input '%s'" % s)
        return None
예제 #2
0
def build_bonechain(allbones: List[pmxstruct.PmxBone],
                    endbone: str) -> List[Bone]:
    nextbone = endbone
    buildme = []
    while True:
        r = core.my_list_search(allbones,
                                lambda x: x.name_jp == nextbone,
                                getitem=True)
        if r is None:
            core.MY_PRINT_FUNC(
                "ERROR: unable to find '" + nextbone +
                "' in input file, unable to build parentage chain")
            raise RuntimeError()
        # 0 = bname, 5 = parent index, 234 = xyz position
        nextbone = allbones[r.parent_idx].name_jp
        newrow = Bone(r.name_jp, r.pos[0], r.pos[1], r.pos[2])
        buildme.append(newrow)
        # if parent index is -1, that means there is no parent. so we reached root. so break.
        if r.parent_idx == -1:
            break
    buildme.reverse()
    return buildme
예제 #3
0
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")
    # input_filename_pmx = "../../python_scripts/grasstest_better.pmx"
    pmx = pmxlib.read_pmx(input_filename_pmx, moreinfo=moreinfo)

    ##################################
    # user flow:
    # first ask whether they want to add armtwist, yes/no
    # second ask whether they want to add legtwist, yes/no
    # then do it
    # then write out to file
    ##################################

    working_queue = []

    s = core.MY_SIMPLECHOICE_FUNC((1, 2), [
        "Do you wish to add magic twistbones to the ARMS?", "1 = Yes, 2 = No"
    ])
    if s == 1:
        # add upperarm set and lowerarm set to the queue
        working_queue.append(armset)
        working_queue.append(wristset)
        pass
    s = core.MY_SIMPLECHOICE_FUNC((1, 2), [
        "Do you wish to add magic twistbones to the LEGS?", "1 = Yes, 2 = No"
    ])
    if s == 1:
        # TODO detect whether d-bones exist or not
        # add legs or d-legs set to the queue
        pass

    if not working_queue:
        core.MY_PRINT_FUNC("Nothing was changed")
        core.MY_PRINT_FUNC("Done")
        return None

    # for each set in the queue,
    for boneset in working_queue:
        # boneset = (start, end, preferred, oldrigs, bezier)
        for side in [jp_l, jp_r]:
            # print(side)
            # print(boneset)
            # 1. first, validate that start/end exist, these are required
            # NOTE: remember to prepend 'side' before all jp names!
            start_jp = side + boneset[0]
            start_idx = core.my_list_search(pmx.bones,
                                            lambda x: x.name_jp == start_jp)
            if start_idx is None:
                core.MY_PRINT_FUNC(
                    "ERROR: standard bone '%s' not found in model, this is required!"
                    % start_jp)
                continue
            end_jp = side + boneset[1]
            end_idx = core.my_list_search(pmx.bones,
                                          lambda x: x.name_jp == end_jp)
            if end_idx is None:
                core.MY_PRINT_FUNC(
                    "ERROR: standard bone '%s' not found in model, this is required!"
                    % end_jp)
                continue

            # 2. determine whether the 'preferredparent' exists and therefore what to acutally use as the parent
            parent_jp = side + boneset[2]
            parent_idx = core.my_list_search(pmx.bones,
                                             lambda x: x.name_jp == parent_jp)
            if parent_idx is None:
                parent_idx = start_idx

            # 3. attempt to collapse known armtwist rig names onto 'parent' so that the base case is further automated
            # for each bonename in boneset[3], if it exists, collapse onto boneidx parent_idx
            for bname in boneset[3]:
                rig_idx = core.my_list_search(
                    pmx.bones, lambda x: x.name_jp == side + bname)
                if rig_idx is None: continue  # if not found, try the next
                # when it is found, what 'factor' do i use?
                # print(side+bname)
                if pmx.bones[rig_idx].inherit_rot and pmx.bones[
                        rig_idx].inherit_parent_idx == parent_idx and pmx.bones[
                            rig_idx].inherit_ratio != 0:
                    # if using partial rot inherit AND inheriting from parent_idx AND ratio != 0, use that
                    # think this is good, if twistbones exist they should be children of preferred
                    f = pmx.bones[rig_idx].inherit_ratio
                elif pmx.bones[rig_idx].parent_idx == parent_idx:
                    # this should be just in case?
                    f = 1
                elif pmx.bones[rig_idx].parent_idx == start_idx:
                    # this should catch magic armtwist bones i previously created
                    f = 1
                else:
                    core.MY_PRINT_FUNC(
                        "Warning, found unusual relationship when collapsing old armtwist rig, assuming ratio=1"
                    )
                    f = 1
                transfer_bone_weights(pmx, parent_idx, rig_idx, f)
                pass
            # also collapse 'start' onto 'preferredparent' if it exists... want to transfer weight from 'arm' to 'armtwist'
            # if start == preferredparent this does nothing, no harm done
            transfer_bone_weights(pmx, parent_idx, start_idx, scalefactor=1)

            # 4. run the weight-cleanup function
            normalize_weights(pmx)

            # 5. append 3 new bones to end of bonelist
            # 	armYZ gets pos = start pos & parent = start parent
            basename_jp = pmx.bones[start_idx].name_jp
            armYZ_new_idx = len(pmx.bones)
            # armYZ = [basename_jp + yz_suffix, local_translate(basename_jp + yz_suffix)]  # name_jp,en
            # armYZ += pmx[5][start_idx][2:]					# copy the whole rest of the bone
            # armYZ[10:12] = [False, False]					# visible=false, enabled=false
            # armYZ[12:14] = [True, [armYZ_new_idx + 1]]		# tail type = tail, tail pointat = armYZend
            # armYZ[14:19] = [False, False, [], False, []]	# disable partial inherit + fixed axis
            # # local axis is copy
            # armYZ[21:25] = [False, [], False, []]			# disable ext parent + ik
            armYZ = pmxstruct.PmxBone(
                name_jp=basename_jp + yz_suffix,
                name_en=local_translate(basename_jp + yz_suffix),
                pos=pmx.bones[start_idx].pos,
                parent_idx=pmx.bones[start_idx].parent_idx,
                deform_layer=pmx.bones[start_idx].deform_layer,
                deform_after_phys=pmx.bones[start_idx].deform_after_phys,
                has_localaxis=True,
                localaxis_x=pmx.bones[start_idx].localaxis_x,
                localaxis_z=pmx.bones[start_idx].localaxis_z,
                tail_type=True,
                tail=armYZ_new_idx + 1,
                has_rotate=True,
                has_translate=True,
                has_visible=False,
                has_enabled=True,
                has_ik=False,
                inherit_rot=False,
                inherit_trans=False,
                has_fixedaxis=False,
                has_externalparent=False,
            )

            # 	armYZend gets pos = end pos & parent = armYZ
            # armYZend = [basename_jp + yz_suffix + "先", local_translate(basename_jp + yz_suffix + "先")]  # name_jp,en
            # armYZend += pmx[5][end_idx][2:]					# copy the whole rest of the bone
            # armYZend[5] = armYZ_new_idx						# parent = armYZ
            # armYZend[10:12] = [False, False]				# visible=false, enabled=false
            # armYZend[12:14] = [True, [-1]]					# tail type = tail, tail pointat = none
            # armYZend[14:19] = [False, False, [], False, []]	# disable partial inherit + fixed axis
            # # local axis is copy
            # armYZend[21:25] = [False, [], False, []]		# disable ext parent + ik
            armYZend = pmxstruct.PmxBone(
                name_jp=basename_jp + yz_suffix + "先",
                name_en=local_translate(basename_jp + yz_suffix + "先"),
                pos=pmx.bones[end_idx].pos,
                parent_idx=armYZ_new_idx,
                deform_layer=pmx.bones[end_idx].deform_layer,
                deform_after_phys=pmx.bones[end_idx].deform_after_phys,
                has_localaxis=True,
                localaxis_x=pmx.bones[end_idx].localaxis_x,
                localaxis_z=pmx.bones[end_idx].localaxis_z,
                tail_type=True,
                tail=-1,
                has_rotate=True,
                has_translate=True,
                has_visible=False,
                has_enabled=True,
                has_ik=False,
                inherit_rot=False,
                inherit_trans=False,
                has_fixedaxis=False,
                has_externalparent=False,
            )

            # # 	elbowIK gets pos = end pos & parent = end parent
            # armYZIK = [basename_jp + yz_suffix + "IK", local_translate(basename_jp + yz_suffix + "IK")]  # name_jp,en
            # armYZIK += pmx[5][end_idx][2:]					# copy the whole rest of the bone
            # armYZIK[10:12] = [False, False]					# visible=false, enabled=false
            # armYZIK[12:14] = [True, [-1]]					# tail type = tail, tail pointat = none
            # armYZIK[14:19] = [False, False, [], False, []]	# disable partial inherit + fixed axis
            # # local axis is copy
            # armYZIK[21:23] = [False, []]					# disable ext parent
            # armYZIK[23] = True								# ik=true
            # # add the ik info: [target, loops, anglelimit, [[link_idx, []], [link_idx, []]] ]
            # armYZIK[24] = [armYZ_new_idx+1, newik_loops, newik_angle, [[armYZ_new_idx, []]]]
            armYZIK = pmxstruct.PmxBone(
                name_jp=basename_jp + yz_suffix + "IK",
                name_en=local_translate(basename_jp + yz_suffix + "IK"),
                pos=pmx.bones[end_idx].pos,
                parent_idx=pmx.bones[end_idx].parent_idx,
                deform_layer=pmx.bones[end_idx].deform_layer,
                deform_after_phys=pmx.bones[end_idx].deform_after_phys,
                has_localaxis=True,
                localaxis_x=pmx.bones[end_idx].localaxis_x,
                localaxis_z=pmx.bones[end_idx].localaxis_z,
                tail_type=True,
                tail=-1,
                has_rotate=True,
                has_translate=True,
                has_visible=False,
                has_externalparent=False,
                has_enabled=True,
                inherit_rot=False,
                inherit_trans=False,
                has_fixedaxis=False,
                has_ik=True,
                ik_target_idx=armYZ_new_idx + 1,
                ik_numloops=newik_loops,
                ik_angle=newik_angle,
                ik_links=[pmxstruct.PmxBoneIkLink(idx=armYZ_new_idx)])

            # now append them to the bonelist
            pmx.bones.append(armYZ)
            pmx.bones.append(armYZend)
            pmx.bones.append(armYZIK)

            # 6. build the bezier curve
            bezier_curve = core.MyBezier(boneset[4][0],
                                         boneset[4][1],
                                         resolution=50)

            # 7. find relevant verts & determine unbounded percentile for each
            (verts, percentiles,
             centers) = calculate_percentiles(pmx, start_idx, end_idx,
                                              parent_idx)

            if moreinfo:
                core.MY_PRINT_FUNC(
                    "Blending between bones '{}'/'{}'=ZEROtwist and '{}'/'{}'=FULLtwist"
                    .format(armYZ.name_jp, armYZ.name_en,
                            pmx.bones[parent_idx].name_jp,
                            pmx.bones[parent_idx].name_en))
                core.MY_PRINT_FUNC(
                    "   Found %d potentially relevant vertices" % len(verts))

            # 8. use X or Y to choose border points, print for debugging, also scale the percentiles
            # first sort ascending by percentile value
            vert_zip = list(zip(verts, percentiles, centers))
            vert_zip.sort(key=lambda x: x[1])
            verts, percentiles, centers = zip(*vert_zip)  # unzip

            # X. highest point mode
            # "liberal" endpoints: extend as far as i can, include all good stuff even if i include some bad stuff with it
            # start at each end and work inward until i find a vert controlled by only parent_idx
            i_min_liberal = 0
            i_max_liberal = len(verts) - 1
            i_min_conserv = -1
            i_max_conserv = len(verts)
            for i_min_liberal in range(
                    0, len(verts)):  # start at head and work down,
                if pmx.verts[verts[
                        i_min_liberal]].weighttype == 0:  # if the vertex is BDEF1 type,
                    break  # then stop looking,
            p_min_liberal = percentiles[
                i_min_liberal]  # and save the percentile it found.
            for i_max_liberal in reversed(range(
                    0, len(verts))):  # start at tail and work up,
                if pmx.verts[verts[
                        i_max_liberal]].weighttype == 0:  # if the vertex is BDEF1 type,
                    break  # then stop looking,
            p_max_liberal = percentiles[
                i_max_liberal]  # and save the percentile it found.
            # Y. lowest highest point mode
            # "conservative" endpoints: define ends such that no bad stuff exists within bounds, even if i miss some good stuff
            # start in the middle and work outward until i find a vert NOT controlled by only parent_idx, then back off 1
            # where is the middle? use "bisect_left"
            middle = core.bisect_left(percentiles, 0.5)
            for i_min_conserv in reversed(
                    range(middle - 1)):  # start in middle, work toward head,
                if pmx.verts[verts[
                        i_min_conserv]].weighttype != 0:  # if the vertex is NOT BDEF1 type,
                    break  # then stop looking,
            i_min_conserv += 1  # and step back 1 to find the last vert that was good BDEF1,
            p_min_conserv = percentiles[
                i_min_conserv]  # and save the percentile it found.
            for i_max_conserv in range(
                    middle + 1,
                    len(verts)):  # start in middle, work toward tail,
                if pmx.verts[verts[
                        i_max_conserv]].weighttype != 0:  # if the vertex is NOT BDEF1 type,
                    break  # then stop looking,
            i_max_conserv -= 1  # and step back 1 to find the last vert that was good BDEF1,
            p_max_conserv = percentiles[
                i_max_conserv]  # and save the percentile it found.

            foobar = False
            if not (i_min_liberal <= i_min_conserv <= i_max_conserv <=
                    i_max_liberal):
                core.MY_PRINT_FUNC(
                    "ERROR: bounding indexes do not follow the expected relationship, results may be bad!"
                )
                foobar = True
            if foobar or moreinfo:
                core.MY_PRINT_FUNC(
                    "   Max liberal bounds:      idx = %d to %d, %% = %f to %f"
                    % (i_min_liberal, i_max_liberal, p_min_liberal,
                       p_max_liberal))
                core.MY_PRINT_FUNC(
                    "   Max conservative bounds: idx = %d to %d, %% = %f to %f"
                    % (i_min_conserv, i_max_conserv, p_min_conserv,
                       p_max_conserv))

            # IDEA: WEIGHTED BLEND! sliding scale!
            avg_factor = core.clamp(ENDPOINT_AVERAGE_FACTOR, 0.0, 1.0)
            if p_min_liberal != p_min_conserv:
                p_min = (p_min_liberal * avg_factor) + (p_min_conserv *
                                                        (1 - avg_factor))
            else:
                p_min = p_min_liberal
            if p_max_liberal != p_max_conserv:
                p_max = (p_max_liberal * avg_factor) + (p_max_conserv *
                                                        (1 - avg_factor))
            else:
                p_max = p_max_liberal
            # clamp just in case
            p_min = core.clamp(p_min, 0.0, 1.0)
            p_max = core.clamp(p_max, 0.0, 1.0)
            if moreinfo:
                i_min = core.bisect_left(percentiles, p_min)
                i_max = core.bisect_left(percentiles, p_max)
                core.MY_PRINT_FUNC(
                    "   Compromise bounds:       idx = %d to %d, %% = %f to %f"
                    % (i_min, i_max, p_min, p_max))

            # now normalize the percentiles to these endpoints
            p_len = p_max - p_min
            percentiles = [(p - p_min) / p_len for p in percentiles]

            # 9. divide weight between preferredparent (or parent) and armYZ
            vert_zip = list(zip(verts, percentiles, centers))
            num_modified, num_bleeders = divvy_weights(
                pmx=pmx,
                vert_zip=vert_zip,
                axis_limits=(pmx.bones[start_idx].pos, pmx.bones[end_idx].pos),
                bone_hasweight=parent_idx,
                bone_getsweight=armYZ_new_idx,
                bezier=bezier_curve)
            if moreinfo:
                core.MY_PRINT_FUNC(
                    "  Modified %d verts to use blending, %d are questionable 'bleeding' points"
                    % (num_modified, num_bleeders))
            pass
        pass

    # 10. run final weight-cleanup
    normalize_weights(pmx)

    # 11. write out
    output_filename_pmx = input_filename_pmx[0:-4] + "_magictwist.pmx"
    output_filename_pmx = core.get_unused_file_name(output_filename_pmx)
    pmxlib.write_pmx(output_filename_pmx, pmx, moreinfo=moreinfo)
    core.MY_PRINT_FUNC("Done!")
    return None
예제 #4
0
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
예제 #5
0
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)

    ##################################
    # user flow:
    # ask for the helper bone (to be merged)
    # ask for the destination bone (merged onto)
    # try to infer proper merge factor, if it cannot infer then prompt user
    # then write out to file
    ##################################

    dest_idx = 0
    while True:
        # any input is considered valid
        s = core.MY_GENERAL_INPUT_FUNC(lambda x: True, [
            "Please specify the DESTINATION bone that weights will be transferred to.",
            "Enter bone #, JP name, or EN name (names are case sensitive).",
            "Empty input will quit the script."
        ])
        # if empty, leave & do nothing
        if s == "":
            dest_idx = -1
            break
        # then get the bone index from this
        # search JP names first
        dest_idx = core.my_list_search(pmx.bones, lambda x: x.name_jp == s)
        if dest_idx is not None: break  # did i find a match?
        # search EN names next
        dest_idx = core.my_list_search(pmx.bones, lambda x: x.name_en == s)
        if dest_idx is not None: break  # did i find a match?
        # try to cast to int next
        try:
            dest_idx = int(s)
            if 0 <= dest_idx < len(pmx.bones):
                break  # is this within the proper bounds?
            else:
                core.MY_PRINT_FUNC("valid bone indexes are 0-%d" %
                                   (len(pmx.bones) - 1))
        except ValueError:
            pass
        core.MY_PRINT_FUNC("unable to find matching bone for name '%s'" % s)

    if dest_idx == -1:
        core.MY_PRINT_FUNC("quitting")
        return None

    dest_tag = "bone #{} JP='{}' / EN='{}'".format(dest_idx,
                                                   pmx.bones[dest_idx].name_jp,
                                                   pmx.bones[dest_idx].name_jp)
    source_idx = 0
    while True:
        # any input is considered valid
        s = core.MY_GENERAL_INPUT_FUNC(lambda x: True, [
            "Please specify the SOURCE bone that will be merged onto %s." %
            dest_tag,
            "Enter bone #, JP name, or EN name (names are case sensitive).",
            "Empty input will quit the script."
        ])
        # if empty, leave & do nothing
        if s == "":
            source_idx = -1
            break
        # then get the morph index from this
        # search JP names first
        source_idx = core.my_list_search(pmx.bones, lambda x: x.name_jp == s)
        if source_idx is not None: break  # did i find a match?
        # search EN names next
        source_idx = core.my_list_search(pmx.bones, lambda x: x.name_en == s)
        if source_idx is not None: break  # did i find a match?
        # try to cast to int next
        try:
            source_idx = int(s)
            if 0 <= source_idx < len(pmx.bones):
                break  # is this within the proper bounds?
            else:
                core.MY_PRINT_FUNC("valid bone indexes are 0-%d" %
                                   (len(pmx.bones) - 1))
        except ValueError:
            pass
        core.MY_PRINT_FUNC("unable to find matching bone for name '%s'" % s)

    if source_idx == -1:
        core.MY_PRINT_FUNC("quitting")
        return None

    # print to confirm
    core.MY_PRINT_FUNC(
        "Merging bone #{} JP='{}' / EN='{}' ===> bone #{} JP='{}' / EN='{}'".
        format(source_idx, pmx.bones[source_idx].name_jp,
               pmx.bones[source_idx].name_en, dest_idx,
               pmx.bones[dest_idx].name_jp, pmx.bones[dest_idx].name_en))
    # now try to infer the merge factor

    f = 0.0
    if pmx.bones[source_idx].inherit_rot and pmx.bones[
            source_idx].inherit_parent_idx == dest_idx and pmx.bones[
                source_idx].inherit_ratio != 0:
        # if using partial rot inherit AND inheriting from dest_idx AND ratio != 0, use that
        # think this is good, if twistbones exist they should be children of preferred
        f = pmx.bones[source_idx].inherit_ratio
    elif pmx.bones[source_idx].parent_idx == dest_idx:
        # if they have a direct parent-child relationship, then factor is 1
        f = 1
    else:
        # otherwise, prompt for the factor
        factor_str = core.MY_GENERAL_INPUT_FUNC(
            is_float,
            "Unable to infer relationship, please specify a merge factor:")
        if factor_str == "":
            core.MY_PRINT_FUNC("quitting")
            return None
        f = float(factor_str)

    # do the actual transfer
    transfer_bone_weights(pmx, dest_idx, source_idx, f)

    # run the weight-cleanup function
    dummy = normalize_weights(pmx)

    # write out
    output_filename_pmx = input_filename_pmx[0:-4] + "_weightmerge.pmx"
    output_filename_pmx = core.get_unused_file_name(output_filename_pmx)
    pmxlib.write_pmx(output_filename_pmx, 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)
    # prompt VMD file name
    core.MY_PRINT_FUNC("")
    core.MY_PRINT_FUNC(
        "Please enter name of VMD motion or VPD pose file to check compatability with:"
    )
    input_filename = core.MY_FILEPROMPT_FUNC(".vmd .vpd")
    if not input_filename.lower().endswith(".vpd"):
        # the actual VMD part isn't even used, only bonedict and morphdict
        vmd = vmdlib.read_vmd(input_filename, moreinfo=moreinfo)
    else:
        vmd = vpdlib.read_vpd(input_filename, moreinfo=moreinfo)
    bonedict = vmdlib.parse_vmd_used_dict(vmd.boneframes,
                                          frametype="bone",
                                          moreinfo=moreinfo)
    morphdict = vmdlib.parse_vmd_used_dict(vmd.morphframes,
                                           frametype="morph",
                                           moreinfo=moreinfo)

    core.MY_PRINT_FUNC("")

    # must use same encoding as I used when the VMD was unpacked, since the hex bytes only have meaning in that encoding
    core.set_encoding("shift_jis")

    ##############################################
    # check morph compatability

    # build list of morphs used in the dance VMD
    morphs_in_vmd = list(morphdict.keys())

    # build list of ALL morphs in the PMX
    morphs_in_model = [pmxmorph.name_jp for pmxmorph in pmx.morphs]

    # ensure that the VMD contains at least some morphs, to prevent zero-divide error
    if len(morphs_in_vmd) == 0:
        core.MY_PRINT_FUNC(
            "MORPH SKIP: VMD '%s' does not contain any morphs that are used in a meaningful way."
            % core.get_clean_basename(input_filename))
    elif len(morphs_in_model) == 0:
        core.MY_PRINT_FUNC(
            "MORPH SKIP: PMX '%s' does not contain any morphs." %
            core.get_clean_basename(input_filename_pmx))
    else:

        # convert pmx-morph names to bytes
        # these can plausibly fail shift_jis encoding because they came from the UTF-8 pmx file
        morphs_in_model_b = []
        for a in morphs_in_model:
            try:
                b = core.encode_string_with_escape(a)
            except UnicodeEncodeError as e:
                newerrstr = "%s: '%s' codec cannot encode char '%s' within string '%s'" % (
                    e.__class__.__name__, e.encoding, e.reason[e.start:e.end],
                    e.reason)
                core.MY_PRINT_FUNC(newerrstr)
                b = bytearray()
            morphs_in_model_b.append(b)

        # convert vmd-morph names to bytes
        # these might be truncated but cannot fail because they were already decoded from the shift_jis vmd file
        morphs_in_vmd_b = [
            core.encode_string_with_escape(a) for a in morphs_in_vmd
        ]

        matching_morphs = {}
        missing_morphs = {}
        # iterate over list of morphs
        for vmdmorph, vmdmorph_b in zip(morphs_in_vmd, morphs_in_vmd_b):
            # question: does "vmdmorph" match something in "morphs_in_model"?
            # BUT, doing comparison in bytes-space to handle escape characters: vmdmorph_b vs morphs_in_model_b
            # NOTE: MMD does not try to use "begins-with" matching like I had hoped/assumed, it only looks for exact matches
            # return list of ALL matches, this way i can raise an error if there are multiple matches
            # exact match
            modelmorphmatch_b = [
                a for a in morphs_in_model_b if a == vmdmorph_b
            ]

            # copy the key,val in one of the dicts depending on results of matching attempt
            if len(modelmorphmatch_b) == 0:
                # MISS! key is the VMD morph name since that's the best clue Ive got
                missing_morphs[vmdmorph] = morphdict[vmdmorph]
            elif len(modelmorphmatch_b) == 1:
                # MATCH! key is the PMX morph name it matched against, since it might be a longer version wtihout escape char
                matching_morphs[core.decode_bytes_with_escape(
                    modelmorphmatch_b[0])] = morphdict[vmdmorph]
            else:
                # more than 1 morph was a match!?
                core.MY_PRINT_FUNC(
                    "Warning: VMDmorph '%s' matched multiple PMXmorphs, its behavior is uncertain."
                    % vmdmorph)
                modelmorphmatch = [
                    core.decode_bytes_with_escape(a) for a in modelmorphmatch_b
                ]
                # core.MY_PRINT_FUNC(modelmorphmatch)
                matching_morphs[modelmorphmatch[0]] = morphdict[vmdmorph]

        # display results!
        r = "PASS" if len(matching_morphs) == len(morphs_in_vmd) else "FAIL"
        core.MY_PRINT_FUNC(
            "MORPH {}: {} / {} = {:.1%} of the morphs are supported".format(
                r, len(matching_morphs), len(morphs_in_vmd),
                len(matching_morphs) / len(morphs_in_vmd)))

        # if there are no missing morphs (all match), don't print anything at all
        if missing_morphs:
            if not moreinfo:
                core.MY_PRINT_FUNC(
                    "For detailed list, please re-run with 'more info' enabled"
                )
            else:
                # convert the dicts to lists and sort for printing
                # sort in-place descending by 2nd element as primary
                missing_morphs_list = sorted(list(missing_morphs.items()),
                                             key=core.get2nd,
                                             reverse=True)
                # justify the names!
                missing_just = core.MY_JUSTIFY_STRINGLIST(
                    ["'" + m[0] + "'" for m in missing_morphs_list])
                # re-attach the justified names to the usage numbers
                missing_morphs_list = list(
                    zip(missing_just, [m[1] for m in missing_morphs_list]))

                core.MY_PRINT_FUNC("")
                core.MY_PRINT_FUNC("Unsupported morphs: name + times used")
                for m, num in missing_morphs_list:
                    core.MY_PRINT_FUNC("  %s  ||  %d" % (m, int(num)))

                # only print the matching morphs if there are some, and if enabled by options
                if matching_morphs and PRINT_MATCHING_ITEMS:
                    matching_morphs_list = list(matching_morphs.items())
                    matching_morphs_list.sort(
                        key=core.get2nd, reverse=True
                    )  # sort in-place descending by 2nd element as primary
                    matching_just = core.MY_JUSTIFY_STRINGLIST(
                        ["'" + m[0] + "'" for m in matching_morphs_list])
                    matching_morphs_list = list(
                        zip(matching_just,
                            [m[1] for m in matching_morphs_list]))
                    core.MY_PRINT_FUNC("")
                    core.MY_PRINT_FUNC("Supported morphs: name + times used")
                    for m, num in matching_morphs_list:
                        core.MY_PRINT_FUNC("  %s  ||  %d" % (m, int(num)))

    ##############################################
    # check bone compatability
    core.MY_PRINT_FUNC("")

    # build list of bones used in the dance VMD
    bones_in_vmd = list(bonedict.keys())

    # build list of ALL bones in the PMX
    # first item of pmxbone is the jp name
    bones_in_model = [pmxbone.name_jp for pmxbone in pmx.bones]

    # ensure that the VMD contains at least some bones, to prevent zero-divide error
    if len(bones_in_vmd) == 0:
        core.MY_PRINT_FUNC(
            "BONE SKIP: VMD '%s' does not contain any bones that are used in a meaningful way."
            % core.get_clean_basename(input_filename))
    elif len(bones_in_model) == 0:
        core.MY_PRINT_FUNC("BONE SKIP: PMX '%s' does not contain any bones." %
                           core.get_clean_basename(input_filename_pmx))
    else:

        # convert pmx-bone names to bytes
        # these can plausibly fail shift_jis encoding because they came from the UTF-8 pmx file
        bones_in_model_b = []
        for a in bones_in_model:
            try:
                b = core.encode_string_with_escape(a)
            except UnicodeEncodeError as e:
                newerrstr = "%s: '%s' codec cannot encode char '%s' within string '%s'" % (
                    e.__class__.__name__, e.encoding, e.reason[e.start:e.end],
                    e.reason)
                core.MY_PRINT_FUNC(newerrstr)
                b = bytearray()
            bones_in_model_b.append(b)

        # convert vmd-bone names to bytes
        # these might be truncated but cannot fail because they were already decoded from the shift_jis vmd file
        bones_in_vmd_b = [
            core.encode_string_with_escape(a) for a in bones_in_vmd
        ]

        matching_bones = {}
        missing_bones = {}
        # iterate over list of bones that pass the size check
        for vmdbone, vmdbone_b in zip(bones_in_vmd, bones_in_vmd_b):
            # question: does "vmdbone" match something in "bones_in_model"?
            # BUT, doing comparison in bytes-space to handle escape characters: vmdbone_b vs bones_in_model_b
            # NOTE: MMD does not try to use "begins-with" matching like I had hoped/assumed, it only looks for exact matches
            # return list of ALL matches, this way i can raise an error if there are multiple matches
            # exact match
            modelbonematch_b = [a for a in bones_in_model_b if a == vmdbone_b]

            # copy the key,val in one of the dicts depending on results of matching attempt
            if len(modelbonematch_b) == 0:
                # MISS! key is the VMD bone name since that's the best clue Ive got
                missing_bones[vmdbone] = bonedict[vmdbone]
            elif len(modelbonematch_b) == 1:
                # MATCH! key is the PMX bone name it matched against, since it might be a longer version wtihout escape char
                matching_bones[core.decode_bytes_with_escape(
                    modelbonematch_b[0])] = bonedict[vmdbone]
            else:
                # more than 1 bone was a match!?
                core.MY_PRINT_FUNC(
                    "Warning: VMDbone '%s' matched multiple PMXbones, its behavior is uncertain."
                    % vmdbone)
                modelbonematch = [
                    core.decode_bytes_with_escape(a) for a in modelbonematch_b
                ]
                # core.MY_PRINT_FUNC(modelbonematch)
                matching_bones[modelbonematch[0]] = bonedict[vmdbone]

        # display results!
        r = "PASS" if len(matching_bones) == len(bones_in_vmd) else "FAIL"
        core.MY_PRINT_FUNC(
            "BONE {}: {} / {} = {:.1%} of the bones are supported".format(
                r, len(matching_bones), len(bones_in_vmd),
                len(matching_bones) / len(bones_in_vmd)))

        # if there are no missing bones (all match), don't print anything at all
        if missing_bones:
            if not moreinfo:
                core.MY_PRINT_FUNC(
                    "For detailed list, please re-run with 'more info' enabled"
                )
            else:
                # convert the dicts to lists and sort for printing
                # sort in-place descending by 2nd element as primary
                missing_bones_list = sorted(list(missing_bones.items()),
                                            key=core.get2nd,
                                            reverse=True)
                # justify the names!
                missing_just = core.MY_JUSTIFY_STRINGLIST(
                    ["'" + m[0] + "'" for m in missing_bones_list])
                # re-attach the justified names to the usage numbers
                missing_bones_list = list(
                    zip(missing_just, [m[1] for m in missing_bones_list]))

                core.MY_PRINT_FUNC("")
                core.MY_PRINT_FUNC("Unsupported bones: name + times used")
                for m, num in missing_bones_list:
                    core.MY_PRINT_FUNC("  %s  ||  %d" % (m, int(num)))

                # only print the matching bones if there are some, and if enabled by options
                if matching_bones and PRINT_MATCHING_ITEMS:
                    matching_bones_list = list(matching_bones.items())
                    matching_bones_list.sort(
                        key=core.get2nd, reverse=True
                    )  # sort in-place descending by 2nd element as primary
                    matching_just = core.MY_JUSTIFY_STRINGLIST(
                        ["'" + m[0] + "'" for m in matching_bones_list])
                    matching_bones_list = list(
                        zip(matching_just,
                            [m[1] for m in matching_bones_list]))
                    core.MY_PRINT_FUNC("")
                    core.MY_PRINT_FUNC("Supported bones: name + times used")
                    for m, num in matching_bones_list:
                        core.MY_PRINT_FUNC("  %s  ||  %d" % (m, int(num)))

        # NEW: among matching bones, check whether any bones have unsupported translation/rotation
        for bonestr in sorted(list(matching_bones.keys())):
            # get the bone to get whether rot/trans enabled
            bone = core.my_list_search(pmx.bones,
                                       lambda x: x.name_jp == bonestr,
                                       getitem=True)
            # get all the frames from the VMD that are relevant to this bone
            thisboneframes = [f for f in vmd.boneframes if f.name == bonestr]
            # does the VMD use rotation? probably, check anyway
            vmd_use_rot = any(f.rot != [0, 0, 0] for f in thisboneframes)
            if vmd_use_rot and not (bone.has_rotate and bone.has_enabled):
                # raise some sort of warning
                w = "Warning: supported bone '%s' uses rotation in VMD, but rotation not allowed by PMX" % bonestr
                core.MY_PRINT_FUNC(w)
            # does the VMD use translation?
            vmd_use_trans = any(f.pos != [0, 0, 0] for f in thisboneframes)
            if vmd_use_trans and not (bone.has_translate and bone.has_enabled):
                # raise some sort of warning
                w = "Warning: supported bone '%s' uses move/shift in VMD, but move/shift not allowed by PMX" % bonestr
                core.MY_PRINT_FUNC(w)

    core.MY_PRINT_FUNC("")
    core.MY_PRINT_FUNC("Done!")
    return None
def make_autotwist_segment(pmx: pmxstruct.Pmx, side, arm_s, armtwist_s,
                           elbow_s, moreinfo):
    # note: will be applicable to elbow-wristtwist-wrist as well! just named like armtwist for simplicity

    # 1, locate arm/armtwist/elbow idx and obj
    r = []
    for n in (arm_s, armtwist_s, elbow_s):
        n2 = side[0] + n[0]
        i = core.my_list_search(pmx.bones, lambda x: x.name_jp == n2)
        if i is None:
            core.MY_PRINT_FUNC(
                "ERROR: standard bone '%s' not found in model, this is required!"
                % n2)
            return None
        r.append(i)
    arm_idx, armtwist_idx, elbow_idx = r  # unpack into named variables
    arm = pmx.bones[arm_idx]
    armtwist = pmx.bones[armtwist_idx]
    elbow = pmx.bones[elbow_idx]

    # 2, find all armtwist-sub bones
    armtwist_sub_obj = []
    # # dont forget to refresh elbow_idx
    # elbow_idx = core.my_list_search(pmx.bones, lambda x: x.name_jp == side + elbow_s)
    for d, bone in enumerate(pmx.bones):
        # anything that partial inherit from armtwist (except arm)
        if bone.inherit_rot and bone.inherit_parent_idx == armtwist_idx:
            if d == arm_idx: continue
            armtwist_sub_obj.append(bone)
        # anything that has armtwist as parent (except elbow or elbow helper (full parent armtwist, partial parent elbow))
        if bone.parent_idx == armtwist_idx:
            if d == elbow_idx: continue
            if bone.inherit_rot and bone.inherit_parent_idx == elbow_idx:
                continue
            armtwist_sub_obj.append(bone)

    # 3, calculate "perpendicular" location
    # get axis from arm to elbow, normalize to 1
    axis = [b - a for a, b in zip(arm.pos, elbow.pos)]
    axis = core.normalize_distance(axis)
    # calc vector in XZ plane at 90-deg from axis
    frontback = core.my_cross_product(axis, [0, 1, 0])
    frontback = core.normalize_distance(frontback)
    # calc vector in the same vertical plane as axis, at 90-deg from axis
    perpendicular = core.my_cross_product(axis, frontback)
    perpendicular = core.normalize_distance(perpendicular)
    # if result has negative y, invert
    if perpendicular[1] < 0:
        perpendicular = [p * -1 for p in perpendicular]
    # normalize to perpendicular_offset_dist
    perpendicular = [perpendicular_offset_dist * t for t in perpendicular]
    # add this to elbow pos and save
    perp_pos = [a + b for a, b in zip(elbow.pos, perpendicular)]

    # 4, create the six ik bones
    # cannot reference other bone idxs until they are inserted!!
    start = max(arm_idx, armtwist_idx)
    armD_idx = start + 1
    armDend_idx = start + 2
    armDik_idx = start + 3
    armT_idx = start + 4
    armTend_idx = start + 5
    armTik_idx = start + 6

    # make armD, pos=arm.pos, parent=arm.parent, tail=armDend
    armD = pmxstruct.PmxBone(
        name_jp=side[0] + arm_s[0] + n_base[0],
        name_en=arm_s[1] + n_base[1] + side[1],
        pos=arm.pos,
        parent_idx=-99,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=False,
        has_visible=False,
        has_enabled=False,
        has_ik=False,
        tail_usebonelink=True,
        tail=-99,
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=arm.has_localaxis,
        localaxis_x=arm.localaxis_x,
        localaxis_z=arm.localaxis_z,
        has_externalparent=False,
    )
    # make armDend, pos=elbow.pos, parent=armD_idx
    armDend = pmxstruct.PmxBone(
        name_jp=side[0] + arm_s[0] + n_base[0] + n_end[0],
        name_en=arm_s[1] + n_base[1] + side[1] + n_end[1],
        pos=elbow.pos,
        parent_idx=-99,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=False,
        has_visible=False,
        has_enabled=False,
        has_ik=False,
        tail_usebonelink=True,
        tail=-1,
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    # make armD_IK, pos=elbow.pos, parent=elbow.parent, target=armDend, link=armD
    armDik = pmxstruct.PmxBone(
        name_jp=side[0] + arm_s[0] + n_base[0] + n_ik[0],
        name_en=arm_s[1] + n_base[1] + n_ik[1] + side[1],
        pos=elbow.pos,
        parent_idx=-99,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=False,
        has_enabled=False,
        has_ik=True,
        tail_usebonelink=True,
        tail=-1,
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
        ik_target_idx=-99,
        ik_numloops=ik_numloops,
        ik_angle=ik_angle,
        ik_links=[
            pmxstruct.PmxBoneIkLink(idx=-99,
                                    limit_min=ikD_lim_min,
                                    limit_max=ikD_lim_max)
        ])

    # make armT, pos=elbow.pos, parent=armD_idx, tail=armTend
    armT = pmxstruct.PmxBone(
        name_jp=side[0] + arm_s[0] + n_twist[0],
        name_en=arm_s[1] + n_twist[1] + side[1],
        pos=elbow.pos,
        parent_idx=-99,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=False,
        has_visible=False,
        has_enabled=False,
        has_ik=False,
        tail_usebonelink=True,
        tail=-99,
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    # make armTend, pos=perp_pos, parent=armT_idx
    armTend = pmxstruct.PmxBone(
        name_jp=side[0] + arm_s[0] + n_twist[0] + n_end[0],
        name_en=arm_s[1] + n_twist[1] + side[1] + n_end[1],
        pos=perp_pos,
        parent_idx=-99,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=False,
        has_visible=False,
        has_enabled=False,
        has_ik=False,
        tail_usebonelink=True,
        tail=-1,
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    # make armT_IK, pos=perp_pos, parent=elbow.parent, target=armTend, link=armT
    armTik = pmxstruct.PmxBone(
        name_jp=side[0] + arm_s[0] + n_twist[0] + n_ik[0],
        name_en=arm_s[1] + n_twist[1] + n_ik[1] + side[1],
        pos=perp_pos,
        parent_idx=-99,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=False,
        has_enabled=False,
        has_ik=True,
        tail_usebonelink=True,
        tail=-1,
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
        ik_target_idx=-99,
        ik_numloops=ik_numloops,
        ik_angle=ik_angle,
        ik_links=[pmxstruct.PmxBoneIkLink(idx=-99)])

    # insert these 6 bones
    # TODO: create more efficient function for multi-insert? nah, this is fine
    insert_single_bone(pmx, armD, armD_idx)
    insert_single_bone(pmx, armDend, armDend_idx)
    insert_single_bone(pmx, armDik, armDik_idx)
    insert_single_bone(pmx, armT, armT_idx)
    insert_single_bone(pmx, armTend, armTend_idx)
    insert_single_bone(pmx, armTik, armTik_idx)
    # fix all references to other bones (-99s)
    armD.tail = armDend_idx
    armT.tail = armTend_idx
    armD.parent_idx = arm.parent_idx
    armDend.parent_idx = armD_idx
    armDik.parent_idx = elbow.parent_idx
    armT.parent_idx = armD_idx
    armTend.parent_idx = armT_idx
    armTik.parent_idx = elbow.parent_idx
    armDik.ik_target_idx = armDend_idx
    armDik.ik_links[0].idx = armD_idx
    armTik.ik_target_idx = armTend_idx
    armTik.ik_links[0].idx = armT_idx

    # 5, modify the armtwist-sub bones
    # first go back from obj to indices, since the bones moved
    armtwist_sub = [b.idx_within(pmx.bones) for b in armtwist_sub_obj]
    for b_idx in armtwist_sub:
        bone = pmx.bones[b_idx]
        # change parent from arm to armD
        bone.parent_idx = armD_idx
        # change partial inherit from armtwist to armT
        bone.inherit_parent_idx = armT_idx

    # 6, insert additional armtwist-sub bones and transfer weight to them

    # first, check whether armtwistX would receive any weights/RBs
    # note, transferring from armtwist to armtwist changes nothing, this is harmless, just for looking
    armtwistX_used = transfer_to_armtwist_sub(pmx, armtwist_idx, armtwist_idx)
    if armtwistX_used:
        asdf = len(armtwist_sub) + 1
        # make armtwistX, pos=armtwist.pos, parent=armD_idx, inherit armT=1.00
        armtwistX = pmxstruct.PmxBone(
            name_jp=side[0] + armtwist_s[0] + str(asdf),
            name_en=armtwist_s[1] + str(asdf) + side[1],
            pos=armtwist.pos,
            parent_idx=-99,
            deform_layer=0,
            deform_after_phys=False,
            has_rotate=True,
            has_translate=False,
            has_visible=False,
            has_enabled=True,
            has_ik=False,
            tail_usebonelink=True,
            tail=-1,
            inherit_rot=True,
            inherit_trans=False,
            inherit_parent_idx=-99,
            inherit_ratio=1.00,
            has_fixedaxis=False,
            has_localaxis=False,
            has_externalparent=False,
        )
        # insert armtwistX at max(armtwist_sub) + 1
        armtwistX_idx = max(armtwist_sub) + 1
        insert_single_bone(pmx, armtwistX, armtwistX_idx)
        # fix references to other bones
        armtwistX.parent_idx = armD_idx
        armtwistX.inherit_parent_idx = armT_idx

        # transfer all weight and rigidbody references from armtwist to armtwistX
        # this time the return val is not needed
        transfer_to_armtwist_sub(pmx, armtwist_idx, armtwistX_idx)
        armtwist_sub_obj.append(armtwistX)

    # second, do the same thing for armtwist0
    armtwist0_used = transfer_to_armtwist_sub(pmx, arm_idx, arm_idx)
    if armtwist0_used:
        # make armtwist0, pos=arm.pos, parent=armD_idx, inherit armT=0.00
        armtwist0 = pmxstruct.PmxBone(
            name_jp=side[0] + armtwist_s[0] + "0",
            name_en=armtwist_s[1] + "0" + side[1],
            pos=arm.pos,
            parent_idx=-99,
            deform_layer=0,
            deform_after_phys=False,
            has_rotate=True,
            has_translate=False,
            has_visible=False,
            has_enabled=True,
            has_ik=False,
            tail_usebonelink=True,
            tail=-1,
            inherit_rot=True,
            inherit_trans=False,
            inherit_parent_idx=-99,
            inherit_ratio=0.00,
            has_fixedaxis=False,
            has_localaxis=False,
            has_externalparent=False,
        )
        # insert armtwist0 at min(armtwist_sub)
        armtwist0_idx = min(armtwist_sub)
        insert_single_bone(pmx, armtwist0, armtwist0_idx)
        # fix references to other bones
        armtwist0.parent_idx = armD_idx
        armtwist0.inherit_parent_idx = armT_idx

        # transfer all weight and rigidbody references from arm to armtwist0
        # this time the return val is not needed
        transfer_to_armtwist_sub(pmx, arm_idx, armtwist0_idx)
        armtwist_sub_obj.append(armtwist0)

    # 7, detect & fix incorrect structure among primary bones
    # refresh list of armtwist_sub indixes cuz stuff was inserted
    armtwist_sub = [b.idx_within(pmx.bones) for b in armtwist_sub_obj]
    # elbow should be a child of arm or armtwist, NOT any of the armtwist-sub bones
    # this is to prevent deform layers from getting all fucky
    if elbow.parent_idx in armtwist_sub:
        newparent = max(arm_idx, armtwist_idx)
        core.MY_PRINT_FUNC("WARNING: fixing improper parenting for bone '%s'" %
                           elbow.name_jp)
        core.MY_PRINT_FUNC("parent was '%s', changing to '%s'" %
                           (pmx.bones[elbow.parent_idx].name_jp,
                            pmx.bones[newparent].name_jp))
        core.MY_PRINT_FUNC(
            "if this bone has a 'helper bone' please change its parent in the same way"
        )
        elbow.parent_idx = newparent

    # 8, fix shoulder-helper and elbow-helper if they exist
    # shoulder helper: parent=shoulder(C), inherit=arm
    # goto:            parent=shoulder(C), inherit=armD
    # elbow helper: parent=arm(twist), inherit=elbow
    # goto:         parent=armT,       inherit=elbowD

    # need to refresh elbow idx cuz it moved
    elbow_idx = elbow.idx_within(pmx.bones)
    for d, bone in enumerate(pmx.bones):
        # transfer "inherit arm" to "inherit armD"
        # this should be safe for all bones
        if bone.inherit_rot and bone.inherit_parent_idx == arm_idx:
            bone.inherit_parent_idx = armD_idx
        # transfer "parent armtwist" to "parent armT"
        # this needs to exclude elbow, D_IK, T_IK, armtwist, arm
        if bone.parent_idx == armtwist_idx:
            if d not in (elbow_idx, armDik_idx, armTik_idx, armtwist_idx,
                         arm_idx):
                bone.parent_idx = armT_idx
        if bone.parent_idx == arm_idx:
            if d not in (elbow_idx, armDik_idx, armTik_idx, armtwist_idx,
                         arm_idx):
                bone.parent_idx = armD_idx

    # 9, set the deform order of all the bones so that it doesn't break when armIK is added
    # what deform level should they start from?
    base_deform = max(arm.deform_layer, armtwist.deform_layer,
                      elbow.deform_layer)
    armD.deform_layer = base_deform + 2
    armDend.deform_layer = base_deform + 2
    armDik.deform_layer = base_deform + 2
    armT.deform_layer = base_deform + 3
    armTend.deform_layer = base_deform + 3
    armTik.deform_layer = base_deform + 3
    for bone in armtwist_sub_obj:
        bone.deform_layer = base_deform + 4

    # fix deform for anything hanging off of the armtwist bones (rare but sometimes exists)
    deform_changed = 0
    deform_changed += fix_deform_for_children(pmx, armD_idx)
    deform_changed += fix_deform_for_children(pmx, armT_idx)
    for idx in armtwist_sub:
        deform_changed += fix_deform_for_children(pmx, idx)

    if moreinfo and deform_changed:
        core.MY_PRINT_FUNC("modified deform order for %d existing bones" %
                           deform_changed)

    # done with this function???
    return None
def main(moreinfo=True):
	# the goal: extract rotation around the "arm" bone local X? axis and transfer it to rotation around the "armtwist" bone local axis
	
	# 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)
	core.MY_PRINT_FUNC("")
	# get bones
	realbones = pmx.bones
	
	twistbone_axes = []
	# then, grab the "twist" bones & save their fixed-rotate axes, if they have them
	# fallback plan: find the arm-to-elbow and elbow-to-wrist unit vectors and use those
	for i in range(len(jp_twistbones)):
		r = core.my_list_search(realbones, lambda x: x.name_jp == jp_twistbones[i], getitem=True)
		if r is None:
			core.MY_PRINT_FUNC("ERROR1: twist bone '{}'({}) cannot be found model, unable to continue. Ensure they use the correct semistandard names, or edit the script to change the JP names it is looking for.".format(jp_twistbones[i], eng_twistbones[i]))
			raise RuntimeError()
		if r.has_fixedaxis:
			# this bone DOES have fixed-axis enabled! use the unit vector in r[18]
			twistbone_axes.append(r.fixedaxis)
		else:
			# i can infer local axis by angle from arm-to-elbow or elbow-to-wrist
			start = core.my_list_search(realbones, lambda x: x.name_jp == jp_sourcebones[i], getitem=True)
			if start is None:
				core.MY_PRINT_FUNC("ERROR2: semistandard bone '%s' is missing from the model, unable to infer axis of rotation" % jp_sourcebones[i])
				raise RuntimeError()
			end = core.my_list_search(realbones, lambda x: x.name_jp == jp_pointat_bones[i], getitem=True)
			if end is None:
				core.MY_PRINT_FUNC("ERROR3: semistandard bone '%s' is missing from the model, unable to infer axis of rotation" % jp_pointat_bones[i])
				raise RuntimeError()
			start_pos = start.pos
			end_pos = end.pos
			# now have both startpoint and endpoint! find the delta!
			delta = [b - a for a,b in zip(start_pos, end_pos)]
			# normalize to length of 1
			length = core.my_euclidian_distance(delta)
			unit = [t / length for t in delta]
			twistbone_axes.append(unit)
	
	# done extracting axes limits from bone CSV, in list "twistbone_axes"
	core.MY_PRINT_FUNC("...done extracting axis limits from PMX...")
	
	
	###################################################################################
	# prompt VMD file name
	core.MY_PRINT_FUNC("Please enter name of VMD dance input file:")
	input_filename_vmd = core.MY_FILEPROMPT_FUNC(".vmd")
	
	# next, read/use/prune the dance vmd
	nicelist_in = vmdlib.read_vmd(input_filename_vmd, moreinfo=moreinfo)
	
	# sort boneframes into individual lists: one for each [Larm + Lelbow + Rarm + Relbow] and remove them from the master boneframelist
	# frames for all other bones stay in the master boneframelist
	all_sourcebone_frames = []
	for sourcebone in jp_sourcebones:
		# partition & writeback
		temp, nicelist_in.boneframes = core.my_list_partition(nicelist_in.boneframes, lambda x: x.name == sourcebone)
		# all frames for "sourcebone" get their own sublist here
		all_sourcebone_frames.append(temp)
	
	# verify that there is actually arm/elbow frames to process
	sourcenumframes = sum([len(x) for x in all_sourcebone_frames])
	if sourcenumframes == 0:
		core.MY_PRINT_FUNC("No arm/elbow bone frames are found in the VMD, nothing for me to do!")
		core.MY_PRINT_FUNC("Aborting: no files were changed")
		return None
	else:
		core.MY_PRINT_FUNC("...source contains " + str(sourcenumframes) + " arm/elbow bone frames to decompose...")
	
	if USE_OVERKEY_BANDAID:
		# to fix the path that the arms take during interpolation we need to overkey the frames
		# i.e. create intermediate frames that they should have been passing through already, to FORCE it to take the right path
		# i'm replacing the interpolation curves with actual frames
		for sublist in all_sourcebone_frames:
			newframelist = []
			sublist.sort(key=lambda x: x.f) # ensure they are sorted by frame number
			# for each frame
			for i in range(1, len(sublist)):
				this = sublist[i]
				prev = sublist[i-1]
				# use interpolation curve i to interpolate from i-1 to i
				# first: do i need to do anything or are they already close on the timeline?
				thisframenum = this.f
				prevframenum = prev.f
				if (thisframenum - prevframenum) <= OVERKEY_FRAME_SPACING:
					continue
				# if they are far enough apart that i need to do something,
				thisframequat = core.euler_to_quaternion(this.rot)
				prevframequat = core.euler_to_quaternion(prev.rot)
				# 3, 7, 11, 15 = r_ax, r_ay, r_bx, r_by
				bez = core.MyBezier((this.interp[3], this.interp[7]), (this.interp[11], this.interp[15]), resolution=50)
				# create new frames at these frame numbers, spacing is OVERKEY_FRAME_SPACING
				for interp_framenum in range(prevframenum + OVERKEY_FRAME_SPACING, thisframenum, OVERKEY_FRAME_SPACING):
					# calculate the x time percentage from prev frame to this frame
					x = (interp_framenum - prevframenum) / (thisframenum - prevframenum)
					# apply the interpolation curve to translate X to Y
					y = bez.approximate(x)
					# interpolate from prev to this by amount Y
					interp_quat = core.my_slerp(prevframequat, thisframequat, y)
					# begin building the new frame
					newframe = vmdstruct.VmdBoneFrame(
						name=this.name,  # same name
						f=interp_framenum,  # overwrite frame num
						pos=list(this.pos),  # same pos (but make a copy)
						rot=list(core.quaternion_to_euler(interp_quat)),  # overwrite euler angles
						phys_off=this.phys_off,  # same phys_off
						interp=list(core.bone_interpolation_default_linear)  # overwrite interpolation
					)
					newframelist.append(newframe)
				# overwrite thisframe interp curve with default too
				this.interp = list(core.bone_interpolation_default_linear) # overwrite custom interpolation
			# concat the new frames onto the existing frames for this sublist
			sublist += newframelist
			
	# re-count the number of frames for printing purposes
	totalnumframes = sum([len(x) for x in all_sourcebone_frames])
	overkeyframes = totalnumframes - sourcenumframes
	if overkeyframes != 0:
		core.MY_PRINT_FUNC("...overkeying added " + str(overkeyframes) + " arm/elbow bone frames...")
	core.MY_PRINT_FUNC("...beginning decomposition of " + str(totalnumframes) + " arm/elbow bone frames...")
	
	# now i am completely done reading the VMD file and parsing its data! everything has been distilled down to:
	# all_sourcebone_frames = [Larm, Lelbow, Rarm, Relbow] plus nicelist_in[1]
	
	###################################################################################
	# begin the actual calculations
	
	# output array
	new_twistbone_frames = []
	# progress tracker
	curr_progress = 0
	
	# for each sourcebone & corresponding twistbone,
	for (twistbone, axis_orig, sourcebone_frames) in zip(jp_twistbones, twistbone_axes, all_sourcebone_frames):
		# for each frame of the sourcebone,
		for frame in sourcebone_frames:
			# XYZrot = 567 euler
			quat_in = core.euler_to_quaternion(frame.rot)
			axis = list(axis_orig)	# make a copy to be safe
			
			# "swing twist decomposition"
			# swing = "local" x rotation and nothing else
			# swing = sourcebone, twist = twistbone
			(swing, twist) = swing_twist_decompose(quat_in, axis)
			
			# modify "frame" in-place
			# only modify the XYZrot to use new values
			new_sourcebone_euler = core.quaternion_to_euler(swing)
			frame.rot = list(new_sourcebone_euler)
			
			# create & store new twistbone frame
			# name=twistbone, framenum=copy, XYZpos=copy, XYZrot=new, phys=copy, interp16=copy
			new_twistbone_euler = core.quaternion_to_euler(twist)
			newframe = vmdstruct.VmdBoneFrame(
				name=twistbone,
				f=frame.f,
				pos=list(frame.pos),
				rot=list(new_twistbone_euler),
				phys_off=frame.phys_off,
				interp=list(frame.interp)
			)
			new_twistbone_frames.append(newframe)
			# print progress updates
			curr_progress += 1
			core.print_progress_oneline(curr_progress / totalnumframes)
	
	
	######################################################################
	# done with calculations!
	core.MY_PRINT_FUNC("...done with decomposition, now reassembling output...")
	# attach the list of newly created boneframes, modify the original input
	for sublist in all_sourcebone_frames:
		nicelist_in.boneframes += sublist
	nicelist_in.boneframes += new_twistbone_frames
	
	core.MY_PRINT_FUNC("")
	# write out the VMD
	output_filename_vmd = "%s_twistbones_for_%s.vmd" % \
						   (input_filename_vmd[0:-4], core.get_clean_basename(input_filename_pmx))
	output_filename_vmd = core.get_unused_file_name(output_filename_vmd)
	vmdlib.write_vmd(output_filename_vmd, nicelist_in, moreinfo=moreinfo)
	
	core.MY_PRINT_FUNC("Done!")
	return None
예제 #9
0
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
예제 #10
0
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
def identify_unused_bones(pmx: pmxstruct.Pmx, moreinfo: bool) -> List[int]:
    """
	Process the PMX and return a list of all unused bone indicies in the model.
	1. get bones used by a rigidbody.
	2. get bones that have weight on at least 1 vertex.
	3. mark "exception" bones, done here so parents of exception bones are kept too.
	4. inheritance, aka "bones used by bones", recursively climb the tree & get all bones the "true" used bones depend on.
	5. tails or point-ats.
	6. invert used to get set of unused.

	:param pmx: PMX list-of-lists object
	:param moreinfo: print extra info for debug or whatever
	:return: list of bone indices that are not used
	"""
    # python set: no duplicates! .add(newbone), "in", .discard(delbone)
    # true_used_bones is set of BONE INDEXES
    true_used_bones = set()  # exception bones + rigidbody bones + vertex bones
    vertex_ct = {
    }  # how many vertexes does each bone control? sometimes useful info

    # first: bones used by a rigidbody
    for body in pmx.rigidbodies:
        true_used_bones.add(body.bone_idx)

    # second: bones used by a vertex i.e. has nonzero weight
    # any vertex that has nonzero weight for that bone
    for vert in pmx.verts:
        for boneidx, weightval in vert.weight:
            if weightval != 0:
                true_used_bones.add(boneidx)
                core.increment_occurance_dict(vertex_ct, boneidx)

    # NOTE: some vertices/rigidbodies depend on "invalid" (-1) bones, clean that up here
    true_used_bones.discard(-1)

    # third: mark the "exception" bones as "used" if they are in the model
    for protect in BONES_TO_PROTECT:
        # get index from JP name
        i = core.my_list_search(pmx.bones, lambda x: x.name_jp == protect)
        if i is not None:
            true_used_bones.add(i)

    # build ik groups here
    # IKbone + chain + target are treated as a group... if any 1 is used, all of them are used. build those groups now.
    ik_groups = []  # list of sets
    for d, bone in enumerate(pmx.bones):
        if bone.has_ik:  # if ik enabled for this bone,
            ik_set = set()
            ik_set.add(d)  # this bone
            ik_set.add(bone.ik_target_idx)  # this bone's target
            for link in bone.ik_links:
                ik_set.add(link.idx)  # all this bone's IK links
            ik_groups.append(ik_set)

    # fourth: NEW APPROACH FOR SOLVING INHERITANCE: RECURSION!
    # for each bone that we know to be used, run UP the inheritance tree and collect everything that it depends on
    # recursion inputs: pmx bonelist, ik groups, set of already-known-used, and the bone to start from
    # bonelist is readonly, ik groups are readonly
    # set of already-known-used overlaps with set-being-built, probably just use one global ref to save time merging sets
    # standard way: input is set-of-already-known, return set-built-from-target, that requires merging results after each call tho
    # BUT each function level adds exactly 1 or 0 bones to the set, therefore can just modify the set that is being passed around

    def recursive_climb_inherit_tree(target: int, set_being_built):
        # implicitly inherits variables pmx, ik_groups from outer scope
        if target in set_being_built or target == -1:
            # stop condition: if this bone idx is already known to be used, i have already ran recursion from this node. don't do it again.
            # also abort if the target is -1 which means invalid bone
            return
        # if not already in the set, but recursion is being called on this, then this bone is a "parent" of a used bone and should be added.
        set_being_built.add(target)
        # now the parents of THIS bone are also used, so recurse into those.
        bb = pmx.bones[target]
        # acutal parent
        recursive_climb_inherit_tree(bb.parent_idx, set_being_built)
        # partial inherit: if partial rot or partial move, and ratio is nonzero and parent is valid
        if (bb.inherit_rot or bb.inherit_trans
            ) and bb.inherit_ratio != 0 and bb.inherit_parent_idx != -1:
            recursive_climb_inherit_tree(bb.inherit_parent_idx,
                                         set_being_built)
        # IK groups: if in an IK group, recurse to all members of that IK group
        for group in ik_groups:
            if target in group:
                for ik_member in group:
                    recursive_climb_inherit_tree(ik_member, set_being_built)

    parent_used_bones = set()  # true_used_bones + parents + point-at links
    # now that the recursive function is defined, actually invoke the function from every truly-used bone
    for tu in true_used_bones:
        recursive_climb_inherit_tree(tu, parent_used_bones)

    # fifth: "tail" or point-at links
    # propogate DOWN the inheritance tree exactly 1 level, no more.
    # also get all bones these tails depend on, it shouldn't depend on anything new but it theoretically can.
    final_used_bones = set()
    for bidx in parent_used_bones:
        b = pmx.bones[bidx]
        # if this bone has a tail,
        if b.tail_usebonelink:
            # add it and anything it depends on to the set.
            recursive_climb_inherit_tree(b.tail, final_used_bones)
    # now merge the two sets
    final_used_bones = final_used_bones.union(parent_used_bones)

    # sixth: assemble the final "unused" set by inverting
    # set of all bones, for inverting purposes
    all_bones_list = list(range(len(pmx.bones)))
    all_bones_set = set(all_bones_list)

    unused_bones = all_bones_set.difference(final_used_bones)
    unused_bones_list = sorted(list(unused_bones))

    # print neat stuff
    if moreinfo:
        if unused_bones_list:
            core.MY_PRINT_FUNC(
                "Bones: total=%d, true_used=%d, parents=%d, tails=%d, unused=%d"
                %
                (len(pmx.bones), len(true_used_bones), len(parent_used_bones) -
                 len(true_used_bones), len(final_used_bones) -
                 len(parent_used_bones), len(unused_bones_list)))
        # debug aid
        if PRINT_VERTICES_CONTROLLED_BY_EACH_BONE:
            core.MY_PRINT_FUNC("Number of vertices controlled by each bone:")
            for bp in all_bones_list:
                if bp in vertex_ct:
                    core.MY_PRINT_FUNC("#: %d    ct: %d" % (bp, vertex_ct[bp]))

    return unused_bones_list
예제 #12
0
def main(moreinfo=True):
    # copied codes
    core.MY_PRINT_FUNC("Please enter name of PMX model file:")
    input_filename_pmx = core.MY_FILEPROMPT_FUNC(".pmx")

    # object
    pmx_file_obj: pmxstruct.Pmx = pmxlib.read_pmx(input_filename_pmx,
                                                  moreinfo=moreinfo)

    # since there is an update to Valve Bip tools (I guess?), there is different bone names: the old and new one
    # only prefixes are changed along with order, thus there is a little bit scripting here to find the last leg
    big_dict: dict = {**body_dict, **leg_dict, **arm_dict, **finger_dict}

    #########################################################################
    # stage 1: create & insert core/base bones (grooves, mother,...)
    #########################################################################

    # base bone section
    # base order: 上半身, 下半身, 腰 (b_1), グルーブ, センター, 全ての親
    base_bone_4_name = "全ての親"  # motherbone
    base_bone_3_name = "センター"  # center
    base_bone_2_name = "グルーブ"  # groove
    base_bone_1_name = "腰"  # waist

    # note: Source models apparently have a much larger scale than MMD models
    base_bone_4_pos = [0, 0, 0]
    base_bone_3_pos = [0, 21, -0.533614993095398]
    base_bone_2_pos = base_bone_3_pos
    base_bone_1_pos = [0, 32, -0.533614993095398]

    # pelvis_pos = [-4.999999873689376e-06, 38.566917419433594, -0.533614993095398]

    # 全ての親, name_en, [0.0, 0.0, -0.4735046625137329], -1, 0, False,
    # True, True, True, True,
    # False, [0.0, 0.0, 0.0], False, False, None,
    # None, False, None, False, None, None, False, None, False,
    # None, None, None, None

    # base order: 上半身, 下半身, 腰 (b_1), グルーブ, センター, 全ての親
    base_bone_4_obj = pmxstruct.PmxBone(
        name_jp=base_bone_4_name,
        name_en="",
        pos=base_bone_4_pos,
        parent_idx=-1,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=False,
        tail_usebonelink=False,
        tail=[0, 3, 0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    insert_single_bone(pmx_file_obj, base_bone_4_obj, 0)

    base_bone_3_obj = pmxstruct.PmxBone(
        name_jp=base_bone_3_name,
        name_en="",
        pos=base_bone_3_pos,
        parent_idx=0,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=False,
        tail_usebonelink=False,
        tail=[0, -3, 0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    insert_single_bone(pmx_file_obj, base_bone_3_obj, 1)

    base_bone_2_obj = pmxstruct.PmxBone(
        name_jp=base_bone_2_name,
        name_en="",
        pos=base_bone_2_pos,
        parent_idx=1,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=False,
        tail_usebonelink=False,
        tail=[0, 0, 1.5],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    insert_single_bone(pmx_file_obj, base_bone_2_obj, 2)

    base_bone_1_obj = pmxstruct.PmxBone(
        name_jp=base_bone_1_name,
        name_en="",
        pos=base_bone_1_pos,
        parent_idx=2,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=False,
        tail_usebonelink=False,
        tail=[0, 0, 0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
    )
    insert_single_bone(pmx_file_obj, base_bone_1_obj, 3)

    #########################################################################
    # phase 2: translate Source names to MMD names
    #########################################################################

    # for each mapping of source-name to mmd-name,
    for mmd_name, source_possible_names in big_dict.items():
        # for each bone,
        for index, bone_object in enumerate(pmx_file_obj.bones):
            # if it has a source-name, replace with mmd-name
            if bone_object.name_jp in source_possible_names:
                pmx_file_obj.bones[index].name_jp = mmd_name

    # next, fix the lowerbody bone
    # find lowerbod
    lowerbod_obj = core.my_list_search(pmx_file_obj.bones,
                                       lambda x: x.name_jp == "下半身",
                                       getitem=True)
    # elif bone_object.name_jp in ["ValveBiped.Bip01_Pelvis", "bip_pelvis"]:
    if lowerbod_obj is not None:
        # should not be translateable
        lowerbod_obj.has_translate = False
        # parent should be waist
        lowerbod_obj.parent_idx = 3
    # next, fix the upperbody bone
    upperbod_obj = core.my_list_search(pmx_file_obj.bones,
                                       lambda x: x.name_jp == "上半身",
                                       getitem=True)
    if upperbod_obj is not None:
        # should not be translateable
        upperbod_obj.has_translate = False
        # parent should be waist
        upperbod_obj.parent_idx = 3

    #########################################################################
    # phase 3: create & insert IK bones for leg/toe
    #########################################################################
    # find the last leg item index
    # when creating IK bones, want to insert the IK bones after both legs
    r_l_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "右足")
    r_k_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "右ひざ")
    r_a_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "右足首")
    r_t_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "右つま先")
    l_l_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "左足")
    l_k_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "左ひざ")
    l_a_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "左足首")
    l_t_index = core.my_list_search(pmx_file_obj.bones,
                                    lambda x: x.name_jp == "左つま先")
    # if somehow they aren't found, default to 0
    if r_l_index is None: r_l_index = 0
    if r_k_index is None: r_k_index = 0
    if r_a_index is None: r_a_index = 0
    if r_t_index is None: r_t_index = 0
    if l_l_index is None: l_l_index = 0
    if l_k_index is None: l_k_index = 0
    if l_a_index is None: l_a_index = 0
    if l_t_index is None: l_t_index = 0

    if r_t_index > l_t_index:
        last_leg_item_index = r_t_index
    else:
        last_leg_item_index = l_t_index

    leg_left_ik_name = "左足IK"
    leg_left_toe_ik_name = "左つま先IK"
    leg_right_ik_name = "右足IK"
    leg_right_toe_ik_name = "右つま先IK"

    # these limits in degrees
    knee_limit_1 = [-180, 0.0, 0.0]
    knee_limit_2 = [-0.5, 0.0, 0.0]
    # other parameters
    ik_loops = 40
    ik_toe_loops = 8
    ik_angle = 114.5916  # degrees, =2 radians
    ik_toe_angle = 229.1831  # degrees, =4 radians

    # adding IK and such
    leg_left_ankle_obj = pmx_file_obj.bones[l_a_index]
    leg_left_toe_obj = pmx_file_obj.bones[l_t_index]
    leg_right_ankle_obj = pmx_file_obj.bones[r_a_index]
    leg_right_toe_obj = pmx_file_obj.bones[r_t_index]

    leg_left_ankle_pos = leg_left_ankle_obj.pos
    leg_left_toe_pos = leg_left_toe_obj.pos
    leg_right_ankle_pos = leg_right_ankle_obj.pos
    leg_right_toe_pos = leg_right_toe_obj.pos

    # toe /// places of some value wont match with the struct /// taken from hololive's korone model
    # name, name, [-0.823277473449707, 0.2155265510082245, -1.8799238204956055], 112, 0, False,
    # True, True, True, True,
    # False, [0.0, -1.3884940147399902, 1.2653569569920364e-07] /// This is offset, False, False, None,
    # None, False, None, False, None, None, False, None, True,
    # 111, 160, 1.0, [[110, None, None]]

    # leg
    # 右足IK, en_name, [-0.8402935862541199, 1.16348397731781, 0.3492986857891083], 0, 0, False,
    # True, True, True, True,
    # False, [0.0, -2.53071505085245e-07, 1.3884940147399902], False, False, None,
    # None, False, None, False, None, None, False, None, True,
    # 110, 85, 1.9896754026412964, [[109, [-3.1415927410125732, 0.0, 0.0], [-0.008726646192371845, 0.0, 0.0]]
    # /// These ik_links are in radians /// , [108, None, None]]

    leg_left_ik_obj = pmxstruct.PmxBone(
        name_jp=leg_left_ik_name,
        name_en="",
        pos=leg_left_ankle_pos,
        parent_idx=0,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=True,
        tail_usebonelink=False,
        tail=[0.0, 0.0, 1.0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
        ik_target_idx=l_a_index,
        ik_numloops=ik_loops,
        ik_angle=ik_angle,
        ik_links=[
            pmxstruct.PmxBoneIkLink(idx=l_k_index,
                                    limit_min=knee_limit_1,
                                    limit_max=knee_limit_2),
            pmxstruct.PmxBoneIkLink(idx=l_l_index)
        ],
    )
    insert_single_bone(pmx_file_obj, leg_left_ik_obj, last_leg_item_index + 1)

    leg_left_toe_ik_obj = pmxstruct.PmxBone(
        name_jp=leg_left_toe_ik_name,
        name_en="",
        pos=leg_left_toe_pos,
        parent_idx=last_leg_item_index + 1,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=True,
        tail_usebonelink=False,
        tail=[0.0, -1.0, 0.0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
        ik_target_idx=l_t_index,
        ik_numloops=ik_toe_loops,
        ik_angle=ik_toe_angle,
        ik_links=[pmxstruct.PmxBoneIkLink(idx=l_a_index)],
    )
    insert_single_bone(pmx_file_obj, leg_left_toe_ik_obj,
                       last_leg_item_index + 2)

    leg_right_ik_obj = pmxstruct.PmxBone(
        name_jp=leg_right_ik_name,
        name_en="",
        pos=leg_right_ankle_pos,
        parent_idx=0,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=True,
        tail_usebonelink=False,
        tail=[0.0, 0.0, 1.0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
        ik_target_idx=r_a_index,
        ik_numloops=ik_loops,
        ik_angle=ik_angle,
        ik_links=[
            pmxstruct.PmxBoneIkLink(idx=r_k_index,
                                    limit_min=knee_limit_1,
                                    limit_max=knee_limit_2),
            pmxstruct.PmxBoneIkLink(idx=r_l_index)
        ],
    )
    insert_single_bone(pmx_file_obj, leg_right_ik_obj, last_leg_item_index + 3)

    leg_right_toe_ik_obj = pmxstruct.PmxBone(
        name_jp=leg_right_toe_ik_name,
        name_en="",
        pos=leg_right_toe_pos,
        parent_idx=last_leg_item_index + 3,
        deform_layer=0,
        deform_after_phys=False,
        has_rotate=True,
        has_translate=True,
        has_visible=True,
        has_enabled=True,
        has_ik=True,
        tail_usebonelink=False,
        tail=[0.0, -1.0, 0.0],
        inherit_rot=False,
        inherit_trans=False,
        has_fixedaxis=False,
        has_localaxis=False,
        has_externalparent=False,
        ik_target_idx=r_t_index,
        ik_numloops=ik_toe_loops,
        ik_angle=ik_toe_angle,
        ik_links=[pmxstruct.PmxBoneIkLink(idx=r_a_index)],
    )
    insert_single_bone(pmx_file_obj, leg_right_toe_ik_obj,
                       last_leg_item_index + 4)

    # output the file
    output_filename_pmx = input_filename_pmx[0:-4] + "_sourcetrans.pmx"
    pmxlib.write_pmx(output_filename_pmx, pmx_file_obj, moreinfo=moreinfo)
    core.MY_PRINT_FUNC("Done!")
    return None