def parse_vmd_morphframe(raw:bytearray, moreinfo:bool) -> List[vmdstruct.VmdMorphFrame]: # get all the morph-frames, store in a list of lists morphframe_list = [] # is there enough file left to read a single number? if (len(raw) - core.get_readfrom_byte()) < struct.calcsize(fmt_number): core.MY_PRINT_FUNC("Warning: expected morphframe_ct field but file ended unexpectedly! Assuming 0 morphframes and continuing...") return morphframe_list ############################ # get the number of morph frames morphframe_ct = core.my_unpack(fmt_number, raw) if moreinfo: core.MY_PRINT_FUNC("...# of morphframes = %d" % morphframe_ct) for z in range(morphframe_ct): try: # unpack the morphframe (mname_str, f, v) = core.my_unpack(fmt_morphframe, raw) morphframe_list.append(vmdstruct.VmdMorphFrame(name=mname_str, f=f, val=v)) # display progress printouts core.print_progress_oneline(core.get_readfrom_byte() / len(raw)) except Exception as e: core.MY_PRINT_FUNC(e.__class__.__name__, e) core.MY_PRINT_FUNC("frame=", z) core.MY_PRINT_FUNC("totalframes=", morphframe_ct) core.MY_PRINT_FUNC("section=morphframe") core.MY_PRINT_FUNC("Err: something went wrong while parsing, file is probably corrupt/malformed") raise RuntimeError() return morphframe_list
def read_vmdtext_morphframe(rawlist_text: List[list]) -> List[vmdstruct.VmdMorphFrame]: ########################################### # morph frames global readfrom_line morph_list = [] # first check for bad format check2_match_first_item(rawlist_text, keystr_morphframect) morphframe_ct = rawlist_text[readfrom_line][1] core.MY_PRINT_FUNC("...# of morphframes = %d" % morphframe_ct) readfrom_line += 1 if morphframe_ct > 0: # ensure the key-line is where i think it is check3_match_keystr(rawlist_text, keystr_morphframekey) # if it is indeed here, then inc the readpointer readfrom_line += 1 for i in range(morphframe_ct): # ensure it has the right # of items on the line check1_match_len(rawlist_text, len(keystr_morphframekey)) r = rawlist_text[readfrom_line] newframe = vmdstruct.VmdMorphFrame(name=r[0], f=r[1], val=r[2]) morph_list.append(newframe) # increment the readfrom_line pointer readfrom_line += 1 # progress tracker just because core.print_progress_oneline(i / morphframe_ct) return morph_list
def read_vpd(vpd_filepath: str, moreinfo=False) -> vmdstruct.Vmd: """ Read a VPD text file and convert it to a VMD object with all boneframes and morphframes at time=0. :param vpd_filepath: destination filepath/name, relative from CWD or absolute :param moreinfo: if true, get extra printouts with more info about stuff :return: VMD object """ cleanname = core.get_clean_basename(vpd_filepath) + ".vpd" core.MY_PRINT_FUNC("Begin reading VPD file '%s'" % cleanname) # read textfile to linelist, no CSV fields to untangle here lines = core.read_txtfile_to_list(vpd_filepath, use_jis_encoding=True) # verify magic header "Vocaloid Pose Data file" if lines[0] != "Vocaloid Pose Data file": core.MY_PRINT_FUNC( "warning: did not find expected magic header! this might not be a real VPD file!" ) # get rid of the header lines.pop(0) # this var is a state machine that keeps track of what I expect to find next # if i find anything other than blankspace or what I expect, then err & die parse_state = 0 # save this so I know when I'm done reading all the bones the header promised num_bones = 0 # temp vars to hold stuff from previous lines temp_title = "qwertyuiop" temp_name = "foobar" temp_pos = tuple() temp_rot = tuple() temp_value = 0.0 # this is the VMD object that will be ultimately returned vmd_boneframes = [] vmd_morphframes = [] # iterate over the remaining lines until end-of-file for d, line in enumerate(lines): # vertical whitespace is always acceptable if not line or line.isspace(): continue # if line is not blank, it had better be something good: if parse_state == 0: # 0 = model title m = title_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find model title" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() temp_title = m.group( 1) # if valid match, then grab the actual title if moreinfo: core.MY_PRINT_FUNC("...model name = JP:'%s'" % temp_title) parse_state = 10 # next thing to look for is #bones elif parse_state == 10: # 10 = #bones m = f1_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find number of bones" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() num_bones = int(float(m.group( 1))) # if a valid match, then grab the actual # of bones if moreinfo: core.MY_PRINT_FUNC("...# of boneframes = %d" % num_bones) if num_bones == 0: parse_state = 30 # if there are 0 bones then immediately begin with the morphs else: parse_state = 20 # otherwise look for bones next elif parse_state == 20: # 20 = boneA, name m = bone_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find bone name" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() idx, name = m.group(1, 2) # get idx and name temp_name = name # can i use idx for anything? or is it totally useless? parse_state = 21 # next look for quaternion rotation elif parse_state == 21: # 21 = boneB, xyz pos m = f3_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find bone XYZ position" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() pos = m.group(1, 2, 3) # get all 3 components temp_pos = [float(f) for f in pos] # convert strings to floats parse_state = 22 # next look for quaternion rotation elif parse_state == 22: # 22 = boneC, xyzw quaternion rotation m = f4_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find bone XYZW rotation" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() quat = m.group(1, 2, 3, 4) # get all 4 components quat = [float(f) for f in quat] # convert strings to floats quat.insert( 0, quat.pop(-1)) # WXYZW -> XYZW, AKA move tail (w) to head temp_rot = core.quaternion_to_euler( quat) # convert quaternion to euler angles parse_state = 23 # next look for closing curly elif parse_state == 23: # 23 = boneD, closing curly m = close_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: bone item not properly closed" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() # finish the bone-obj and add to VMD structure # this_boneframe = [bname_str, f, xp, yp, zp, xrot, yrot, zrot, phys_off, x_ax, y_ax, z_ax, r_ax, x_ay, y_ay, # z_ay, r_ay, x_bx, y_bx, z_bx, r_bx, x_by, y_by, z_by, r_by] newframe = vmdstruct.VmdBoneFrame( name=temp_name, f=0, pos=temp_pos, rot=list(temp_rot), phys_off=False, interp=list(core.bone_interpolation_default_linear)) vmd_boneframes.append(newframe) if len(vmd_boneframes) == num_bones: parse_state = 30 # if i got all the bones i expected, move to morphs else: parse_state = 20 # otherwise, get another bone elif parse_state == 30: # 30 = morphA, name m = morph_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find morph name" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() idx, name = m.group(1, 2) # get idx and name temp_name = name # can i use idx for anything? or is it totally useless? parse_state = 31 # next look for value elif parse_state == 31: # 31 = morphB, value m = f1_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: failed to find morph value" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() v = m.group(1) # get value temp_value = float(v) # convert strings to floats parse_state = 32 # next look for close elif parse_state == 32: # 32 = morphC, closing curly m = close_re.match(line) # regex match from beginning of line if m is None: core.MY_PRINT_FUNC( "Parse err line %d state %d: morph item not properly closed" % (d + 2, parse_state)) core.MY_PRINT_FUNC("line = '%s'" % line) raise RuntimeError() # finish the morph-obj and add to VMD structure # morphframe_list.append([mname_str, f, v]) newframe = vmdstruct.VmdMorphFrame(name=temp_name, f=0, val=temp_value) vmd_morphframes.append(newframe) parse_state = 30 # loop morphs until end-of-file else: core.MY_PRINT_FUNC("this should not happen, err & die") raise RuntimeError() if moreinfo: core.MY_PRINT_FUNC("...# of morphframes = %d" % len(vmd_morphframes)) # verify we did not hit end-of-file unexpectedly, looking-for-morphA is only valid ending state if parse_state != 30: core.MY_PRINT_FUNC("Parse err state %d: hit end-of-file unexpectedly" % parse_state) raise RuntimeError() # after hitting end-of-file, assemble the parts of the final returnable VMD-list thing # builds object (header, boneframe_list, morphframe_list, camframe_list, lightframe_list, shadowframe_list, ikdispframe_list) vmd_retme = vmdstruct.Vmd( vmdstruct.VmdHeader(version=2, modelname=temp_title), vmd_boneframes, vmd_morphframes, list(), list(), list(), list()) core.MY_PRINT_FUNC("Done reading VPD file '%s'" % cleanname) return vmd_retme