def main():
    m = core.prompt_user_filename(".pmx")

    pmx = pmxlib.read_pmx(m)

    core.MY_PRINT_FUNC("")
    # valid input is any string that can matched aginst a morph idx
    s = core.MY_GENERAL_INPUT_FUNC(
        lambda x: morph_scale.get_idx_in_pmxsublist(x, pmx.morphs) is not None,
        [
            "Please specify the target morph: morph #, JP name, or EN name (names are not case sensitive).",
            "Empty input will quit the script."
        ])
    # do it again, cuz the lambda only returns true/false
    morph = morph_scale.get_idx_in_pmxsublist(s, pmx.morphs)
    print(pmx.morphs[morph].name_jp)

    newmorphitems = []

    print("target morph controls %d verts" % len(pmx.morphs[morph].items))
    count = 0

    for item in pmx.morphs[morph].items:
        item: pmxstruct.PmxMorphItemVertex

        v = pmx.verts[item.vert_idx]
        wtype = v.weighttype
        w = v.weight
        # already know its all mode1

        rot = 0
        # only care about BDEF2, right? or sdef
        # if not a bdef2 vertex, then rot=0 meaning no change
        if wtype == 1 or wtype == 3:
            for b, r in zip(matchbones, rotamt):
                # get the weight %, multiply it by how much the bone is rotated by
                if w[0] == b:
                    rot += r * w[2]
                elif w[1] == b:
                    rot += r * (1 - w[2])
            # count how many actually get rotated
            if rot != 0: count += 1
        # convert from degrees to radians for rotate2d()
        rot = math.radians(rot)

        # now the YZ component of the morph vector is rotated around the origin
        ny, nz = core.rotate2d((0, 0), rot, item.move[1:3])
        newitem = pmxstruct.PmxMorphItemVertex(item.vert_idx,
                                               [item.move[0], ny, nz])
        newmorphitems.append(newitem)

    print("partial-rotated %d verts" % count)

    newmorph = pmxstruct.PmxMorph("v-rot", "v-rot", 1, 1, newmorphitems)
    pmx.morphs.append(newmorph)
    # done iter, now write
    OUT = core.get_unused_file_name("NEW.pmx")
    pmxlib.write_pmx(OUT, pmx)
    print("done")
Пример #2
0
def main():
    pmxname = core.prompt_user_filename(".pmx")
    pmxname_done = "edgeweightapplied.pmx"
    maskname = core.prompt_user_filename(".png")

    pmx1 = pmxlib.read_pmx(pmxname, moreinfo=True)

    im = Image.open(maskname).convert('RGBA')
    # isolate only one of the layers, all values should be equal
    r, g, b, a = im.split()

    px = r.load()

    # if uv = 1, then access index 4095 not 4096
    print(im.size)
    s = (im.size[0] - 1, im.size[1] - 1)

    print("numverts =", len(pmx1.verts))
    for d, v in enumerate(pmx1.verts):
        progprint(d / len(pmx1.verts))

        # have vertex v
        # convert uv coords to nearest pixel idx
        # which is x/y??
        # do i need to invert an axis?
        x = round(v.uv[0] * s[0])
        y = round(v.uv[1] * s[1])
        # get the pixel at this xy
        p = px[x, y]
        # print(p)

        # convert pixel value 0-255 to 0-1 edge factor
        # not sure whether i need to invert or not? 50/50 shot so lets go
        e = p / 255

        # store into v
        v.edgescale = e

        pass
    pmxlib.write_pmx(pmxname_done, pmx1, moreinfo=True)
    print("done")
Пример #3
0
def main():
    pmxname = core.prompt_user_filename(".pmx")

    pmx = pmxlib.read_pmx(pmxname, moreinfo=True)

    # first, attach the vert index to the vert object, so i can determine the before-after map
    idxlist = list(range(len(pmx.verts)))
    vertlist = list(zip(pmx.verts, idxlist))
    # lambda func to use for sorting: returns a list of keys to sort by
    # use + to append lists
    # this key will sort by u then by v then by x then by y then by z
    sortkey = lambda x: x[0].uv + x[0].pos
    # then, sort the list
    print("sorting")
    vertlist.sort(key=sortkey)

    # unzip
    new_vertlist = [a for a, b in vertlist]
    old_idxs = [b for a, b in vertlist]

    # put the newly sorted list into the pmx struct
    pmx.verts = new_vertlist

    # build a map of old index to new index
    old_to_new = dict(zip(old_idxs, idxlist))

    # now update all faces
    print("doing faces")
    for f in pmx.faces:
        for i in range(3):
            f[i] = old_to_new[f[i]]

    # now update all morphs
    print("doing morphs")
    for m in pmx.morphs:
        if m.morphtype == pmxstruct.MorphType.VERTEX:  #vertex
            for item in m.items:
                item.vert_idx = old_to_new[item.vert_idx]
        if m.morphtype in (
                pmxstruct.MorphType.UV,
                pmxstruct.MorphType.UV_EXT1,
                pmxstruct.MorphType.UV_EXT2,
                pmxstruct.MorphType.UV_EXT3,
                pmxstruct.MorphType.UV_EXT4,
        ):  # uv
            for item in m.items:
                item.vert_idx = old_to_new[item.vert_idx]

    # softbodies: eh, who cares

    pmxname_done = pmxname[:-4] + "_Vsort.pmx"
    pmxlib.write_pmx(pmxname_done, pmx, moreinfo=True)
    print("done")
