Exemplo n.º 1
0
    def create_model_view(self, cf: VmdCameraFrame):
        # モデル座標系(原点を見るため、単位行列)
        model_view = MMatrix4x4()
        model_view.setToIdentity()

        # カメラ角度
        camera_qq = self.calc_camera_qq(cf)

        # カメラの原点(グローバル座標)
        mat_origin = MMatrix4x4()
        mat_origin.setToIdentity()
        mat_origin.translate(cf.position)
        mat_origin.rotate(camera_qq)
        mat_origin.translate(MVector3D(0, 0, cf.length))
        camera_origin = mat_origin * MVector3D()

        mat_up = MMatrix4x4()
        mat_up.setToIdentity()
        mat_up.rotate(camera_qq)
        camera_up = mat_up * MVector3D(0, 1, 0)

        # カメラ座標系の行列
        # eye: カメラの原点(グローバル座標)
        # center: カメラの注視点(グローバル座標)
        # up: カメラの上方向ベクトル
        model_view.lookAt(camera_origin, cf.position, camera_up)

        return model_view
Exemplo n.º 2
0
    def calc_project_square_pos(self, cf: VmdCameraFrame,
                                global_vec: MVector3D):
        # モデル座標系
        model_view = self.create_model_view(cf)

        # プロジェクション座標系
        projection_view = self.create_projection_view(cf)

        # viewport
        viewport_rect = MRect(0, 0, 16, 9)

        # プロジェクション座標位置
        project_vec = global_vec.project(model_view, projection_view,
                                         viewport_rect)

        # プロジェクション座標正規位置
        project_square_vec = MVector3D()
        project_square_vec.setX(project_vec.x() / 16)

        if cf.length <= 0:
            project_square_vec.setY((-project_vec.y() + 9) / 9)
        else:
            project_square_vec.setY(project_vec.y() / 9)

        project_square_vec.setZ(project_vec.z())

        return project_square_vec
