def calc_bf_pos(self, prev_bf: VmdBoneFrame, fill_bf: VmdBoneFrame, next_bf: VmdBoneFrame): # 補間曲線を元に間を埋める if prev_bf.position != next_bf.position: # http://rantyen.blog.fc2.com/blog-entry-65.html # X移動補間曲線 xx, xy, xt = MBezierUtils.evaluate(next_bf.interpolation[MBezierUtils.MX_x1_idxs[3]], next_bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.MX_x2_idxs[3]], next_bf.interpolation[MBezierUtils.MX_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) # Y移動補間曲線 yx, yy, yt = MBezierUtils.evaluate(next_bf.interpolation[MBezierUtils.MY_x1_idxs[3]], next_bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.MY_x2_idxs[3]], next_bf.interpolation[MBezierUtils.MY_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) # Z移動補間曲線 zx, zy, zt = MBezierUtils.evaluate(next_bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], next_bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], next_bf.interpolation[MBezierUtils.MZ_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) fill_pos = MVector3D() fill_pos.setX(prev_bf.position.x() + ((next_bf.position.x() - prev_bf.position.x()) * xy)) fill_pos.setY(prev_bf.position.y() + ((next_bf.position.y() - prev_bf.position.y()) * yy)) fill_pos.setZ(prev_bf.position.z() + ((next_bf.position.z() - prev_bf.position.z()) * zy)) return fill_pos return prev_bf.position.copy()
def get_split_fill_fno(self, target_bone_name: str, prev_bf: VmdBoneFrame, next_bf: VmdBoneFrame, \ x1_idxs: list, y1_idxs: list, x2_idxs: list, y2_idxs: list): next_x1v = next_bf.interpolation[x1_idxs[3]] next_y1v = next_bf.interpolation[y1_idxs[3]] next_x2v = next_bf.interpolation[x2_idxs[3]] next_y2v = next_bf.interpolation[y2_idxs[3]] if not MBezierUtils.is_fit_bezier_mmd([MVector2D(), MVector2D(next_x1v, next_y1v), MVector2D(next_x2v, next_y2v), MVector2D()]): # ベジェ曲線がMMDの範囲内に収まっていない場合、中点で分割 new_fill_fno, _, _ = MBezierUtils.evaluate_by_t(next_x1v, next_y1v, next_x2v, next_y2v, prev_bf.fno, next_bf.fno, 0.5) if prev_bf.fno < new_fill_fno < next_bf.fno: return new_fill_fno # 範囲内でない場合 return -1
def splitBezier(self): # ベジェ曲線の分割 t, x, y, bresult, aresult, before_bz, after_bz = \ MBezierUtils.split_bezier_mmd(self.base_bezier.start_x.GetValue(), self.base_bezier.start_y.GetValue(), \ self.base_bezier.end_x.GetValue(), self.base_bezier.end_y.GetValue(), \ self.frame_start_ctrl.GetValue(), int(self.slice_slider_ctrl.GetValue()), self.frame_end_ctrl.GetValue()) if bresult: # 前半のキー self.before_bezier.panel.iserror = False self.before_bezier.start_x.SetValue(int(before_bz[1].x())) self.before_bezier.start_y.SetValue(int(before_bz[1].y())) self.before_bezier.end_x.SetValue(int(before_bz[2].x())) self.before_bezier.end_y.SetValue(int(before_bz[2].y())) else: self.before_bezier.panel.iserror = True if aresult: # 後半のキー self.after_bezier.panel.iserror = False self.after_bezier.start_x.SetValue(int(after_bz[1].x())) self.after_bezier.start_y.SetValue(int(after_bz[1].y())) self.after_bezier.end_x.SetValue(int(after_bz[2].x())) self.after_bezier.end_y.SetValue(int(after_bz[2].y())) else: self.after_bezier.panel.iserror = True
def join_bf(self, prev_bf: VmdBoneFrame, fill_bfs: list, next_bf: VmdBoneFrame, is_rot: bool, is_mov: bool, offset=0): rot_values = [] x_values = [] y_values = [] z_values = [] if is_rot: rot_values = np.array([prev_bf.rotation.toDegree() * np.sign(prev_bf.rotation.x())] \ + [bf.rotation.toDegree() * np.sign(bf.rotation.x()) for bf in fill_bfs] \ + [next_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())]) - (prev_bf.rotation.toDegree() * np.sign(next_bf.rotation.x())) logger.test("f: %s, %s, rot_values: %s", prev_bf.fno, prev_bf.name, rot_values) if is_mov: # X ------------ x_values = np.array([prev_bf.position.x()] + [bf.position.x() for bf in fill_bfs] + [next_bf.position.x()]) - prev_bf.position.x() # Y ----------- y_values = np.array([prev_bf.position.y()] + [bf.position.y() for bf in fill_bfs] + [next_bf.position.y()]) - prev_bf.position.y() # Z ----------- z_values = np.array([prev_bf.position.z()] + [bf.position.z() for bf in fill_bfs] + [next_bf.position.z()]) - prev_bf.position.z() logger.test("f: %s, %s, x_values: %s", prev_bf.fno, prev_bf.name, x_values) logger.test("f: %s, %s, y_values: %s", prev_bf.fno, prev_bf.name, y_values) logger.test("f: %s, %s, z_values: %s", prev_bf.fno, prev_bf.name, z_values) # 結合したベジェ曲線 joined_rot_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, rot_values, offset=offset, diff_limit=0.1) if is_rot else True joined_x_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, x_values, offset=offset, diff_limit=0.01) if is_mov else True joined_y_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, y_values, offset=offset, diff_limit=0.01) if is_mov else True joined_z_bzs = MBezierUtils.join_value_2_bezier(fill_bfs[-1].fno, next_bf.name, z_values, offset=offset, diff_limit=0.01) if is_mov else True if joined_rot_bzs and joined_x_bzs and joined_y_bzs and joined_z_bzs: # 結合できた場合、補間曲線をnextに設定 if is_rot: self.reset_interpolation_parts(prev_bf.name, next_bf, joined_rot_bzs, MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) if is_mov: self.reset_interpolation_parts(prev_bf.name, next_bf, joined_x_bzs, MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) self.reset_interpolation_parts(prev_bf.name, next_bf, joined_y_bzs, MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) self.reset_interpolation_parts(prev_bf.name, next_bf, joined_z_bzs, MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) return True # 結合できなかった場合、False return False
def test_split_bezier(self): x, y, t, before_bz, after_bz = MBezierUtils.split_bezier( 127, 0, 0, 127, 0, 3, 12) print("x: %s" % x) print("y: %s" % y) print("t: %s" % t) print("before_bz[0]: %s" % before_bz[0]) print("before_bz[1]: %s" % before_bz[1]) print("before_bz[2]: %s" % before_bz[2]) print("before_bz[3]: %s" % before_bz[3]) print("after_bz[0]: %s" % after_bz[0]) print("after_bz[1]: %s" % after_bz[1]) print("after_bz[2]: %s" % after_bz[2]) print("after_bz[3]: %s" % after_bz[3]) self.assertTrue(MBezierUtils.is_fit_bezier_mmd(before_bz)) self.assertFalse(MBezierUtils.is_fit_bezier_mmd(after_bz))
def test_MBezierUtils_evaluate03(self): x, y, t = MBezierUtils.evaluate(104, 63, 13, 111, 0, 5, 10) print("x: %s" % x) print("y: %s" % y) print("t: %s" % t) self.assertAlmostEqual(x, 0.5, delta=0.01) self.assertAlmostEqual(y, 0.74, delta=0.01) self.assertAlmostEqual(t, 0.61, delta=0.01)
def test_MBezierUtils_evaluate02(self): x, y, t = MBezierUtils.evaluate(20, 20, 107, 107, 181, 200, 205) print("x: %s" % x) print("y: %s" % y) print("t: %s" % t) self.assertAlmostEqual(x, 0.79, delta=0.01) self.assertAlmostEqual(y, 0.79, delta=0.01) self.assertAlmostEqual(t, 0.74, delta=0.01)
def test_scale_bezier(self): s1, s2, s3, s4 = MBezierUtils.scale_bezier(MVector2D(0.2, 0.7), MVector2D(0.3, -0.5), MVector2D(0.4, 1.2), MVector2D(1.2, 2)) print("s1: %s" % s1) print("s2: %s" % s2) print("s3: %s" % s3) print("s4: %s" % s4)
def test_MBezierUtils_evaluate05(self): x, y, t = MBezierUtils.evaluate(0, 127, 127, 0, 0, 2, 30) print("x: %s" % x) print("y: %s" % y) print("t: %s" % t) self.assertAlmostEqual(x, 0.06, delta=0.01) self.assertAlmostEqual(y, 0.34, delta=0.01) self.assertAlmostEqual(t, 0.16, delta=0.01)
def test_MBezierUtils_evaluate01(self): x, y, t = MBezierUtils.evaluate(20, 20, 107, 107, 0, 5, 10) print("x: %s" % x) print("y: %s" % y) print("t: %s" % t) self.assertAlmostEqual(x, 0.5, delta=0.01) self.assertAlmostEqual(y, 0.5, delta=0.01) self.assertAlmostEqual(t, 0.5, delta=0.01)
def calc_bf_rot(self, prev_bf: VmdBoneFrame, fill_bf: VmdBoneFrame, next_bf: VmdBoneFrame): if prev_bf.rotation != next_bf.rotation: # 回転補間曲線 rx, ry, rt = MBezierUtils.evaluate(next_bf.interpolation[MBezierUtils.R_x1_idxs[3]], next_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.R_x2_idxs[3]], next_bf.interpolation[MBezierUtils.R_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) return MQuaternion.slerp(prev_bf.rotation, next_bf.rotation, ry) return prev_bf.rotation.copy()
def test_split_bezier_mmd(self): x, y, t, is_fit_before_bz, is_fit_after_bz, before_bz, after_bz = MBezierUtils.split_bezier_mmd( 127, 0, 0, 127, 0, 8, 15) print("x: %s" % x) print("y: %s" % y) print("t: %s" % t) print("is_fit_before_bz: %s" % is_fit_before_bz) print("is_fit_after_bz: %s" % is_fit_after_bz) print("before_bz[0]: %s" % before_bz[0]) print("before_bz[1]: %s" % before_bz[1]) print("before_bz[2]: %s" % before_bz[2]) print("before_bz[3]: %s" % before_bz[3]) print("after_bz[0]: %s" % after_bz[0]) print("after_bz[1]: %s" % after_bz[1]) print("after_bz[2]: %s" % after_bz[2]) print("after_bz[3]: %s" % after_bz[3]) self.assertFalse(is_fit_before_bz) self.assertTrue(is_fit_after_bz)
def prepare_curve_morph(self, morph_name: str): try: logger.copy(self.options) logger.info("【スムージング1回目】%s 開始", morph_name) # 全キーフレを取得 fnos = self.options.motion.get_morph_fnos(morph_name) logger.debug("get_morph_fnos morph_name: %s, fnos: %s", morph_name, fnos) m_values = [] for fno in fnos: mf = self.options.motion.calc_mf(morph_name, fno) m_values.append(mf.ratio) m_all_values = MBezierUtils.calc_value_from_catmullrom(morph_name, fnos, m_values) logger.debug("get_morph_fnos morph_name: %s, m_all_values: %s", morph_name, m_all_values[:10]) # カトマル曲線で生成した値を全打ち for fno, mr in enumerate(m_all_values): mf = VmdMorphFrame(fno) mf.set_name(morph_name) if math.isnan(mr) or math.isinf(mr): logger.debug("** mr: (%s)%s", fno, mr) mf.ratio = mr self.options.motion.regist_mf(mf, morph_name, fno) logger.info("【スムージング1回目】%s 終了", morph_name) return True except SizingException as se: logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) return False except Exception as e: logger.error("スムージング処理が意図せぬエラーで終了しました。", e) return False
def test_scale_bezier_point(self): v = MBezierUtils.scale_bezier_point(MVector2D(1, 2), MVector2D(3, 4), MVector2D()) print("v: %s" % v) self.assertAlmostEqual(v.x(), 0, delta=0.1) self.assertAlmostEqual(v.y(), 0, delta=0.1)
def test_round_bezier_mmd(self): v = MBezierUtils.round_bezier_mmd(MVector2D(0.56, 0)) print("v: %s" % v) self.assertAlmostEqual(v.x(), 71, delta=0.1) self.assertAlmostEqual(v.y(), 0, delta=0.1)
def test_round_integer(self): self.assertEqual(MBezierUtils.round_integer(3.56), 4) self.assertEqual(MBezierUtils.round_integer(3.52), 4) self.assertEqual(MBezierUtils.round_integer(3.55), 4) self.assertEqual(MBezierUtils.round_integer(3.48), 3) self.assertEqual(MBezierUtils.round_integer(3.12), 3)
def calc_bf(self, bone_name: str, fno: int, is_key=False, is_read=False, is_reset_interpolation=False): fill_bf = VmdBoneFrame(fno) if bone_name not in self.bones: self.bones[bone_name] = {fno: fill_bf} fill_bf.set_name(bone_name) return fill_bf # 条件に合致するフレーム番号を探す # is_key: 登録対象のキーを探す # is_read: データ読み込み時のキーを探す if fno in self.bones[bone_name] and (not is_key or (is_key and self.bones[bone_name][fno].key)) and (not is_read or (is_read and self.bones[bone_name][fno].read)): # 合致するキーが見つかった場合、それを返す return self.bones[bone_name][fno] else: # 合致するキーが見つからなかった場合 if is_key or is_read: # 既存キーのみ探している場合はNone return None # 番号より前のフレーム番号 before_fnos = [x for x in sorted(self.bones[bone_name].keys()) if (x < fno)] # 番号より後のフレーム番号 after_fnos = [x for x in sorted(self.bones[bone_name].keys()) if (x > fno)] if len(after_fnos) == 0 and len(before_fnos) == 0: fill_bf.set_name(bone_name) return fill_bf if len(after_fnos) == 0: # 番号より前があって、後のがない場合、前のをコピーして返す fill_bf = self.bones[bone_name][before_fnos[-1]].copy() fill_bf.fno = fno fill_bf.key = False fill_bf.read = False return fill_bf if len(before_fnos) == 0: # 番号より後があって、前がない場合、後のをコピーして返す fill_bf = self.bones[bone_name][after_fnos[0]].copy() fill_bf.fno = fno fill_bf.key = False fill_bf.read = False return fill_bf prev_bf = self.bones[bone_name][before_fnos[-1]] next_bf = self.bones[bone_name][after_fnos[0]] # 名前をコピー fill_bf.name = prev_bf.name fill_bf.bname = prev_bf.bname # 補間曲線を元に間を埋める fill_bf.rotation = self.calc_bf_rot(prev_bf, fill_bf, next_bf) fill_bf.position = self.calc_bf_pos(prev_bf, fill_bf, next_bf) if is_reset_interpolation: # 補間曲線再設定の場合、範囲外でも構わないので補間曲線を設定する # 回転の分割 r_x, r_y, r_t, r_bresult, r_aresult, r_before_bz, r_after_bz \ = MBezierUtils.split_bezier_mmd(next_bf.interpolation[MBezierUtils.R_x1_idxs[3]], next_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.R_x2_idxs[3]], next_bf.interpolation[MBezierUtils.R_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) # 移動Xの分割 x_x, x_y, x_t, x_bresult, x_aresult, x_before_bz, x_aftex_bz \ = MBezierUtils.split_bezier_mmd(next_bf.interpolation[MBezierUtils.MX_x1_idxs[3]], next_bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.MX_x2_idxs[3]], next_bf.interpolation[MBezierUtils.MX_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) # 移動Yの分割 y_x, y_y, y_t, y_bresult, y_aresult, y_before_bz, y_aftey_bz \ = MBezierUtils.split_bezier_mmd(next_bf.interpolation[MBezierUtils.MY_x1_idxs[3]], next_bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.MY_x2_idxs[3]], next_bf.interpolation[MBezierUtils.MY_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) # 移動Zの分割 z_x, z_y, z_t, z_bresult, z_aresult, z_before_bz, z_aftez_bz \ = MBezierUtils.split_bezier_mmd(next_bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], next_bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ next_bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], next_bf.interpolation[MBezierUtils.MZ_y2_idxs[3]], \ prev_bf.fno, fill_bf.fno, next_bf.fno) # 強制設定 self.reset_interpolation(bone_name, prev_bf, fill_bf, next_bf, r_before_bz, r_after_bz, \ MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) self.reset_interpolation(bone_name, prev_bf, fill_bf, next_bf, x_before_bz, x_aftex_bz, \ MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) self.reset_interpolation(bone_name, prev_bf, fill_bf, next_bf, y_before_bz, y_aftey_bz, \ MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) self.reset_interpolation(bone_name, prev_bf, fill_bf, next_bf, z_before_bz, z_aftez_bz, \ MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) return fill_bf
def prepare_curve(self, bone_name: str): try: logger.copy(self.options) logger.info("【スムージング1回目】%s 開始", bone_name) # 全キーフレを取得 fnos = self.options.motion.get_bone_fnos(bone_name, is_read=True) rx_values = [] ry_values = [] rz_values = [] mx_values = [] my_values = [] mz_values = [] for fno in fnos: bf = self.options.motion.calc_bf(bone_name, fno) if self.options.model.bones[bone_name].getRotatable(): euler = bf.rotation.toEulerAngles() rx_values.append(euler.x()) ry_values.append(euler.y()) rz_values.append(euler.z()) if self.options.model.bones[bone_name].getTranslatable(): mx_values.append(bf.position.x()) my_values.append(bf.position.y()) mz_values.append(bf.position.z()) if self.options.model.bones[bone_name].getRotatable(): rx_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, rx_values) logger.info("【スムージング1回目】%s - 回転X 終了", bone_name) ry_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, ry_values) logger.info("【スムージング1回目】%s - 回転Y 終了", bone_name) rz_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, rz_values) logger.info("【スムージング1回目】%s - 回転Z 終了", bone_name) else: if len(fnos) > 0: rx_all_values = np.zeros(fnos[-1] + 1) ry_all_values = np.zeros(fnos[-1] + 1) rz_all_values = np.zeros(fnos[-1] + 1) else: rx_all_values = [0] ry_all_values = [0] rz_all_values = [0] if self.options.model.bones[bone_name].getTranslatable(): mx_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, mx_values) logger.info("【スムージング1回目】%s - 移動X 終了", bone_name) my_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, my_values) logger.info("【スムージング1回目】%s - 移動Y 終了", bone_name) mz_all_values = MBezierUtils.calc_value_from_catmullrom(bone_name, fnos, mz_values) logger.info("【スムージング1回目】%s - 移動Z 終了", bone_name) else: if len(fnos) > 0: mx_all_values = np.zeros(fnos[-1] + 1) my_all_values = np.zeros(fnos[-1] + 1) mz_all_values = np.zeros(fnos[-1] + 1) else: mx_all_values = [0] my_all_values = [0] mz_all_values = [0] # カトマル曲線で生成した値を全打ち for fno, (rx, ry, rz, mx, my, mz) in enumerate(zip(rx_all_values, ry_all_values, rz_all_values, mx_all_values, my_all_values, mz_all_values)): bf = self.options.motion.calc_bf(bone_name, fno) bf.rotation = MQuaternion.fromEulerAngles(rx, ry, rz) bf.position = MVector3D(mx, my, mz) self.options.motion.regist_bf(bf, bone_name, fno) logger.info("【スムージング1回目】%s 終了", bone_name) return True except SizingException as se: logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) return False except Exception as e: logger.error("スムージング処理が意図せぬエラーで終了しました。", e) return False