def print_header(self):
     core.MY_PRINT_FUNC(core.PACKAGE_VERSION)
     core.MY_PRINT_FUNC(
         "Begin by selecting a script above, then click 'Run'")
     core.MY_PRINT_FUNC(
         "Click 'Help' to print out details of what the selected script does"
     )
     return
Exemplo n.º 2
0
	def change_mode(self, *args):
		# need to have *args here even if i dont use them
		# the the currently displayed item in the dropdown menu
		newstr = self.optionvar.get()
		# find which index within all_script_list it corresponds to
		idx = [x[0] for x in self.all_script_list].index(newstr)
		# set helptext and execute func
		self.helptext = self.all_script_list[idx][1]
		self.payload = self.all_script_list[idx][2]
		core.MY_PRINT_FUNC(">>>>>>>>>>")
		core.MY_PRINT_FUNC("Load new script '%s'" % newstr)
		core.MY_PRINT_FUNC("")
		return
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")
Exemplo n.º 4
0
	def do_the_thing(self):
		core.MY_PRINT_FUNC("="*50)
		core.MY_PRINT_FUNC(str(self.optionvar.get()))
		# disable all gui elements for the duration of this function
		# run_butt, spinbox, clear, help, debug
		self.run_butt.configure(state='disabled')
		self.which_script.configure(state='disabled')
		self.clear_butt.configure(state='disabled')
		self.help_butt.configure(state='disabled')
		self.debug_check.configure(state='disabled')
		
		try:
			self.payload(bool(self.debug_check_var.get()))
		except Exception as e:
			core.MY_PRINT_FUNC(e.__class__.__name__, e)
			core.MY_PRINT_FUNC("ERROR: failed to complete target script")
		
		# re-enable GUI elements when finished running
		self.run_butt.configure(state='normal')
		self.which_script.configure(state='normal')
		self.clear_butt.configure(state='normal')
		self.help_butt.configure(state='normal')
		self.debug_check.configure(state='normal')
		return
def gui_fileprompt(extensions: str) -> str:
    """
	Use a Tkinter File Dialogue popup to prompt for a file. Same signature as core.prompt_user_filename().
	
	:param extensions: string of valid extensions, separated by spaces
	:return: case-correct absolute file path
	"""
    # replaces core func MY_FILEPROMPT_FUNC when running in GUI mode

    # make this list into a new, separate thing: list of identifiers + globs
    if extensions in FILE_EXTENSION_MAP:
        extensions_labels = FILE_EXTENSION_MAP[extensions]
    else:
        extensions_labels = ("Unknown type", extensions)
    extensions_labels = (extensions_labels, )

    # dont trust file dialog to remember last-opened path, manually save/read it
    recordpath = core.get_persistient_storage_path("last_opened_dir.txt")
    c = core.read_txtfile_to_list(recordpath, quiet=True)
    if c:
        # if it has been used before, use the path from last time.
        c = c[0]
        # if the path from last time does not exist, walk up the path till I find a level that does still exist.
        while c and not path.isdir(c):
            c = path.dirname(c)
        start_here = c
    else:
        # if never used before, start in the executable directory
        start_here = "."

    newpath = fdg.askopenfilename(initialdir=start_here,
                                  title="Select input file: {%s}" % extensions,
                                  filetypes=extensions_labels)

    # if user closed the prompt before giving a file path, quit here
    if newpath == "":
        core.MY_PRINT_FUNC("ERROR: this script requires an input file to run")
        raise RuntimeError()

    # they got an existing file! update the last_opened_dir file
    core.write_list_to_txtfile(recordpath, [path.dirname(newpath)], quiet=True)

    return newpath
Exemplo n.º 6
0
def gui_inputpopup_trigger(args, explain_info=None):
	# print("trig")
	global inputpopup_args
	# write into simplechoice_args to signify that I want a popup
	inputpopup_args = [args, explain_info]
	# wait for a choice to be made from within the popup
	inputpopup_done.wait()
	inputpopup_done.clear()
	# if they clicked x ...
	if inputpopup_result is None:
		if callable(args):
			# this is general-input mode
			# return empty string (usually aborts the script)
			return ""
		else:
			# this is simplechoice (multichoice) mode
			# return the first option
			return args[0]
	else:
		core.MY_PRINT_FUNC(str(inputpopup_result))
		return inputpopup_result