Exemplo n.º 3
0
    def prepare_circle(self, bone_name: str):
        try:
            logger.copy(self.options)

            logger.info("【スムージング1回目】%s 開始", bone_name)

            if self.options.model.bones[bone_name].fixed_axis != MVector3D():
                # 回転は3Dベクトルに直すため、そのボーンのローカル軸の向きを先に計算しておく
                local_axis = calc_local_axis(self.options.model, bone_name)
            else:
                # 通常ボーンはそのまま回す
                local_axis = MVector3D()

            prev_sep_fno = 0
            fnos = self.options.motion.get_bone_fnos(bone_name, is_read=True)
            for prev_fno, now_fno, next_fno in zip(fnos[:-2], fnos[1:-1],
                                                   fnos[2:]):

                prev_bf = self.options.motion.calc_bf(bone_name, prev_fno)
                now_bf = self.options.motion.calc_bf(bone_name, now_fno)
                next_bf = self.options.motion.calc_bf(bone_name, next_fno)

                # 読み込み時のキーで3キーフレで伸ばす
                for fno in range(prev_fno, next_fno):
                    if fno in fnos:
                        # 読み込みキーはスルー
                        continue

                    target_bf = VmdBoneFrame(fno)
                    target_bf.set_name(bone_name)
                    target_bf.key = True

                    if self.options.model.bones[bone_name].getRotatable():
                        # 回転可ボーンの場合、回転円形補間
                        self.interpolate_rot_circle(prev_bf, now_bf, next_bf,
                                                    target_bf, local_axis)

                    if self.options.model.bones[bone_name].getTranslatable():
                        # 移動可ボーンの場合、移動円形補間
                        self.interpolate_mov_circle(prev_bf, now_bf, next_bf,
                                                    target_bf)

                    self.options.motion.regist_bf(target_bf, bone_name, fno)

                    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("【スムージング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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
 def __init__(self):
     self.fno = 0
     self.length = 0
     self.position = MVector3D(0, 0, 0)
     self.euler = MVector3D(0, 0, 0)
     self.interpolation = [20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107, 20, 107]
     self.angle = 0
     self.perspective = 0
     self.org_length = 0
     self.org_position = MVector3D(0, 0, 0)
Exemplo n.º 6
0
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
Exemplo n.º 7
0
    def calc_bone_length(self, bones, bone_indexes):
        for k, v in bones.items():
            if k in ["左足IK", "右足IK", "右足IK親", "左足IK親"] and v.getIkFlag():
                #   足IKの場合、ひざボーンの位置を採用する
                knee_pos = MVector3D(0, 0, 0)
                for lk in v.ik.link:
                    logger.test("k %s, link %s", k, lk)
                    if lk.bone_index in bone_indexes and "ひざ" in bones[bone_indexes[lk.bone_index]].name:
                        # 存在するボーンで、大きい方を採用
                        knee_pos = bones[bone_indexes[lk.bone_index]].position
                v.len_1d = knee_pos.length()

            elif k in ["左つま先IK", "右つま先IK"] and v.getIkFlag():
                # IKの場合、リンクボーンの離れている方を採用する
                farer_pos = MVector3D(0, 0, 0)
                for lk in v.ik.link:
                    logger.test("k %s, link %s", k, lk)
                    if lk.bone_index in bone_indexes and farer_pos.length() < bones[bone_indexes[lk.bone_index]].position.length():
                        # 存在するボーンで、大きい方を採用
                        farer_pos = bones[bone_indexes[lk.bone_index]].position
                        logger.test("farer: %s", bones[bone_indexes[lk.bone_index]].position)
                # 最も大きな値(離れている)のを採用
                v.len_1d = farer_pos.length()

            elif k in ["グルーブ", "センター", "腰"]:
                # 親がグルーブの場合、センターとの連動は行わない
                v.len_1d = v.position.length()
                if k == "センター":
                    v.len_3d = MVector3D(1, v.position.length(), 1)
                else:
                    v.len_3d = MVector3D(1, 1, 1)
            else:
                # IK以外の場合、親ボーンとの間の長さを「親ボーン」に設定する
                if v.parent_index is not None and v.parent_index in bone_indexes and not bone_indexes[v.parent_index] in ["腰", "グルーブ", "センター", "左足IK", "右足IK", "左つま先IK", "右つま先IK", "右足IK親", "左足IK親"]:
                    # 親ボーンを採用
                    pos = v.position - bones[bone_indexes[v.parent_index]].position
                    if v.len_1d > 0:
                        # 既にある場合、平均値を求めて設定する
                        bones[bone_indexes[v.parent_index]].len_1d = (v.len_1d + pos.length()) / 2
                        bones[bone_indexes[v.parent_index]].len_3d = (v.len_3d + pos) / 2
                    else:
                        # 0の場合はそのまま追加
                        bones[bone_indexes[v.parent_index]].len_1d = pos.length()
                        bones[bone_indexes[v.parent_index]].len_3d = pos

                    logger.test("bone: %s, len_3d: %s", bone_indexes[v.parent_index], bones[bone_indexes[v.parent_index]].len_3d)
                else:
                    # 自分が最親の場合、そのまま長さ
                    v.len_1d = v.position.length()
                    v.len_3d = v.position

                    logger.test("bone: %s, len_3d: %s", v.name, v.len_3d)
Exemplo n.º 8
0
    def test_MQuaternion_rotationTo(self):
        # base_v = MVector3D(2.617903093134025, 17.718517382408315, -0.12363630515345259)
        from_v = MVector3D(3.1009119396999303, 18.20660094549826, 0.3268970645363245)
        new_to_v = MVector3D(3.8692103699906015, 17.718517382408315, -0.12363630515345259)
        old_to_v = MVector3D(3.7909166767007814, 17.28083485225695, 1.101139547122786)

        new_from_v = new_to_v - from_v
        print(new_from_v)
        old_from_v = old_to_v - from_v
        print(old_from_v)
                
        qq1 = MQuaternion.rotationTo(new_from_v, old_from_v)
        print(qq1.toEulerAngles4MMD())
Exemplo n.º 9
0
    def test_create_link_2_top_one_01(self):
        pmx_data = PmxModel()
        pmx_data.bones["SIZING_ROOT_BONE"] = Bone("SIZING_ROOT_BONE",
                                                  "SIZING_ROOT_BONE",
                                                  MVector3D(), -1, 0, 0)
        pmx_data.bones["SIZING_ROOT_BONE"].index = -1
        pmx_data.bones["右肩"] = Bone("右肩", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["右肩"].index = 0

        with self.assertRaises(SizingException):
            links = pmx_data.create_link_2_top_one("右手首")

            print(links)
Exemplo n.º 10
0
    def test_create_link_2_top_one_02(self):
        pmx_data = PmxModel()
        pmx_data.bones["SIZING_ROOT_BONE"] = Bone("SIZING_ROOT_BONE",
                                                  "SIZING_ROOT_BONE",
                                                  MVector3D(), -1, 0, 0)
        pmx_data.bones["SIZING_ROOT_BONE"].index = -1
        pmx_data.bones["右肩"] = Bone("右肩", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["右肩"].index = 0
        pmx_data.bones["右手首"] = Bone("右手首", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["右手首"].index = 1

        links = pmx_data.create_link_2_top_one("右手首")
        self.assertEqual(len(links.all()), 1)
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
def fit_links(org_model: PmxModel, rep_model: PmxModel, rep_links: BoneLinks):
    # そのまま弄るとモデルのリンクも変わってしまうので、コピー
    fit_rep_links = copy.deepcopy(rep_links)

    # 上半身2の調整
    if fit_rep_links.get("上半身2"):
        if org_model.bones["上半身2"] and rep_model.bones["上半身2"] and org_model.bones["上半身"] and rep_model.bones["上半身"] \
           and org_model.bones["左腕"] and rep_model.bones["左腕"]:

            org_upper2_pos = org_model.bones["上半身2"].position
            org_upper_pos = org_model.bones["上半身"].position
            org_left_arm_pos = org_model.bones["左腕"].position

            rep_upper2_pos = rep_model.bones["上半身2"].position
            rep_upper_pos = rep_model.bones["上半身"].position
            rep_left_arm_pos = rep_model.bones["左腕"].position

            org_upper_diff = MVector3D(0, org_left_arm_pos.y(),
                                       0) - org_upper_pos
            org_upper_diff.setZ(1)
            org_upper_diff.abs()

            org_upper2_diff = org_upper2_pos - org_upper_pos
            org_upper2_diff.abs()

            rep_upper_diff = MVector3D(0, rep_left_arm_pos.y(),
                                       0) - rep_upper_pos
            rep_upper_diff.setZ(1)
            rep_upper_diff.abs()

            rep_upper2_diff = rep_upper2_pos - rep_upper_pos
            rep_upper2_diff.abs()

            logger.test("org_upper_diff: %s ", org_upper_diff)
            logger.test("org_upper2_diff: %s ", org_upper2_diff)
            logger.test("rep_upper_diff: %s ", rep_upper_diff)
            logger.test("rep_upper2_diff: %s ", rep_upper2_diff)

            upper2_diff_ratio = (org_upper2_diff / org_upper_diff) / (
                rep_upper2_diff / rep_upper_diff)
            logger.test("upper2_diff_ratio: %s ", upper2_diff_ratio)

            # 補正値
            rep_upper2_correction = (rep_upper2_diff *
                                     upper2_diff_ratio) - rep_upper2_diff
            rep_upper2_correction.effective()
            logger.info("rep_upper2_correction: %s ", rep_upper2_correction)

            fit_rep_links.get("上半身2").position += rep_upper2_correction

    return fit_rep_links
Exemplo n.º 13
0
    def test_MMatrix4x4_rotate(self):
        mat = MMatrix4x4()
        mat.setToIdentity()
        mat.translate(MVector3D(1, 2, 3))
        mat.rotate(MQuaternion.fromEulerAngles(10, 20, 30))

        print(mat)
Exemplo n.º 14
0
    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()
Exemplo n.º 15
0
 def __init__(self, fno=0):
     self.name = ''
     self.bname = ''
     self.fno = fno
     self.position = MVector3D()
     self.rotation = MQuaternion()
     self.org_position = MVector3D()
     self.org_rotation = MQuaternion()
     self.interpolation = [20, 20, 0, 0, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 20, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0] # noqa
     self.org_interpolation = [20, 20, 0, 0, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 20, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 20, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 20, 20, 20, 20, 20, 107, 107, 107, 107, 107, 107, 107, 107, 0, 0, 0] # noqa
     # 登録対象であるか否か
     self.key = False
     # VMD読み込み処理で読み込んだキーか
     self.read = False
     # 接触回避の方向
     self.avoidance = ""
Exemplo n.º 16
0
 def is_active_bones(self, bone_name):
     for bf in self.bones[bone_name].values():
         if bf.position != MVector3D():
             return True
         if bf.rotation != MQuaternion():
             return True
     
     return False
Exemplo n.º 17
0
    def test_stance_shoulder_01(self):
        arm_slope = MVector3D(-1.837494969367981, 18.923479080200195,
                              0.4923856854438782)
        shoulder_slope = MVector3D(-0.8141360282897949, 19.6701602935791,
                                   0.4931679964065552)
        neck_base = MVector3D(0.0, 18.923479080200195, 0.4923856854438782)

        # 傾きパターン
        test_slope_param = [arm_slope, shoulder_slope, neck_base]
        slope_test_params = list(itertools.product(test_slope_param, repeat=2))
        # random.shuffle(slope_test_params)

        # 数値パターン
        test_number_param = [0, -1, 1]
        number_test_params = list(
            itertools.product(test_number_param, repeat=3))
        # random.shuffle(number_test_params)

        target_test_params = list(
            itertools.product(slope_test_params, number_test_params))

        for param in target_test_params:
            print("------------------")
            print("param[0][0]: %s" % param[0][0])
            print("param[0][1]: %s" % param[0][1])
            print("param[1][0]: %s" % param[1][0])
            print("param[1][1]: %s" % param[1][1])
            print("param[1][2]: %s" % param[1][2])
            rep_shoulder_slope = (param[0][0] - param[0][1]).normalized()
            rep_shoulder_slope_up = MVector3D(param[1][0], param[1][1],
                                              param[1][2])
            rep_shoulder_slope_cross = MVector3D.crossProduct(
                rep_shoulder_slope, rep_shoulder_slope_up).normalized()

            rep_shoulder_initial_slope_qq = MQuaternion.fromDirection(
                rep_shoulder_slope, rep_shoulder_slope_cross)
            print("rep_shoulder_slope: %s" % rep_shoulder_slope)
            print("rep_shoulder_slope_up: %s" % rep_shoulder_slope_up)
            print("qq: %s" % rep_shoulder_initial_slope_qq.toEulerAngles())

        self.assertTrue(True)
Exemplo n.º 18
0
    def smooth_filter_bf(self, data_set_no: int, bone_name: str, is_rot: bool, is_mov: bool, loop=1, \
                         config={"freq": 30, "mincutoff": 0.3, "beta": 0.01, "dcutoff": 0.25}, start_fno=-1, end_fno=-1, is_show_log=True):
        
        for n in range(loop):
            # 移動用フィルタ
            pxfilter = OneEuroFilter(**config)
            pyfilter = OneEuroFilter(**config)
            pzfilter = OneEuroFilter(**config)

            # 回転用フィルタ
            rxfilter = OneEuroFilter(**config)
            ryfilter = OneEuroFilter(**config)
            rzfilter = OneEuroFilter(**config)

            fnos = self.get_bone_fnos(bone_name)
            prev_sep_fno = 0

            # キーフレを取得する
            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)

            # 全区間をフィルタにかける
            for fno in fnos:
                now_bf = self.calc_bf(bone_name, fno)

                if is_mov:
                    # 移動XYZそれぞれにフィルターをかける
                    px = pxfilter(now_bf.position.x(), fno)
                    py = pyfilter(now_bf.position.y(), fno)
                    pz = pzfilter(now_bf.position.z(), fno)
                    now_bf.position = MVector3D(px, py, pz)
                
                if is_rot:
                    # 回転XYZそれぞれにフィルターをかける(オイラー角)
                    now_qq = now_bf.rotation

                    r = now_qq.toEulerAngles()
                    rx = rxfilter(r.x(), fno)
                    ry = ryfilter(r.y(), fno)
                    rz = rzfilter(r.z(), fno)

                    # クォータニオンに戻して保持
                    new_qq = MQuaternion.fromEulerAngles(rx, ry, rz)
                    now_bf.rotation = new_qq

                if is_show_log and data_set_no > 0 and fno // 1000 > prev_sep_fno and fnos[-1] > 0:
                    logger.info("-- %sフレーム目:終了(%s%)【No.%s - フィルタリング - %s(%s)】", fno, round((fno / fnos[-1]) * 100, 3), data_set_no, bone_name, (n + 1))
                    prev_sep_fno = fno // 1000
Exemplo n.º 19
0
    def calc_project_vec(self, cf: VmdCameraFrame, global_vec: MVector3D):
        # モデル座標系
        model_view = self.create_model_view(cf)

        # プロジェクション座標系
        projection_view = self.create_projection_view(cf)

        # viewport
        viewport_rect = MRect(0, 0, 16, 9)

        # プロジェクション座標位置
        project_vec = global_vec.project(model_view, projection_view,
                                         viewport_rect)

        return project_vec
Exemplo n.º 20
0
    def calc_unproject_vec_from_square(self, cf: VmdCameraFrame,
                                       square_vec: MVector3D):
        # モデル座標系
        model_view = self.create_model_view(cf)

        # プロジェクション座標系
        projection_view = self.create_projection_view(cf)

        # viewport
        viewport_rect = MRect(0, 0, 16, 9)

        # 画面サイズに広げる
        project_vec = square_vec * MVector3D(16, 9, 1)

        # グローバル座標位置
        grobal_vec = project_vec.unproject(model_view, projection_view,
                                           viewport_rect)

        return grobal_vec
Exemplo n.º 21
0
def calc_global_pos_by_direction(direction_qq: MQuaternion,
                                 target_pos_3ds_dic: OrderedDict):
    direction_pos_dic = OrderedDict()

    for bone_name, target_pos in target_pos_3ds_dic.items():
        # # その地点の回転後の位置
        # direction_pos_dic[bone_name] = direction_qq * target_pos
        mat = MMatrix4x4()
        # 初期化
        mat.setToIdentity()
        # 指定位置
        mat.translate(target_pos)
        # 回転させる
        mat.rotate(direction_qq)
        # その地点の回転後の位置
        direction_pos_dic[bone_name] = mat * MVector3D()
        # logger.test("f: %s, direction_qq: %s", bone_name, direction_qq.toEulerAngles4MMD())
        # logger.test("f: %s, target_pos: %s", bone_name, target_pos)
        # logger.test("f: %s, direction_pos_dic: %s", bone_name, direction_pos_dic[bone_name])

    return direction_pos_dic
    def convert_noise(self, copy_no: int, seed: float):
        logger.info("ゆらぎ複製 【No.%s】", (copy_no + 1),
                    decoration=MLogger.DECORATION_LINE)

        # データをコピーしてそっちを弄る
        motion = self.options.motion.copy()

        for bone_name in motion.bones.keys():
            if not self.options.finger_noise_flg and "指" in bone_name:
                logger.info("-- 指スキップ【No.%s - %s】", copy_no + 1, bone_name)
                continue

            fnos = motion.get_bone_fnos(bone_name)
            prev_fno = 0
            prev_sep_fno = 0

            # 事前に細分化
            self.prepare_split_stance(motion, bone_name)
            logger.info("-- 準備完了【No.%s - %s】", copy_no + 1, bone_name)

            for fno in fnos:
                bf = motion.bones[bone_name][fno]
                org_bf = self.options.motion.calc_bf(bone_name, fno)

                # 移動
                if bf.position != MVector3D():
                    prev_org_bf = self.options.motion.calc_bf(
                        bone_name, prev_fno)

                    if org_bf.position == prev_org_bf.position and fno > 0:
                        bf.position = motion.calc_bf(bone_name,
                                                     prev_fno).position
                    else:
                        # 0だったら動かさない
                        if round(org_bf.position.x(), 1) != 0:
                            if self.options.motivation_flg:
                                bf.position.setX(
                                    bf.position.x() * seed +
                                    (0.5 - np.random.rand()) *
                                    (self.options.noise_size / 10))
                            else:
                                bf.position.setX(
                                    bf.position.x() +
                                    (0.5 - np.random.rand()) *
                                    (self.options.noise_size / 10))
                        if round(org_bf.position.y(),
                                 1) != 0 and "足IK" not in bone_name:
                            # 足IKのYは動かさない
                            if self.options.motivation_flg:
                                if org_bf.position.y() < 0:
                                    # Yはオリジナルがマイナスの場合は、マイナスのみに動かす
                                    bf.position.setY(
                                        bf.position.y() * seed +
                                        (0 - np.random.rand()) *
                                        (self.options.noise_size / 10))
                                elif org_bf.position.y() > 0:
                                    bf.position.setY(
                                        bf.position.y() * seed +
                                        (0.5 - np.random.rand()) *
                                        (self.options.noise_size / 10))
                            else:
                                bf.position.setY(
                                    bf.position.y() +
                                    (0.5 - np.random.rand()) *
                                    (self.options.noise_size / 10))
                        if round(org_bf.position.z(), 1) != 0:
                            if self.options.motivation_flg:
                                bf.position.setZ(
                                    bf.position.z() * seed +
                                    (0.5 - np.random.rand()) *
                                    (self.options.noise_size / 10))
                            else:
                                bf.position.setZ(
                                    bf.position.z() +
                                    (0.5 - np.random.rand()) *
                                    (self.options.noise_size / 10))

                        # 移動補間曲線
                        for (bz_idx1, bz_idx2, bz_idx3, bz_idx4) in [MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs, \
                                                                     MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs, \
                                                                     MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs]:
                            noise_interpolation = bf.interpolation[
                                bz_idx1] + math.ceil((0.5 - np.random.rand()) *
                                                     self.options.noise_size)
                            bf.interpolation[bz_idx1] = bf.interpolation[
                                bz_idx2] = bf.interpolation[
                                    bz_idx3] = bf.interpolation[bz_idx4] = int(
                                        noise_interpolation)

                # 回転
                euler = bf.rotation.toEulerAngles()
                # 回転は元が0であっても動かす(足は除く)
                if "足" not in bone_name and "ひざ" not in bone_name and "足首" not in bone_name:
                    if self.options.motivation_flg:
                        euler.setX(euler.x() * seed +
                                   (0.5 - np.random.rand()) *
                                   self.options.noise_size)
                        euler.setY(euler.y() * seed +
                                   (0.5 - np.random.rand()) *
                                   self.options.noise_size)
                        euler.setZ(euler.z() * seed +
                                   (0.5 - np.random.rand()) *
                                   self.options.noise_size)
                    else:
                        euler.setX(euler.x() + (0.5 - np.random.rand()) *
                                   self.options.noise_size)
                        euler.setY(euler.y() + (0.5 - np.random.rand()) *
                                   self.options.noise_size)
                        euler.setZ(euler.z() + (0.5 - np.random.rand()) *
                                   self.options.noise_size)
                bf.rotation = MQuaternion.fromEulerAngles(
                    euler.x(), euler.y(), euler.z())

                # 回転補間曲線
                for (bz_idx1, bz_idx2, bz_idx3, bz_idx4) in [
                        MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs,
                        MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs
                ]:
                    noise_interpolation = bf.interpolation[bz_idx1] + math.ceil(
                        (0.5 - np.random.rand()) * self.options.noise_size)

                    bf.interpolation[bz_idx1] = bf.interpolation[
                        bz_idx2] = bf.interpolation[
                            bz_idx3] = bf.interpolation[bz_idx4] = int(
                                noise_interpolation)

                # 前回fno保持
                prev_fno = fno

                if fno // 2000 > prev_sep_fno and fnos[-1] > 0:
                    logger.count(f"【No.{copy_no + 1} - {bone_name}】", fno,
                                 fnos)
                    prev_sep_fno = fno // 2000

        output_path = self.options.output_path.replace(
            "nxxx", "n{0:03d}".format(copy_no + 1))
        output_path = output_path.replace(
            "axxx", "a{0:+03d}".format(int(seed * 100) - 100))

        # 最後に出力
        VmdWriter(
            MOptionsDataSet(motion, None, self.options.model, output_path,
                            False, False, [], None, 0, [])).write()

        logger.info("出力成功: %s",
                    os.path.basename(output_path),
                    decoration=MLogger.DECORATION_BOX)

        return True
Exemplo n.º 23
0
def deform_rotation(model: PmxModel, motion: VmdMotion, bf: VmdBoneFrame):
    if bf.name not in model.bones:
        return MQuaternion()

    bone = model.bones[bf.name]
    rot = bf.rotation.normalized().copy()

    if bone.fixed_axis != MVector3D():
        # 回転角度を求める
        if rot != MQuaternion():
            # 回転補正
            if "右" in bone.name and rot.x() > 0 and bone.fixed_axis.x() <= 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)
            elif "左" in bone.name and rot.x() < 0 and bone.fixed_axis.x() >= 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)
            # 回転補正(コロン式ミクさん等軸反転パターン)
            elif "右" in bone.name and rot.x() < 0 and bone.fixed_axis.x() > 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)
            elif "左" in bone.name and rot.x() > 0 and bone.fixed_axis.x() < 0:
                rot.setX(rot.x() * -1)
                rot.setScalar(rot.scalar() * -1)

            rot.normalize()

        # 軸固定の場合、回転を制限する
        rot = MQuaternion.fromAxisAndAngle(bone.fixed_axis, rot.toDegree())

    if bone.getExternalRotationFlag(
    ) and bone.effect_index in model.bone_indexes:

        effect_parent_bone = bone
        effect_bone = model.bones[model.bone_indexes[bone.effect_index]]
        cnt = 0

        while cnt < 100:
            # 付与親が取得できたら、該当する付与親の回転を取得する
            effect_bf = motion.calc_bf(effect_bone.name, bf.fno)

            # 自身の回転量に付与親の回転量を付与率を加味して付与する
            if effect_parent_bone.effect_factor < 0:
                # マイナス付与の場合、逆回転
                rot = rot * (effect_bf.rotation *
                             abs(effect_parent_bone.effect_factor)).inverted()
            else:
                rot = rot * (effect_bf.rotation *
                             effect_parent_bone.effect_factor)

            if effect_bone.getExternalRotationFlag(
            ) and effect_bone.effect_index in model.bone_indexes:
                # 付与親の親として現在のeffectboneを保持
                effect_parent_bone = effect_bone
                # 付与親置き換え
                effect_bone = model.bones[model.bone_indexes[
                    effect_bone.effect_index]]
            else:
                break

            cnt += 1

    return rot
