コード例 #1
0
    def test_MQuaternion_inverted(self):
        parent_qq = MQuaternion.fromEulerAngles(4.444088972232067, -131.68893846184505, 6.602773293502102)
        from_orientation = MQuaternion.fromEulerAngles(-28.005875419210547, -34.26259576800703, -81.65247193883769)
        initial = MQuaternion.fromEulerAngles(-0.0, 90.0, -90.0)
        cancel_qq = MQuaternion(-0.9992778836743259, -0.037996199801564046, -5.551115123125783e-17, 5.551115123125783e-17)

        from_rotation = parent_qq.inverted() * from_orientation * initial.inverted() * cancel_qq.inverted()
        print(from_rotation.toEulerAngles4MMD())
コード例 #2
0
    def test_MQuaternion_inverted02(self):
        initial = MQuaternion.fromEulerAngles(-23.093702434660457, 104.29537469962791, -100.75495862294058)
        orientation = MQuaternion.fromEulerAngles(2.8645534869583353, 112.98932633880118, -74.65035471978359)

        rot = orientation * initial.inverted()
        print(rot.toEulerAngles())

        rot = initial.inverted() * orientation
        print(rot.toEulerAngles())
コード例 #3
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)
コード例 #4
0
    def calc_camera_qq(self, cf: VmdCameraFrame):
        # カメラ角度
        camera_qq = MQuaternion.fromEulerAngles(-cf.euler.x(), cf.euler.y(),
                                                cf.euler.z())
        camera_qq.setX(-camera_qq.x())
        camera_qq.setScalar(-camera_qq.scalar())

        return camera_qq
コード例 #5
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
コード例 #6
0
ファイル: VmdData.py プロジェクト: JerryAJIAN/vmd_sizing
    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
コード例 #7
0
ファイル: test_utils.py プロジェクト: miu200521358/vmd_sizing
    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)
コード例 #8
0
    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
コード例 #9
0
    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
