def prepare_curve_morph(self, morph_name: str): try: logger.copy(self.options) logger.info("【スムージング1回目】%s 開始", morph_name) # 全キーフレを取得 fnos = self.options.motion.get_morph_fnos(morph_name) logger.debug("get_morph_fnos morph_name: %s, fnos: %s", morph_name, fnos) m_values = [] for fno in fnos: mf = self.options.motion.calc_mf(morph_name, fno) m_values.append(mf.ratio) m_all_values = MBezierUtils.calc_value_from_catmullrom(morph_name, fnos, m_values) logger.debug("get_morph_fnos morph_name: %s, m_all_values: %s", morph_name, m_all_values[:10]) # カトマル曲線で生成した値を全打ち for fno, mr in enumerate(m_all_values): mf = VmdMorphFrame(fno) mf.set_name(morph_name) if math.isnan(mr) or math.isinf(mr): logger.debug("** mr: (%s)%s", fno, mr) mf.ratio = mr self.options.motion.regist_mf(mf, morph_name, fno) logger.info("【スムージング1回目】%s 終了", morph_name) return True except SizingException as se: logger.error("スムージング処理が処理できないデータで終了しました。\n\n%s", se.message) return False except Exception as e: logger.error("スムージング処理が意図せぬエラーで終了しました。", e) return False
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 replace_morph(self, data_set_idx: int, data_set: MOptionsDataSet): try: # 置換前のモーフリスト original_morphs = {} # 置換後のモーフリスト replaced_morphs = {} for (org_morph_name, rep_morph_name, morph_ratio) in data_set.morph_list: if org_morph_name in data_set.motion.morphs: # 置換元モーフがある場合、保持 original_morphs[org_morph_name] = copy.deepcopy( data_set.motion.morphs[org_morph_name]) if rep_morph_name in data_set.motion.morphs and org_morph_name != rep_morph_name: # 置換先モーフがある場合、保持(元と先が同じ場合、スルー) replaced_morphs[rep_morph_name] = copy.deepcopy( data_set.motion.morphs[rep_morph_name]) # 保持したので、モーフ削除 for (org_morph_name, rep_morph_name, morph_ratio) in data_set.morph_list: if org_morph_name in data_set.motion.morphs: del data_set.motion.morphs[org_morph_name] if rep_morph_name in data_set.motion.morphs: del data_set.motion.morphs[rep_morph_name] for (org_morph_name, rep_morph_name, morph_ratio) in data_set.morph_list: if org_morph_name in original_morphs and rep_morph_name not in replaced_morphs: # 元にあって、先にない場合、置換元を置換先の名前で登録 for fno, org_morph_data in original_morphs[ org_morph_name].items(): morph_data = VmdMorphFrame(fno) # 名前を置き換える morph_data.set_name(rep_morph_name) # 大きさに比率をかける morph_data.ratio = org_morph_data.ratio * morph_ratio if rep_morph_name not in replaced_morphs: replaced_morphs[rep_morph_name] = {} # 置換後モーフデータを設定 replaced_morphs[rep_morph_name][fno] = morph_data elif org_morph_name in original_morphs and rep_morph_name in replaced_morphs: # 元にあって、先にもある場合 for fno, org_morph_data in original_morphs[ org_morph_name].items(): morph_data = VmdMorphFrame(fno) # 名前を置き換える morph_data.set_name(rep_morph_name) # 大きさに比率をかける morph_data.ratio = org_morph_data.ratio * morph_ratio if fno in replaced_morphs[rep_morph_name]: # 既に対象fnoが登録されている場合、加算して登録 morph_data.ratio += replaced_morphs[ rep_morph_name][fno].ratio # 置換後モーフデータを追加 replaced_morphs[rep_morph_name][fno] = morph_data logger.info("モーフ置換 %s → %s (%s)【No.%s】", org_morph_name, rep_morph_name, morph_ratio, (data_set_idx + 1)) # 置換が全部終わったら、再登録 for new_rep_morph_name, new_rep_morph_data in replaced_morphs.items( ): data_set.motion.morphs[new_rep_morph_name] = new_rep_morph_data 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.format_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 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