def prepare_link(self, org_model: PmxModel, rep_model: PmxModel,
                     org_links: list, org_link_target: dict, rep_links: dict,
                     link_bone_name_list: list, target_bone_name_list: list):
        if (link_bone_name_list[0] in org_model.bones and link_bone_name_list[0] in rep_model.bones) or \
                (link_bone_name_list[1] in org_model.bones and link_bone_name_list[1] in rep_model.bones):
            # 元と先の両方に末端があればリンク作成
            org_link = org_model.create_link_2_top_one(*link_bone_name_list)
            # 先は、判定対象ボーンとそのボーンを生成するリンクのペアを登録する
            rep_target_bone_name_list = []
            for target_bone_name in target_bone_name_list:
                if target_bone_name in org_link.all().keys(
                ) and target_bone_name in rep_model.bones:
                    # 元リンクの中にあり、かつ先ボーンの中にある場合のみ登録
                    rep_links[
                        target_bone_name] = rep_model.create_link_2_top_one(
                            target_bone_name)
                    rep_target_bone_name_list.append(target_bone_name)

            if len(rep_target_bone_name_list) > 0:
                # 先に処理対象が1件でもある場合、リンク登録
                # リンクINDEXで参照するボーン名リストを保持する
                for bone_name in rep_target_bone_name_list:
                    org_link_target[bone_name] = len(org_links)
                org_links.append(org_link)
            else:
                # 処理対象がない場合、スルー
                pass
    def prepare_link(self, org_model: PmxModel, rep_model: PmxModel, camera_offset_y: float, org_links: list, org_link_target: dict, \
                     rep_links: dict, link_bone_name_list: list, target_bone_name_list: list):
        if (link_bone_name_list[0] in org_model.bones and link_bone_name_list[0] in rep_model.bones) or \
                (link_bone_name_list[1] in org_model.bones and link_bone_name_list[1] in rep_model.bones):
            # 元と先の両方に末端があればリンク作成
            org_link = org_model.create_link_2_top_one(*link_bone_name_list)

            # 先は、判定対象ボーンとそのボーンを生成するリンクのペアを登録する
            rep_target_bone_name_list = []
            for target_bone_name in target_bone_name_list:
                if target_bone_name in org_link.all().keys(
                ) and target_bone_name in rep_model.bones:
                    # 元リンクの中にあり、かつ先ボーンの中にある場合のみ登録
                    rep_link = rep_model.create_link_2_top_one(
                        target_bone_name)

                    # 頭頂実体がある場合、Yオフセット加味
                    if "頭頂実体" == target_bone_name:
                        rep_link.get("頭頂実体").position.setY(
                            float(rep_link.get("頭頂実体").position.y()) +
                            float(camera_offset_y))

                    rep_links[target_bone_name] = rep_link
                    rep_target_bone_name_list.append(target_bone_name)

            if len(rep_target_bone_name_list) > 0:
                # 先に処理対象が1件でもある場合、リンク登録
                # リンクINDEXで参照するボーン名リストを保持する
                for bone_name in rep_target_bone_name_list:
                    org_link_target[bone_name] = len(org_links)
                org_links.append(org_link)
            else:
                # 処理対象がない場合、スルー
                pass