Exemplo n.º 7
0
	def print_header(self):
		core.MY_PRINT_FUNC("Nuthouse01 - 08/24/2020 - v5.00")
		core.MY_PRINT_FUNC("Begin by selecting a script above, then click 'Run'")
		core.MY_PRINT_FUNC("Click 'Help' to print out details of what the selected script does")
		return
Exemplo n.º 8
0
	def help_func(self):
		core.MY_PRINT_FUNC(self.helptext)
Exemplo n.º 9
0
def gui_inputpopup(args, explain_info=None):
	# print("pop")
	# create popup
	win = tk.Toplevel()
	win.title("User input needed")
	# normally when X button is pressed, it calls "destroy". that would leave the script-thread indefinitely waiting on the flag!
	# this redefine will set the flag so the script resumes when X is clicked
	def on_x():
		global inputpopup_result
		inputpopup_result = None
		inputpopup_done.set()
		win.destroy()
	win.protocol("WM_DELETE_WINDOW", on_x)
	
	# init the result to None, just because
	global inputpopup_result
	inputpopup_result = None
	
	# if explain_info is given, create labels that display those strings
	if isinstance(explain_info, str):
		explain_info = [explain_info]
	if explain_info is not None:
		labelframe = tk.Frame(win)
		labelframe.pack(side=tk.TOP, fill='x')
		for f in explain_info:
			# create labels for each line
			label = tk.Label(labelframe, text=f)
			label.pack(side=tk.TOP, fill='x', padx=10, pady=10)
			core.MY_PRINT_FUNC(f)
	
	# this function commits the result & closes the popup
	def setresult(r):
		global inputpopup_result
		inputpopup_result = r
		# pressing the button should stop the mainloop
		inputpopup_done.set()
		win.destroy()
		
	# build a frame for the interactables to live in
	buttonframe = tk.Frame(win)
	buttonframe.pack(side=tk.TOP)
	
	# guarantee the popup is in front of the main window
	win.lift()
	# guarantee the popup has focus
	win.focus_set()
	
	# decide what the mode is & how to fill the popup
	if callable(args):
		# this is general-input mode, create text-entry box and submit button
		# for some reason the snow/white color still looks beige? :( oh well i tried
		textbox = tk.Entry(buttonframe, width=50, bg='snow')
		textbox.pack(side=tk.TOP, padx=10, pady=10)
		def submit_callback(event=None):
			# validate the text input using the validity check function "args"
			# if its good then invoke "setresult", if its bad then clear the text box
			# the func should be defined to print something explaining why it failed whenever it fails
			t = textbox.get().rstrip()
			if args(t): setresult(t)
			else: textbox.delete(0, tk.END)
		submit = tk.Button(buttonframe, text="Submit", command=submit_callback)
		submit.pack(side=tk.TOP, padx=10, pady=10)
		# "enter" key will be equivalent to clicking the submit button...
		# (technically this will happen whenever focus is inside the popup window, not just when focus is in the text entry box, but oh well)
		win.bind('<Return>', submit_callback)
		# guarantee the textbox within the popup has focus so user can start typing immediately (requires overall popup to already have focus)
		textbox.focus_set()
	else:
		# this is simplechoice (multichoice) mode, "args" is a list... create buttons for each option
		# create buttons for each numbered option
		for i in args:
			# each button will call "setresult" with its corresponding number, lambda needs to be written EXACTLY like this, i forget why it works
			c = lambda v=i: setresult(v)
			button = tk.Button(buttonframe, text=str(i), command=c)
			button.pack(side=tk.LEFT, padx=10, pady=10)
	
	return None