コード例 #10
0
    def convert_vmd(self):
        dt_now = datetime.now()

        bone_fpath = None
        bone_motion = VmdMotion()

        if self.options.bone_csv_path and os.path.exists(self.options.bone_csv_path):
            # ボーンモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(self.options.bone_csv_path)
            # ボーンモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(os.path.basename(self.options.bone_csv_path))

            bone_fpath = "{0}\\{1}_bone_{2:%Y%m%d_%H%M%S}.vmd".format(motion_csv_dir_path, motion_csv_file_name, dt_now)

            # ボーンCSV読み込み
            with open(self.options.bone_csv_path, encoding='cp932', mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for row in reader:
                    bf = VmdBoneFrame()

                    # ボーン名
                    bf.set_name(row[0])

                    # フレーム
                    bf.fno = int(float(row[1]))

                    # 位置
                    bf.position = MVector3D(float(row[2]), float(row[3]), float(row[4]))

                    # 回転
                    bf.rotation = MQuaternion.fromEulerAngles(float(row[5]), float(row[6]) * -1, float(row[7]) * -1)

                    # 補間曲線
                    # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換)
                    bf.interpolation = [int(float(row[8])), int(float(row[9])), int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), \
                                        int(float(row[14])), int(float(row[15])), int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), \
                                        int(float(row[20])), int(float(row[21])), int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), \
                                        int(float(row[26])), int(float(row[27])), int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), \
                                        int(float(row[32])), int(float(row[33])), int(float(row[34])), int(float(row[35])), int(float(row[36])), int(float(row[37])), \
                                        int(float(row[38])), int(float(row[39])), int(float(row[40])), int(float(row[41])), int(float(row[42])), int(float(row[43])), \
                                        int(float(row[44])), int(float(row[45])), int(float(row[46])), int(float(row[47])), int(float(row[48])), int(float(row[49])), \
                                        int(float(row[50])), int(float(row[51])), int(float(row[52])), int(float(row[53])), int(float(row[54])), int(float(row[55])), \
                                        int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \
                                        int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \
                                        int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))]
                    
                    bf.read = True
                    bf.key = True

                    if bf.name not in bone_motion.bones:
                        bone_motion.bones[bf.name] = {}

                    bone_motion.bones[bf.name][bf.fno] = bf

                    cnt += 1

                    if cnt % 10000 == 0:
                        logger.info("[ボーン] %sキー目:終了", cnt)

        if self.options.morph_csv_path and os.path.exists(self.options.morph_csv_path):
            # モーフモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(self.options.morph_csv_path)
            # モーフモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(os.path.basename(self.options.morph_csv_path))

            if not bone_fpath:
                bone_fpath = "{0}\\{1}_morph_{2:%Y%m%d_%H%M%S}.vmd".format(motion_csv_dir_path, motion_csv_file_name, dt_now)

            # モーフCSV読み込み
            with open(self.options.morph_csv_path, encoding='cp932', mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for row in reader:
                    mf = VmdMorphFrame()

                    # ボーン名
                    mf.set_name(row[0])

                    # フレーム
                    mf.fno = int(float(row[1]))

                    # 位置
                    mf.ratio = float(row[2])

                    if mf.name not in bone_motion.morphs:
                        bone_motion.morphs[mf.name] = {}

                    bone_motion.morphs[mf.name][mf.fno] = mf

                    cnt += 1

                    if cnt % 1000 == 0:
                        logger.info("[モーフ] %sキー目:終了", cnt)

        if len(bone_motion.bones.keys()) > 0 or len(bone_motion.morphs.keys()) > 0:
            # ボーンかモーフのキーがある場合、まとめて出力

            model = PmxModel()
            model.name = "CSV Convert Model"
            data_set = MOptionsDataSet(bone_motion, None, model, bone_fpath, False, False, [], None, None, [])

            VmdWriter(data_set).write()

            logger.info("ボーン・モーフモーションVMD: %s", bone_fpath, decoration=MLogger.DECORATION_BOX)

        if self.options.camera_csv_path and os.path.exists(self.options.camera_csv_path):
            # カメラモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(self.options.camera_csv_path)
            # カメラモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(os.path.basename(self.options.camera_csv_path))

            camera_fpath = "{0}\\{1}_camera_{2:%Y%m%d_%H%M%S}.vmd".format(motion_csv_dir_path, motion_csv_file_name, dt_now)
            camera_motion = VmdMotion()

            # カメラCSV読み込み
            with open(self.options.camera_csv_path, encoding='cp932', mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for row in reader:
                    cf = VmdCameraFrame()

                    # フレーム
                    cf.fno = int(row[0])

                    # 位置
                    cf.position = MVector3D(float(row[1]), float(row[2]), float(row[3]))

                    # 回転(オイラー角)
                    cf.euler = MVector3D(float(row[4]), float(row[5]), float(row[6]))

                    # 距離
                    cf.length = -(float(row[7]))

                    # 視野角
                    cf.angle = int(row[8])

                    # パース
                    cf.perspective = int(row[9])

                    # 補間曲線
                    cf.interpolation = [int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), int(float(row[14])), int(float(row[15])), \
                                        int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \
                                        int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \
                                        int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))]

                    camera_motion.cameras[cf.fno] = cf

                    cnt += 1

                    if cnt % 500 == 0:
                        logger.info("[カメラ] %sキー目:終了", cnt)

            if len(camera_motion.cameras) > 0:
                # ボーンかモーフのキーがある場合、まとめて出力

                model = PmxModel()
                model.name = "カメラ・照明"
                data_set = MOptionsDataSet(camera_motion, None, model, camera_fpath, False, False, [], None, None, [])

                VmdWriter(data_set).write()

                logger.info("カメラモーションVMD: %s", camera_fpath, decoration=MLogger.DECORATION_BOX)

        return True
コード例 #11
0
    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