def main():
    print("")
    print(
        "Move each vertex in set A to match the position of the closest vertex in set B"
    )
    print("")

    ######################## mode selection #######################

    # promt vertex CSV name
    # input: vertex CSV file with all the vertexes that I want to modify
    print(
        "Please enter name of vertex CSV input file with the vertices to be moved:"
    )
    input_filename_vertex_source = core.prompt_user_filename(".csv")
    rawlist_vertex_source = core.read_file_to_csvlist(
        input_filename_vertex_source, use_jis_encoding=True)

    # verify that these are the correct kind of CSVs
    if rawlist_vertex_source[0] != core.pmxe_vertex_csv_header:
        core.pause_and_quit("Err: '{}' is not a valid vertex CSV".format(
            input_filename_vertex_source))

    # promt vertex CSV name
    # input: vertex CSV file with all the vertexes that I want to modify
    print(
        "Please enter name of vertex CSV input file with the destination geometry:"
    )
    input_filename_vertex_dest = core.prompt_user_filename(".csv")
    rawlist_vertex_dest = core.read_file_to_csvlist(input_filename_vertex_dest,
                                                    use_jis_encoding=True)

    # verify that these are the correct kind of CSVs
    if rawlist_vertex_dest[0] != core.pmxe_vertex_csv_header:
        core.pause_and_quit("Err: '{}' is not a valid vertex CSV".format(
            input_filename_vertex_dest))

    ##########################################################

    # for each vertex in vertex_source, find the closest vertex in vertex_dest and move the source vertex to that dest position

    for v in rawlist_vertex_source:
        if v[0] != core.pmxe_vertex_csv_tag:
            continue
        # calculate the dist from this v to all d, and store the minimum
        min_coord = [0, 0, 0]
        min_val = 100
        for d in rawlist_vertex_dest:
            if d[0] != core.pmxe_vertex_csv_tag:
                continue
            dist = (v[2] - d[2])**2 + (v[3] - d[3])**2 + (v[4] - d[4])**2
            if dist < min_val:
                min_val = dist
                min_coord = d[2:5]
        # found the minimum
        # now apply it to v
        v[2:5] = min_coord

    # write out
    # build the output file name and ensure it is free
    output_filename = input_filename_vertex_source[:-4] + "_aligned.csv"
    output_filename = core.get_unused_file_name(output_filename)
    print("Writing aligned result to '" + output_filename + "'...")
    # export modified CSV
    core.write_csvlist_to_file(output_filename,
                               rawlist_vertex_source,
                               use_jis_encoding=True)

    core.pause_and_quit("Done with everything! Goodbye!")

    return None