Exemplo n.º 24
0
def calc_global_pos(model: PmxModel,
                    links: BoneLinks,
                    motion: VmdMotion,
                    fno: int,
                    limit_links=None,
                    return_matrix=False,
                    is_local_x=False):
    trans_vs = calc_relative_position(model, links, motion, fno, limit_links)
    add_qs = calc_relative_rotation(model, links, motion, fno, limit_links)

    # 行列
    matrixs = [MMatrix4x4() for i in range(links.size())]

    for n, (lname, v, q) in enumerate(zip(links.all().keys(), trans_vs,
                                          add_qs)):
        # 行列を生成
        matrixs[n] = MMatrix4x4()
        # 初期化
        matrixs[n].setToIdentity()
        # 移動
        matrixs[n].translate(v)
        # 回転
        matrixs[n].rotate(q)

    total_mats = {}
    global_3ds_dic = OrderedDict()

    for n, (lname, v) in enumerate(zip(links.all().keys(), trans_vs)):
        if n == 0:
            total_mats[lname] = MMatrix4x4()
            total_mats[lname].setToIdentity()

        for m in range(n):
            # 最後のひとつ手前までループ
            if m == 0:
                # 0番目の位置を初期値とする
                total_mats[lname] = matrixs[0].copy()
            else:
                # 自分より前の行列結果を掛け算する
                total_mats[lname] *= matrixs[m].copy()

        # 自分は、位置だけ掛ける
        global_3ds_dic[lname] = total_mats[lname] * v

        # 最後の行列をかけ算する
        total_mats[lname] *= matrixs[n].copy()

        # ローカル軸の向きを調整する
        if n > 0 and is_local_x:
            # ボーン自身にローカル軸が設定されているか
            local_x_matrix = MMatrix4x4()
            local_x_matrix.setToIdentity()

            local_axis_qq = MQuaternion()

            if model.bones[lname].local_x_vector == MVector3D():
                # ローカル軸が設定されていない場合、計算

                # 自身から親を引いた軸の向き
                local_axis = model.bones[lname].position - links.get(
                    lname, offset=-1).position
                local_axis_qq = MQuaternion.fromDirection(
                    local_axis.normalized(), MVector3D(0, 0, 1))
            else:
                # ローカル軸が設定されている場合、その値を採用
                local_axis_qq = MQuaternion.fromDirection(
                    model.bones[lname].local_x_vector.normalized(),
                    MVector3D(0, 0, 1))

            local_x_matrix.rotate(local_axis_qq)

            total_mats[lname] *= local_x_matrix

    if return_matrix:
        # 行列も返す場合
        return global_3ds_dic, total_mats

    return global_3ds_dic