def main():
    print(
        "Open all PMX files at the selected level and replace usages of texure file XXXXX with YYYYY"
    )

    core.MY_PRINT_FUNC("Please enter name of PMX model file:")
    input_filename_pmx = core.MY_FILEPROMPT_FUNC(".pmx")

    # absolute path to directory holding the pmx
    input_filename_pmx_abs = os.path.normpath(
        os.path.abspath(input_filename_pmx))
    startpath, input_filename_pmx_rel = os.path.split(input_filename_pmx_abs)

    # =========================================================================================================
    # =========================================================================================================
    # =========================================================================================================
    # first, build the list of ALL files that actually exist, then filter it down to neighbor PMXs
    relative_all_exist_files = file_sort_textures.walk_filetree_from_root(
        startpath)
    # now fill "neighbor_pmx" by finding files without path separator that end in PMX
    # these are relative paths tho
    pmx_filenames = [
        f for f in relative_all_exist_files
        if (f.lower().endswith(".pmx")) and (os.path.sep not in f)
    ]

    # now read all the PMX objects & store in dict alongside the relative name
    # dictionary where keys are filename and values are resulting pmx objects
    all_pmx_obj = {}
    for this_pmx_name in pmx_filenames:
        this_pmx_obj = pmxlib.read_pmx(os.path.join(startpath, this_pmx_name),
                                       moreinfo=False)
        all_pmx_obj[this_pmx_name] = this_pmx_obj

    core.MY_PRINT_FUNC("ALL PMX FILES:")
    for pmxname in pmx_filenames:
        core.MY_PRINT_FUNC("    " + pmxname)

    core.MY_PRINT_FUNC("\n\n\n")
    core.MY_PRINT_FUNC(
        "WARNING: this script will overwrite all PMX files it operates on. This does NOT create a backup. Be very careful what you type!"
    )
    core.MY_PRINT_FUNC("\n\n\n")

    findme = core.MY_GENERAL_INPUT_FUNC(
        lambda x: True, "Please specify which filepath to find:")
    findme = os.path.normpath(findme.strip())  # sanitize it
    # if empty, quit
    if findme == "" or findme is None:
        core.MY_PRINT_FUNC("quitting")
        return None

    replacewith = core.MY_GENERAL_INPUT_FUNC(
        lambda x: True, "Please specify which filepath to replace it with:")
    replacewith = os.path.normpath(replacewith.strip())  # sanitize it

    # if empty, quit
    if replacewith == "" or replacewith is None:
        core.MY_PRINT_FUNC("quitting")
        return None

    core.MY_PRINT_FUNC("Replacing '%s' with '%s'" % (findme, replacewith))

    # now do find & replace!
    # for each pmx,
    for this_pmx_name, this_pmx_obj in all_pmx_obj.items():
        # do find-and-replace
        howmany = file_sort_textures.texname_find_and_replace(this_pmx_obj,
                                                              findme,
                                                              replacewith,
                                                              sanitize=True)
        # then report how many
        core.MY_PRINT_FUNC("")
        core.MY_PRINT_FUNC("'%s': replaced %d" % (this_pmx_name, howmany))

        if howmany != 0:
            # NOTE: this is OVERWRITING THE PREVIOUS PMX FILE, NOT CREATING A NEW ONE
            # because I make a zipfile backup I don't need to feel worried about preserving the old version
            output_filename_pmx = os.path.join(startpath, this_pmx_name)
            # output_filename_pmx = core.get_unused_file_name(output_filename_pmx)
            pmxlib.write_pmx(output_filename_pmx, this_pmx_obj, moreinfo=False)

    core.MY_PRINT_FUNC("Done!")
    return None
