def test_separate(self): MLogger.initialize(level=MLogger.TEST, is_file=True) logger = MLogger(__name__, level=MLogger.TEST) # motion = VmdReader("D:\\MMD\\MikuMikuDance_v926x64\\UserFile\\Motion\\ダンス_1人\\桃源恋歌配布用motion moka\\ノーマルTda式用0-2000.vmd").read_data() model = PmxReader( "D:\\MMD\\MikuMikuDance_v926x64\\UserFile\\Model\\VOCALOID\\初音ミク\\Tda式初音ミク・アペンドVer1.10\\Tda式初音ミク・アペンド_Ver1.10.pmx", is_check=False).read_data() bone_axis_dict = {} for bone_name in ["左ひじ", "右ひじ"]: local_x_axis = model.get_local_x_axis("左ひじ") local_z_axis = MVector3D(0, 0, -1) local_y_axis = MVector3D.crossProduct(local_x_axis, local_z_axis).normalized() bone_axis_dict[bone_name] = { "x": local_x_axis, "y": local_y_axis, "z": local_z_axis } new_ik_qq = MQuaternion.fromEulerAngles(24.58152072747821, 135.9182003500461, 56.36785502950723) ik_bone = model.bones["左ひじ"] fno = 394 x_qq, y_qq, z_qq, yz_qq = MServiceUtils.separate_local_qq( fno, ik_bone.name, new_ik_qq, bone_axis_dict[ik_bone.name]["x"]) logger.debug( f"now: {new_ik_qq.toEulerAngles()} -> {(y_qq * x_qq * z_qq).toEulerAngles()}" ) logger.debug( f"now: x: {x_qq.toDegree()}, y: {y_qq.toDegree()}, z: {z_qq.toDegree()}" ) for (x_sign, y_sign, z_sign) in list(itertools.product((1, -1), (1, -1), (1, -1))): new_x_qq = MQuaternion.fromAxisAndAngle(x_qq.vector(), x_qq.toDegree() * x_sign) new_y_qq = MQuaternion.fromAxisAndAngle(y_qq.vector(), y_qq.toDegree() * y_sign) new_z_qq = MQuaternion.fromAxisAndAngle(z_qq.vector(), z_qq.toDegree() * z_sign) logger.debug( f"x: {x_sign}, y: {y_sign}, z: {z_sign} -> {(new_y_qq * new_x_qq * new_z_qq).toEulerAngles()}" ) self.assertTrue(True)
def split_bf(self, fno: int, bf: VmdBoneFrame, local_x_axis: MVector3D, bone_name: str, rrxbn: str, rrybn: str, rrzbn: str, rmxbn: str, rmybn: str, rmzbn: str): motion = self.options.motion model = self.options.model if model.bones[bone_name].getRotatable(): # 回転を分ける if local_x_axis: # ローカルX軸がある場合 x_qq, y_qq, z_qq, _ = MServiceUtils.separate_local_qq( fno, bone_name, bf.rotation, local_x_axis) else: # ローカルX軸の指定が無い場合、グローバルで分ける euler = bf.rotation.toEulerAngles() x_qq = MQuaternion.fromEulerAngles(euler.x(), 0, 0) y_qq = MQuaternion.fromEulerAngles(0, euler.y(), 0) z_qq = MQuaternion.fromEulerAngles(0, 0, euler.z()) logger.debug( f"fno: {fno}, x_qq: {x_qq.toEulerAngles4MMD().to_log()}, y_qq: {y_qq.toEulerAngles4MMD().to_log()}, z_qq: {z_qq.toEulerAngles4MMD().to_log()}" ) if len(rrybn) > 0: ry_bf = motion.calc_bf(rrybn, fno) ry_bf.rotation = y_qq * ry_bf.rotation motion.regist_bf(ry_bf, ry_bf.name, fno) # 減算 bf.rotation *= y_qq.inverted() if len(rrxbn) > 0: rx_bf = motion.calc_bf(rrxbn, fno) rx_bf.rotation = x_qq * rx_bf.rotation motion.regist_bf(rx_bf, rx_bf.name, fno) # 減算 bf.rotation *= x_qq.inverted() if len(rrzbn) > 0: rz_bf = motion.calc_bf(rrzbn, fno) rz_bf.rotation = z_qq * rz_bf.rotation motion.regist_bf(rz_bf, rz_bf.name, fno) # 減算 bf.rotation *= z_qq.inverted() if len(rrxbn) > 0 and len(rrybn) > 0 and len(rrzbn) > 0: bf.rotation = MQuaternion() motion.regist_bf(bf, bf.name, fno) if model.bones[bone_name].getTranslatable(): # 移動を分ける if len(rmxbn) > 0: mx_bf = motion.calc_bf(rmxbn, fno) mx_bf.position.setX(mx_bf.position.x() + bf.position.x()) motion.regist_bf(mx_bf, mx_bf.name, fno) # 減算 bf.position.setX(0) if len(rmybn) > 0: my_bf = motion.calc_bf(rmybn, fno) my_bf.position.setY(my_bf.position.y() + bf.position.y()) motion.regist_bf(my_bf, my_bf.name, fno) # 減算 bf.position.setY(0) if len(rmzbn) > 0: mz_bf = motion.calc_bf(rmzbn, fno) mz_bf.position.setZ(mz_bf.position.z() + bf.position.z()) motion.regist_bf(mz_bf, mz_bf.name, fno) # 減算 bf.position.setZ(0) if len(rmxbn) > 0 and len(rmybn) > 0 and len(rmzbn) > 0: bf.position = MVector3D() motion.regist_bf(bf, bf.name, fno)
def convert_multi_split(self, bone_name: str, rrxbn: str, rrybn: str, rrzbn: str, rmxbn: str, rmybn: str, rmzbn: str, center_mx: str, center_my: str, center_mz: str): logger.info("多段分割【%s】", bone_name, decoration=MLogger.DECORATION_LINE) motion = self.options.motion model = self.options.model # 事前に変化量全打ち if bone_name == "センター" or bone_name == "グルーブ": fnos = self.prev_motion.get_differ_fnos(0, ["センター", "グルーブ"], limit_degrees=70, limit_length=1) else: fnos = self.prev_motion.get_differ_fnos(0, [bone_name], limit_degrees=70, limit_length=1) if len(fnos) == 0: return prev_sep_fno = 0 for fno in fnos: # 一度そのままキーを登録 motion.regist_bf(motion.calc_bf(bone_name, fno), bone_name, fno) # 補間曲線のため、もう一度取得しなおし bf = motion.calc_bf(bone_name, fno) if model.bones[bone_name].getRotatable(): rx_bf = motion.calc_bf(rrxbn, fno) motion.copy_interpolation(bf, rx_bf, MBezierUtils.BZ_TYPE_R) motion.regist_bf(rx_bf, rx_bf.name, fno, copy_interpolation=True) ry_bf = motion.calc_bf(rrybn, fno) motion.copy_interpolation(bf, ry_bf, MBezierUtils.BZ_TYPE_R) motion.regist_bf(ry_bf, ry_bf.name, fno, copy_interpolation=True) rz_bf = motion.calc_bf(rrzbn, fno) motion.copy_interpolation(bf, rz_bf, MBezierUtils.BZ_TYPE_R) motion.regist_bf(rz_bf, rz_bf.name, fno, copy_interpolation=True) if model.bones[bone_name].getTranslatable(): mx_bf = motion.calc_bf(rmxbn, fno) motion.copy_interpolation(bf, mx_bf, MBezierUtils.BZ_TYPE_MX) motion.regist_bf(mx_bf, mx_bf.name, fno, copy_interpolation=True) my_bf = motion.calc_bf(rmybn, fno) motion.copy_interpolation(bf, my_bf, MBezierUtils.BZ_TYPE_MY) motion.regist_bf(my_bf, my_bf.name, fno, copy_interpolation=True) mz_bf = motion.calc_bf(rmzbn, fno) motion.copy_interpolation(bf, mz_bf, MBezierUtils.BZ_TYPE_MZ) motion.regist_bf(mz_bf, mz_bf.name, fno, copy_interpolation=True) if fno // 500 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【キーフレ追加 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) prev_sep_fno = fno // 500 logger.info("分割準備完了【%s】", bone_name, decoration=MLogger.DECORATION_LINE) # ローカルX軸 local_x_axis = model.bones[bone_name].local_x_vector if local_x_axis == MVector3D(1, 0, 0) or local_x_axis == MVector3D(): # 指定が無い場合、腕系はローカルX軸、それ以外はノーマル if "腕" in bone_name or "ひじ" in bone_name or "手首" in bone_name: local_x_axis = model.get_local_x_axis(bone_name) else: local_x_axis = None logger.debug(f"{bone_name}, local_x_axis: {local_x_axis}") prev_sep_fno = 0 for fno in fnos: bf = motion.calc_bf(bone_name, fno) # 多段分割 self.split_bf(fno, bf, local_x_axis, bone_name, rrxbn, rrybn, rrzbn, rmxbn, rmybn, rmzbn) if fno // 500 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【多段分割 - %s】", fno, round((fno / fnos[-1]) * 100, 3), bone_name) prev_sep_fno = fno // 500 check_fnos = [] check_prev_next_fnos = {} # 分離後に乖離起こしてないかチェック for fno_idx, (prev_fno, next_fno) in enumerate(zip(fnos[:-1], fnos[1:])): fno = int(prev_fno + ((next_fno - prev_fno) / 2)) if fno not in fnos: check_fnos.append(fno) check_prev_next_fnos[fno] = { "prev": prev_fno, "next": next_fno } check_fnos = list(sorted(list(set(check_fnos)))) logger.debug("bone_name: %s, check_fnos: %s", bone_name, check_fnos) prev_sep_fno = 0 for fno in check_fnos: is_subdiv = False prev_motion_bf = self.prev_motion.calc_bf(bone_name, fno).copy() if model.bones[bone_name].getRotatable(): # 回転を分ける if local_x_axis: # ローカルX軸がある場合 x_qq, y_qq, z_qq, _ = MServiceUtils.separate_local_qq( fno, bone_name, prev_motion_bf.rotation, local_x_axis) else: # ローカルX軸の指定が無い場合、グローバルで分ける euler = prev_motion_bf.rotation.toEulerAngles() x_qq = MQuaternion.fromEulerAngles(euler.x(), 0, 0) y_qq = MQuaternion.fromEulerAngles(0, euler.y(), 0) z_qq = MQuaternion.fromEulerAngles(0, 0, euler.z()) if len(rrxbn) > 0: rx_bf = motion.calc_bf(rrxbn, fno) dot = MQuaternion.dotProduct(x_qq.normalized(), rx_bf.rotation.normalized()) if dot < 0.98: is_subdiv = True if len(rrybn) > 0: ry_bf = motion.calc_bf(rrybn, fno) dot = MQuaternion.dotProduct(y_qq.normalized(), ry_bf.rotation.normalized()) if dot < 0.98: is_subdiv = True if len(rrzbn) > 0: rz_bf = motion.calc_bf(rrzbn, fno) dot = MQuaternion.dotProduct(z_qq.normalized(), rz_bf.rotation.normalized()) if dot < 0.98: is_subdiv = True if model.bones[bone_name].getTranslatable(): if len(center_mx) > 0 or len(center_my) > 0 or len( center_mz) > 0: # センターとグルーブを両方分割してる場合 prev_center_motion_bf = self.prev_motion.calc_bf( "センター", fno).copy() if len(center_mx) > 0 and rmxbn == center_mx: prev_motion_bf.position.setX( prev_motion_bf.position.x() + prev_center_motion_bf.position.x()) if len(center_my) > 0 and rmybn == center_my: prev_motion_bf.position.setY( prev_motion_bf.position.y() + prev_center_motion_bf.position.y()) if len(center_mz) > 0 and rmzbn == center_mz: prev_motion_bf.position.setZ( prev_motion_bf.position.z() + prev_center_motion_bf.position.z()) # 移動を分ける if len(rmxbn) > 0: mx_bf = motion.calc_bf(rmxbn, fno) if np.diff( [mx_bf.position.x(), prev_motion_bf.position.x()]) > 0.1: is_subdiv = True if len(rmybn) > 0: my_bf = motion.calc_bf(rmybn, fno) if np.diff( [my_bf.position.y(), prev_motion_bf.position.y()]) > 0.1: is_subdiv = True if len(rmzbn) > 0: mz_bf = motion.calc_bf(rmzbn, fno) if np.diff( [mz_bf.position.z(), prev_motion_bf.position.z()]) > 0.1: is_subdiv = True if is_subdiv: # 細分化ONの場合、更に分割する if model.bones[bone_name].getRotatable(): if len(rrxbn) > 0: motion.regist_bf(self.prev_motion.calc_bf(rrxbn, fno), rrxbn, fno) if len(rrybn) > 0: motion.regist_bf(self.prev_motion.calc_bf(rrybn, fno), rrybn, fno) if len(rrzbn) > 0: motion.regist_bf(self.prev_motion.calc_bf(rrzbn, fno), rrzbn, fno) if model.bones[bone_name].getTranslatable(): if len(rmxbn) > 0: motion.regist_bf(self.prev_motion.calc_bf(rmxbn, fno), rmxbn, fno) if len(rmybn) > 0: motion.regist_bf(self.prev_motion.calc_bf(rmybn, fno), rmybn, fno) if len(rmzbn) > 0: motion.regist_bf(self.prev_motion.calc_bf(rmzbn, fno), rmzbn, fno) # 分割前の値を再登録 motion.regist_bf(self.prev_motion.calc_bf(bone_name, fno), bone_name, fno) subdiv_bf = motion.calc_bf(bone_name, fno) if bone_name == "グルーブ" and (len(center_mx) > 0 or len(center_my) > 0 or len(center_mz) > 0): prev_center_motion_bf = self.prev_motion.calc_bf( "センター", fno) if len(center_mx) > 0 and rmxbn == center_mx: subdiv_bf.position.setX( subdiv_bf.position.x() + prev_center_motion_bf.position.x()) if len(center_my) > 0 and rmybn == center_my: subdiv_bf.position.setY( subdiv_bf.position.y() + prev_center_motion_bf.position.y()) if len(center_mz) > 0 and rmzbn == center_mz: subdiv_bf.position.setZ( subdiv_bf.position.z() + prev_center_motion_bf.position.z()) # 多段分割 self.split_bf(fno, subdiv_bf, local_x_axis, bone_name, rrxbn, rrybn, rrzbn, rmxbn, rmybn, rmzbn) # prev_fno = check_prev_next_fnos[fno]["prev"] # next_fno = check_prev_next_fnos[fno]["next"] # logger.info(f"-- 軌跡ズレ防止のため、「{bone_name}」の{prev_fno}F~{next_fno}F間を細分化・不要キー除去します") # for f in range(prev_fno, next_fno + 1): # # 区間内を初期登録 # if model.bones[bone_name].getRotatable(): # # 回転を分ける # if local_x_axis: # # ローカルX軸がある場合 # x_qq, y_qq, z_qq, _ = MServiceUtils.separate_local_qq(f, bone_name, prev_motion_bf.rotation, local_x_axis) # else: # # ローカルX軸の指定が無い場合、グローバルで分ける # euler = prev_motion_bf.rotation.toEulerAngles() # x_qq = MQuaternion.fromEulerAngles(euler.x(), 0, 0) # y_qq = MQuaternion.fromEulerAngles(0, euler.y(), 0) # z_qq = MQuaternion.fromEulerAngles(0, 0, euler.z()) # if len(rrxbn) > 0: # prev_rx_bf = self.prev_motion.calc_bf(rrxbn, f).copy() # prev_rx_bf.rotation = x_qq # motion.regist_bf(prev_rx_bf, rrxbn, f) # if len(rrybn) > 0: # prev_ry_bf = self.prev_motion.calc_bf(rrybn, f).copy() # prev_ry_bf.rotation = y_qq # motion.regist_bf(prev_ry_bf, rrybn, f) # if len(rrzbn) > 0: # prev_rz_bf = self.prev_motion.calc_bf(rrzbn, f).copy() # prev_rz_bf.rotation = z_qq # motion.regist_bf(prev_rz_bf, rrzbn, f) # if model.bones[bone_name].getTranslatable(): # if len(center_mx) > 0 or len(center_my) > 0 or len(center_mz) > 0: # # センターとグルーブを両方分割してる場合 # prev_center_motion_bf = self.prev_motion.calc_bf("センター", fno).copy() # if len(center_mx) > 0 and rmxbn == center_mx: # prev_motion_bf.position.setX(prev_motion_bf.position.x() + prev_center_motion_bf.position.x()) # if len(center_my) > 0 and rmybn == center_my: # prev_motion_bf.position.setY(prev_motion_bf.position.y() + prev_center_motion_bf.position.y()) # if len(center_mz) > 0 and rmzbn == center_mz: # prev_motion_bf.position.setZ(prev_motion_bf.position.z() + prev_center_motion_bf.position.z()) # if len(rmxbn) > 0: # prev_mx_bf = self.prev_motion.calc_bf(rmxbn, f).copy() # prev_mx_bf.position.setX(prev_motion_bf.position.x()) # motion.regist_bf(prev_mx_bf, rmxbn, f) # if len(rmybn) > 0: # prev_my_bf = self.prev_motion.calc_bf(rmybn, f).copy() # prev_my_bf.position.setY(prev_motion_bf.position.y()) # motion.regist_bf(prev_my_bf, rmybn, f) # if len(rmzbn) > 0: # prev_mz_bf = self.prev_motion.calc_bf(rmzbn, f).copy() # prev_mz_bf.position.setZ(prev_motion_bf.position.z()) # motion.regist_bf(prev_mz_bf, rmzbn, f) # # 不要キー削除 # futures = [] # with ThreadPoolExecutor(thread_name_prefix="remove", max_workers=self.options.max_workers) as executor: # if model.bones[bone_name].getRotatable(): # if len(rrxbn) > 0: # futures.append(executor.submit(self.remove_unnecessary_bf, rrxbn, start_fno=prev_fno, end_fno=next_fno)) # if len(rrybn) > 0: # futures.append(executor.submit(self.remove_unnecessary_bf, rrybn, start_fno=prev_fno, end_fno=next_fno)) # if len(rrzbn) > 0: # futures.append(executor.submit(self.remove_unnecessary_bf, rrzbn, start_fno=prev_fno, end_fno=next_fno)) # if model.bones[bone_name].getTranslatable(): # if len(rmxbn) > 0: # futures.append(executor.submit(self.remove_unnecessary_bf, rmxbn, start_fno=prev_fno, end_fno=next_fno)) # if len(rmybn) > 0: # futures.append(executor.submit(self.remove_unnecessary_bf, rmybn, start_fno=prev_fno, end_fno=next_fno)) # if len(rmzbn) > 0: # futures.append(executor.submit(self.remove_unnecessary_bf, rmzbn, start_fno=prev_fno, end_fno=next_fno)) # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: # if not f.result(): # return False if fno // 1000 > prev_sep_fno and fnos[-1] > 0: logger.count(f"【分割後チェック - {bone_name}】", fno, fnos) prev_sep_fno = fno // 1000 logger.info("分割完了【%s】", bone_name, decoration=MLogger.DECORATION_LINE) # 元のボーン削除 if rrxbn != bone_name and rrybn != bone_name and rrzbn != bone_name and rmxbn != bone_name and rmybn != bone_name and rmzbn != bone_name: del motion.bones[bone_name] # # 跳ねてるの除去 # futures = [] # with ThreadPoolExecutor(thread_name_prefix="smooth", max_workers=self.options.max_workers) as executor: # if model.bones[bone_name].getRotatable(): # if len(rrxbn) > 0: # futures.append(executor.submit(self.smooth_bf, rrxbn)) # if len(rrybn) > 0: # futures.append(executor.submit(self.smooth_bf, rrybn)) # if len(rrzbn) > 0: # futures.append(executor.submit(self.smooth_bf, rrzbn)) # if model.bones[bone_name].getTranslatable(): # if len(rmxbn) > 0: # futures.append(executor.submit(self.smooth_bf, rmxbn)) # if len(rmybn) > 0: # futures.append(executor.submit(self.smooth_bf, rmybn)) # if len(rmzbn) > 0: # futures.append(executor.submit(self.smooth_bf, rmzbn)) # concurrent.futures.wait(futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) # for f in futures: # if not f.result(): # return False return True
def convert_target_ik2fk(self, ik_bone: Bone): motion = self.options.motion model = self.options.model bone_name = ik_bone.name logger.info("-- 腕IK変換準備:開始【%s】", bone_name) # モデルのIKボーンのリンク target_links = model.create_link_2_top_one(bone_name, is_defined=False) # モデルの人差し指先ボーンのリンク finger_bone_name = "{0}人指先実体".format(bone_name[0]) finger2_bone_name = "{0}小指先実体".format(bone_name[0]) wrist_bone_name = "{0}手首".format(bone_name[0]) finger_links = model.create_link_2_top_one(finger_bone_name, is_defined=False) finger2_links = model.create_link_2_top_one(finger2_bone_name, is_defined=False) # モデルのIKリンク if not (ik_bone.ik.target_index in model.bone_indexes and model.bone_indexes[ik_bone.ik.target_index] in model.bones): raise SizingException("{0} のTargetが有効なINDEXではありません。PMXの構造を確認してください。".format(bone_name)) # IKエフェクタ effector_bone_name = model.bone_indexes[ik_bone.ik.target_index] effector_links = model.create_link_2_top_one(effector_bone_name, is_defined=False) ik_links = BoneLinks() # 末端にエフェクタ effector_bone = model.bones[effector_bone_name].copy() effector_bone.degree_limit = math.degrees(ik_bone.ik.limit_radian) ik_links.append(effector_bone) for ik_link in ik_bone.ik.link: # IKリンクを末端から順に追加 if not (ik_link.bone_index in model.bone_indexes and model.bone_indexes[ik_link.bone_index] in model.bones): raise SizingException("{0} のLinkに無効なINDEXが含まれています。PMXの構造を確認してください。".format(bone_name)) link_bone = model.bones[model.bone_indexes[ik_link.bone_index]].copy() if link_bone.fixed_axis != MVector3D(): # 捩り系は無視 continue # 単位角 link_bone.degree_limit = math.degrees(ik_bone.ik.limit_radian) # # 角度制限 # if ik_link.limit_angle == 1: # link_bone.limit_min = ik_link.limit_min # link_bone.limit_max = ik_link.limit_max ik_links.append(link_bone) # 回転移管先ボーン transferee_bone = self.get_transferee_bone(ik_bone, effector_bone) # 移管先ボーンのローカル軸 transferee_local_x_axis = model.get_local_x_axis(transferee_bone.name) # 差異の大きい箇所にFKキーフレ追加 fnos = motion.get_differ_fnos(0, [bone_name], limit_degrees=20, limit_length=0.5) prev_sep_fno = 0 for fno in fnos: for link_name in list(ik_links.all().keys())[1:]: bf = motion.calc_bf(link_name, fno) motion.regist_bf(bf, link_name, fno) if fno // 500 > prev_sep_fno and fnos[-1] > 0: logger.count(f"【キーフレ追加 - {bone_name}】", fno, fnos) prev_sep_fno = fno // 500 logger.info("-- 腕IK変換準備:終了【%s】", bone_name) org_motion = motion.copy() # 元モーションを保持したら、IKキーフレ削除 del motion.bones[bone_name] for fno in fnos: # グローバル位置計算(元モーションの位置) target_ik_global_3ds = MServiceUtils.calc_global_pos(model, target_links, org_motion, fno) finger_global_3ds = MServiceUtils.calc_global_pos(model, finger_links, org_motion, fno) finger2_global_3ds = MServiceUtils.calc_global_pos(model, finger2_links, org_motion, fno) target_effector_pos = target_ik_global_3ds[bone_name] prev_diff = MVector3D() org_bfs = {} for link_name in list(ik_links.all().keys())[1:]: # 元モーションの角度で保持 bf = org_motion.calc_bf(link_name, fno).copy() org_bfs[link_name] = bf # 今のモーションも前のキーフレをクリアして再セット motion.regist_bf(bf, link_name, fno) # IK計算実行 for ik_cnt in range(50): MServiceUtils.calc_IK(model, effector_links, motion, fno, target_effector_pos, ik_links, max_count=1) # どちらにせよ一旦bf確定 for link_name in list(ik_links.all().keys())[1:]: ik_bf = motion.calc_bf(link_name, fno) motion.regist_bf(ik_bf, link_name, fno) # 現在のエフェクタ位置 now_global_3ds = MServiceUtils.calc_global_pos(model, effector_links, motion, fno) now_effector_pos = now_global_3ds[effector_bone_name] # 現在のエフェクタ位置との差分(エフェクタ位置が指定されている場合のみ) diff_pos = MVector3D() if target_effector_pos == MVector3D() else target_effector_pos - now_effector_pos if prev_diff == MVector3D() or (prev_diff != MVector3D() and diff_pos.length() < prev_diff.length()): if diff_pos.length() < 0.1: logger.debug("☆腕IK変換成功(%s): f: %s(%s), 指定 [%s], 現在[%s], 差異[%s(%s)]", ik_cnt, fno, bone_name, \ target_effector_pos.to_log(), now_effector_pos.to_log(), diff_pos.to_log(), diff_pos.length()) # org_bfを保持し直し for link_name in list(ik_links.all().keys())[1:]: bf = motion.calc_bf(link_name, fno).copy() org_bfs[link_name] = bf logger.test("org_bf保持: %s [%s]", link_name, bf.rotation.toEulerAngles().to_log()) # そのまま終了 break elif prev_diff == MVector3D() or diff_pos.length() < prev_diff.length(): logger.debug("☆腕IK変換ちょっと失敗採用(%s): f: %s(%s), 指定 [%s], 現在[%s], 差異[%s(%s)]", ik_cnt, fno, bone_name, \ target_effector_pos.to_log(), now_effector_pos.to_log(), diff_pos.to_log(), diff_pos.length()) # org_bfを保持し直し for link_name in list(ik_links.all().keys())[1:]: bf = motion.calc_bf(link_name, fno).copy() org_bfs[link_name] = bf logger.test("org_bf保持: %s [%s]", link_name, bf.rotation.toEulerAngles().to_log()) # 前回とまったく同じ場合か、充分に近い場合、IK的に動きがないので終了 if prev_diff == diff_pos or np.count_nonzero(np.where(np.abs(diff_pos.data()) > 0.05, 1, 0)) == 0: logger.debug("動きがないので終了") break # 前回差異を保持 prev_diff = diff_pos else: logger.debug("★腕IK変換ちょっと失敗不採用(%s): f: %s(%s), 指定 [%s], 現在[%s], 差異[%s(%s)]", ik_cnt, fno, bone_name, \ target_effector_pos.to_log(), now_effector_pos.to_log(), diff_pos.to_log(), diff_pos.length()) # 前回とまったく同じ場合か、充分に近い場合、IK的に動きがないので終了 if prev_diff == diff_pos or np.count_nonzero(np.where(np.abs(diff_pos.data()) > 0.05, 1, 0)) == 0: break else: logger.debug("★腕IK変換失敗(%s): f: %s(%s), 指定 [%s], 現在[%s], 差異[%s(%s)]", ik_cnt, fno, bone_name, \ target_effector_pos.to_log(), now_effector_pos.to_log(), diff_pos.to_log(), diff_pos.length()) # 前回とまったく同じ場合か、充分に近い場合、IK的に動きがないので終了 if prev_diff == diff_pos or np.count_nonzero(np.where(np.abs(diff_pos.data()) > 0.05, 1, 0)) == 0: logger.debug("動きがないので終了") break # 最後に成功したところに戻す for link_name in list(ik_links.all().keys())[1:]: bf = org_bfs[link_name].copy() logger.debug("確定bf: %s [%s]", link_name, bf.rotation.toEulerAngles().to_log()) motion.regist_bf(bf, link_name, fno) # 指先の現在の位置を再計算 if finger_links and finger_links.get(finger_bone_name) and finger_links.get(wrist_bone_name) and \ finger2_links and finger2_links.get(finger2_bone_name) and finger2_links.get(wrist_bone_name) and transferee_bone.name == wrist_bone_name: # 手首がある場合のみ、再計算 now_finger_global_3ds, now_finger_matrixs = MServiceUtils.calc_global_pos(model, finger_links, motion, fno, return_matrix=True) # 人指先のローカル位置 finger_initial_local_pos = now_finger_matrixs[wrist_bone_name].inverted() * now_finger_global_3ds[finger_bone_name] finger_local_pos = now_finger_matrixs[wrist_bone_name].inverted() * finger_global_3ds[finger_bone_name] finger_rotation = MQuaternion.rotationTo(finger_initial_local_pos, finger_local_pos) logger.debug("finger_rotation: %s [%s]", finger_bone_name, finger_rotation.toEulerAngles().to_log()) finger_x_qq, finger_y_qq, finger_z_qq, _ = MServiceUtils.separate_local_qq(fno, bone_name, finger_rotation, transferee_local_x_axis) logger.debug("finger_x_qq: %s [%s]", finger_bone_name, finger_x_qq.toEulerAngles().to_log()) logger.debug("finger_y_qq: %s [%s]", finger_bone_name, finger_y_qq.toEulerAngles().to_log()) logger.debug("finger_z_qq: %s [%s]", finger_bone_name, finger_z_qq.toEulerAngles().to_log()) # 手首の回転は、手首から見た指先の方向 transferee_bf = motion.calc_bf(transferee_bone.name, fno) transferee_bf.rotation = finger_rotation # 捩りなしの状態で一旦登録する motion.regist_bf(transferee_bf, transferee_bone.name, fno) now_finger2_global_3ds, now_finger2_matrixs = MServiceUtils.calc_global_pos(model, finger2_links, motion, fno, return_matrix=True) # 小指先のローカル位置 finger2_initial_local_pos = now_finger2_matrixs[wrist_bone_name].inverted() * now_finger2_global_3ds[finger2_bone_name] finger2_local_pos = now_finger2_matrixs[wrist_bone_name].inverted() * finger2_global_3ds[finger2_bone_name] finger2_rotation = MQuaternion.rotationTo(finger2_initial_local_pos, finger2_local_pos) logger.debug("finger2_rotation: %s [%s]", finger2_bone_name, finger2_rotation.toEulerAngles().to_log()) finger2_x_qq = MQuaternion.fromAxisAndAngle(transferee_local_x_axis, finger_x_qq.toDegree() + finger2_rotation.toDegree()) transferee_bf.rotation = finger_y_qq * finger2_x_qq * finger_z_qq motion.regist_bf(transferee_bf, transferee_bone.name, fno) logger.debug("transferee_qq: %s [%s], x [%s]", transferee_bone.name, transferee_bf.rotation.toEulerAngles().to_log(), finger2_x_qq.toEulerAngles().to_log()) logger.count("【腕IK変換 - {0}】".format(bone_name), fno, fnos)