Exemplo n.º 25
0
def calc_IK(model: PmxModel,
            links: BoneLinks,
            motion: VmdMotion,
            fno: int,
            target_pos: MVector3D,
            ik_links: BoneLinks,
            max_count=10):
    for bone_name in list(ik_links.all().keys())[1:]:
        # bfをモーションに登録
        bf = motion.calc_bf(bone_name, fno)
        motion.regist_bf(bf, bone_name, fno)

    local_effector_pos = MVector3D()
    local_target_pos = MVector3D()

    for cnt in range(max_count):
        # 規定回数ループ
        for ik_idx, joint_name in enumerate(list(ik_links.all().keys())[1:]):
            # 処理対象IKボーン
            ik_bone = ik_links.get(joint_name)

            # 現在のボーングローバル位置と行列を取得
            global_3ds_dic, total_mats = calc_global_pos(model,
                                                         links,
                                                         motion,
                                                         fno,
                                                         return_matrix=True)

            # エフェクタ(末端)
            global_effector_pos = global_3ds_dic[ik_links.first_name()]

            # 注目ノード(実際に動かすボーン)
            joint_mat = total_mats[joint_name]

            # ワールド座標系から注目ノードの局所座標系への変換
            inv_coord = joint_mat.inverted()

            # 注目ノードを起点とした、エフェクタのローカル位置
            local_effector_pos = inv_coord * global_effector_pos
            local_target_pos = inv_coord * target_pos

            #  (1) 基準関節→エフェクタ位置への方向ベクトル
            basis2_effector = local_effector_pos.normalized()
            #  (2) 基準関節→目標位置への方向ベクトル
            basis2_target = local_target_pos.normalized()

            # ベクトル (1) を (2) に一致させるための最短回転量(Axis-Angle)
            # 回転角
            rotation_dot = MVector3D.dotProduct(basis2_effector, basis2_target)
            # 回転角度
            rotation_radian = math.acos(max(-1, min(1, rotation_dot)))

            if abs(rotation_radian) > 0.0001:
                # 一定角度以上の場合

                # 回転軸
                rotation_axis = MVector3D.crossProduct(
                    basis2_effector, basis2_target).normalized()
                # 回転角度
                rotation_degree = math.degrees(rotation_radian)

                # 関節回転量の補正(最大変位量を制限する)
                correct_qq = MQuaternion.fromAxisAndAngle(
                    rotation_axis, min(rotation_degree, ik_bone.degree_limit))

                # ジョイントに補正をかける
                bf = motion.calc_bf(joint_name, fno)
                new_ik_qq = correct_qq * bf.rotation

                # IK軸制限がある場合、上限下限をチェック
                if ik_bone.ik_limit_min != MVector3D(
                ) and ik_bone.ik_limit_max != MVector3D():
                    x_qq, y_qq, z_qq, yz_qq = separate_local_qq(
                        fno, bone_name, new_ik_qq,
                        model.get_local_x_axis(ik_bone.name))

                    logger.test("new_ik_qq: %s, x_qq: %s, y_qq: %s, z_qq: %s",
                                new_ik_qq.toEulerAngles(),
                                x_qq.toEulerAngles(), y_qq.toEulerAngles(),
                                z_qq.toEulerAngles())
                    logger.test("new_ik_qq: %s, x_qq: %s, y_qq: %s, z_qq: %s",
                                new_ik_qq.toDegree(), x_qq.toDegree(),
                                y_qq.toDegree(), z_qq.toDegree())

                    euler_x = min(
                        ik_bone.ik_limit_max.x(),
                        max(ik_bone.ik_limit_min.x(), x_qq.toDegree()))
                    euler_y = min(
                        ik_bone.ik_limit_max.y(),
                        max(ik_bone.ik_limit_min.y(), y_qq.toDegree()))
                    euler_z = min(
                        ik_bone.ik_limit_max.z(),
                        max(ik_bone.ik_limit_min.z(), z_qq.toDegree()))

                    logger.test(
                        "limit_qq: %s -> %s", new_ik_qq.toEulerAngles(),
                        MQuaternion.fromEulerAngles(euler_x, euler_y,
                                                    euler_z).toEulerAngles())

                    new_ik_qq = MQuaternion.fromEulerAngles(
                        euler_x, euler_y, euler_z)

                bf.rotation = new_ik_qq

        # 位置の差がほとんどない場合、終了
        if (local_effector_pos - local_target_pos).lengthSquared() < 0.0001:
            return

    return
