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
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())
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)
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
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
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()
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