def test_split_bf_by_fno(self): motion = VmdReader(u"test/data/補間曲線テスト01.vmd").read_data() model = PmxReader( "D:/MMD/MikuMikuDance_v926x64/UserFile/Model/ダミーボーン頂点追加2.pmx" ).read_data() target_bone_name = "ボーン01" prev_bf = motion.bones[target_bone_name][0] next_bf = motion.bones[target_bone_name][15] motion.split_bf_by_fno(target_bone_name, prev_bf, next_bf, 8) for fno in motion.get_bone_fnos(target_bone_name): bf = motion.bones[target_bone_name][fno] print("fno: %s ------------" % bf.fno) print("position: %s" % bf.position) print("rotation: %s" % bf.rotation.toEulerAngles4MMD()) print("int move x: %s, %s, %s, %s" % (bf.interpolation[MBezierUtils.MX_x1_idxs[3]], bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ bf.interpolation[MBezierUtils.MX_x2_idxs[3]], bf.interpolation[MBezierUtils.MX_y2_idxs[3]])) print("int move y: %s, %s, %s, %s" % (bf.interpolation[MBezierUtils.MY_x1_idxs[3]], bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ bf.interpolation[MBezierUtils.MY_x2_idxs[3]], bf.interpolation[MBezierUtils.MY_y2_idxs[3]])) print("int move z: %s, %s, %s, %s" % (bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], bf.interpolation[MBezierUtils.MZ_y2_idxs[3]])) print("int rot: %s, %s, %s, %s" % (bf.interpolation[MBezierUtils.R_x1_idxs[3]], bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ bf.interpolation[MBezierUtils.R_x2_idxs[3]], bf.interpolation[MBezierUtils.R_y2_idxs[3]])) data_set = MOptionsDataSet( motion, model, model, "E:/WebDownload/test_split_bf_by_fno_{0:%Y%m%d_%H%M%S}.vmd".format( datetime.now()), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path)
def execute(self): logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]") try: service_data_txt = "腕IK変換処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name) \ service_data_txt = "{service_data_txt} VMD: {vmd}\n".format(service_data_txt=service_data_txt, vmd=os.path.basename(self.options.motion.path)) # noqa service_data_txt = "{service_data_txt} モデル: {model}({model_name})\n".format(service_data_txt=service_data_txt, model=os.path.basename(self.options.motion.path), model_name=self.options.model.name) # noqa service_data_txt = "{service_data_txt} 不要キー削除: {center_rotation}\n".format(service_data_txt=service_data_txt, center_rotation=self.options.remove_unnecessary_flg) # noqa logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) # 処理に成功しているか result = self.convert_ik2fk() # 最後に出力 VmdWriter(MOptionsDataSet(self.options.motion, None, self.options.model, self.options.output_path, False, False, [], None, 0, [])).write() logger.info("出力終了: %s", os.path.basename(self.options.output_path), decoration=MLogger.DECORATION_BOX, title="成功") return result except SizingException as se: logger.error("腕IK変換処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) except Exception: logger.critical("腕IK変換処理が意図せぬエラーで終了しました。\n\n%s", traceback.format_exc(), decoration=MLogger.DECORATION_BOX) finally: logging.shutdown()
def execute(self): logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]") try: service_data_txt = "スムージング処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name) \ service_data_txt = "{service_data_txt} VMD: {vmd}\n".format( service_data_txt=service_data_txt, vmd=os.path.basename(self.options.motion.path)) # noqa service_data_txt = "{service_data_txt} モデル: {model}({model_name})\n".format( service_data_txt=service_data_txt, model=os.path.basename(self.options.motion.path), model_name=self.options.model.name) # noqa service_data_txt = "{service_data_txt} 処理回数: {loop_cnt}回\n".format( service_data_txt=service_data_txt, loop_cnt=self.options.loop_cnt) # noqa service_data_txt = "{service_data_txt} 補間方法: {interpolation}\n".format( service_data_txt=service_data_txt, interpolation=("補間曲線に従う" if self.options.interpolation == 0 else "補間曲線無視(円形)" if self.options.interpolation == 1 else "補間曲線無視(曲線)")) # noqa logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) # 処理に成功しているか result = self.convert_smooth() # 最後に出力 VmdWriter( MOptionsDataSet(self.options.motion, None, self.options.model, self.options.output_path, False, False, [], None, 0, [])).write() logger.info("出力終了: %s", os.path.basename(self.options.output_path), decoration=MLogger.DECORATION_BOX, title="成功") return result except SizingException as se: logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) except Exception: logger.critical("スムージング処理が意図せぬエラーで終了しました。\n\n%s", traceback.format_exc(), decoration=MLogger.DECORATION_BOX) finally: logging.shutdown()
def test_vmd_output(self): motion = VmdReader(u"test/data/補間曲線テスト01.vmd").read_data() model = PmxReader( "D:/MMD/MikuMikuDance_v926x64/UserFile/Model/ダミーボーン頂点追加2.pmx" ).read_data() for n in range(100): fill_fno = 8 fill_bone_name = "右腕{0:03d}".format(n) fill_bf = motion.calc_bf(fill_bone_name, fill_fno) fill_bf.key = True motion.bones[fill_bone_name][fill_fno] = fill_bf data_set = MOptionsDataSet( motion, model, model, "E:/WebDownload/test_vmd_output_{0:%Y%m%d_%H%M%S}.vmd".format( datetime.now()), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path)
def execute(self): logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]") try: service_data_txt = "多段分割処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name) \ service_data_txt = "{service_data_txt} VMD: {vmd}\n".format( service_data_txt=service_data_txt, vmd=os.path.basename(self.options.motion.path)) # noqa service_data_txt = "{service_data_txt} モデル: {model}({model_name})\n".format( service_data_txt=service_data_txt, model=os.path.basename(self.options.motion.path), model_name=self.options.model.name) # noqa service_data_txt = "{service_data_txt} 不要キー削除: {center_rotation}\n".format( service_data_txt=service_data_txt, center_rotation=self.options.remove_unnecessary_flg) # noqa selections = ["{0} → 回転(1): {1}, 回転(2): {2}, 回転(3): {3}, 移動(1): {4}, 移動(2): {5}, 移動(3): {6}" \ .format(bset[0], bset[1], bset[2], bset[3], bset[4], bset[5], bset[6]) for bset in self.options.target_bones] service_data_txt = "{service_data_txt} 対象ボーン: {target_bones}\n".format( service_data_txt=service_data_txt, target_bones='\n'.join(selections)) # noqa logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) motion = self.options.motion model = self.options.model futures = [] with ThreadPoolExecutor( thread_name_prefix="split", max_workers=self.options.max_workers) as executor: center_mx = "" center_my = "" center_mz = "" for (bone_name, rrxbn, rrybn, rrzbn, rmxbn, rmybn, rmzbn) in self.options.target_bones: if bone_name not in model.bones or bone_name not in motion.bones: continue if bone_name == "センター": center_mx = rmxbn center_my = rmybn center_mz = rmzbn if bone_name == "グルーブ": futures.append( executor.submit(self.convert_multi_split, bone_name, rrxbn, rrybn, rrzbn, rmxbn, rmybn, rmzbn, center_mx, center_my, center_mz)) else: futures.append( executor.submit(self.convert_multi_split, bone_name, rrxbn, rrybn, rrzbn, rmxbn, rmybn, rmzbn, "", "", "")) concurrent.futures.wait( futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False if self.options.remove_unnecessary_flg: # 不要キー削除 futures = [] with ThreadPoolExecutor( thread_name_prefix="remove", max_workers=self.options.max_workers) as executor: for (bone_name, rrxbn, rrybn, rrzbn, rmxbn, rmybn, rmzbn) in self.options.target_bones: if model.bones[bone_name].getRotatable(): if len(rrxbn) > 0: futures.append( executor.submit(self.remove_unnecessary_bf, rrxbn)) if len(rrybn) > 0: futures.append( executor.submit(self.remove_unnecessary_bf, rrybn)) if len(rrzbn) > 0: futures.append( executor.submit(self.remove_unnecessary_bf, rrzbn)) if model.bones[bone_name].getTranslatable(): if len(rmxbn) > 0: futures.append( executor.submit(self.remove_unnecessary_bf, rmxbn)) if len(rmybn) > 0: futures.append( executor.submit(self.remove_unnecessary_bf, rmybn)) if len(rmzbn) > 0: futures.append( executor.submit(self.remove_unnecessary_bf, rmzbn)) concurrent.futures.wait( futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False # 最後に出力 VmdWriter( MOptionsDataSet(self.options.motion, None, self.options.model, self.options.output_path, False, False, [], None, 0, [])).write() logger.info("出力終了: %s", os.path.basename(self.options.output_path), decoration=MLogger.DECORATION_BOX, title="成功") return True except MKilledException: return False except SizingException as se: logger.error("多段分割処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) except Exception: logger.critical("多段分割処理が意図せぬエラーで終了しました。\n\n%s", traceback.format_exc(), decoration=MLogger.DECORATION_BOX) finally: logging.shutdown()
def blend_morph(self): # モーションVMDディレクトリパス pmx_dir_path = MFileUtils.get_dir_path(self.options.model.path) # モーションVMDファイル名・拡張子 pmx_file_name, pmx_ext = os.path.splitext( os.path.basename(self.options.model.path)) dt_now = datetime.now() blend_fpath = "{0}\\{1}_blend_{2:%Y%m%d_%H%M%S}.vmd".format( pmx_dir_path, pmx_file_name, dt_now) bone_motion = VmdMotion() # 処理対象モーフ名(文字列) target_morphs = self.options.eye_list + self.options.eyebrow_list + self.options.lip_list + self.options.other_list # 処理対象モーフデータ all_morphs = [] for mk, mv in self.options.model.morphs.items(): if mv.display and mv.name in target_morphs: all_morphs.append(mv) bone_motion.morphs[mk] = {} # 変化量(少ない方のの割合を多くする) ratio_values = [self.options.inc_value * x for x in range(math.ceil(self.options.min_value / self.options.inc_value), \ math.ceil(self.options.max_value / self.options.inc_value) + 1) if self.options.min_value <= self.options.inc_value * x <= self.options.max_value] ratio_lower_values = [self.options.inc_value * x for x in range(math.ceil(self.options.min_value / self.options.inc_value), \ math.ceil(self.options.max_value / self.options.inc_value / 2) + 1) if self.options.min_value <= self.options.inc_value * x <= self.options.max_value] ratio_zero_values = [0 for x in range(len(all_morphs))] # 全体の比率増減量 ratio_total_values = copy.deepcopy(ratio_values) ratio_total_values.extend(ratio_lower_values) ratio_total_values.extend(ratio_lower_values) ratio_total_values.extend(ratio_zero_values) # モーフの組合せ数 morph_comb_cnt = 1 # 変化量の組合せ数 ratio_product_cnt = 1 for n in range(len(all_morphs), 0, -1): morph_comb_cnt *= n ratio_product_cnt *= len(ratio_values) morph_total_cnt = 0 if morph_comb_cnt * ratio_product_cnt <= 1000: # モーフの組合せ morph_comb = list( itertools.combinations(all_morphs, len(all_morphs))) logger.debug("morph_comb: %s", len(morph_comb)) # 変化量の直積(同じ値を許容する) ratio_product = list( itertools.product(ratio_values, repeat=len(all_morphs))) logger.debug("ratio_product: %s", len(ratio_product)) # 組合せが1000以下なら組合せをそのまま出力 brend_mr_pairs_list = list( itertools.product(morph_comb, ratio_product)) logger.debug("brend_mr_pairs_list: %s", len(brend_mr_pairs_list)) for mframe, (morphs, ratios) in enumerate(brend_mr_pairs_list): # 上限までしか登録しない if morph_total_cnt > 19000: break for morph, ratio in zip(morphs, ratios): vmd_morph = VmdMorphFrame() vmd_morph.fno = mframe vmd_morph.set_name(morph.name) vmd_morph.ratio = ratio vmd_morph.key = True bone_motion.morphs[morph.name][mframe] = vmd_morph logger.test(vmd_morph) morph_total_cnt += 1 else: # 組合せが1000より多い場合、ランダム for mframe in range(1000): # 上限までしか登録しない if morph_total_cnt > 19000: break for morph in all_morphs: ratio = ratio_total_values[random.randint( 0, len(ratio_values) - 1)] vmd_morph = VmdMorphFrame() vmd_morph.fno = mframe vmd_morph.set_name(morph.name) vmd_morph.ratio = ratio vmd_morph.key = True bone_motion.morphs[morph.name][mframe] = vmd_morph logger.test(vmd_morph) morph_total_cnt += 1 data_set = MOptionsDataSet(bone_motion, self.options.model, self.options.model, blend_fpath, False, False, [], None, 0, []) VmdWriter(data_set).write() logger.info("モーフブレンドVMD: %s", blend_fpath, decoration=MLogger.DECORATION_BOX) return True
def convert_noise(self, copy_no: int, seed: float): logger.info("ゆらぎ複製 【No.%s】", (copy_no + 1), decoration=MLogger.DECORATION_LINE) # データをコピーしてそっちを弄る motion = self.options.motion.copy() for bone_name in motion.bones.keys(): if not self.options.finger_noise_flg and "指" in bone_name: logger.info("-- 指スキップ【No.%s - %s】", copy_no + 1, bone_name) continue fnos = motion.get_bone_fnos(bone_name) prev_fno = 0 prev_sep_fno = 0 # 事前に細分化 self.prepare_split_stance(motion, bone_name) logger.info("-- 準備完了【No.%s - %s】", copy_no + 1, bone_name) for fno in fnos: bf = motion.bones[bone_name][fno] org_bf = self.options.motion.calc_bf(bone_name, fno) # 移動 if bf.position != MVector3D(): prev_org_bf = self.options.motion.calc_bf( bone_name, prev_fno) if org_bf.position == prev_org_bf.position and fno > 0: bf.position = motion.calc_bf(bone_name, prev_fno).position else: # 0だったら動かさない if round(org_bf.position.x(), 1) != 0: if self.options.motivation_flg: bf.position.setX( bf.position.x() * seed + (0.5 - np.random.rand()) * (self.options.noise_size / 10)) else: bf.position.setX( bf.position.x() + (0.5 - np.random.rand()) * (self.options.noise_size / 10)) if round(org_bf.position.y(), 1) != 0 and "足IK" not in bone_name: # 足IKのYは動かさない if self.options.motivation_flg: if org_bf.position.y() < 0: # Yはオリジナルがマイナスの場合は、マイナスのみに動かす bf.position.setY( bf.position.y() * seed + (0 - np.random.rand()) * (self.options.noise_size / 10)) elif org_bf.position.y() > 0: bf.position.setY( bf.position.y() * seed + (0.5 - np.random.rand()) * (self.options.noise_size / 10)) else: bf.position.setY( bf.position.y() + (0.5 - np.random.rand()) * (self.options.noise_size / 10)) if round(org_bf.position.z(), 1) != 0: if self.options.motivation_flg: bf.position.setZ( bf.position.z() * seed + (0.5 - np.random.rand()) * (self.options.noise_size / 10)) else: bf.position.setZ( bf.position.z() + (0.5 - np.random.rand()) * (self.options.noise_size / 10)) # 移動補間曲線 for (bz_idx1, bz_idx2, bz_idx3, bz_idx4) in [MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs, \ MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs, \ MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs]: noise_interpolation = bf.interpolation[ bz_idx1] + math.ceil((0.5 - np.random.rand()) * self.options.noise_size) bf.interpolation[bz_idx1] = bf.interpolation[ bz_idx2] = bf.interpolation[ bz_idx3] = bf.interpolation[bz_idx4] = int( noise_interpolation) # 回転 euler = bf.rotation.toEulerAngles() # 回転は元が0であっても動かす(足は除く) if "足" not in bone_name and "ひざ" not in bone_name and "足首" not in bone_name: if self.options.motivation_flg: euler.setX(euler.x() * seed + (0.5 - np.random.rand()) * self.options.noise_size) euler.setY(euler.y() * seed + (0.5 - np.random.rand()) * self.options.noise_size) euler.setZ(euler.z() * seed + (0.5 - np.random.rand()) * self.options.noise_size) else: euler.setX(euler.x() + (0.5 - np.random.rand()) * self.options.noise_size) euler.setY(euler.y() + (0.5 - np.random.rand()) * self.options.noise_size) euler.setZ(euler.z() + (0.5 - np.random.rand()) * self.options.noise_size) bf.rotation = MQuaternion.fromEulerAngles( euler.x(), euler.y(), euler.z()) # 回転補間曲線 for (bz_idx1, bz_idx2, bz_idx3, bz_idx4) in [ MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs ]: noise_interpolation = bf.interpolation[bz_idx1] + math.ceil( (0.5 - np.random.rand()) * self.options.noise_size) bf.interpolation[bz_idx1] = bf.interpolation[ bz_idx2] = bf.interpolation[ bz_idx3] = bf.interpolation[bz_idx4] = int( noise_interpolation) # 前回fno保持 prev_fno = fno if fno // 2000 > prev_sep_fno and fnos[-1] > 0: logger.count(f"【No.{copy_no + 1} - {bone_name}】", fno, fnos) prev_sep_fno = fno // 2000 output_path = self.options.output_path.replace( "nxxx", "n{0:03d}".format(copy_no + 1)) output_path = output_path.replace( "axxx", "a{0:+03d}".format(int(seed * 100) - 100)) # 最後に出力 VmdWriter( MOptionsDataSet(motion, None, self.options.model, output_path, False, False, [], None, 0, [])).write() logger.info("出力成功: %s", os.path.basename(output_path), decoration=MLogger.DECORATION_BOX) return True
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 execute(self): logging.basicConfig(level=self.options.logging_level, format="%(message)s [%(module_name)s]") try: service_data_txt = "足IK変換処理実行\n------------------------\nexeバージョン: {version_name}\n".format(version_name=self.options.version_name) \ service_data_txt = "{service_data_txt} VMD: {vmd}\n".format( service_data_txt=service_data_txt, vmd=os.path.basename(self.options.motion.path)) # noqa service_data_txt = "{service_data_txt} モデル: {model}({model_name})\n".format( service_data_txt=service_data_txt, model=os.path.basename(self.options.motion.path), model_name=self.options.model.name) # noqa service_data_txt = "{service_data_txt} 不要キー削除: {center_rotation}\n".format( service_data_txt=service_data_txt, center_rotation=self.options.remove_unnecessary_flg) # noqa logger.info(service_data_txt, decoration=MLogger.DECORATION_BOX) futures = [] with ThreadPoolExecutor( thread_name_prefix="leffk", max_workers=self.options.max_workers) as executor: futures.append(executor.submit(self.convert_leg_fk2ik, "右")) futures.append(executor.submit(self.convert_leg_fk2ik, "左")) concurrent.futures.wait( futures, timeout=None, return_when=concurrent.futures.FIRST_EXCEPTION) for f in futures: if not f.result(): return False # 最後に出力 VmdWriter( MOptionsDataSet(self.options.motion, None, self.options.model, self.options.output_path, False, False, [], None, 0, [])).write() logger.info("出力終了: %s", os.path.basename(self.options.output_path), decoration=MLogger.DECORATION_BOX, title="成功") return True except MKilledException: return False except SizingException as se: logger.error("足IK変換処理が処理できないデータで終了しました。\n\n%s", se.message, decoration=MLogger.DECORATION_BOX) except Exception: logger.critical("足IK変換処理が意図せぬエラーで終了しました。\n\n%s", traceback.format_exc(), decoration=MLogger.DECORATION_BOX) finally: logging.shutdown()
def a_test_split_bf_by_fno02(self): original_motion = VmdReader(u"test/data/補間曲線テスト01.vmd").read_data() model = PmxReader( "D:/MMD/MikuMikuDance_v926x64/UserFile/Model/ダミーボーン頂点追加2.pmx" ).read_data() target_bone_name = "ボーン01" links = BoneLinks() links.append(model.bones["SIZING_ROOT_BONE"]) links.append(model.bones["ボーン01"]) base_params = [0, 16, 32, 127] # https://qiita.com/wakame1367/items/0744268e928a28810c20 for xparams, yparams, zparams, rparams in zip(np.array(np.meshgrid(base_params, base_params, base_params, base_params)).T.reshape(-1, 4), \ np.array(np.meshgrid(base_params, base_params, base_params, base_params)).T.reshape(-1, 4), \ np.array(np.meshgrid(base_params, base_params, base_params, base_params)).T.reshape(-1, 4), \ np.array(np.meshgrid(base_params, base_params, base_params, base_params)).T.reshape(-1, 4)): try: for fill_fno in range( original_motion.get_bone_fnos(target_bone_name)[0] + 1, original_motion.get_bone_fnos(target_bone_name)[-1]): motion = cPickle.loads(cPickle.dumps(original_motion, -1)) # bfの補間曲線を再設定する next_bf = motion.bones[target_bone_name][ motion.get_bone_fnos(target_bone_name)[-1]] motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(xparams[0], xparams[1]), MVector2D(xparams[2], xparams[3]), None], \ MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(yparams[0], yparams[1]), MVector2D(yparams[2], yparams[3]), None], \ MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(zparams[0], zparams[1]), MVector2D(zparams[2], zparams[3]), None], \ MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(rparams[0], rparams[1]), MVector2D(rparams[2], rparams[3]), None], \ MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) # 補間曲線を再設定したモーションを再保持 org_motion = cPickle.loads(cPickle.dumps(motion, -1)) # 間のキーフレをテスト prev_bf = motion.bones[target_bone_name][ motion.get_bone_fnos(target_bone_name)[0]] next_bf = motion.bones[target_bone_name][ motion.get_bone_fnos(target_bone_name)[-1]] result = motion.split_bf_by_fno(target_bone_name, prev_bf, next_bf, fill_fno) # 分割に成功した場合、誤差小。失敗してる場合は誤差大 delta = 0.3 if result else 1 # print("-----------------------------") # for now_fno in motion.get_bone_fnos(target_bone_name): # # 有効なキーフレをテスト # now_bf = motion.calc_bf(target_bone_name, now_fno) # org_pos_dic = MServiceUtils.calc_global_pos(model, links, org_motion, now_fno) # now_pos_dic = MServiceUtils.calc_global_pos(model, links, motion, now_fno) # print("fill_fno: %s, now_fno: %s key: %s ------------" % (fill_fno, now_bf.fno, now_bf.key)) # print("xparams: %s" % xparams) # print("yparams: %s" % yparams) # print("zparams: %s" % zparams) # print("rparams: %s" % rparams) # print("position: %s" % now_bf.position) # print("rotation: %s" % now_bf.rotation.toEulerAngles4MMD()) # print("int move x: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MX_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ # now_bf.interpolation[MBezierUtils.MX_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y2_idxs[3]])) # print("int move y: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MY_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ # now_bf.interpolation[MBezierUtils.MY_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y2_idxs[3]])) # print("int move z: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ # now_bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y2_idxs[3]])) # print("int rot: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.R_x1_idxs[3]], now_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ # now_bf.interpolation[MBezierUtils.R_x2_idxs[3]], now_bf.interpolation[MBezierUtils.R_y2_idxs[3]])) # self.assertAlmostEqual(org_pos_dic[target_bone_name].x(), now_pos_dic[target_bone_name].x(), delta=(delta * 2)) # self.assertAlmostEqual(org_pos_dic[target_bone_name].y(), now_pos_dic[target_bone_name].y(), delta=(delta * 3)) # self.assertAlmostEqual(org_pos_dic[target_bone_name].z(), now_pos_dic[target_bone_name].z(), delta=(delta * 4)) print("-----------------------------") for fno in range( motion.get_bone_fnos(target_bone_name)[-1]): # org_bf = org_motion.calc_bf(target_bone_name, fno) now_bf = motion.calc_bf(target_bone_name, fno) org_pos_dic = MServiceUtils.calc_global_pos( model, links, org_motion, fno) now_pos_dic = MServiceUtils.calc_global_pos( model, links, motion, fno) print("** fill_fno: %s, fno: %s key: %s ------------" % (fill_fno, now_bf.fno, now_bf.key)) print("xparams: %s" % xparams) print("yparams: %s" % yparams) print("zparams: %s" % zparams) print("rparams: %s" % rparams) print("** position: %s" % now_bf.position) print("** rotation: %s" % now_bf.rotation.toEulerAngles4MMD()) print("** int move x: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MX_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MX_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y2_idxs[3]])) print("** int move y: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MY_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MY_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y2_idxs[3]])) print("** int move z: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y2_idxs[3]])) print("** int rot: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.R_x1_idxs[3]], now_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.R_x2_idxs[3]], now_bf.interpolation[MBezierUtils.R_y2_idxs[3]])) self.assertAlmostEqual( org_pos_dic[target_bone_name].x(), now_pos_dic[target_bone_name].x(), delta=(delta * 2)) self.assertAlmostEqual( org_pos_dic[target_bone_name].y(), now_pos_dic[target_bone_name].y(), delta=(delta * 3)) self.assertAlmostEqual( org_pos_dic[target_bone_name].z(), now_pos_dic[target_bone_name].z(), delta=(delta * 4)) # now = datetime.now() # data_set = MOptionsDataSet(motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}.vmd".format(now), False, False) # VmdWriter(data_set).write() # print(data_set.output_vmd_path) # data_set = MOptionsDataSet(org_motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}_orignal.vmd".format(now), False, False) # VmdWriter(data_set).write() # print(data_set.output_vmd_path) except Exception as e: # エラーになったらデータを出力する now = datetime.now() data_set = MOptionsDataSet( motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}.vmd" .format(now), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path) data_set = MOptionsDataSet( org_motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}_orignal.vmd" .format(now), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path) raise e
def test_split_bf_by_fno01(self): original_motion = VmdReader(u"test/data/補間曲線テスト01.vmd").read_data() model = PmxReader( "D:/MMD/MikuMikuDance_v926x64/UserFile/Model/ダミーボーン頂点追加2.pmx" ).read_data() target_bone_name = "ボーン01" links = BoneLinks() links.append(model.bones["SIZING_ROOT_BONE"]) links.append(model.bones["ボーン01"]) for pidx in range(10): try: params = np.random.randint(0, 127, (1, 4)) # params = [[116, 24, 22, 82]] for fill_fno in range( original_motion.get_bone_fnos(target_bone_name)[0] + 1, original_motion.get_bone_fnos(target_bone_name)[-1]): motion = original_motion.copy() # bfの補間曲線を再設定する next_bf = motion.bones[target_bone_name][ motion.get_bone_fnos(target_bone_name)[-1]] motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(20, 20), MVector2D(107, 107), None], \ MBezierUtils.R_x1_idxs, MBezierUtils.R_y1_idxs, MBezierUtils.R_x2_idxs, MBezierUtils.R_y2_idxs) motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(params[0][0], params[0][1]), MVector2D(params[0][2], params[0][3]), None], \ MBezierUtils.MX_x1_idxs, MBezierUtils.MX_y1_idxs, MBezierUtils.MX_x2_idxs, MBezierUtils.MX_y2_idxs) motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(20, 20), MVector2D(107, 107), None], \ MBezierUtils.MY_x1_idxs, MBezierUtils.MY_y1_idxs, MBezierUtils.MY_x2_idxs, MBezierUtils.MY_y2_idxs) motion.reset_interpolation_parts(target_bone_name, next_bf, [None, MVector2D(20, 20), MVector2D(107, 107), None], \ MBezierUtils.MZ_x1_idxs, MBezierUtils.MZ_y1_idxs, MBezierUtils.MZ_x2_idxs, MBezierUtils.MZ_y2_idxs) # 補間曲線を再設定したモーションを再保持 org_motion = motion.copy() # 間のキーフレをテスト prev_bf = motion.bones[target_bone_name][ motion.get_bone_fnos(target_bone_name)[0]] next_bf = motion.bones[target_bone_name][ motion.get_bone_fnos(target_bone_name)[-1]] result = motion.split_bf_by_fno(target_bone_name, prev_bf, next_bf, fill_fno) # 分割に成功した場合、誤差小。失敗してる場合は誤差大 delta = 0.3 if result else 1 print("-----------------------------") for now_fno in motion.get_bone_fnos(target_bone_name): # 有効なキーフレをテスト now_bf = motion.calc_bf(target_bone_name, now_fno) org_pos_dic = MServiceUtils.calc_global_pos( model, links, org_motion, now_fno) now_pos_dic = MServiceUtils.calc_global_pos( model, links, motion, now_fno) print( "fill_fno: %s, now_fno: %s key: %s (%s) ------------" % (fill_fno, now_bf.fno, now_bf.key, pidx)) print("params: %s" % params) print("position: %s" % now_bf.position) print("rotation: %s" % now_bf.rotation.toEulerAngles4MMD()) print("int move x: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MX_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MX_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y2_idxs[3]])) print("int move y: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MY_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MY_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y2_idxs[3]])) print("int move z: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y2_idxs[3]])) print("int rot: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.R_x1_idxs[3]], now_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.R_x2_idxs[3]], now_bf.interpolation[MBezierUtils.R_y2_idxs[3]])) self.assertAlmostEqual( org_pos_dic[target_bone_name].x(), now_pos_dic[target_bone_name].x(), delta=0.2) self.assertAlmostEqual( org_pos_dic[target_bone_name].y(), now_pos_dic[target_bone_name].y(), delta=0.2) self.assertAlmostEqual( org_pos_dic[target_bone_name].z(), now_pos_dic[target_bone_name].z(), delta=0.2) print("-----------------------------") for fno in range( motion.get_bone_fnos(target_bone_name)[-1]): # org_bf = org_motion.calc_bf(target_bone_name, fno) now_bf = motion.calc_bf(target_bone_name, fno) org_pos_dic = MServiceUtils.calc_global_pos( model, links, org_motion, fno) now_pos_dic = MServiceUtils.calc_global_pos( model, links, motion, fno) print( "** fill_fno: %s, fno: %s key: %s (%s) ------------" % (fill_fno, now_bf.fno, now_bf.key, pidx)) print("** params: %s" % params) print("** position: %s" % now_bf.position) print("** rotation: %s" % now_bf.rotation.toEulerAngles4MMD()) print("** int move x: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MX_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MX_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MX_y2_idxs[3]])) print("** int move y: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MY_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MY_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MY_y2_idxs[3]])) print("** int move z: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.MZ_x1_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.MZ_x2_idxs[3]], now_bf.interpolation[MBezierUtils.MZ_y2_idxs[3]])) print("** int rot: %s, %s, %s, %s" % (now_bf.interpolation[MBezierUtils.R_x1_idxs[3]], now_bf.interpolation[MBezierUtils.R_y1_idxs[3]], \ now_bf.interpolation[MBezierUtils.R_x2_idxs[3]], now_bf.interpolation[MBezierUtils.R_y2_idxs[3]])) self.assertAlmostEqual( org_pos_dic[target_bone_name].x(), now_pos_dic[target_bone_name].x(), delta=(delta * 2)) self.assertAlmostEqual( org_pos_dic[target_bone_name].y(), now_pos_dic[target_bone_name].y(), delta=(delta * 3)) self.assertAlmostEqual( org_pos_dic[target_bone_name].z(), now_pos_dic[target_bone_name].z(), delta=(delta * 4)) now = datetime.now() data_set = MOptionsDataSet( motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}.vmd" .format(now), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path) data_set = MOptionsDataSet( org_motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}_orignal.vmd" .format(now), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path) except Exception as e: # エラーになったらデータを出力する now = datetime.now() data_set = MOptionsDataSet( motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}.vmd" .format(now), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path) data_set = MOptionsDataSet( org_motion, model, model, "E:/WebDownload/test_split_bf_by_fno01_{0:%Y%m%d_%H%M%S%f}_orignal.vmd" .format(now), False, False) VmdWriter(data_set).write() print(data_set.output_vmd_path) 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 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