Exemplo n.º 26
0
def separate_local_qq(fno: int, bone_name: str, qq: MQuaternion,
                      global_x_axis: MVector3D):
    # ローカル座標系(ボーンベクトルが(1,0,0)になる空間)の向き
    local_axis = MVector3D(1, 0, 0)

    # グローバル座標系(Aスタンス)からローカル座標系(ボーンベクトルが(1,0,0)になる空間)への変換
    global2local_qq = MQuaternion.rotationTo(global_x_axis, local_axis)
    local2global_qq = MQuaternion.rotationTo(local_axis, global_x_axis)

    # X成分を抽出する ------------

    mat_x1 = MMatrix4x4()
    mat_x1.setToIdentity()  # 初期化
    mat_x1.rotate(qq)  # 入力qq
    mat_x1.translate(global_x_axis)  # グローバル軸方向に伸ばす
    mat_x1_vec = mat_x1 * MVector3D()

    # YZの回転量(自身のねじれを無視する)
    yz_qq = MQuaternion.rotationTo(global_x_axis, mat_x1_vec)

    # 除去されたX成分を求める
    mat_x2 = MMatrix4x4()
    mat_x2.setToIdentity()  # 初期化
    mat_x2.rotate(qq)  # 元々の回転量

    mat_x3 = MMatrix4x4()
    mat_x3.setToIdentity()  # 初期化
    mat_x3.rotate(yz_qq)  # YZの回転量

    x_qq = (mat_x2 * mat_x3.inverted()).toQuaternion()

    # YZ回転からZ成分を抽出する --------------

    mat_z1 = MMatrix4x4()
    mat_z1.setToIdentity()  # 初期化
    mat_z1.rotate(yz_qq)  # YZの回転量
    mat_z1.rotate(global2local_qq)  # グローバル軸の回転量からローカルの回転量に変換
    mat_z1.translate(local_axis)  # ローカル軸方向に伸ばす

    mat_z1_vec = mat_z1 * MVector3D()
    mat_z1_vec.setZ(0)  # Z方向の移動量を潰す

    # ローカル軸からZを潰した移動への回転量
    local_z_qq = MQuaternion.rotationTo(local_axis, mat_z1_vec)

    # ボーンローカル座標系の回転をグローバル座標系の回転に戻す
    mat_z2 = MMatrix4x4()
    mat_z2.setToIdentity()  # 初期化
    mat_z2.rotate(local_z_qq)  # ローカル軸上のZ回転
    mat_z2.rotate(local2global_qq)  # ローカル軸上からグローバル軸上に変換

    z_qq = mat_z2.toQuaternion()

    # YZ回転からY成分だけ取り出す -----------

    mat_y1 = MMatrix4x4()
    mat_y1.setToIdentity()  # 初期化
    mat_y1.rotate(yz_qq)  # グローバルYZの回転量

    mat_y2 = MMatrix4x4()
    mat_y2.setToIdentity()  # 初期化
    mat_y2.rotate(z_qq)  # グローバルZの回転量
    mat_y2_qq = (mat_y1 * mat_y2.inverted()).toQuaternion()

    # X成分の捻れが混入したので、XY回転からYZ回転を取り出すことでXキャンセルをかける。
    mat_y3 = MMatrix4x4()
    mat_y3.setToIdentity()
    mat_y3.rotate(mat_y2_qq)
    mat_y3.translate(global_x_axis)
    mat_y3_vec = mat_y3 * MVector3D()

    y_qq = MQuaternion.rotationTo(global_x_axis, mat_y3_vec)

    return x_qq, y_qq, z_qq, yz_qq