コード例 #12
0
    def not_test_stance_shoulder_08(self):
        new_rep_to_pos = MVector3D(16.638640587237894, 19.455697325211673,
                                   4.067013732312591)
        # rep_base_pos = MVector3D(15.541596036361701, 18.419343683301417, 2.4565491530944494)
        rep_from_pos = MVector3D(16.07129122417615, 19.252113744303983,
                                 2.952803072461259)
        up_pos = MVector3D(0.02597404821369409, -0.6341368197928823,
                           0.7727844735774392)
        parent_qq = MQuaternion.fromEulerAngles(4.444080622855673,
                                                131.6889133202979,
                                                -6.602768822699441)

        arm_pos = MVector3D(-1.837494969367981, 18.923479080200195,
                            0.4923856854438782)
        shoulder_pos = MVector3D(-0.8141360282897949, 19.6701602935791,
                                 0.4931679964065552)
        neck_base_pos = MVector3D(0.0, 18.923479080200195, 0.4923856854438782)
        another_arm_pos = MVector3D(1.837494969367981, 18.923479080200195,
                                    0.4923856854438782)
        another_shoulder_pos = MVector3D(0.8141360282897949, 19.6701602935791,
                                         0.493167906999588)

        # 傾きパターン
        test_slope_param = [arm_pos, shoulder_pos, neck_base_pos]
        all_slope_test_params = list(
            itertools.product(test_slope_param, repeat=2))
        slope_test_params = [(x00, x01) for (x00, x01) in all_slope_test_params
                             if x00 != x01]

        # upパターン
        test_up_param = [
            arm_pos, shoulder_pos, neck_base_pos, another_arm_pos,
            another_shoulder_pos
        ]
        all_up_test_params = list(itertools.product(test_up_param, repeat=2))
        up_test_params = [(x00, x01) for (x00, x01) in all_up_test_params
                          if x00 != x01]

        # 数値パターン
        test_number_param = [0, -1, 1]
        all_number_test_params = list(
            itertools.product(test_number_param, repeat=3))
        number_test_params = [(x00, x01, x02)
                              for (x00, x01, x02) in all_number_test_params
                              if x00 == 0 or x01 == 0 or x02 == 0]

        target_test_params = list(
            itertools.product(slope_test_params, number_test_params,
                              up_test_params))
        print(len(target_test_params))

        random.shuffle(target_test_params)

        for params in target_test_params:
            print("-----------------------")
            print(params[0][0])
            print(params[0][1])
            print(params[1])
            print(params[2][0])
            print(params[2][1])

            rep_shoulder_slope = (params[0][0] - params[0][1]).normalized()
            print("rep_shoulder_slope: %s" % rep_shoulder_slope)

            rep_shoulder_slope_up = MVector3D(params[1][0], params[1][1],
                                              params[1][2])
            print("rep_shoulder_slope_up: %s" % rep_shoulder_slope_up)

            shoulder_cross = MVector3D.crossProduct(
                rep_shoulder_slope, rep_shoulder_slope_up).normalized()
            print("shoulder_cross: %s" % shoulder_cross)

            initial = MQuaternion.fromDirection(rep_shoulder_slope,
                                                shoulder_cross)
            print("initial: %s" % initial.toEulerAngles())

            direction = new_rep_to_pos - rep_from_pos
            up = MVector3D.crossProduct(direction, up_pos)
            from_orientation = MQuaternion.fromDirection(
                direction.normalized(), up.normalized())

            from_rotation = parent_qq.inverted(
            ) * from_orientation * initial.inverted()
            from_rotation.normalize()
            print("rot %s" % from_rotation.toEulerAngles4MMD())
            print("original: %s" % MVector3D(
                21.338723875696445, 15.845333083479046, 37.954108757721826))
コード例 #13
0
    def test_MQuaternion_dotProduct(self):
        qq1 = MQuaternion.fromEulerAngles(0, 0, 0)
        qq2 = MQuaternion.fromEulerAngles(160, 0, 0)
        dot = MQuaternion.dotProduct(qq1, qq2)

        print(dot)
コード例 #14
0
 def test_MQuaternion_toMatrix4x4(self):
     qq = MQuaternion.fromEulerAngles(0, 0, 0)
     print(qq.toMatrix4x4())
     self.assertTrue(True)
