def read_bone_csv(bone_csv_path: str): model = PmxModel() model.name = osp.splitext(osp.basename(bone_csv_path))[0] with open(bone_csv_path, "r", encoding=get_file_encoding(bone_csv_path)) as bf: reader = csv.reader(bf) # 列名行の代わりにルートボーン登録 # サイジング用ルートボーン sizing_root_bone = Bone("SIZING_ROOT_BONE", "SIZING_ROOT_BONE", MVector3D(), -1, 0, 0) sizing_root_bone.index = -999 model.bones[sizing_root_bone.name] = sizing_root_bone # インデックス逆引きも登録 model.bone_indexes[sizing_root_bone.index] = sizing_root_bone.name for ridx, row in enumerate(reader): if row[0] == "Bone": bone = Bone(row[1], row[2], MVector3D(float(row[5]), float(row[6]), float(row[7])), row[13], int(row[3]), \ int(row[8]) | int(row[9]) | int(row[10]) | int(row[11]) | int(row[12])) bone.index = ridx - 1 model.bones[row[1]] = bone model.bone_indexes[bone.index] = row[1] for bidx, bone in model.bones.items(): # 親ボーンINDEXの設定 if bone.parent_index and bone.parent_index in model.bones: bone.parent_index = model.bones[bone.parent_index].index else: bone.parent_index = -1 return model
def get_vec3(joints: dict, jname: str): if jname in joints: joint = joints[jname] return MVector3D(joint["x"], joint["y"], joint["z"]) / SCALE_MIKU else: if jname == "pelvis2": # 尾てい骨くらい right_hip_vec = get_vec3(joints, "right_hip") left_hip_vec = get_vec3(joints, "left_hip") return (right_hip_vec + left_hip_vec) / 2 return MVector3D()
def calc_center(frame_joints: dict): # プロジェクション座標系の位置 px = (frame_joints["proj_joints"]["pelvis"]["x"] - (frame_joints["image"]["width"] / 2)) * SCALE_MIKU py = ((frame_joints["image"]["height"] / 2) - frame_joints["proj_joints"]["pelvis"]["y"]) * SCALE_MIKU cz = frame_joints["depth"]["depth"] * SCALE_MIKU return MVector3D(px, py, cz)
def calc_direction_qq(bf: VmdBoneFrame, motion: VmdMotion, joints: dict, direction_from_name: str, direction_to_name: str, up_from_name: str, up_to_name: str): direction_from_vec = get_vec3(joints["joints"], direction_from_name) direction_to_vec = get_vec3(joints["joints"], direction_to_name) up_from_vec = get_vec3(joints["joints"], up_from_name) up_to_vec = get_vec3(joints["joints"], up_to_name) direction = (direction_to_vec - direction_from_vec).normalized() up = (up_to_vec - up_from_vec).normalized() cross = MVector3D.crossProduct(direction, up) qq = MQuaternion.fromDirection(direction, cross) return qq
def calc_bone_direction_qq2(bf: VmdBoneFrame, motion: VmdMotion, model: PmxModel, jname: str, direction_from_name: str, direction_to_name: str, up_from_name: str, up_to_name: str, cross_from_name: str, cross_to_name: str): direction_from_vec = get_bone_vec3(model, direction_from_name) direction_to_vec = get_bone_vec3(model, direction_to_name) up_from_vec = get_bone_vec3(model, up_from_name) up_to_vec = get_bone_vec3(model, up_to_name) cross_from_vec = get_bone_vec3(model, cross_from_name) cross_to_vec = get_bone_vec3(model, cross_to_name) direction = (direction_to_vec - direction_from_vec).normalized() up = (up_to_vec - up_from_vec).normalized() cross = (cross_to_vec - cross_from_vec).normalized() qq = MQuaternion.fromDirection(direction, MVector3D.crossProduct(up, cross)) return qq
def calc_finger(bf: VmdBoneFrame, motion: VmdMotion, model: PmxModel, jname: str, default_joints: dict, frame_joints: dict, name_list: list, parent_list: list): rotation = calc_direction_qq(bf.fno, motion, frame_joints, *name_list) bone_initial = calc_bone_direction_qq(bf, motion, model, jname, *name_list) qq = MQuaternion() for parent_name in reversed(parent_list): qq *= motion.calc_bf(parent_name, bf.fno).rotation.inverted() qq = qq * rotation * bone_initial.inverted() _, _, z_qq, _ = separate_local_qq(bf.fno, bf.name, qq, model.get_local_x_axis(bf.name)) z_limited_qq = MQuaternion.fromAxisAndAngle( MVector3D(0, 0, -1 * (-1 if "right" in jname else 1)), min(90, z_qq.toDegree())) bf.rotation = z_limited_qq motion.regist_bf(bf, bf.name, bf.fno)
def read_bone_csv(bone_csv_path: str): model = PmxModel() model.name = osp.splitext(osp.basename(bone_csv_path))[0] with open(bone_csv_path, "r", encoding=get_file_encoding(bone_csv_path)) as f: reader = csv.reader(f) # 列名行の代わりにルートボーン登録 # サイジング用ルートボーン sizing_root_bone = Bone("SIZING_ROOT_BONE", "SIZING_ROOT_BONE", MVector3D(), -1, 0, 0) sizing_root_bone.index = -999 model.bones[sizing_root_bone.name] = sizing_root_bone # インデックス逆引きも登録 model.bone_indexes[sizing_root_bone.index] = sizing_root_bone.name for ridx, row in enumerate(reader): if row[0] == "Bone": bone = Bone(row[1], row[2], MVector3D(float(row[5]), float(row[6]), float(row[7])), row[13], int(row[3]), \ int(row[8]) | int(row[9]) | int(row[10]) | int(row[11]) | int(row[12])) bone.index = ridx - 1 model.bones[row[1]] = bone model.bone_indexes[bone.index] = row[1] for bidx, bone in model.bones.items(): # 親ボーンINDEXの設定 if bone.parent_index and bone.parent_index in model.bones: bone.parent_index = model.bones[bone.parent_index].index else: bone.parent_index = -1 # 首根元ボーン if "左肩" in model.bones and "右肩" in model.bones: neck_base_vertex = Vertex( -1, (model.bones["左肩"].position + model.bones["右肩"].position) / 2 + MVector3D(0, -0.1, 0), MVector3D(), [], [], Vertex.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 model.bones: # 上半身2がある場合、表示先は、上半身2 neck_base_bone.parent_index = model.bones["上半身2"].index neck_base_bone.tail_index = model.bones["上半身2"].index elif "上半身" in model.bones: neck_base_bone.parent_index = model.bones["上半身"].index neck_base_bone.tail_index = model.bones["上半身"].index neck_base_bone.index = len(model.bones.keys()) model.bones[neck_base_bone.name] = neck_base_bone model.bone_indexes[neck_base_bone.index] = neck_base_bone.name # 鼻ボーン if "頭" in model.bones and "首" in model.bones: nose_bone = Bone( "鼻", "nose", MVector3D(0, model.bones["頭"].position.y(), model.bones["頭"].position.z() - 0.5), -1, 0, 0) nose_bone.parent_index = model.bones["首"].index nose_bone.tail_index = model.bones["頭"].index nose_bone.index = len(model.bones.keys()) model.bones[nose_bone.name] = nose_bone model.bone_indexes[nose_bone.index] = nose_bone.name # 頭頂ボーン if "頭" in model.bones: head_top_bone = Bone( "頭頂", "top of head", MVector3D(0, model.bones["頭"].position.y() + 1, 0), -1, 0, 0) head_top_bone.parent_index = model.bones["鼻"].index head_top_bone.index = len(model.bones.keys()) model.bones[head_top_bone.name] = head_top_bone model.bone_indexes[head_top_bone.index] = head_top_bone.name if "右つま先" in model.bones: right_big_toe_bone = Bone( "右足親指", "", model.bones["右つま先"].position + MVector3D(0.5, 0, 0), -1, 0, 0) right_big_toe_bone.parent_index = model.bones["右足首"].index right_big_toe_bone.index = len(model.bones.keys()) model.bones[right_big_toe_bone.name] = right_big_toe_bone model.bone_indexes[right_big_toe_bone.index] = right_big_toe_bone.name right_small_toe_bone = Bone( "右足小指", "", model.bones["右つま先"].position + MVector3D(-0.5, 0, 0), -1, 0, 0) right_small_toe_bone.parent_index = model.bones["右足首"].index right_small_toe_bone.index = len(model.bones.keys()) model.bones[right_small_toe_bone.name] = right_small_toe_bone model.bone_indexes[ right_small_toe_bone.index] = right_small_toe_bone.name if "左つま先" in model.bones: left_big_toe_bone = Bone( "左足親指", "", model.bones["左つま先"].position + MVector3D(-0.5, 0, 0), -1, 0, 0) left_big_toe_bone.parent_index = model.bones["左足首"].index left_big_toe_bone.index = len(model.bones.keys()) model.bones[left_big_toe_bone.name] = left_big_toe_bone model.bone_indexes[left_big_toe_bone.index] = left_big_toe_bone.name left_small_toe_bone = Bone( "左足小指", "", model.bones["左つま先"].position + MVector3D(0.5, 0, 0), -1, 0, 0) left_small_toe_bone.parent_index = model.bones["左足首"].index left_small_toe_bone.index = len(model.bones.keys()) model.bones[left_small_toe_bone.name] = left_small_toe_bone model.bone_indexes[ left_small_toe_bone.index] = left_small_toe_bone.name return model
def get_bone_vec3(model: PmxModel, joint_name: str): bone_name, _, _, _, _ = VMD_CONNECTIONS[joint_name] if bone_name in model.bones: return model.bones[bone_name].position return MVector3D()
def execute(cmd_args): folder_path = cmd_args.folder_path bone_csv_path = cmd_args.bone_csv_path model = read_bone_csv(bone_csv_path) logger.info(model) motion = VmdMotion() # 動画上の関節位置 for fno, joints_path in enumerate( glob.glob(osp.join(folder_path, '**/*_joints.json'))): logger.info(f"■ fno: {fno} -----") frame_joints = {} with open(joints_path, 'r') as f: frame_joints = json.load(f) bf = VmdBoneFrame(fno) bf.set_name("センター") bf.position = calc_center(frame_joints) motion.regist_bf(bf, bf.name, bf.fno) for jname, (bone_name, calc_bone, name_list, parent_list, ranges) in VMD_CONNECTIONS.items(): if name_list is None: continue bf = VmdBoneFrame(fno) bf.set_name(bone_name) if calc_bone is None: if len(name_list) == 4: rotation = calc_direction_qq(bf.fno, motion, frame_joints, *name_list) initial = calc_bone_direction_qq(bf, motion, model, jname, *name_list) else: rotation = calc_direction_qq2(bf.fno, motion, frame_joints, *name_list) initial = calc_bone_direction_qq2(bf, motion, model, jname, *name_list) qq = MQuaternion() for parent_name in reversed(parent_list): qq *= motion.calc_bf(parent_name, bf.fno).rotation.inverted() qq = qq * rotation * initial.inverted() if ranges: # 可動域指定がある場合 x_qq, y_qq, z_qq, _ = separate_local_qq( bf.fno, bf.name, qq, model.get_local_x_axis(bf.name)) local_x_axis = model.get_local_x_axis(bf.name) local_z_axis = MVector3D( 0, 0, -1 * (-1 if "right" in jname else 1)) local_y_axis = MVector3D.crossProduct( local_x_axis, local_z_axis) x_limited_qq = MQuaternion.fromAxisAndAngle( local_x_axis, max( ranges["x"]["min"], min( ranges["x"]["max"], x_qq.toDegree() * MVector3D.dotProduct( local_x_axis, x_qq.vector())))) y_limited_qq = MQuaternion.fromAxisAndAngle( local_y_axis, max( ranges["y"]["min"], min( ranges["y"]["max"], y_qq.toDegree() * MVector3D.dotProduct( local_y_axis, y_qq.vector())))) z_limited_qq = MQuaternion.fromAxisAndAngle( local_z_axis, max( ranges["z"]["min"], min( ranges["z"]["max"], z_qq.toDegree() * MVector3D.dotProduct( local_z_axis, z_qq.vector())))) bf.rotation = y_limited_qq * x_limited_qq * z_limited_qq else: bf.rotation = qq motion.regist_bf(bf, bf.name, bf.fno) # 動画内の半分は地面に足が着いていると見なす center_values = np.zeros((1, 3)) for bf in motion.bones["センター"].values(): center_values = np.insert( center_values, 0, np.array([bf.position.x(), bf.position.y(), bf.position.z()]), axis=0) center_median = np.median(center_values, axis=0) for bf in motion.bones["センター"].values(): bf.position.setY(bf.position.y() - center_median[1]) writer = VmdWriter( motion, model, osp.join( folder_path, "output_{0}.vmd".format( datetime.datetime.now().strftime('%Y%m%d_%H%M%S')))) writer.write()