def interpolate_rot_circle(self, prev_bf: VmdBoneFrame, now_bf: VmdBoneFrame, next_bf: VmdBoneFrame, target_bf: VmdBoneFrame, local_axis: MVector3D): if local_axis != MVector3D(): # 軸がある場合、その方向に回す p_qq = MQuaternion.fromAxisAndAngle(local_axis, prev_bf.rotation.toDegree()) w_qq = MQuaternion.fromAxisAndAngle(local_axis, now_bf.rotation.toDegree()) n_qq = MQuaternion.fromAxisAndAngle(local_axis, next_bf.rotation.toDegree()) p = p_qq.toEulerAngles() w = w_qq.toEulerAngles() n = n_qq.toEulerAngles() else: p_qq = prev_bf.rotation.copy() w_qq = now_bf.rotation.copy() n_qq = next_bf.rotation.copy() # 軸がない場合、そのまま回転 p = p_qq.toEulerAngles() w = w_qq.toEulerAngles() n = n_qq.toEulerAngles() if target_bf.fno < now_bf.fno: # 変化量 t = (target_bf.fno - prev_bf.fno) / (now_bf.fno - prev_bf.fno) # デフォルト値 d_qq = MQuaternion.slerp(p_qq, w_qq, t) d = d_qq.toEulerAngles() out = interpolate_vec3(p, w, n, d, t, target_bf.fno) else: # 変化量 t = (target_bf.fno - now_bf.fno) / (next_bf.fno - now_bf.fno) # デフォルト値 d_qq = MQuaternion.slerp(w_qq, n_qq, t) d = d_qq.toEulerAngles() out = interpolate_vec3(w, n, p, d, t, target_bf.fno) out_qq = MQuaternion.fromEulerAngles(out.x(), out.y(), out.z()) if local_axis != MVector3D(): # 回転を元に戻す if target_bf.fno < now_bf.fno: d2_qq = MQuaternion.slerp(prev_bf.rotation, now_bf.rotation, t) else: d2_qq = MQuaternion.slerp(now_bf.rotation, next_bf.rotation, t) result_qq = (d_qq.inverted() * out_qq * d2_qq) else: result_qq = out_qq target_bf.rotation = result_qq
def smooth_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, limit_degrees: float, start_fno=-1, end_fno=-1, is_show_log=True): # キーフレを取得する if start_fno < 0 and end_fno < 0: # 範囲指定がない場合、全範囲 fnos = self.get_bone_fnos(bone_name) else: # 範囲指定がある場合はその範囲内だけ fnos = self.get_bone_fnos(bone_name, start_fno=start_fno, end_fno=end_fno) prev_sep_fno = 0 if len(fnos) > 2: for fno in fnos: prev_bf = self.calc_bf(bone_name, fno - 1) now_bf = self.calc_bf(bone_name, fno) next_bf = self.calc_bf(bone_name, fno + 1) if is_rot and now_bf.key: # 前後の内積 prev_next_dot = MQuaternion.dotProduct(prev_bf.rotation, next_bf.rotation) # 自分と後の内積 now_next_dot = MQuaternion.dotProduct(now_bf.rotation, next_bf.rotation) # 内積差分 diff = np.abs(np.diff([prev_next_dot, now_next_dot])) logger.test("set: %s, %s, f: %s, diff: %s, prev_next_dot: %s, now_next_dot: %s", data_set_no, bone_name, fno, diff, prev_next_dot, now_next_dot) # 前後と自分の内積の差が一定以上の場合、円滑化 if prev_next_dot > now_next_dot and diff > math.radians(limit_degrees): logger.debug("★ 円滑化 set: %s, %s, f: %s, diff: %s, prev_next_dot: %s, now_next_dot: %s", \ data_set_no, bone_name, fno, diff, prev_next_dot, now_next_dot) now_bf.rotation = MQuaternion.slerp(prev_bf.rotation, next_bf.rotation, ((now_bf.fno - prev_bf.fno) / (next_bf.fno - prev_bf.fno))) if is_show_log and data_set_no > 0 and fno // 500 > prev_sep_fno and fnos[-1] > 0: logger.info("-- %sフレーム目:終了(%s%)【No.%s - 円滑化 - %s】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name) prev_sep_fno = fno // 500
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 interpolate_vec3(op: MVector3D, ow: MVector3D, on: MVector3D, d: MVector3D, t: float, f: int): # 念のためコピー p = op.copy() w = ow.copy() n = on.copy() # 半径は3点間の距離の最長の半分 r = max(p.distanceToPoint(w), p.distanceToPoint(n), w.distanceToPoint(n)) / 2 logger.test("op: %s, ow: %s, on: %s, d: %s, t: %s, f: %s, r: %s", p.to_log(), w.to_log(), n.to_log(), d.to_log(), t, f, r) if r == 0: # 半径が取れなかった場合、そもそもまったく移動がないので、線分移動 return (p + n) * t if p == w or p == n or w == n: # 半径が0の場合か、どれか同じ値の場合、線対称な値を使用する n = d # 3点を通る球体の原点を求める c, radius = calc_sphere_center(p, w, n, r) if radius == 0: # 半径が取れなかった場合、そもそもまったく移動がないので、線分移動 return (p + n) * t # prev -> now の t分の回転量 pn_qq = MQuaternion.rotationTo((p - c).normalized(), (c - c).normalized()) pw_qq = MQuaternion.rotationTo((p - c).normalized(), (w - c).normalized()) # 球形補間の移動量 t_qq = MQuaternion.slerp(pn_qq, pw_qq, t) logger.test("(p - c): %s, (c - c): %s, (w - c): %s", (p - c).normalized(), (c - c).normalized(), (w - c).normalized()) logger.test("pn_qq: %s, pw: %s, t: %s", pn_qq, pw_qq, t_qq) out = t_qq * (p - c) + c # 値の変化がない場合、上書き if p.x() == w.x() == n.x(): out.setX(w.x()) if p.y() == w.y() == n.y(): out.setY(w.y()) if p.z() == w.z() == n.z(): out.setZ(w.z()) out.effective() logger.test(out.to_log()) return out