def main():
    print(
        "Transfer a morph from one model to another, assuming the geometry is in the same position"
    )
    print("Needs source PMX, source morph, and dest PMX")

    # prompt PMX name
    print("Please enter name of DESTINATION PMX model file:")
    dest_name_pmx = core.prompt_user_filename(".pmx")
    dest_pmx = pmxlib.read_pmx(dest_name_pmx, moreinfo=True)

    # prompt PMX name
    print("Please enter name of SOURCE PMX model file:")
    source_name_pmx = core.prompt_user_filename(".pmx")
    source_pmx = pmxlib.read_pmx(source_name_pmx, moreinfo=True)

    while True:
        print("Please enter/paste JP name of morph to transfer:")
        s = input("name: >")
        # exit condition is empty input
        if s == "": break

        # find the morph with the matching name
        source_morph_idx = core.my_list_search(source_pmx.morphs,
                                               lambda x: x.name_jp == s)
        if source_morph_idx is None:
            print("err: could not find that name, try again")
            continue

        # verify vertex morph
        source_morph = source_pmx.morphs[source_morph_idx]
        if source_morph.morphtype != 1:
            print("err: for now, only support vertex morphs")
            continue

        newmorph = pmxstruct.PmxMorph(name_jp=source_morph.name_jp,
                                      name_en=source_morph.name_en,
                                      panel=source_morph.panel,
                                      morphtype=source_morph.morphtype,
                                      items=[])
        # have source, have dest, have morph
        # begin iterating!
        # for each vert ref in vertex morph, go to vert in source PMX to get position
        #
        already_used_verts = set()

        for asdf, morphitem in enumerate(source_morph.items):
            core.print_progress_oneline(asdf / len(source_morph.items))
            vertid = morphitem.vert_idx
            vertpos = source_pmx.verts[vertid].pos  # get vertex xyz
            # find the vert or verts in dest_pmx that correspond to this vert in source_pmx
            # problem: multiple vertices at the same location
            # in most cases, all verts at a location will move the same amount... but not always? how to handle?
            # TODO check thru source pmx morph for "diverging" vertices like this? same location in source but not same offset?
            # if some get left behind that's OK, that's usually material borders, easy to use morph editor, only see materials I don't want to morph, and remove those verts from the morph
            # solution: all verts within some radius? not perfect solution
            # radius is hardcoded... if no dest vert found within radius, then what? warn & report nearest?
            # maybe find nearest vertex, and then find all vertices within 110% of that radius?

            # calculate dist from here to each vert in dest_pmx
            # find all verts within this dist threshold
            matching_verts = []
            dist_list = []
            for d, v2 in enumerate(dest_pmx.verts):
                dist = core.my_euclidian_distance(
                    [vertpos[i] - v2.pos[i] for i in range(3)])
                dist_list.append(dist)
                if dist < THRESHOLD:
                    matching_verts.append(d)
            if not matching_verts:
                print(
                    "warning: unable to find any verts within the threshold for source vert ID %d"
                    % vertid)
                print("nearest vert is dist=%f" % min(dist_list))
            for v in matching_verts:
                if v not in already_used_verts:
                    already_used_verts.add(v)
                    newitem = pmxstruct.PmxMorphItemVertex(vert_idx=v,
                                                           move=morphitem.move)
                    newmorph.items.append(newitem)

            pass  # end of for-each-morphitem loop

        # done building the new morph, hopefully
        # make the vertices sorted cuz i can
        newmorph.items.sort(key=lambda x: x.vert_idx)

        if len(newmorph.items) != len(source_morph.items):
            print(
                "warning: length mismatch! source has %d and new has %d, this requires closer attention"
                % (len(source_morph.items), len(newmorph.items)))

        # add it to the dest pmx
        dest_pmx.morphs.append(newmorph)

        pass  # end of while-loop

    print("DONE")
    pmxlib.write_pmx("TRANSFER.pmx", dest_pmx)

    return None