def main(moreinfo=True):
    # prompt PMX name
    core.MY_PRINT_FUNC("Please enter name of PMX input file:")
    input_filename_pmx = core.MY_FILEPROMPT_FUNC(".pmx")
    pmx = pmxlib.read_pmx(input_filename_pmx, moreinfo=moreinfo)

    # coordinates are stored as list[x, y, z], convert this --> tuple --> hash for much faster comparing
    vert_coord_hashes = [hash(tuple(v.pos)) for v in pmx.verts]

    all_vert_sets = []
    all_face_sets = []
    all_bone_indices = []
    all_rigidbody_indices = []

    start_vert = 0
    start_face = 0
    # i know i'm done when i have consumed all verts & all faces
    while start_face < len(pmx.faces) and start_vert < len(pmx.verts):
        # 1. start a new sets for the vertices and faces
        vert_set = set()
        face_set = set()
        # 2. pick a vertex that hasn't been used yet and add it to the set, ez
        # 2b. optimization: a fragment is guaranteed to have at least 4 faces (to make a closed 3d solid) and therefore at least 4 verts
        # can i safely assume that they are "sharp" corners and therefore there are 12 verts?
        for i in range(12):
            vert_set.add(start_vert + i)
        # also, init the faces set with the minimum of 4 faces, and add any verts included in those faces to the vert set
        for i in range(4):
            face_set.add(start_face + i)
            for v in pmx.faces[start_face + i]:  # for each vert in this face,
                vert_set.add(v)  # add this vert to the vert set
        # guarantee that it is contiguous from start_vert to the highest index that was in the faces
        vert_set = set(list(range(start_vert, max(vert_set) + 1)))
        # now i have initialized the set with everything i know is guarnateed part of the fragment

        highest_known_vert = max(vert_set)
        highest_known_face = max(face_set)

        # print(str(len(vert_set)) + " ", end="")

        # begin looping & flooding until i don't detect any more
        while True:
            # 3. note the number of verts collected so far
            set_size_A = len(vert_set)

            # 4. find all faces that include any vertex in the "fragment set",
            # whenever i find one, add all verts that it includes to the "fragment set" as well
            '''
			# zero-assumption brute-force method:
			for f_id in range(len(pmx.faces)):
				face = pmx.faces[f_id]
				if face[0] in vert_set or face[1] in vert_set or face[2] in vert_set: # we got a hit!
					face_set.add(f_id)
					vert_set.add(face[0])
					vert_set.add(face[1])
					vert_set.add(face[2])
			'''
            # optimization: scan only faces index 'highest_known_face+1' thru 'highest_known_face'+LOOKAHEAD
            #	because 0 thru start_face is guaranteed to not be part of the group
            #	and start_face thru highest_known_face is already guaranteed to be part of the group
            #	if chunks are bigger than LOOKAHEAD, then it's not guaranteed to succeed or fail, could do either
            for f_id in range(
                    highest_known_face + 1,
                    min(highest_known_face + LOOKAHEAD, len(pmx.faces))):
                face = pmx.faces[f_id]
                if face[0] in vert_set or face[1] in vert_set or face[
                        2] in vert_set:
                    # we got a hit!
                    face_set.add(f_id)
                    vert_set.add(face[0])
                    vert_set.add(face[1])
                    vert_set.add(face[2])
                    # optimization: if this is farther than what i thought was the end, then everything before it should be added too
                    if f_id > highest_known_face:
                        for x in range(highest_known_face + 1, f_id):
                            face_set.add(x)
                            vert_set.add(pmx.faces[x][0])
                            vert_set.add(pmx.faces[x][1])
                            vert_set.add(pmx.faces[x][2])
                        highest_known_face = f_id

            set_size_B = len(vert_set)

            # update the set of vertex coord hashes for easier comparing
            vert_set_hashes = set([vert_coord_hashes[i] for i in vert_set])
            # 5. find all vertices that have the same exact coordinates as any vertex in the "fragment set",
            # then and add them to the "fragment set"
            '''
			# zero-assumption brute-force method:
			for v_id in range(len(vert_coord_hashes)):
				vert_hash = vert_coord_hashes[v_id]
				if vert_hash in vert_set_hashes: # we got a hit!
					vert_set.add(v_id)
			'''
            # optimization: scan only verts index 'highest_known_vert+1' thru 'highest_known_vert'+LOOKAHEAD
            #	because 0 thru start_vert is guaranteed to not be part of the group
            #	and start_vert thru highest_known_vert is already guaranteed to be part of the group
            #	if chunks are bigger than LOOKAHEAD, then it's not guaranteed to succeed or fail, could do either
            for v_id in range(
                    highest_known_vert + 1,
                    min(highest_known_vert + LOOKAHEAD, len(pmx.verts))):
                vert_hash = vert_coord_hashes[v_id]
                if vert_hash in vert_set_hashes:
                    # we got a hit!
                    vert_set.add(v_id)
                    # optimization: if this is farther than what i thought was the end, then everything before it should be added too
                    if v_id > highest_known_vert:
                        for x in range(highest_known_vert + 1, v_id):
                            vert_set.add(x)
                        highest_known_vert = v_id

            set_size_C = len(vert_set)

            print("+%d +%d, " %
                  (set_size_B - set_size_A, set_size_C - set_size_B),
                  end="")

            # 6. if the number of verts did not change, we are done
            if set_size_C == set_size_A:
                break
            pass
        print("")
        # 7. now i have a complete fragment in vert_set and face_set !! :)
        all_vert_sets.append(vert_set)
        all_face_sets.append(face_set)
        # increment the face-start and vert-start indices, this is my stop condition
        start_vert += len(vert_set)
        start_face += len(face_set)
        # move on to the next fragment if i still have more verts to parse
        pass
    # done with identifying all fragments!

    # double-check that all vertices got sorted into one and only one fragment
    assert sum([len(vs) for vs in all_vert_sets]) == len(pmx.verts)
    temp = set()
    for vs in all_vert_sets:
        temp.update(vs)
    assert len(temp) == len(pmx.verts)

    # double-check that all faces got sorted into one and only one fragment
    assert sum([len(fs) for fs in all_face_sets]) == len(pmx.faces)
    temp = set()
    for fs in all_face_sets:
        temp.update(fs)
    assert len(temp) == len(pmx.faces)

    print("")
    print("Identified %d discrete fragments!" % (len(all_vert_sets), ))

    # BONES AND WEIGHTS
    for fragnum in range(len(all_vert_sets)):
        # name
        newbone_name = "fragment%d" % fragnum
        # position: average of all vertices in the fragment? sure why not
        # TODO is there a "better" way of calculating the average/centroid/center of mass? idk
        newbone_pos = [0, 0, 0]
        for v_id in all_vert_sets[fragnum]:
            # accumulate the XYZ for each vertex in the fragment
            newbone_pos[0] += pmx.verts[v_id].pos[0]
            newbone_pos[1] += pmx.verts[v_id].pos[1]
            newbone_pos[2] += pmx.verts[v_id].pos[2]
        # divide by the number of verts in the fragment to get the average
        newbone_pos[0] /= len(all_vert_sets[fragnum])
        newbone_pos[1] /= len(all_vert_sets[fragnum])
        newbone_pos[2] /= len(all_vert_sets[fragnum])
        # create the new bone object
        newbone_obj = pmxstruct.PmxBone(
            name_jp=newbone_name,
            name_en=newbone_name,
            pos=newbone_pos,
            parent_idx=0,
            deform_layer=0,
            deform_after_phys=False,
            has_rotate=True,
            has_translate=True,
            has_visible=True,
            has_enabled=True,
            has_ik=False,
            tail_usebonelink=False,
            tail=[0, 0, 0],
            inherit_rot=False,
            inherit_trans=False,
            has_fixedaxis=False,
            has_localaxis=False,
            has_externalparent=False,
        )
        # note the index it will be inserted at
        thisboneindex = len(pmx.bones)
        all_bone_indices.append(thisboneindex)
        # append it onto the list of bones
        pmx.bones.append(newbone_obj)
        # for each vertex in this fragment, give it 100% weight on that bone
        for v_id in all_vert_sets[fragnum]:
            v = pmx.verts[v_id]
            v.weighttype = pmxstruct.WeightMode.BDEF1  # BDEF1
            v.weight = [[thisboneindex, 1]]
        pass

    # RIGID BODIES
    for fragnum in range(len(all_vert_sets)):
        newbody_name = "body%d-0" % fragnum
        newbody_pos = pmx.bones[all_bone_indices[fragnum]].pos
        # hmmm, what do do here? this is the really hard part!
        # let's just make a sphere with radius equal to the distance to the nearest vertex of this fragment?
        # TODO: the bodies created from this are intersecting eachother when at rest!
        #  the distance to the closest vertex is greater than the distance to the closest point on the closest face!
        #  therefore there is a small bit of overlap
        newbody_radius = dist_to_nearest_vertex(newbody_pos,
                                                all_vert_sets[fragnum], pmx)

        # TODO: to "fill a fragment with several rigidbody spheres", you need to a) select a center for each, b) select a size for each
        #  the sizes can come from algorithm roughed out in dist_to_nearest_point_on_mesh_surface()
        #  the centers... idk? how can you do this?
        #  https://doc.babylonjs.com/toolsAndResources/utilities/InnerMeshPoints might be able to reuse some of the ideas from this?

        # phys params: set mass equal to the VOLUME of this new rigid body! oh that seems clever, i like that, bigger ones are heavier
        # if i figure out how to create multiple bodies, each body's mass should be proportional to its volume like this
        volume = 3.14 * (4 / 3) * (newbody_radius**3)
        mass = volume * MASS_FACTOR
        # phys params: use the default damping/friction/etc parameters cuz idk why not
        phys_move_damp = 0.95
        phys_rot_damp = 0.95
        phys_friction = 0.95
        phys_repel = 0.3  # bounciness?

        # this gif is with these params: https://gyazo.com/3d143f33b79c1151c1ccbffcc578448b

        # groups: for now, since each fragment is only one body, i can just ignore groups stuff
        # groups: later, if each fragment is several bodies... assign the groups in round-robin? each fragment will clip thru 1/15 of the
        # other fragments but i think that's unavoidable. also need to reserve group16 for the floor! so set each fragment's cluster of
        # bodies to nocollide with the group# assigned to that cluster, but respect all others.

        # bone_idx: if there are more than 1 rigidbodies associated with each fragment, one "main" body is connected to the bone
        # all the others are set to bone -1 and connected to the mainbody via joints
        newbody_obj = pmxstruct.PmxRigidBody(
            name_jp=newbody_name,
            name_en=newbody_name,
            bone_idx=all_bone_indices[fragnum],
            pos=newbody_pos,
            rot=[0, 0, 0],
            size=[newbody_radius, 0, 0],
            shape=pmxstruct.RigidBodyShape.SPHERE,
            group=1,
            nocollide_set=set(),
            phys_mode=pmxstruct.RigidBodyPhysMode.PHYSICS,
            phys_mass=mass,
            phys_move_damp=phys_move_damp,
            phys_rot_damp=phys_rot_damp,
            phys_repel=phys_repel,
            phys_friction=phys_friction)

        # note the index that this will be inserted at
        bodyindex = len(pmx.rigidbodies)
        all_rigidbody_indices.append(bodyindex)
        pmx.rigidbodies.append(newbody_obj)
        pass

    # JOINTS
    # if there is only one body per fragment then this is okay without any joints
    # if there are several bodies then we need to create joints from the "center" rigidbody to the others
    # even if you try to limit the joint to 0 rotation and 0 slide it still has some wiggle in it :( not perfectly rigid
    # TODO: i'll deal with this if and only if an algorithm for filling fragments with rigidbodies is created
    for fragnum in range(len(all_vert_sets)):
        pass

    core.MY_PRINT_FUNC("")

    # write out
    output_filename_pmx = input_filename_pmx[0:-4] + "_fragfix.pmx"
    output_filename_pmx = core.get_unused_file_name(output_filename_pmx)
    pmxlib.write_pmx(output_filename_pmx, pmx, moreinfo=moreinfo)
    core.MY_PRINT_FUNC("Done!")
    return None
        all_rigidbody_indices.append(bodyindex)
        pmx.rigidbodies.append(newbody_obj)
        pass

    # JOINTS
    # if there is only one body per fragment then this is okay without any joints
    # if there are several bodies then we need to create joints from the "center" rigidbody to the others
    # even if you try to limit the joint to 0 rotation and 0 slide it still has some wiggle in it :( not perfectly rigid
    # TODO: i'll deal with this if and only if an algorithm for filling fragments with rigidbodies is created
    for fragnum in range(len(all_vert_sets)):
        pass

    core.MY_PRINT_FUNC("")

    # write out
    output_filename_pmx = input_filename_pmx[0:-4] + "_fragfix.pmx"
    output_filename_pmx = core.get_unused_file_name(output_filename_pmx)
    pmxlib.write_pmx(output_filename_pmx, pmx, moreinfo=moreinfo)
    core.MY_PRINT_FUNC("Done!")
    return None