コード例 #15
0
    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)
コード例 #16
0
    def not_test_stance_shoulder_06(self):
        new_rep_to_pos = MVector3D(16.638640587237887, 19.455697325211673,
                                   4.067013732312591)
        rep_base_pos = MVector3D(15.527995423468052, 19.158781645638516,
                                 2.353690174209908)
        up_pos = MVector3D(0.02597404821369409, -0.6341368197928823,
                           0.7727844735774392)
        parent_qq = MQuaternion.fromEulerAngles(4.444080622855673,
                                                131.6889133202979,
                                                -6.602768822699441)

        arm_pos = MVector3D(-1.837494969367981, 18.923479080200195,
                            0.4923856854438782)
        shoulder_pos = MVector3D(-0.8141360282897949, 19.6701602935791,
                                 0.4931679964065552)
        neck_base_pos = MVector3D(0.0, 19.6701602935791, 0.4931679517030716)

        # stance_shoulder2neck_qq = MQuaternion.fromEulerAngles(0.015789129707102025, -0.040575112727633096, 42.52534119964952)
        # stance_shoulder2arm_qq = MQuaternion.fromEulerAngles(0.011536139571057251, 0.03538278693844499, -36.1158910081693)

        # 傾きパターン
        test_slope_param = [arm_pos, shoulder_pos, neck_base_pos]
        all_slope_test_params = list(
            itertools.product(test_slope_param, repeat=2))
        slope_test_params = [(x00, x01) for (x00, x01) in all_slope_test_params
                             if x00 != x01]

        # 数値パターン
        test_number_param = [0, -1, 1]
        all_number_test_params = list(
            itertools.product(test_number_param, repeat=3))
        number_test_params = [(x00, x01, x02)
                              for (x00, x01, x02) in all_number_test_params
                              if x00 == 0 or x01 == 0 or x02 == 0]

        # qqパターン
        # test_qq_param = [stance_shoulder2neck_qq, stance_shoulder2neck_qq.inverted(), stance_shoulder2arm_qq, stance_shoulder2arm_qq.inverted(), MQuaternion()]
        test_qq_param = [MQuaternion()]

        target_test_params = list(
            itertools.product(slope_test_params, number_test_params,
                              test_qq_param))
        print(len(target_test_params))

        random.shuffle(target_test_params)

        for params in target_test_params:
            print("-----------------------")
            print(params[0][0])
            print(params[0][1])
            print(params[1])
            print(params[2].toEulerAngles())

            rep_shoulder_slope = (params[0][0] - params[0][1]).normalized()
            print("rep_shoulder_slope: %s" % rep_shoulder_slope)

            rep_shoulder_slope_up = MVector3D(params[1][0], params[1][1],
                                              params[1][2])
            print("rep_shoulder_slope_up: %s" % rep_shoulder_slope_up)

            initial = MQuaternion.fromDirection(rep_shoulder_slope,
                                                rep_shoulder_slope_up)
            print("initial: %s" % initial.toEulerAngles())

            direction = new_rep_to_pos - rep_base_pos
            up = MVector3D.crossProduct(direction, up_pos)
            from_orientation = MQuaternion.fromDirection(
                direction.normalized(), up.normalized())

            from_rotation = parent_qq.inverted(
            ) * from_orientation * initial.inverted() * params[2]
            from_rotation.normalize()
            print("rot %s" % from_rotation.toEulerAngles4MMD())