Пример #6
0
def main():
    # print info to explain the purpose of this file
    print(
        "This tool will rotate the given geometry such that the two 'prime vertices' specified have equal values along the desired axis"
    )
    print("Choose from 6 modes to pick axis of alignment and axis of rotation")
    print("Also choose whether to force their position to 0 or their average")
    # print info to explain what inputs it needs
    print(
        "Inputs: vertex CSV 'whatever.csv' containing all the geometry that will be rotated"
    )
    # print info to explain what outputs it creates
    print(
        "Outputs: vertex CSV 'whatever_aligned.csv' where all the input geometry has been rotated"
    )
    print("")

    ######################## mode selection #######################
    print(
        "Choose an axis to align the points on, and an axis to rotate around:")
    print(
        " 1 = X-align, Y-rotate: create left-right symmetry by rotating left/right"
    )  # DONE
    print(
        " 2 = X-align, Z-rotate: create left-right symmetry by tilting left/right"
    )  # DONE
    print(
        " 3 = Y-align, X-rotate: create front-back level by tilting front/back"
    )  # DONE
    print(
        " 4 = Y-align, Z-rotate: create left-right level by tilting left/right"
    )  # DONE
    print(" 5 = Z-align, X-rotate: create vertical by tilting front/back"
          )  # DONE
    print(
        " 6 = Z-align, Y-rotate: create left-right symmetry by rotating left/right"
    )  # DONE

    mode = core.prompt_user_choice((1, 2, 3, 4, 5, 6))
    if mode == 1:
        # DEFAULT CASE
        axis = "X"
        Xidx = 2
        Zidx = 4
    elif mode == 2:
        axis = "X"
        Xidx = 2
        Zidx = 3
    elif mode == 3:
        axis = "Y"
        Xidx = 3
        Zidx = 4
    elif mode == 4:
        axis = "Y"
        Xidx = 3
        Zidx = 2
    elif mode == 5:
        axis = "Z"
        Xidx = 4
        Zidx = 3
    elif mode == 6:
        axis = "Z"
        Xidx = 4
        Zidx = 2
    else:
        axis = "wtf"
        Xidx = 9999
        Zidx = 9999
        print("congrats you somehow broke it")

    # choose whether to align to axis=0 or align to axis=avg
    print("After rotate, shift prime points to " + axis + "=0 or leave at " +
          axis + "=avg?")
    print(" 1 = " + axis + "=0")
    print(" 2 = " + axis + "=avg")
    useavg = core.prompt_user_choice((1, 2))
    useavg = bool(useavg - 1)

    # promt vertex CSV name
    # input: vertex CSV file with all the vertexes that I want to modify
    print("Please enter name of vertex CSV input file:")
    input_filename_vertex = core.prompt_user_filename(".csv")
    rawlist_vertex = core.read_file_to_csvlist(input_filename_vertex,
                                               use_jis_encoding=True)

    # verify that these are the correct kind of CSVs
    if rawlist_vertex[0] != core.pmxe_vertex_csv_header:
        core.pause_and_quit("Err: '{}' is not a valid vertex CSV".format(
            input_filename_vertex))

    # i plan to re-write this out as a csv file, so let's maintain as much of the input formatting as possible
    header = rawlist_vertex.pop(0)

    ##########################################################
    # prompt for the two prime points:
    # text input 2 point IDs, to indicate the two points to align to x=0
    prime_vertex_one = input("Prime vertex ID #1: ")
    prime_points = []
    v1 = core.my_list_search(rawlist_vertex,
                             lambda x: x[1] == int(prime_vertex_one),
                             getitem=True)
    if v1 is None:
        core.pause_and_quit("Err: unable to find '" + prime_vertex_one +
                            "' in vertex CSV, unable to operate")
    prime_points.append(copy.copy(v1))

    prime_vertex_two = input("Prime vertex ID #2: ")
    v2 = core.my_list_search(rawlist_vertex,
                             lambda x: x[1] == int(prime_vertex_two),
                             getitem=True)
    if v2 is None:
        core.pause_and_quit("Err: unable to find '" + prime_vertex_two +
                            "' in vertex CSV, unable to operate")
    prime_points.append(copy.copy(v2))

    # CALCULATE THE AVERAGE
    # position of the two prime points... rotate around this point
    avgx = (prime_points[0][2] + prime_points[1][2]) / 2.0
    avgy = (prime_points[0][3] + prime_points[1][3]) / 2.0
    avgz = (prime_points[0][4] + prime_points[1][4]) / 2.0
    avg = [avgx, avgy, avgz]

    # note: rotating around Y-axis, so Y never ever comes into it. Z is my effective Y.
    # CALCULATE THE ANGLE:
    # first shift both prime points so that prime0 is on 0/0/0
    prime_points[1][2] -= prime_points[0][2]
    prime_points[1][3] -= prime_points[0][3]
    prime_points[1][4] -= prime_points[0][4]
    prime_points[0][2:5] = [0.0, 0.0, 0.0]
    # then calculate the angle between the two prime points
    # ensure deltaZ is positive: calculate the angle to whichever point has the greater z
    if prime_points[0][Zidx] < prime_points[1][Zidx]:
        vpx = prime_points[1][Xidx]
        vpz = prime_points[1][Zidx]
    else:
        vpx = -prime_points[1][Xidx]
        vpz = -prime_points[1][Zidx]
    # then use atan to calculate angle in radians
    angle = math.atan2(vpx, vpz)
    print("Angle = " + str(math.degrees(angle)))

    # APPLY THE ROTATION
    # horizontally rotate all points around the average point
    origin = (avg[Xidx - 2], avg[Zidx - 2])
    origin_nrm = (0.0, 0.0)
    for i, v in enumerate(rawlist_vertex):
        point = (v[Xidx], v[Zidx])
        newpoint = core.rotate2d(origin, angle, point)
        v[Xidx], v[Zidx] = newpoint
        # also rotate each normal!
        point_nrm = (v[Xidx + 3], v[Zidx + 3])
        newnrm = core.rotate2d(origin_nrm, angle, point_nrm)
        v[Xidx + 3], v[Zidx + 3] = newnrm
        # progress printout
        core.print_progress_oneline(i / len(rawlist_vertex))
    print("done rotating              ")
    # FORCE TO ZERO
    if not useavg:
        # first shift all geometry so that one of the prime points is on the x=0
        # choose to shift by prime0
        shift = avg[Xidx - 2]
        for v in rawlist_vertex:
            v[Xidx] -= shift
            # # anything extremely close to 0 becomes set to exactly 0
            if -0.000000001 < v[Xidx] < 0.000000001:
                v[Xidx] = 0.0
        print("done shifting to zero              ")

    # write out
    # re-add the header line
    rawlist_vertex = [header] + rawlist_vertex
    # build the output file name and ensure it is free
    output_filename = input_filename_vertex[:-4] + "_aligned.csv"
    output_filename = core.get_unused_file_name(output_filename)
    print("Writing aligned result to '" + output_filename + "'...")
    # export modified CSV
    core.write_csvlist_to_file(output_filename,
                               rawlist_vertex,
                               use_jis_encoding=True)

    core.pause_and_quit("Done with everything! Goodbye!")

    return None