Exemplo n.º 27
0
 def read_Vector3D(self):
     return MVector3D(self.read_float(), self.read_float(),
                      self.read_float())
Exemplo n.º 28
0
    def is_loaded_valid(self):
        if self.set_no == 0:
            # CSVとかのファイルは番号出力なし
            display_set_no = ""
        else:
            display_set_no = "{0}番目の".format(self.set_no)

        # 両方のPMXが読めて、モーションも読み込めた場合、キーチェック
        not_org_bones = []
        not_org_morphs = []
        not_rep_bones = []
        not_rep_morphs = []
        mismatch_bones = []

        motion = self.motion_vmd_file_ctrl.data
        org_pmx = self.org_model_file_ctrl.data
        rep_pmx = self.rep_model_file_ctrl.data

        if not motion or not org_pmx or not rep_pmx:
            # どれか読めてなければそのまま終了
            return True

        if motion.motion_cnt == 0:
            logger.warning("%sボーンモーションデータにキーフレームが登録されていません。",
                           display_set_no,
                           decoration=MLogger.DECORATION_BOX)
            return True

        result = True
        is_warning = False

        # ボーン
        for k in motion.bones.keys():
            bone_fnos = motion.get_bone_fnos(k)
            for fno in bone_fnos:
                if motion.bones[k][fno].position != MVector3D(
                ) or motion.bones[k][fno].rotation != MQuaternion():
                    # キーが存在しており、かつ初期値ではない値が入っている場合、警告対象

                    if k not in org_pmx.bones:
                        not_org_bones.append(k)

                    if k not in rep_pmx.bones:
                        not_rep_bones.append(k)

                    if k in org_pmx.bones and k in rep_pmx.bones:
                        mismatch_types = []
                        # 両方にボーンがある場合、フラグが同じであるかチェック
                        if org_pmx.bones[k].getRotatable(
                        ) != rep_pmx.bones[k].getRotatable():
                            mismatch_types.append("性能:回転")
                        if org_pmx.bones[k].getTranslatable(
                        ) != rep_pmx.bones[k].getTranslatable():
                            mismatch_types.append("性能:移動")
                        if org_pmx.bones[k].getIkFlag(
                        ) != rep_pmx.bones[k].getIkFlag():
                            mismatch_types.append("性能:IK")
                        if org_pmx.bones[k].getVisibleFlag(
                        ) != rep_pmx.bones[k].getVisibleFlag():
                            mismatch_types.append("性能:表示")
                        if org_pmx.bones[k].getManipulatable(
                        ) != rep_pmx.bones[k].getManipulatable():
                            mismatch_types.append("性能:操作")
                        if org_pmx.bones[k].display != rep_pmx.bones[k].display:
                            mismatch_types.append("表示枠")

                        if len(mismatch_types) > 0:
                            mismatch_bones.append(
                                f"{k}  【差異】{', '.join(mismatch_types)})")

                    # 1件あればOK
                    break

        for k in motion.morphs.keys():
            morph_fnos = motion.get_morph_fnos(k)
            for fno in morph_fnos:
                if motion.morphs[k][fno].ratio != 0:
                    # キーが存在しており、かつ初期値ではない値が入っている場合、警告対象

                    if k not in org_pmx.morphs:
                        not_org_morphs.append(k)

                    if k not in rep_pmx.morphs:
                        not_rep_morphs.append(k)

                    # 1件あればOK
                    break

        if len(not_org_bones) > 0 or len(not_org_morphs) > 0:
            logger.warning("%s%sに、モーションで使用されているボーン・モーフが不足しています。\nモデル: %s\n不足ボーン: %s\n不足モーフ: %s", \
                           display_set_no, self.org_model_file_ctrl.title, org_pmx.name, ",".join(not_org_bones), ",".join(not_org_morphs), decoration=MLogger.DECORATION_BOX)
            is_warning = True

        if len(not_rep_bones) > 0 or len(not_rep_morphs) > 0:
            logger.warning("%s%sに、モーションで使用されているボーン・モーフが不足しています。\nモデル: %s\n不足ボーン: %s\n不足モーフ: %s", \
                           display_set_no, self.rep_model_file_ctrl.title, rep_pmx.name, ",".join(not_rep_bones), ",".join(not_rep_morphs), decoration=MLogger.DECORATION_BOX)
            is_warning = True

        if len(mismatch_bones) > 0:
            logger.warning("%s%sで、モーションで使用されているボーンの性能等が異なっています。\nモデル: %s\n差異ボーン:\n %s", \
                           display_set_no, self.rep_model_file_ctrl.title, rep_pmx.name, "\n ".join(mismatch_bones), decoration=MLogger.DECORATION_BOX)
            is_warning = True

        if not is_warning:
            logger.info("モーションで使用されているボーン・モーフが揃っています。",
                        decoration=MLogger.DECORATION_BOX,
                        title="OK")

        return result
    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