コード例 #17
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
コード例 #18
0
    def convert_vmd(self):
        dt_now = datetime.now()

        bone_fpath = None
        bone_motion = VmdMotion()

        if self.options.bone_csv_path and os.path.exists(
                self.options.bone_csv_path):
            # ボーンモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(
                self.options.bone_csv_path)
            # ボーンモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(
                os.path.basename(self.options.bone_csv_path))

            bone_fpath = "{0}\\{1}_bone_{2:%Y%m%d_%H%M%S}.vmd".format(
                motion_csv_dir_path, motion_csv_file_name, dt_now)

            # ボーンCSV読み込み
            with open(self.options.bone_csv_path, encoding='cp932',
                      mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for ridx, row in enumerate(reader):
                    bf = VmdBoneFrame()
                    rno = ridx + 1

                    try:
                        if len(row) < 0 or not row[0]:
                            logger.error("[ボーン] %s行目のボーン名(1列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # ボーン名
                        bf.set_name(row[0])
                    except Exception as e:
                        logger.error("[ボーン] %s行目のボーン名の読み取りに失敗しました\n%s",
                                     rno,
                                     e,
                                     decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 1 or not row[1]:
                            logger.error("[ボーン] %s行目のフレーム番号(2列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # フレーム
                        bf.fno = int(float(row[1]))

                        if bf.fno < 0:
                            logger.error("[ボーン] %s行目のフレーム番号(2列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 4 or not row[2] or not row[3] or not row[4]:
                            logger.error("[ボーン] %s行目の位置(3-5列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 位置
                        bf.position = MVector3D(float(row[2]), float(row[3]),
                                                float(row[4]))
                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 7 or not row[5] or not row[6] or not row[7]:
                            logger.error("[ボーン] %s行目の回転(6-8列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 回転
                        bf.rotation = MQuaternion.fromEulerAngles(
                            float(row[5]),
                            float(row[6]) * -1,
                            float(row[7]) * -1)
                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目の回転の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 71:
                            logger.error(
                                "[ボーン] %s行目の補間曲線(9-72列目)のいずれかが設定されていません",
                                rno,
                                decoration=MLogger.DECORATION_BOX)
                            return False

                        for cidx in range(8, 72):
                            if not row[cidx]:
                                logger.error("[ボーン] %s行目の補間曲線の%s番目が設定されていません",
                                             rno,
                                             cidx - 7,
                                             decoration=MLogger.DECORATION_BOX)
                                return False

                        # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換)
                        bf.interpolation = [int(float(row[8])), int(float(row[9])), int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), \
                                            int(float(row[14])), int(float(row[15])), int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), \
                                            int(float(row[20])), int(float(row[21])), int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), \
                                            int(float(row[26])), int(float(row[27])), int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), \
                                            int(float(row[32])), int(float(row[33])), int(float(row[34])), int(float(row[35])), int(float(row[36])), int(float(row[37])), \
                                            int(float(row[38])), int(float(row[39])), int(float(row[40])), int(float(row[41])), int(float(row[42])), int(float(row[43])), \
                                            int(float(row[44])), int(float(row[45])), int(float(row[46])), int(float(row[47])), int(float(row[48])), int(float(row[49])), \
                                            int(float(row[50])), int(float(row[51])), int(float(row[52])), int(float(row[53])), int(float(row[54])), int(float(row[55])), \
                                            int(float(row[56])), int(float(row[57])), int(float(row[58])), int(float(row[59])), int(float(row[60])), int(float(row[61])), \
                                            int(float(row[62])), int(float(row[63])), int(float(row[64])), int(float(row[65])), int(float(row[66])), int(float(row[67])), \
                                            int(float(row[68])), int(float(row[69])), int(float(row[70])), int(float(row[71]))]

                        for bidx, bi in enumerate(bf.interpolation):
                            if 0 > bi:
                                logger.error(
                                    "[ボーン] %s行目の補間曲線(%s列目)に負数が設定されています",
                                    rno,
                                    bidx + 9,
                                    decoration=MLogger.DECORATION_BOX)
                                return False

                    except Exception as e:
                        logger.error(
                            "[ボーン] %s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    bf.read = True
                    bf.key = True

                    if bf.name not in bone_motion.bones:
                        bone_motion.bones[bf.name] = {}

                    bone_motion.bones[bf.name][bf.fno] = bf

                    cnt += 1

                    if cnt % 10000 == 0:
                        logger.info("[ボーン] %sキー目:終了", cnt)

        if self.options.morph_csv_path and os.path.exists(
                self.options.morph_csv_path):
            # モーフモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(
                self.options.morph_csv_path)
            # モーフモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(
                os.path.basename(self.options.morph_csv_path))

            if not bone_fpath:
                bone_fpath = "{0}\\{1}_morph_{2:%Y%m%d_%H%M%S}.vmd".format(
                    motion_csv_dir_path, motion_csv_file_name, dt_now)

            # モーフCSV読み込み
            with open(self.options.morph_csv_path, encoding='cp932',
                      mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for ridx, row in enumerate(reader):
                    mf = VmdMorphFrame()
                    rno = ridx + 1

                    try:
                        if len(row) < 0 or not row[0]:
                            logger.error("[モーフ] %s行目のモーフ名(1列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # ボーン名
                        mf.set_name(row[0])
                    except Exception as e:
                        logger.error("[モーフ] %s行目のモーフ名の読み取りに失敗しました\n%s",
                                     rno,
                                     e,
                                     decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 1 or not row[1]:
                            logger.error("[モーフ] %s行目のフレーム番号(2列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # フレーム
                        mf.fno = int(float(row[1]))

                        if mf.fno < 0:
                            logger.error("[モーフ] %s行目のフレーム番号(2列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False
                    except Exception as e:
                        logger.error(
                            "[モーフ] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 2 or not row[2]:
                            logger.error("[モーフ] %s行目の大きさ(3列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 値
                        mf.ratio = float(row[2])
                    except Exception as e:
                        logger.error(
                            "[モーフ] %s行目の大きさの読み取りに失敗しました\n大きさは半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    if mf.name not in bone_motion.morphs:
                        bone_motion.morphs[mf.name] = {}

                    bone_motion.morphs[mf.name][mf.fno] = mf

                    cnt += 1

                    if cnt % 1000 == 0:
                        logger.info("[モーフ] %sキー目:終了", cnt)

        if len(bone_motion.bones.keys()) > 0 or len(
                bone_motion.morphs.keys()) > 0:
            # ボーンかモーフのキーがある場合、まとめて出力

            model = PmxModel()
            model.name = "CSV Convert Model"
            data_set = MOptionsDataSet(bone_motion, model, model, bone_fpath,
                                       False, False, [], None, 0, [])

            VmdWriter(data_set).write()

            logger.info("ボーン・モーフモーションVMD: %s",
                        bone_fpath,
                        decoration=MLogger.DECORATION_BOX)

        if self.options.camera_csv_path and os.path.exists(
                self.options.camera_csv_path):
            # カメラモーションCSVディレクトリパス
            motion_csv_dir_path = MFileUtils.get_dir_path(
                self.options.camera_csv_path)
            # カメラモーションCSVファイル名・拡張子
            motion_csv_file_name, _ = os.path.splitext(
                os.path.basename(self.options.camera_csv_path))

            camera_fpath = "{0}\\{1}_camera_{2:%Y%m%d_%H%M%S}.vmd".format(
                motion_csv_dir_path, motion_csv_file_name, dt_now)
            camera_motion = VmdMotion()

            # カメラCSV読み込み
            with open(self.options.camera_csv_path, encoding='cp932',
                      mode='r') as f:
                reader = csv.reader(f)
                next(reader)  # ヘッダーを読み飛ばす

                cnt = 0
                for ridx, row in enumerate(reader):
                    cf = VmdCameraFrame()
                    rno = ridx + 1

                    try:
                        if len(row) < 1 or not row[0]:
                            logger.error("[カメラ] %s行目のフレーム番号(1列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # フレーム
                        cf.fno = int(row[0])

                        if cf.fno < 0:
                            logger.error("[カメラ] %s行目のフレーム番号(1列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目のフレーム番号の読み取りに失敗しました\nフレーム番号は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 3 or not row[1] or not row[2] or not row[3]:
                            logger.error("[カメラ] %s行目の位置(2-4列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 位置
                        cf.position = MVector3D(float(row[1]), float(row[2]),
                                                float(row[3]))
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の位置の読み取りに失敗しました\n位置は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row
                               ) < 6 or not row[4] or not row[5] or not row[6]:
                            logger.error("[カメラ] %s行目の回転(5-7列目)のいずれかが設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 回転(オイラー角)
                        cf.euler = MVector3D(float(row[4]), float(row[5]),
                                             float(row[6]))
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の回転の読み取りに失敗しました\n回転は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 7 or not row[7]:
                            logger.error("[カメラ] %s行目の距離(8列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 距離
                        cf.length = -(float(row[7]))
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の距離の読み取りに失敗しました\n距離は半角数字・符号・小数点のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 8 or not row[8]:
                            logger.error("[カメラ] %s行目の視野角(9列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # 視野角
                        cf.angle = int(row[8])

                        if cf.angle < 0:
                            logger.error("[カメラ] %s行目の視野角(9列目)に負数が設定されています",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の視野角の読み取りに失敗しました\n視野角は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 8 or not row[9]:
                            logger.error("[カメラ] %s行目のパース(10列目)が設定されていません",
                                         rno,
                                         decoration=MLogger.DECORATION_BOX)
                            return False

                        # パース
                        cf.perspective = int(row[9])

                        if cf.perspective not in [0, 1]:
                            logger.error(
                                "[カメラ] %s行目のパース(10列目)に0, 1以外の値が設定されています",
                                rno,
                                decoration=MLogger.DECORATION_BOX)
                            return False
                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目のパースの読み取りに失敗しました\nパースは0, 1のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    try:
                        if len(row) < 33:
                            logger.error(
                                "[カメラ] %s行目の補間曲線(11-34列目)のいずれかが設定されていません",
                                rno,
                                decoration=MLogger.DECORATION_BOX)
                            return False

                        for cidx in range(10, 34):
                            if not row[cidx]:
                                logger.error("[カメラ] %s行目の補間曲線の%s番目が設定されていません",
                                             rno,
                                             cidx - 9,
                                             decoration=MLogger.DECORATION_BOX)
                                return False

                        # 補間曲線(一旦floatで読み込んで指数等も読み込んだ後、intに変換)
                        cf.interpolation = [int(float(row[10])), int(float(row[11])), int(float(row[12])), int(float(row[13])), int(float(row[14])), int(float(row[15])), \
                                            int(float(row[16])), int(float(row[17])), int(float(row[18])), int(float(row[19])), int(float(row[20])), int(float(row[21])), \
                                            int(float(row[22])), int(float(row[23])), int(float(row[24])), int(float(row[25])), int(float(row[26])), int(float(row[27])), \
                                            int(float(row[28])), int(float(row[29])), int(float(row[30])), int(float(row[31])), int(float(row[32])), int(float(row[33]))]

                        for cidx, ci in enumerate(cf.interpolation):
                            if 0 > ci:
                                logger.error(
                                    "[カメラ] %s行目の補間曲線(%s列目)に負数が設定されています",
                                    rno,
                                    cidx + 11,
                                    decoration=MLogger.DECORATION_BOX)
                                return False

                    except Exception as e:
                        logger.error(
                            "[カメラ] %s行目の補間曲線の読み取りに失敗しました\n位置は半角数字のみ入力可能です。\n%s",
                            rno,
                            e,
                            decoration=MLogger.DECORATION_BOX)
                        return False

                    camera_motion.cameras[cf.fno] = cf

                    cnt += 1

                    if cnt % 500 == 0:
                        logger.info("[カメラ] %sキー目:終了", cnt)

            if len(camera_motion.cameras) > 0:
                # ボーンかモーフのキーがある場合、まとめて出力

                model = PmxModel()
                model.name = "カメラ・照明"
                data_set = MOptionsDataSet(camera_motion, model, model,
                                           camera_fpath, False, False, [],
                                           None, 0, [])

                VmdWriter(data_set).write()

                logger.info("カメラモーションVMD: %s",
                            camera_fpath,
                            decoration=MLogger.DECORATION_BOX)

        return True