Exemple #3
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)
    def thread_event(self):
        try:
            start = time.time()

            self.result = self.frame.noise_panel_ctrl.noise_vmd_file_ctrl.load() and self.result

            dummy_model = PmxModel()
            dummy_model.name = "{0}ゆらぎ".format(self.frame.noise_panel_ctrl.noise_vmd_file_ctrl.data.model_name)

            if self.result:
                self.options = MNoiseOptions(\
                    version_name=self.frame.version_name, \
                    logging_level=self.frame.logging_level, \
                    motion=self.frame.noise_panel_ctrl.noise_vmd_file_ctrl.data.copy(), \
                    model=dummy_model, \
                    noise_size=self.frame.noise_panel_ctrl.noise_size_ctrl.GetValue(), \
                    copy_cnt=self.frame.noise_panel_ctrl.copy_cnt_ctrl.GetValue(), \
                    finger_noise_flg=self.frame.noise_panel_ctrl.finger_noise_flg_ctrl.GetValue(), \
                    motivation_flg=self.frame.noise_panel_ctrl.motivation_flg_ctrl.GetValue(), \
                    output_path=self.frame.noise_panel_ctrl.output_noise_vmd_file_ctrl.file_ctrl.GetPath(), \
                    monitor=self.frame.noise_panel_ctrl.console_ctrl, \
                    is_file=False, \
                    outout_datetime=logger.outout_datetime, \
                    max_workers=(1 if self.is_exec_saving else min(5, 32, os.cpu_count() + 4)))
                
                self.result = ConvertNoiseService(self.options).execute() and self.result

            self.elapsed_time = time.time() - start
        except Exception as e:
            logger.critical("ゆらぎ複製処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX)
        finally:
            try:
                logger.debug("★★★result: %s, is_killed: %s", self.result, self.is_killed)
                if self.is_out_log or (not self.result and not self.is_killed):
                    # ログパス生成
                    output_vmd_path = self.frame.noise_panel_ctrl.output_noise_vmd_file_ctrl.file_ctrl.GetPath()
                    output_log_path = re.sub(r'\.vmd$', '.log', output_vmd_path)

                    # 出力されたメッセージを全部出力
                    self.frame.noise_panel_ctrl.console_ctrl.SaveFile(filename=output_log_path)

            except Exception:
                pass

            logging.shutdown()
    def calc_wrist_entity_vertex(self, data_set_idx: int, model: PmxModel,
                                 target_model_type: str, direction: str):
        if "{0}手首実体".format(direction) not in model.bones:
            wrist_entity_vetex = model.get_wrist_vertex(direction)
            # XとZは手首の値(Yだけ頂点値)
            wrist_entity_pos = wrist_entity_vetex.position.copy()
            wrist_entity_pos.setX(
                model.bones["{0}手首".format(direction)].position.x())
            wrist_entity_pos.setZ(
                model.bones["{0}手首".format(direction)].position.z())
            wrist_entity_bone = Bone("{0}手首実体".format(direction), "",
                                     wrist_entity_pos, -1, 0, 0)
            wrist_entity_bone.index = len(model.bones.keys())
            model.bones[wrist_entity_bone.name] = wrist_entity_bone
            model.bone_indexes[
                wrist_entity_bone.index] = wrist_entity_bone.name
            model.wrist_entity_vertex[direction] = wrist_entity_vetex

        logger.info("【No.%s】%sモデルの%s手のひら頂点INDEX: %s (%s)", (data_set_idx + 1), target_model_type, direction, \
                    model.wrist_entity_vertex[direction].index, model.wrist_entity_vertex[direction].position.to_log())
Exemple #6
0
    def test_create_link_2_top_one_03(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 = len(pmx_data.bones) - 1
        pmx_data.bones["右腕"] = Bone("右腕", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["右腕"].index = len(pmx_data.bones) - 1
        pmx_data.bones["右ひじ"] = Bone("右ひじ", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["右ひじ"].index = len(pmx_data.bones) - 1
        pmx_data.bones["右手首"] = Bone("右手首", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["右手首"].index = len(pmx_data.bones) - 1
        pmx_data.bones["左腕"] = Bone("左腕", None, MVector3D(), -1, 0, 0)
        pmx_data.bones["左腕"].index = len(pmx_data.bones) - 1

        links = pmx_data.create_link_2_top_one("右手首")
        self.assertEqual(len(links.all()), 4)
Exemple #7
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
Exemple #8
0
    def read_data(self):
        # Pmxモデル生成
        pmx = PmxModel()
        pmx.path = self.file_path

        try:
            # PMXファイルをバイナリ読み込み
            with open(self.file_path, "rb") as f:
                self.buffer = f.read()
                # logger.test("hashlib.algorithms_available: %s", hashlib.algorithms_available)

                # pmx宣言
                signature = self.unpack(4, "4s")
                logger.test("signature: %s (%s)", signature, self.offset)

                # pmxバージョン
                version = self.read_float()
                logger.test("version: %s (%s)", version, self.offset)

                if signature[:3] != b"PMX" or (version != 2.0 and version != 2.1):
                    # 整合性チェック
                    raise MParseException("PMX2.0/2.1形式外のデータです。signature: {0}, version: {1} ".format(signature, version))

                # flag
                flag_bytes = self.read_int(1)
                logger.test("flag_bytes: %s (%s)", flag_bytes, self.offset)

                # エンコード方式
                text_encoding = self.read_int(1)
                logger.test("text_encoding: %s (%s)", text_encoding, self.offset)
                # エンコードに基づいて文字列解凍処理を定義
                self.read_text = self.define_read_text(text_encoding)

                # 追加UV数
                self.extended_uv = self.read_int(1)
                logger.test("extended_uv: %s (%s)", self.extended_uv, self.offset)

                # 頂点Indexサイズ
                self.vertex_index_size = self.read_int(1)
                logger.test("vertex_index_size: %s (%s)", self.vertex_index_size, self.offset)
                self.read_vertex_index_size = lambda: self.read_int(self.vertex_index_size)

                # テクスチャIndexサイズ
                self.texture_index_size = self.read_int(1)
                logger.test("texture_index_size: %s (%s)", self.texture_index_size, self.offset)
                self.read_texture_index_size = lambda: self.read_int(self.texture_index_size)

                # 材質Indexサイズ
                self.material_index_size = self.read_int(1)
                logger.test("material_index_size: %s (%s)", self.material_index_size, self.offset)
                self.read_material_index_size = lambda: self.read_int(self.material_index_size)

                # ボーンIndexサイズ
                self.bone_index_size = self.read_int(1)
                logger.test("bone_index_size: %s (%s)", self.bone_index_size, self.offset)
                self.read_bone_index_size = lambda: self.read_int(self.bone_index_size)

                # モーフIndexサイズ
                self.morph_index_size = self.read_int(1)
                logger.test("morph_index_size: %s (%s)", self.morph_index_size, self.offset)
                self.read_morph_index_size = lambda: self.read_int(self.morph_index_size)

                # 剛体Indexサイズ
                self.rigidbody_index_size = self.read_int(1)
                logger.test("rigidbody_index_size: %s (%s)", self.rigidbody_index_size, self.offset)
                self.read_rigidbody_index_size = lambda: self.read_int(self.rigidbody_index_size)

                # モデル名(日本語)
                pmx.name = self.read_text()
                logger.test("name: %s (%s)", pmx.name, self.offset)

                # モデル名(英語)
                pmx.english_name = self.read_text()
                logger.test("english_name: %s (%s)", pmx.english_name, self.offset)

                # コメント(日本語)
                pmx.comment = self.read_text()
                logger.test("comment: %s (%s)", pmx.comment, self.offset)

                # コメント(英語)
                pmx.english_comment = self.read_text()
                logger.test("english_comment: %s (%s)", pmx.english_comment, self.offset)

                # 頂点データリスト
                for vertex_idx in range(self.read_int(4)):
                    position = self.read_Vector3D()
                    normal = self.read_Vector3D()
                    uv = self.read_Vector2D()

                    extended_uvs = []
                    if self.extended_uv > 0:
                        # 追加UVがある場合
                        for _ in range(self.extended_uv):
                            extended_uvs.append(self.read_Vector4D())

                    deform = self.read_deform()
                    edge_factor = self.read_float()

                    # 頂点をウェイトボーンごとに分けて保持する
                    vertex = Vertex(vertex_idx, position, normal, uv, extended_uvs, deform, edge_factor)
                    for bone_idx in vertex.deform.get_idx_list():
                        if bone_idx not in pmx.vertices:
                            pmx.vertices[bone_idx] = []
                        pmx.vertices[bone_idx].append(vertex)

                logger.test("len(vertices): %s", len(pmx.vertices))
                logger.test("vertices.keys: %s", pmx.vertices.keys())
                logger.info("-- PMX 頂点読み込み完了")

                # 面データリスト
                for _ in range(self.read_int(4)):
                    if self.vertex_index_size <= 2:
                        # 頂点サイズが2以下の場合、符号なし
                        pmx.indices.append(self.read_uint(self.vertex_index_size))
                    else:
                        pmx.indices.append(self.read_int(self.vertex_index_size))
                logger.test("len(indices): %s", len(pmx.indices))
                
                logger.info("-- PMX 面読み込み完了")

                # テクスチャデータリスト
                for _ in range(self.read_int(4)):
                    pmx.textures.append(self.read_text())
                logger.test("len(textures): %s", len(pmx.textures))

                logger.info("-- PMX テクスチャ読み込み完了")

                # 材質データリスト
                for material_idx in range(self.read_int(4)):
                    material = Material(
                        name=self.read_text(),
                        english_name=self.read_text(),
                        diffuse_color=self.read_RGB(),
                        alpha=self.read_float(),
                        specular_color=self.read_RGB(),
                        specular_factor=self.read_float(),
                        ambient_color=self.read_RGB(),
                        flag=self.read_int(1),
                        edge_color=self.read_RGBA(),
                        edge_size=self.read_float(),
                        texture_index=self.read_texture_index_size(),
                        sphere_texture_index=self.read_texture_index_size(),
                        sphere_mode=self.read_int(1),
                        toon_sharing_flag=self.read_int(1)
                    )
                    material.index = material_idx

                    if material.toon_sharing_flag == 0:
                        material.toon_texture_index = self.read_texture_index_size()
                    elif material.toon_sharing_flag == 1:
                        material.toon_texture_index = self.read_int(1)
                    else:
                        raise MParseException("unknown toon_sharing_flag {0}".format(material.toon_sharing_flag))
                    material.comment = self.read_text()
                    material.vertex_count = self.read_int(4)

                    pmx.materials[material.name] = material
                    pmx.material_indexes[material.index] = material.name
                logger.test("len(materials): %s", len(pmx.materials))

                logger.info("-- PMX 材質読み込み完了")

                # サイジング用ルートボーン
                sizing_root_bone = Bone("SIZING_ROOT_BONE", "SIZING_ROOT_BONE", MVector3D(), -1, 0, 0)
                sizing_root_bone.index = -999

                pmx.bones[sizing_root_bone.name] = sizing_root_bone
                # インデックス逆引きも登録
                pmx.bone_indexes[sizing_root_bone.index] = sizing_root_bone.name

                # ボーンデータリスト
                for bone_idx in range(self.read_int(4)):
                    bone = Bone(
                        name=self.read_text(),
                        english_name=self.read_text(),
                        position=self.read_Vector3D(),
                        parent_index=self.read_bone_index_size(),
                        layer=self.read_int(4),
                        flag=self.read_int(2)
                    )

                    if not bone.getConnectionFlag():
                        bone.tail_position = self.read_Vector3D()
                    elif bone.getConnectionFlag():
                        bone.tail_index = self.read_bone_index_size()
                    else:
                        raise MParseException("unknown bone conenction flag: {0}".format(bone.getConnectionFlag()))

                    if bone.getExternalRotationFlag() or bone.getExternalTranslationFlag():
                        bone.effect_index = self.read_bone_index_size()
                        bone.effect_factor = self.read_float()

                    if bone.getFixedAxisFlag():
                        bone.fixed_axis = self.read_Vector3D()

                    if bone.getLocalCoordinateFlag():
                        bone.local_x_vector = self.read_Vector3D()
                        bone.local_z_vector = self.read_Vector3D()

                    if bone.getExternalParentDeformFlag():
                        bone.external_key = self.read_int(4)

                    if bone.getIkFlag():
                        bone.ik = Ik(
                            target_index=self.read_bone_index_size(),
                            loop=self.read_int(4),
                            limit_radian=self.read_float()
                        )

                        # IKリンク取得
                        for _ in range(self.read_int(4)):

                            link = IkLink(
                                self.read_bone_index_size(),
                                self.read_int(1)
                            )

                            if link.limit_angle == 0:
                                pass
                            elif link.limit_angle == 1:
                                link.limit_min = self.read_Vector3D()
                                link.limit_max = self.read_Vector3D()
                            else:
                                raise MParseException("invalid ik link limit_angle: {0}".format(link.limit_angle))

                            bone.ik.link.append(link)

                    # ボーンのINDEX
                    bone.index = bone_idx

                    if bone.name not in pmx.bones:
                        # まだ未登録の名前のボーンの場合のみ登録
                        pmx.bones[bone.name] = bone
                        # インデックス逆引きも登録
                        pmx.bone_indexes[bone.index] = bone.name
                
                # サイジング用ボーン ---------
                # 頭頂ボーン
                head_top_vertex = pmx.get_head_top_vertex()
                pmx.head_top_vertex = head_top_vertex
                head_top_bone = Bone("頭頂実体", "head_top", head_top_vertex.position.copy(), -1, 0, 0)
                head_top_bone.index = len(pmx.bones.keys())
                pmx.bones[head_top_bone.name] = head_top_bone
                pmx.bone_indexes[head_top_bone.index] = head_top_bone.name

                if "右足IK" in pmx.bones or "右つま先IK" in pmx.bones:
                    # 右つま先ボーン
                    right_toe_vertex = pmx.get_toe_vertex("右")
                    if right_toe_vertex:
                        pmx.right_toe_vertex = right_toe_vertex
                        right_toe_pos = right_toe_vertex.position.copy()
                        right_toe_pos.setY(0)
                        right_toe_bone = Bone("右つま先実体", "right toe entity", right_toe_pos, -1, 0, 0)
                        right_toe_bone.index = len(pmx.bones.keys())
                        pmx.bones[right_toe_bone.name] = right_toe_bone
                        pmx.bone_indexes[right_toe_bone.index] = right_toe_bone.name

                if "左足IK" in pmx.bones or "左つま先IK" in pmx.bones:
                    # 左つま先ボーン
                    left_toe_vertex = pmx.get_toe_vertex("左")
                    if left_toe_vertex:
                        pmx.left_toe_vertex = left_toe_vertex
                        left_toe_pos = left_toe_vertex.position.copy()
                        left_toe_pos.setY(0)
                        left_toe_bone = Bone("左つま先実体", "left toe entity", left_toe_pos, -1, 0, 0)
                        left_toe_bone.index = len(pmx.bones.keys())
                        pmx.bones[left_toe_bone.name] = left_toe_bone
                        pmx.bone_indexes[left_toe_bone.index] = left_toe_bone.name

                if "右足先EX" in pmx.bones or "右足IK" in pmx.bones:
                    # 右足底実体ボーン
                    right_sole_vertex = None
                    if "右足先EX" in pmx.bones:
                        right_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足先EX"].position.x(), 0, pmx.bones["右足先EX"].position.z()), MVector3D(), [], [], Bdef1(-1), -1)
                    elif "右足IK" in pmx.bones:
                        right_sole_vertex = pmx.get_sole_vertex("右")
                    
                    if right_sole_vertex:
                        pmx.right_sole_vertex = right_sole_vertex
                        right_sole_bone = Bone("右足底実体", "right sole entity", right_sole_vertex.position.copy(), -1, 0, 0)
                        right_sole_bone.index = len(pmx.bones.keys())
                        pmx.bones[right_sole_bone.name] = right_sole_bone
                        pmx.bone_indexes[right_sole_bone.index] = right_sole_bone.name

                if "左足先EX" in pmx.bones or "左足IK" in pmx.bones:
                    # 左足底実体ボーン
                    left_sole_vertex = None
                    if "左足先EX" in pmx.bones:
                        left_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足先EX"].position.x(), 0, pmx.bones["左足先EX"].position.z()), MVector3D(), [], [], Bdef1(-1), -1)
                    elif "左足IK" in pmx.bones:
                        left_sole_vertex = pmx.get_sole_vertex("左")
                    
                    if left_sole_vertex:
                        pmx.left_sole_vertex = left_sole_vertex
                        left_sole_bone = Bone("左足底実体", "left sole entity", left_sole_vertex.position.copy(), -1, 0, 0)
                        left_sole_bone.index = len(pmx.bones.keys())
                        pmx.bones[left_sole_bone.name] = left_sole_bone
                        pmx.bone_indexes[left_sole_bone.index] = left_sole_bone.name

                if "右足IK" in pmx.bones:
                    # 右足IK底実体ボーン
                    right_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足IK"].position.x(), 0, pmx.bones["右足IK"].position.z()), MVector3D(), [], [], Bdef1(-1), -1)
                    pmx.right_ik_sole_vertex = right_ik_sole_vertex
                    right_ik_sole_bone = Bone("右足IK底実体", "right ik ik_sole entity", right_ik_sole_vertex.position.copy(), -1, 0, 0)
                    right_ik_sole_bone.index = len(pmx.bones.keys())
                    pmx.bones[right_ik_sole_bone.name] = right_ik_sole_bone
                    pmx.bone_indexes[right_ik_sole_bone.index] = right_ik_sole_bone.name

                if "左足IK" in pmx.bones:
                    # 左足IK底実体ボーン
                    left_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足IK"].position.x(), 0, pmx.bones["左足IK"].position.z()), MVector3D(), [], [], Bdef1(-1), -1)
                    pmx.left_ik_sole_vertex = left_ik_sole_vertex
                    left_ik_sole_bone = Bone("左足IK底実体", "left ik ik_sole entity", left_ik_sole_vertex.position.copy(), -1, 0, 0)
                    left_ik_sole_bone.index = len(pmx.bones.keys())
                    pmx.bones[left_ik_sole_bone.name] = left_ik_sole_bone
                    pmx.bone_indexes[left_ik_sole_bone.index] = left_ik_sole_bone.name

                if "右足IK親" in pmx.bones:
                    # 右足IK親底実体ボーン
                    right_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["右足IK親"].position.x(), 0, pmx.bones["右足IK親"].position.z()), MVector3D(), [], [], Bdef1(-1), -1)
                    pmx.right_ik_sole_vertex = right_ik_sole_vertex
                    right_ik_sole_bone = Bone("右足IK親底実体", "right ik ik_sole entity", right_ik_sole_vertex.position.copy(), -1, 0, 0)
                    right_ik_sole_bone.index = len(pmx.bones.keys())
                    pmx.bones[right_ik_sole_bone.name] = right_ik_sole_bone
                    pmx.bone_indexes[right_ik_sole_bone.index] = right_ik_sole_bone.name

                if "左足IK親" in pmx.bones:
                    # 左足IK親底実体ボーン
                    left_ik_sole_vertex = Vertex(-1, MVector3D(pmx.bones["左足IK親"].position.x(), 0, pmx.bones["左足IK親"].position.z()), MVector3D(), [], [], Bdef1(-1), -1)
                    pmx.left_ik_sole_vertex = left_ik_sole_vertex
                    left_ik_sole_bone = Bone("左足IK親底実体", "left ik ik_sole entity", left_ik_sole_vertex.position.copy(), -1, 0, 0)
                    left_ik_sole_bone.index = len(pmx.bones.keys())
                    pmx.bones[left_ik_sole_bone.name] = left_ik_sole_bone
                    pmx.bone_indexes[left_ik_sole_bone.index] = left_ik_sole_bone.name

                # 首根元ボーン
                if "左肩" in pmx.bones and "右肩" in pmx.bones:
                    neck_base_vertex = Vertex(-1, (pmx.bones["左肩"].position + pmx.bones["右肩"].position) / 2, MVector3D(), [], [], Bdef1(-1), -1)
                    neck_base_vertex.position.setX(0)
                    neck_base_bone = Bone("首根元", "base of neck", neck_base_vertex.position.copy(), -1, 0, 0)

                    if "上半身2" in pmx.bones:
                        # 上半身2がある場合、表示先は、上半身2
                        neck_base_bone.parent_index = pmx.bones["上半身2"].index
                        neck_base_bone.tail_index = pmx.bones["上半身2"].index
                    elif "上半身" in pmx.bones:
                        neck_base_bone.parent_index = pmx.bones["上半身"].index
                        neck_base_bone.tail_index = pmx.bones["上半身"].index

                    neck_base_bone.index = len(pmx.bones.keys())
                    pmx.bones[neck_base_bone.name] = neck_base_bone
                    pmx.bone_indexes[neck_base_bone.index] = neck_base_bone.name

                # 首根元2ボーン
                if "左腕" in pmx.bones and "右腕" in pmx.bones:
                    neck_base2_vertex = Vertex(-1, (pmx.bones["左腕"].position + pmx.bones["右腕"].position) / 2, MVector3D(), [], [], Bdef1(-1), -1)
                    neck_base2_vertex.position.setX(0)
                    neck_base2_bone = Bone("首根元2", "base of neck", neck_base2_vertex.position.copy(), -1, 0, 0)

                    if "首根元" in pmx.bones:
                        # 首根元が既にある場合は首根元
                        neck_base2_bone.parent_index = pmx.bones["首根元"].index
                        neck_base2_bone.tail_index = pmx.bones["首根元"].index
                    elif "上半身2" in pmx.bones:
                        # 上半身2がある場合、表示先は、上半身2
                        neck_base2_bone.parent_index = pmx.bones["上半身2"].index
                        neck_base2_bone.tail_index = pmx.bones["上半身2"].index
                    elif "上半身" in pmx.bones:
                        neck_base2_bone.parent_index = pmx.bones["上半身"].index
                        neck_base2_bone.tail_index = pmx.bones["上半身"].index

                    neck_base2_bone.index = len(pmx.bones.keys())
                    pmx.bones[neck_base2_bone.name] = neck_base2_bone
                    pmx.bone_indexes[neck_base2_bone.index] = neck_base2_bone.name

                if "右肩" in pmx.bones:
                    # 右肩下延長ボーン
                    right_shoulder_under_pos = pmx.bones["右肩"].position.copy()
                    right_shoulder_under_pos.setY(right_shoulder_under_pos.y() - 1)
                    right_shoulder_under_bone = Bone("右肩下延長", "", right_shoulder_under_pos, -1, 0, 0)
                    right_shoulder_under_bone.index = len(pmx.bones.keys())
                    pmx.bones[right_shoulder_under_bone.name] = right_shoulder_under_bone
                    pmx.bone_indexes[right_shoulder_under_bone.index] = right_shoulder_under_bone.name

                if "左肩" in pmx.bones:
                    # 左肩下延長ボーン
                    left_shoulder_under_pos = pmx.bones["左肩"].position.copy()
                    left_shoulder_under_pos.setY(left_shoulder_under_pos.y() - 1)
                    left_shoulder_under_bone = Bone("左肩下延長", "", left_shoulder_under_pos, -1, 0, 0)
                    left_shoulder_under_bone.index = len(pmx.bones.keys())
                    pmx.bones[left_shoulder_under_bone.name] = left_shoulder_under_bone
                    pmx.bone_indexes[left_shoulder_under_bone.index] = left_shoulder_under_bone.name

                if "右ひじ" in pmx.bones and "右手首" in pmx.bones:
                    # 右ひじ手首中間ボーン
                    right_elbow_middle_pos = (pmx.bones["右ひじ"].position + pmx.bones["右手首"].position) / 2
                    right_elbow_middle_bone = Bone("右ひじ手首中間", "", right_elbow_middle_pos, -1, 0, 0)
                    right_elbow_middle_bone.index = len(pmx.bones.keys())
                    pmx.bones[right_elbow_middle_bone.name] = right_elbow_middle_bone
                    pmx.bone_indexes[right_elbow_middle_bone.index] = right_elbow_middle_bone.name

                if "左ひじ" in pmx.bones and "左手首" in pmx.bones:
                    # 左ひじ手首中間ボーン
                    left_elbow_middle_pos = (pmx.bones["左ひじ"].position + pmx.bones["左手首"].position) / 2
                    left_elbow_middle_bone = Bone("左ひじ手首中間", "", left_elbow_middle_pos, -1, 0, 0)
                    left_elbow_middle_bone.index = len(pmx.bones.keys())
                    pmx.bones[left_elbow_middle_bone.name] = left_elbow_middle_bone
                    pmx.bone_indexes[left_elbow_middle_bone.index] = left_elbow_middle_bone.name

                if "右ひじ" in pmx.bones and "右腕" in pmx.bones:
                    # 右腕ひじ中間ボーン
                    right_arm_middle_pos = (pmx.bones["右ひじ"].position + pmx.bones["右腕"].position) / 2
                    right_arm_middle_bone = Bone("右腕ひじ中間", "", right_arm_middle_pos, -1, 0, 0)
                    right_arm_middle_bone.index = len(pmx.bones.keys())
                    pmx.bones[right_arm_middle_bone.name] = right_arm_middle_bone
                    pmx.bone_indexes[right_arm_middle_bone.index] = right_arm_middle_bone.name

                if "左ひじ" in pmx.bones and "左腕" in pmx.bones:
                    # 左腕ひじ中間ボーン
                    left_arm_middle_pos = (pmx.bones["左ひじ"].position + pmx.bones["左腕"].position) / 2
                    left_arm_middle_bone = Bone("左腕ひじ中間", "", left_arm_middle_pos, -1, 0, 0)
                    left_arm_middle_bone.index = len(pmx.bones.keys())
                    pmx.bones[left_arm_middle_bone.name] = left_arm_middle_bone
                    pmx.bone_indexes[left_arm_middle_bone.index] = left_arm_middle_bone.name

                # センター実体
                center_entity_bone = Bone("センター実体", "", MVector3D(), -1, 0, 0)
                center_entity_bone.index = len(pmx.bones.keys())
                pmx.bones[center_entity_bone.name] = center_entity_bone
                pmx.bone_indexes[center_entity_bone.index] = center_entity_bone.name

                # 指先ボーンがない場合、代替で挿入
                for direction in ["左", "右"]:
                    for (finger_name, end_joint_name) in [("親指", "2"), ("人指", "3"), ("中指", "3"), ("薬指", "3"), ("小指", "3")]:
                        end_joint_name = "{0}{1}{2}".format(direction, finger_name, end_joint_name)

                        if end_joint_name not in pmx.bones:
                            continue

                        to_joint_name = "{0}{1}{2}".format(direction, finger_name, "先実体")

                        finger_tail_vertex = pmx.get_finger_tail_vertex(end_joint_name, to_joint_name)
                        if finger_tail_vertex:
                            pmx.finger_tail_vertex = finger_tail_vertex
                            finger_tail_pos = finger_tail_vertex.position.copy()
                            finger_tail_bone = Bone(to_joint_name, "", finger_tail_pos, -1, 0, 0)
                            finger_tail_bone.index = len(pmx.bones.keys())
                            finger_tail_bone.parent_index = pmx.bones[end_joint_name].index
                            pmx.bones[finger_tail_bone.name] = finger_tail_bone
                            pmx.bone_indexes[finger_tail_bone.index] = finger_tail_bone.name

                # 足中間ボーン
                if "左足" in pmx.bones and "右足" in pmx.bones:
                    leg_center_vertex = Vertex(-1, (pmx.bones["左足"].position + pmx.bones["右足"].position) / 2, MVector3D(), [], [], Bdef1(-1), -1)
                    leg_center_vertex.position.setX(0)
                    leg_center_bone = Bone("足中間", "base of neck", leg_center_vertex.position.copy(), -1, 0, 0)

                    if "下半身" in pmx.bones:
                        leg_center_bone.parent_index = pmx.bones["下半身"].index
                        leg_center_bone.tail_index = pmx.bones["下半身"].index

                    leg_center_bone.index = len(pmx.bones.keys())
                    pmx.bones[leg_center_bone.name] = leg_center_bone
                    pmx.bone_indexes[leg_center_bone.index] = leg_center_bone.name

                logger.test("len(bones): %s", len(pmx.bones))

                logger.info("-- PMX ボーン読み込み完了")

                # ボーンの長さを計算する
                self.calc_bone_length(pmx.bones, pmx.bone_indexes)

                # 操作パネル (PMD:カテゴリ) 1:眉(左下) 2:目(左上) 3:口(右上) 4:その他(右下)
                morphs_by_panel = {}
                morphs_by_panel[2] = []  # 目
                morphs_by_panel[1] = []  # 眉
                morphs_by_panel[3] = []  # 口
                morphs_by_panel[4] = []  # 他
                morphs_by_panel[0] = []  # システム予約

                # モーフデータリスト
                for morph_idx in range(self.read_int(4)):
                    morph = Morph(
                        name=self.read_text(),
                        english_name=self.read_text(),
                        panel=self.read_int(1),
                        morph_type=self.read_int(1)
                    )

                    offset_size = self.read_int(4)

                    if morph.morph_type == 0:
                        # group
                        morph.offsets = [self.read_group_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 1:
                        # vertex
                        morph.offsets = [self.read_vertex_position_morph_offset() for _ in range(offset_size)]
                    elif morph.morph_type == 2:
                        # bone
                        morph.offsets = [self.read_bone_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 3:
                        # uv
                        morph.offsets = [self.read_uv_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 4:
                        # uv extended1
                        morph.offsets = [self.read_uv_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 5:
                        # uv extended2
                        morph.offsets = [self.read_uv_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 6:
                        # uv extended3
                        morph.offsets = [self.read_uv_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 7:
                        # uv extended4
                        morph.offsets = [self.read_uv_morph_data() for _ in range(offset_size)]
                    elif morph.morph_type == 8:
                        # material
                        morph.data = [self.read_material_morph_data() for _ in range(offset_size)]
                    else:
                        raise MParseException("unknown morph type: {0}".format(morph.morph_type))

                    # モーフのINDEXは、先頭から順番に設定
                    morph.index = morph_idx
                    # インデックス逆引きも登録
                    pmx.morph_indexes[morph.index] = morph.name

                    if morph.panel not in morphs_by_panel.keys():
                        # ないと思うが念のためパネル情報がなければ追加
                        morphs_by_panel[morph.panel] = 0

                    morphs_by_panel[morph.panel].append(morph)

                # モーフのパネル順に並び替えてモーフを登録していく
                for _, mlist in morphs_by_panel.items():
                    for m in mlist:
                        pmx.morphs[m.name] = m

                logger.test("len(morphs): %s", len(pmx.morphs))

                logger.info("-- PMX モーフ読み込み完了")

                # 表示枠データリスト
                for _ in range(self.read_int(4)):
                    display_slot = DisplaySlot(
                        name=self.read_text(),
                        english_name=self.read_text(),
                        special_flag=self.read_int(1)
                    )

                    display_count = self.read_int(4)

                    for _ in range(display_count):
                        display_type = self.read_int(1)
                        if display_type == 0:
                            born_idx = self.read_bone_index_size()
                            display_slot.references.append((display_type, born_idx))
                            # ボーン表示ON
                            for v in pmx.bones.values():
                                if v.index == born_idx:
                                    v.display = True
                        elif display_type == 1:
                            morph_idx = self.read_morph_index_size()
                            display_slot.references.append((display_type, morph_idx))
                            # モーフ表示ON
                            for v in pmx.morphs.values():
                                if v.index == morph_idx:
                                    v.display = True
                                # logger.test("v: %s, display: %s", v.name, v.display)
                        else:
                            raise MParseException("unknown display_type: {0}".format(display_type))

                    pmx.display_slots[display_slot.name] = display_slot

                logger.test("len(display_slots): %s", len(pmx.display_slots))

                logger.info("-- PMX 表示枠読み込み完了")

                # 剛体データリスト
                for rigidbody_idx in range(self.read_int(4)):
                    rigidbody = RigidBody(
                        name=self.read_text(),
                        english_name=self.read_text(),
                        bone_index=self.read_bone_index_size(),
                        collision_group=self.read_int(1),
                        no_collision_group=self.read_int(2),
                        shape_type=self.read_int(1),
                        shape_size=self.read_Vector3D(),
                        shape_position=self.read_Vector3D(),
                        shape_rotation=self.read_Vector3D(),
                        mass=self.read_float(),
                        linear_damping=self.read_float(),
                        angular_damping=self.read_float(),
                        restitution=self.read_float(),
                        friction=self.read_float(),
                        mode=self.read_int(1)
                    )

                    # ボーンのINDEX
                    rigidbody.index = rigidbody_idx

                    pmx.rigidbodies[rigidbody.name] = rigidbody
                    # インデックス逆引きも登録
                    pmx.rigidbody_indexes[rigidbody.index] = rigidbody.name

                logger.test("len(rigidbodies): %s", len(pmx.rigidbodies))

                logger.info("-- PMX 剛体読み込み完了")

                # ジョイントデータリスト
                for joint_idx in range(self.read_int(4)):
                    joint = Joint(
                        name=self.read_text(),
                        english_name=self.read_text(),
                        joint_type=self.read_int(1),
                        rigidbody_index_a=self.read_rigidbody_index_size(),
                        rigidbody_index_b=self.read_rigidbody_index_size(),
                        position=self.read_Vector3D(),
                        rotation=self.read_Vector3D(),
                        translation_limit_min=self.read_Vector3D(),
                        translation_limit_max=self.read_Vector3D(),
                        rotation_limit_min=self.read_Vector3D(),
                        rotation_limit_max=self.read_Vector3D(),
                        spring_constant_translation=self.read_Vector3D(),
                        spring_constant_rotation=self.read_Vector3D()
                    )

                    pmx.joints[joint.name] = joint

                logger.test("len(joints): %s", len(pmx.joints))

                logger.info("-- PMX ジョイント読み込み完了")

            # ハッシュを設定
            pmx.digest = self.hexdigest()
            logger.test("pmx: %s, hash: %s", pmx.name, pmx.digest)

            if self.is_check:
                # 腕がサイジング可能かチェック
                pmx.can_arm_sizing = pmx.check_arm_bone_can_sizing()
                logger.test("pmx: %s, can_arm_sizing: %s", pmx.name, pmx.can_arm_sizing)

            # # 上半身がサイジング可能かチェック
            # pmx.can_upper_sizing = pmx.check_upper_bone_can_sizing()
            # logger.test("pmx: %s, can_upper_sizing: %s", pmx.name, pmx.can_upper_sizing)

            return pmx
        except MKilledException as ke:
            # 終了命令
            raise ke
        except SizingException as se:
            logger.error("サイジング処理が処理できないデータで終了しました。\n\n%s", se.message)
            return se
        except Exception as e:
            import traceback
            logger.error("サイジング処理が意図せぬエラーで終了しました。\n\n%s", traceback.print_exc())
            raise e
    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
Exemple #10
0
    def execute(self):
        logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]")

        try:
            service_data_txt = "VMDサイジング処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name)

            for data_set_idx, data_set in enumerate(self.options.data_set_list):
                service_data_txt = "{service_data_txt}\n【No.{no}】 --------- \n".format(service_data_txt=service_data_txt, no=(data_set_idx+1)) # noqa
                service_data_txt = "{service_data_txt}  モーション: {motion}\n".format(service_data_txt=service_data_txt,
                                        motion=os.path.basename(data_set.motion.path)) # noqa
                service_data_txt = "{service_data_txt}  作成元モデル: {trace_model} ({model_name})\n".format(service_data_txt=service_data_txt,
                                        trace_model=os.path.basename(data_set.org_model.path), model_name=data_set.org_model.name) # noqa
                service_data_txt = "{service_data_txt}  変換先モデル: {replace_model} ({model_name})\n".format(service_data_txt=service_data_txt,
                                        replace_model=os.path.basename(data_set.rep_model.path), model_name=data_set.rep_model.name) # noqa
                service_data_txt = "{service_data_txt}  カメラ作成元モデル: {trace_model} ({model_name})({offset_y})\n".format(service_data_txt=service_data_txt,
                                        trace_model=os.path.basename(data_set.camera_org_model.path), model_name=data_set.camera_org_model.name, offset_y=data_set.camera_offset_y) # noqa
                service_data_txt = "{service_data_txt}  スタンス追加補正有無: {detail_stance_flg}\n".format(service_data_txt=service_data_txt,
                                        detail_stance_flg=data_set.detail_stance_flg) # noqa
                if data_set.detail_stance_flg:
                    # スタンス追加補正がある場合、そのリストを表示
                    service_data_txt = "{service_data_txt}    {detail_stance_flg}\n".format(service_data_txt=service_data_txt,
                                            detail_stance_flg=", ".join(data_set.selected_stance_details)) # noqa
                    
                service_data_txt = "{service_data_txt}  捩り分散有無: {twist_flg}\n".format(service_data_txt=service_data_txt,
                                        twist_flg=data_set.twist_flg) # noqa

                if data_set_idx in self.options.arm_options.avoidance_target_list:
                    service_data_txt = "{service_data_txt}  対象剛体名: {avoidance_target}\n".format(service_data_txt=service_data_txt,
                                            avoidance_target=", ".join(self.options.arm_options.avoidance_target_list[data_set_idx])) # noqa

            service_data_txt = "{service_data_txt}\n--------- \n".format(service_data_txt=service_data_txt) # noqa

            if self.options.arm_options.avoidance:
                service_data_txt = "{service_data_txt}剛体接触回避: {avoidance}\n".format(service_data_txt=service_data_txt,
                                        avoidance=self.options.arm_options.avoidance) # noqa

            if self.options.arm_options.alignment:
                service_data_txt = "{service_data_txt}手首位置合わせ: {alignment} ({distance})\n".format(service_data_txt=service_data_txt,
                                        alignment=self.options.arm_options.alignment, distance=self.options.arm_options.alignment_distance_wrist) # noqa
                service_data_txt = "{service_data_txt}指位置合わせ: {alignment} ({distance})\n".format(service_data_txt=service_data_txt,
                                        alignment=self.options.arm_options.alignment_finger_flg, distance=self.options.arm_options.alignment_distance_finger) # noqa
                service_data_txt = "{service_data_txt}床位置合わせ: {alignment} ({distance})\n".format(service_data_txt=service_data_txt,
                                        alignment=self.options.arm_options.alignment_floor_flg, distance=self.options.arm_options.alignment_distance_floor) # noqa
            
            if self.options.arm_options.arm_check_skip_flg:
                service_data_txt = "{service_data_txt}腕チェックスキップ: {arm_check_skip}\n".format(service_data_txt=service_data_txt,
                                        arm_check_skip=self.options.arm_options.arm_check_skip_flg) # noqa

            if self.options.camera_motion:
                service_data_txt = "{service_data_txt}カメラ: {camera}\n".format(service_data_txt=service_data_txt,
                                        camera=os.path.basename(self.options.camera_motion.path)) # noqa

            service_data_txt = "{service_data_txt}------------------------".format(service_data_txt=service_data_txt) # noqa

            logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX)

            for data_set_idx, data_set in enumerate(self.options.data_set_list):
                # 足IKのXYZの比率
                data_set.original_xz_ratio, data_set.original_y_ratio = MServiceUtils.calc_leg_ik_ratio(data_set)
            
            # 足IKの比率再計算
            self.options.calc_leg_ratio()

            # 移動補正
            if not MoveService(self.options).execute():
                return False

            # スタンス補正
            if not StanceService(self.options).execute():
                return False

            # 剛体接触回避
            if self.options.arm_options.avoidance:
                if not ArmAvoidanceService(self.options).execute():
                    return False

            # 手首位置合わせ
            if self.options.arm_options.alignment:
                if not ArmAlignmentService(self.options).execute():
                    return False

            # カメラ補正
            if self.options.camera_motion:
                if not CameraService(self.options).execute():
                    return False

            # モーフ置換
            if not MorphService(self.options).execute():
                return False
            
            for data_set_idx, data_set in enumerate(self.options.data_set_list):
                # 実行後、出力ファイル存在チェック
                try:
                    # 出力
                    VmdWriter(data_set).write()

                    Path(data_set.output_vmd_path).resolve(True)

                    logger.info("【No.%s】 出力終了: %s", (data_set_idx + 1), os.path.basename(data_set.output_vmd_path), decoration=MLogger.DECORATION_BOX, title="サイジング成功")

                except FileNotFoundError as fe:
                    logger.error("【No.%s】出力VMDファイルが正常に作成されなかったようです。\nパスを確認してください。%s\n\n%s", (data_set_idx + 1), data_set.output_vmd_path, fe.message, decoration=MLogger.DECORATION_BOX)
            
            if self.options.camera_motion:
                try:
                    camera_model = PmxModel()
                    camera_model.name = "カメラ・照明"
                    data_set = MOptionsDataSet(self.options.camera_motion, None, camera_model, self.options.camera_output_vmd_path, 0, 0, [], None, 0, [])
                    # 出力
                    VmdWriter(data_set).write()

                    Path(data_set.output_vmd_path).resolve(True)

                    logger.info("カメラ出力終了: %s", os.path.basename(data_set.output_vmd_path), decoration=MLogger.DECORATION_BOX, title="サイジング成功")
                except FileNotFoundError as fe:
                    logger.error("カメラ出力VMDファイルが正常に作成されなかったようです。\nパスを確認してください。%s\n\n%s", self.options.camera_output_vmd_path, fe.message, decoration=MLogger.DECORATION_BOX)

            return True
        except MKilledException:
            return False
        except SizingException as se:
            logger.error("サイジング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX)
            return False
        except Exception as e:
            logger.critical("サイジング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX)
            return False
        finally:
            logging.shutdown()
Exemple #11
0
    def execute(self):
        logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]")

        try:
            service_data_txt = "VMDサイジング処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name)

            for data_set_idx, data_set in enumerate(self.options.data_set_list):
                service_data_txt = "{service_data_txt}\n【No.{no}】 --------- \n".format(service_data_txt=service_data_txt, no=(data_set_idx+1)) # noqa
                service_data_txt = "{service_data_txt}  モーション: {motion}\n".format(service_data_txt=service_data_txt,
                                        motion=os.path.basename(data_set.motion.path)) # noqa
                service_data_txt = "{service_data_txt}  作成元モデル: {trace_model} ({model_name})\n".format(service_data_txt=service_data_txt,
                                        trace_model=os.path.basename(data_set.org_model.path), model_name=data_set.org_model.name) # noqa
                service_data_txt = "{service_data_txt}  変換先モデル: {replace_model} ({model_name})\n".format(service_data_txt=service_data_txt,
                                        replace_model=os.path.basename(data_set.rep_model.path), model_name=data_set.rep_model.name) # noqa
                if data_set.camera_org_model:
                    service_data_txt = "{service_data_txt}  カメラ作成元モデル: {trace_model} ({model_name})\n".format(service_data_txt=service_data_txt,
                                            trace_model=os.path.basename(data_set.camera_org_model.path), model_name=data_set.camera_org_model.name) # noqa
                    service_data_txt = "{service_data_txt}  Yオフセット: {camera_offset_y}\n".format(service_data_txt=service_data_txt,
                                            camera_offset_y=data_set.camera_offset_y) # noqa
                service_data_txt = "{service_data_txt}  スタンス追加補正有無: {detail_stance_flg}\n".format(service_data_txt=service_data_txt,
                                        detail_stance_flg=data_set.detail_stance_flg) # noqa
                if data_set.detail_stance_flg:
                    # スタンス追加補正がある場合、そのリストを表示
                    service_data_txt = "{service_data_txt}    {detail_stance_flg}\n".format(service_data_txt=service_data_txt,
                                            detail_stance_flg=", ".join(data_set.selected_stance_details)) # noqa
                    
                service_data_txt = "{service_data_txt}  捩り分散有無: {twist_flg}\n".format(service_data_txt=service_data_txt,
                                        twist_flg=data_set.twist_flg) # noqa

                morph_list = []
                for (org_morph_name, rep_morph_name, morph_ratio) in data_set.morph_list:
                    morph_list.append(f"{org_morph_name} → {rep_morph_name} ({morph_ratio})")
                morph_txt = ", ".join(morph_list)
                service_data_txt = "{service_data_txt}  モーフ置換: {morph_txt}\n".format(service_data_txt=service_data_txt,
                                        morph_txt=morph_txt) # noqa

                if data_set_idx in self.options.arm_options.avoidance_target_list:
                    service_data_txt = "{service_data_txt}  対象剛体名: {avoidance_target}\n".format(service_data_txt=service_data_txt,
                                            avoidance_target=", ".join(self.options.arm_options.avoidance_target_list[data_set_idx])) # noqa

            service_data_txt = "{service_data_txt}\n--------- \n".format(service_data_txt=service_data_txt) # noqa

            if self.options.arm_options.avoidance:
                service_data_txt = "{service_data_txt}剛体接触回避: {avoidance}\n".format(service_data_txt=service_data_txt,
                                        avoidance=self.options.arm_options.avoidance) # noqa

            if self.options.arm_options.alignment:
                service_data_txt = "{service_data_txt}手首位置合わせ: {alignment} ({distance})\n".format(service_data_txt=service_data_txt,
                                        alignment=self.options.arm_options.alignment, distance=self.options.arm_options.alignment_distance_wrist) # noqa
                service_data_txt = "{service_data_txt}指位置合わせ: {alignment} ({distance})\n".format(service_data_txt=service_data_txt,
                                        alignment=self.options.arm_options.alignment_finger_flg, distance=self.options.arm_options.alignment_distance_finger) # noqa
                service_data_txt = "{service_data_txt}床位置合わせ: {alignment} ({distance})\n".format(service_data_txt=service_data_txt,
                                        alignment=self.options.arm_options.alignment_floor_flg, distance=self.options.arm_options.alignment_distance_floor) # noqa
            
            if self.options.arm_options.arm_check_skip_flg:
                service_data_txt = "{service_data_txt}腕チェックスキップ: {arm_check_skip}\n".format(service_data_txt=service_data_txt,
                                        arm_check_skip=self.options.arm_options.arm_check_skip_flg) # noqa

            if self.options.camera_motion:
                service_data_txt = "{service_data_txt}カメラ: {camera}({camera_length})\n".format(service_data_txt=service_data_txt,
                                        camera=os.path.basename(self.options.camera_motion.path), camera_length=self.options.camera_length) # noqa
                service_data_txt = "{service_data_txt}  距離制限: {camera_length}{camera_length_umlimit}\n".format(service_data_txt=service_data_txt,
                                        camera_length=self.options.camera_length, camera_length_umlimit=("" if self.options.camera_length < 5 else "(無制限)")) # noqa

            service_data_txt = "{service_data_txt}------------------------".format(service_data_txt=service_data_txt) # noqa

            if self.options.total_process_ctrl:
                self.options.total_process_ctrl.write(str(self.options.total_process))
                self.options.now_process_ctrl.write("0")
                self.options.now_process_ctrl.write(str(self.options.now_process))

            logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX)

            if self.options.is_sizing_camera_only is True:
                # カメラサイジングのみ実行する場合、出力結果VMDを読み込む
                for data_set_idx, data_set in enumerate(self.options.data_set_list):
                    reader = VmdReader(data_set.output_vmd_path)
                    data_set.motion = reader.read_data()
            else:
                for data_set_idx, data_set in enumerate(self.options.data_set_list):
                    # 足IKのXYZの比率
                    data_set.original_xz_ratio, data_set.original_y_ratio = MServiceUtils.calc_leg_ik_ratio(data_set)
                
                # 足IKの比率再計算
                self.options.calc_leg_ratio()

                # 移動補正
                if not MoveService(self.options).execute():
                    return False

                # スタンス補正
                if not StanceService(self.options).execute():
                    return False

                # 剛体接触回避
                if self.options.arm_options.avoidance:
                    if not ArmAvoidanceService(self.options).execute():
                        return False

                # 手首位置合わせ
                if self.options.arm_options.alignment:
                    if not ArmAlignmentService(self.options).execute():
                        return False

            # カメラ補正
            if self.options.camera_motion:
                if not CameraService(self.options).execute():
                    return False

            if self.options.is_sizing_camera_only is False:
                # モーフ置換
                if not MorphService(self.options).execute():
                    return False
                
                for data_set_idx, data_set in enumerate(self.options.data_set_list):
                    # 実行後、出力ファイル存在チェック
                    try:
                        # 出力
                        VmdWriter(data_set).write()

                        Path(data_set.output_vmd_path).resolve(True)

                        logger.info("【No.%s】 出力終了: %s", (data_set_idx + 1), os.path.basename(data_set.output_vmd_path), decoration=MLogger.DECORATION_BOX, title="サイジング成功")
                    except FileNotFoundError as fe:
                        logger.error("【No.%s】出力VMDファイルが正常に作成されなかったようです。\nパスを確認してください。%s\n\n%s", (data_set_idx + 1), data_set.output_vmd_path, fe, decoration=MLogger.DECORATION_BOX)
                
            if self.options.camera_motion:
                try:
                    camera_model = PmxModel()
                    camera_model.name = "カメラ・照明"
                    data_set = MOptionsDataSet(self.options.camera_motion, None, camera_model, self.options.camera_output_vmd_path, 0, 0, [], None, 0, [])
                    # 出力
                    VmdWriter(data_set).write()

                    Path(data_set.output_vmd_path).resolve(True)

                    logger.info("カメラ出力終了: %s", os.path.basename(data_set.output_vmd_path), decoration=MLogger.DECORATION_BOX, title="サイジング成功")
                except FileNotFoundError as fe:
                    logger.error("カメラ出力VMDファイルが正常に作成されなかったようです。\nパスを確認してください。%s\n\n%s", self.options.camera_output_vmd_path, fe, decoration=MLogger.DECORATION_BOX)

            if int(self.options.total_process) != int(self.options.now_process):
                logger.warning("一部処理がスキップされました。\n画面左下の進捗数をクリックすると、スキップされた処理がグレーで表示されています。", decoration=MLogger.DECORATION_BOX)

            return True
        except MKilledException:
            return False
        except SizingException as se:
            logger.error("サイジング処理が処理できないデータで終了しました。\n\n%s", se, decoration=MLogger.DECORATION_BOX)
            return False
        except Exception as e:
            logger.critical("サイジング処理が意図せぬエラーで終了しました。", e, decoration=MLogger.DECORATION_BOX)
            return False
        finally:
            logging.shutdown()
    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