if __name__ == '__main__':
    print(_SCRIPT_VERSION)
    # print info to explain the purpose of this file
    core.MY_PRINT_FUNC(helptext)
    core.MY_PRINT_FUNC("")

    main()
    core.pause_and_quit("Done with everything! Goodbye!")
Exemplo n.º 13
0
def main(moreinfo=False):

    # 1. user input
    core.MY_PRINT_FUNC("Current dir = '%s'" % os.getcwd())
    core.MY_PRINT_FUNC(
        "Enter the path to the root folder that contains ALL models:")
    while True:
        name = input("root folder = ")
        if not os.path.isdir(name):
            core.MY_PRINT_FUNC(os.path.abspath(name))
            core.MY_PRINT_FUNC(
                "Err: given folder does not exist, did you type it wrong?")
        else:
            break
    # it exists, so make it absolute
    rootdir = os.path.abspath(os.path.normpath(name))

    core.MY_PRINT_FUNC("root folder = '%s'" % rootdir)

    core.MY_PRINT_FUNC("")

    core.MY_PRINT_FUNC("... beginning to index file tree...")
    # 2. build list of ALL file on the system within this folder
    relative_all_exist_files = file_sort_textures.walk_filetree_from_root(
        rootdir)
    core.MY_PRINT_FUNC("... total # of files:", len(relative_all_exist_files))
    relative_all_pmx = [
        f for f in relative_all_exist_files if f.lower().endswith(".pmx")
    ]
    core.MY_PRINT_FUNC("... total # of PMX models:", len(relative_all_pmx))
    relative_exist_img_files = [
        f for f in relative_all_exist_files if f.lower().endswith(IMG_EXT)
    ]
    core.MY_PRINT_FUNC("... total # of image sources:",
                       len(relative_exist_img_files))

    core.MY_PRINT_FUNC("")

    # this will accumulate the list of PMXes
    list_of_pmx_with_missing_tex = []

    list_of_pmx_that_somehow_failed = []

    # 3. for each pmx,
    for d, pmx_name in enumerate(relative_all_pmx):
        # progress print
        core.MY_PRINT_FUNC("\n%d / %d" % (d + 1, len(relative_all_pmx)))
        # wrap the actual work with a try-catch just in case
        # this is a gigantic time investment and I dont want it to fail halfway thru and lose everything
        try:
            # 4. read the pmx, gotta store it in the dict like this cuz shut up thats why
            # dictionary where keys are filename and values are resulting pmx objects
            all_pmx_obj = {}
            this_pmx_obj = pmxlib.read_pmx(os.path.join(rootdir, pmx_name),
                                           moreinfo=False)
            all_pmx_obj[pmx_name] = this_pmx_obj

            # 5. filter images down to only images underneath the same folder as the pmx
            pmx_folder = os.path.dirname(pmx_name).lower()
            possible_img_sources = [
                f for f in relative_exist_img_files
                if f.lower().startswith(pmx_folder)
            ]
            # trim the leading "pmx_folder" portion from these names
            possible_img_sources = [
                os.path.relpath(f, pmx_folder) for f in possible_img_sources
            ]

            # 6. make filerecord_list
            # for each pmx, for each file on disk, match against files used in textures (case-insensitive) and replace with canonical name-on-disk
            # also fill out how much and how each file is used, and unify dupes between files, all that good stuff
            filerecord_list = file_sort_textures.build_filerecord_list(
                all_pmx_obj, possible_img_sources, False)

            # 7. if within filerecordlist, any filerecord is used but does not exist,
            if any(((fr.numused != 0) and (not fr.exists))
                   for fr in filerecord_list):
                # then save this pmx name
                list_of_pmx_with_missing_tex.append(pmx_name)
        except Exception as e:
            core.MY_PRINT_FUNC(e.__class__.__name__, e)
            core.MY_PRINT_FUNC(
                "ERROR! some kind of exception interrupted reading pmx '%s'" %
                pmx_name)
            list_of_pmx_that_somehow_failed.append(pmx_name)

    core.MY_PRINT_FUNC("\n\n")

    # make the paths absolute
    list_of_pmx_that_somehow_failed = [
        os.path.join(rootdir, p) for p in list_of_pmx_that_somehow_failed
    ]
    list_of_pmx_with_missing_tex = [
        os.path.join(rootdir, p) for p in list_of_pmx_with_missing_tex
    ]

    # print & write-to-file
    if list_of_pmx_that_somehow_failed:
        core.MY_PRINT_FUNC("WARNING: failed in some way on %d PMX files" %
                           len(list_of_pmx_that_somehow_failed))
        core.MY_PRINT_FUNC("Writing the full list to text file:")
        output_filename_failures = core.get_unused_file_name(FAILED_LIST_FILE)
        core.write_list_to_txtfile(output_filename_failures,
                                   list_of_pmx_that_somehow_failed)
    core.MY_PRINT_FUNC(
        "Found %d / %d PMX files that are missing at least one texture source"
        % (len(list_of_pmx_with_missing_tex), len(relative_all_pmx)))
    core.MY_PRINT_FUNC("Writing the full list to text file:")
    output_filename_missingtex = core.get_unused_file_name(
        MISSINGTEX_LIST_FILE)
    core.write_list_to_txtfile(output_filename_missingtex,
                               list_of_pmx_with_missing_tex)

    # print(list_of_pmx_with_missing_tex)

    core.MY_PRINT_FUNC("Done!")
    return None