Example #1
0
def load_fgm(ovl_data, fgm_file_path, fgm_sized_str_entry):

    fgm_data = FgmFile()
    fgm_data.load(fgm_file_path)

    sizedstr_bytes = as_bytes(fgm_data.fgm_info) + as_bytes(
        fgm_data.two_frags_pad)

    # todo - move texpad into fragment padding?
    textures_bytes = as_bytes(fgm_data.textures) + as_bytes(fgm_data.texpad)
    attributes_bytes = as_bytes(fgm_data.attributes)

    # the actual injection
    fgm_sized_str_entry.data_entry.update_data((fgm_data.buffer_bytes, ))
    fgm_sized_str_entry.pointers[0].update_data(sizedstr_bytes,
                                                update_copies=True)

    if len(fgm_sized_str_entry.fragments) == 4:
        datas = (textures_bytes, attributes_bytes, fgm_data.zeros_bytes,
                 fgm_data.data_bytes)
    # fgms without zeros
    elif len(fgm_sized_str_entry.fragments) == 3:
        datas = (textures_bytes, attributes_bytes, fgm_data.data_bytes)
    # fgms for variants
    elif len(fgm_sized_str_entry.fragments) == 2:
        datas = (attributes_bytes, fgm_data.data_bytes)
    else:
        raise AttributeError("Unexpected fgm frag count")

    # inject fragment datas
    for frag, data in zip(fgm_sized_str_entry.fragments, datas):
        frag.pointers[1].update_data(data, update_copies=True)
	def __init__(self):
		widgets.MainWindow.__init__(self, "FGM Editor", )
		
		self.resize(450, 500)

		self.fgm_data = FgmFile()
		self.widgets = []
		self.tooltips = config.read_config("util/tooltips/fgm.txt")
		self.shaders = {}
		for game in games:
			self.shaders[game] = config.read_list(f"util/tooltips/fgm-shaders-{game.lower().replace(' ', '-')}.txt")

		self.cleaner = QtCore.QObjectCleanupHandler()

		self.scrollarea = QtWidgets.QScrollArea(self)
		self.scrollarea.setWidgetResizable(True)
		self.setCentralWidget(self.scrollarea)

		# the actual scrollable stuff
		self.widget = QtWidgets.QWidget()
		self.scrollarea.setWidget(self.widget)

		self.game_container = widgets.LabelCombo("Game:", games)
		self.game_container.entry.currentIndexChanged.connect(self.game_changed)
		self.game_container.entry.setEditable(False)
		self.file_widget = widgets.FileWidget(self, self.cfg, dtype="FGM")
		self.shader_container = widgets.LabelCombo("Shader:", ())
		self.shader_container.entry.activated.connect(self.shader_changed)
		self.tex_container = QtWidgets.QGroupBox("Textures")
		self.attrib_container = QtWidgets.QGroupBox("Attributes")

		vbox = QtWidgets.QVBoxLayout()
		vbox.addWidget(self.file_widget)
		vbox.addWidget(self.game_container)
		vbox.addWidget(self.shader_container)
		vbox.addWidget(self.tex_container)
		vbox.addWidget(self.attrib_container)
		vbox.addStretch(1)
		self.widget.setLayout(vbox)

		self.tex_grid = self.create_grid()
		self.attrib_grid = self.create_grid()

		self.tex_container.setLayout(self.tex_grid)
		self.attrib_container.setLayout(self.attrib_grid)

		mainMenu = self.menuBar()
		fileMenu = mainMenu.addMenu('File')
		helpMenu = mainMenu.addMenu('Help')
		button_data = ( (fileMenu, "Open", self.file_widget.ask_open, "CTRL+O", ""), \
						(fileMenu, "Save", self.save_fgm, "CTRL+S", ""), \
						(fileMenu, "Exit", self.close, "", ""), \
						(helpMenu, "Report Bug", self.report_bug, "", ""), \
						(helpMenu, "Documentation", self.online_support, "", ""), \
						)
		self.add_to_menu(button_data)
Example #3
0
def load_fgm(ovl, fgm_file_path, fgm_sized_str_entry):

    versions = get_versions(ovl)
    fgm_data = FgmFile()
    fgm_data.load(fgm_file_path)

    sizedstr_bytes = as_bytes(
        fgm_data.fgm_info, version_info=versions) + as_bytes(
            fgm_data.two_frags_pad, version_info=versions)

    # todo - move texpad into fragment padding?
    textures_bytes = as_bytes(fgm_data.textures,
                              version_info=versions) + as_bytes(
                                  fgm_data.texpad, version_info=versions)
    attributes_bytes = as_bytes(fgm_data.attributes, version_info=versions)

    # the actual injection
    fgm_sized_str_entry.data_entry.update_data((fgm_data.buffer_bytes, ))
    fgm_sized_str_entry.pointers[0].update_data(sizedstr_bytes,
                                                update_copies=True)

    if len(fgm_sized_str_entry.fragments) == 4:
        datas = (textures_bytes, attributes_bytes, fgm_data.zeros_bytes,
                 fgm_data.data_bytes)
    # fgms without zeros
    elif len(fgm_sized_str_entry.fragments) == 3:
        datas = (textures_bytes, attributes_bytes, fgm_data.data_bytes)
    # fgms for variants
    elif len(fgm_sized_str_entry.fragments) == 2:
        datas = (attributes_bytes, fgm_data.data_bytes)
    else:
        raise AttributeError("Unexpected fgm frag count")

    # inject fragment datas
    for frag, data in zip(fgm_sized_str_entry.fragments, datas):
        frag.pointers[1].update_data(data, update_copies=True)

    # update dependencies on ovl
    fgm_file_entry = get_file_entry(ovl, fgm_sized_str_entry)
    for dep_entry, tex_name in zip(fgm_file_entry.dependencies,
                                   fgm_data.texture_names):
        dep_entry.basename = tex_name
        dep_entry.name = dep_entry.basename + dep_entry.ext.replace(":", ".")
        dep_entry.file_hash = djb(tex_name.lower())
Example #4
0
def load_matcol(matcol_path):
    lib_dir = os.path.normpath(os.path.dirname(matcol_path))
    matcol_file = MatcolFile()
    matcol_file.load(matcol_path)
    slots = []
    rootname = "anky_ankylo_backplates"
    basecol = ".pbasecolourtexture"
    baseheight = ".pheighttexture"
    all_textures = [
        file for file in os.listdir(lib_dir) if file.lower().endswith(".png")
    ]
    base_textures = [
        os.path.join(lib_dir, file) for file in all_textures
        if rootname in file and basecol in file
    ]
    height_textures = [
        os.path.join(lib_dir, file) for file in all_textures
        if rootname in file and baseheight in file
    ]
    # print(base_textures)
    # for layer in matcol_file.layered_wrapper:
    # print(layer)
    for layer in matcol_file.layered_wrapper.layers:
        print(layer.name)
        if layer.name == "Default":
            print("Skipping Default layer")
            htex = None
        else:
            fgm_path = os.path.join(lib_dir, layer.name + ".fgm")
            # print(fgm_path)
            fgm_data = FgmFile()
            fgm_data.load(fgm_path)
            if fgm_data.textures[0].is_textured == 8:
                base_index = fgm_data.textures[0].indices[1]
                height_index = fgm_data.textures[1].indices[1]
            else:
                print("tell Developers not using indices")
            print("base_array_index", base_index)
            print("height_array_index", height_index)
            print("base", base_textures[base_index])
            print("height", height_textures[height_index])
            htex = height_textures[height_index]
        slots.append((layer.infos, htex))
    return slots
Example #5
0
def get_fgm_values(gui, start_dir, walk_ovls=True, walk_fgms=True):
    errors = []
    if start_dir:
        export_dir = os.path.join(start_dir, "walker_export")
        if walk_ovls:
            bulk_extract_ovls(errors, export_dir, gui, start_dir, (".fgm", ))

        attributes = {}
        textures = set()
        shaders = set()
        if walk_fgms:
            fgm_data = FgmFile()
            fgm_files = walk_type(export_dir, extension=".fgm")
            mf_max = len(fgm_files)
            for mf_index, fgm_path in enumerate(fgm_files):
                fgm_name = os.path.basename(fgm_path)
                gui.update_progress("Walking FGM files: " + fgm_name,
                                    value=mf_index,
                                    vmax=mf_max)
                try:
                    fgm_data.load(fgm_path)
                    shaders.add(fgm_data.shader_name)
                    for attrib in fgm_data.attributes:
                        attributes[attrib.name] = attrib.dtype
                    for texture in fgm_data.textures:
                        textures.add(texture.name)
                except Exception as ex:
                    traceback.print_exc()
                    errors.append((fgm_path, ex))
        # report
        print("\nThe following errors occured:")
        for file_path, ex in errors:
            print(file_path, str(ex))

        out_path = os.path.join(export_dir,
                                f"fgm_{os.path.basename(start_dir)}.py")
        with open(out_path, "w") as f:
            f.write(f"attributes = {attributes}\n\n")
            f.write(f"textures = {textures}\n\n")
            f.write(f"shaders = {shaders}\n\n")
        print(f"Written to {out_path}")
        gui.update_progress("Operation completed!", value=1, vmax=1)
Example #6
0
def create_material(in_dir, matname):

    print(f"Importing material {matname}")
    # only create the material if it doesn't exist in the blend file, then just grab it
    # but we overwrite its contents anyway
    if matname not in bpy.data.materials:
        b_mat = bpy.data.materials.new(matname)
    else:
        b_mat = bpy.data.materials[matname]

    fgm_path = os.path.join(in_dir, matname + ".fgm")
    # print(fgm_path)
    try:
        fgm_data = FgmFile()
        fgm_data.load(fgm_path)
    except FileNotFoundError:
        print(f"{fgm_path} does not exist!")
        return b_mat
    # base_index = fgm_data.textures[0].layers[1]
    # height_index = fgm_data.textures[1].layers[1]
    tree = get_tree(b_mat)
    output = tree.nodes.new('ShaderNodeOutputMaterial')
    principled = tree.nodes.new('ShaderNodeBsdfPrincipled')

    all_textures = [
        file for file in os.listdir(in_dir) if file.lower().endswith(".png")
    ]
    # map texture names to node
    tex_dic = {}
    for fgm_texture in fgm_data.textures:
        png_base = fgm_texture.name.lower()
        if "blendweights" in png_base or "warpoffset" in png_base:
            continue
        textures = [
            file for file in all_textures if file.lower().startswith(png_base)
        ]
        if not textures:
            png_base = png_base.lower().replace("_eyes", "").replace(
                "_fin", "").replace("_shell", "")
            textures = [
                file for file in all_textures
                if file.lower().startswith(png_base)
            ]
        if not textures:
            textures = [
                png_base + ".png",
            ]
        # print(textures)
        for png_name in textures:
            png_path = os.path.join(in_dir, png_name)
            b_tex = load_tex(tree, png_path)
            k = png_name.lower().split(".")[1]
            tex_dic[k] = b_tex

    # get diffuse and AO
    for diffuse_name in ("pdiffusetexture", "pbasediffusetexture",
                         "pbasecolourtexture", "pbasecolourandmasktexture",
                         "pdiffusealphatexture", "pdiffuse_alphatexture",
                         "palbinobasecolourandmasktexture"):
        # get diffuse
        if diffuse_name in tex_dic:
            diffuse = tex_dic[diffuse_name]
            # get AO
            for ao_name in ("paotexture", "pbasepackedtexture_03"):
                if ao_name in tex_dic:
                    ao = tex_dic[ao_name]
                    ao.image.colorspace_settings.name = "Non-Color"

                    # apply AO to diffuse
                    diffuse_premix = tree.nodes.new('ShaderNodeMixRGB')
                    diffuse_premix.blend_type = "MULTIPLY"
                    diffuse_premix.inputs["Fac"].default_value = .25
                    tree.links.new(diffuse.outputs[0],
                                   diffuse_premix.inputs["Color1"])
                    tree.links.new(ao.outputs[0],
                                   diffuse_premix.inputs["Color2"])
                    diffuse = diffuse_premix
                    break
            # get marking
            fur_names = [
                k for k in tex_dic.keys()
                if "marking" in k and "noise" not in k and "patchwork" not in k
            ]
            lut_names = [k for k in tex_dic.keys() if "pclut" in k]
            if fur_names and lut_names:
                marking = tex_dic[sorted(fur_names)[0]]
                lut = tex_dic[sorted(lut_names)[0]]
                marking.image.colorspace_settings.name = "Non-Color"

                # PZ LUTs usually occupy half of the texture, so scale the incoming greyscale coordinates so that
                # 1 lands in the center of the LUT
                scaler = tree.nodes.new('ShaderNodeMath')
                scaler.operation = "MULTIPLY"
                tree.links.new(marking.outputs[0], scaler.inputs[0])
                scaler.inputs[1].default_value = 0.5
                tree.links.new(scaler.outputs[0], lut.inputs[0])

                # apply AO to diffuse
                diffuse_premix = tree.nodes.new('ShaderNodeMixRGB')
                diffuse_premix.blend_type = "MIX"
                tree.links.new(diffuse.outputs[0],
                               diffuse_premix.inputs["Color1"])
                tree.links.new(lut.outputs[0], diffuse_premix.inputs["Color2"])
                tree.links.new(marking.outputs[0],
                               diffuse_premix.inputs["Fac"])
                diffuse = diffuse_premix
            #  link finished diffuse to shader
            tree.links.new(diffuse.outputs[0], principled.inputs["Base Color"])
            break

    if "pnormaltexture" in tex_dic:
        normal = tex_dic["pnormaltexture"]
        normal.image.colorspace_settings.name = "Non-Color"
        normal_map = tree.nodes.new('ShaderNodeNormalMap')
        tree.links.new(normal.outputs[0], normal_map.inputs[1])
        # normal_map.inputs["Strength"].default_value = 1.0
        tree.links.new(normal_map.outputs[0], principled.inputs["Normal"])

    # PZ - specularity?
    for spec_name in (
            "proughnesspackedtexture_02",
            "pspecularmaptexture_00",
    ):
        if spec_name in tex_dic:
            specular = tex_dic[spec_name]
            specular.image.colorspace_settings.name = "Non-Color"
            tree.links.new(specular.outputs[0], principled.inputs["Specular"])

    # PZ - roughness?
    for roughness_name in (
            "proughnesspackedtexture_01", ):  # "pspecularmaptexture_01" ?
        if roughness_name in tex_dic:
            roughness = tex_dic[roughness_name]
            roughness.image.colorspace_settings.name = "Non-Color"
            tree.links.new(roughness.outputs[0],
                           principled.inputs["Roughness"])

    # JWE dinos - metalness
    for metal_name in ("pbasepackedtexture_02", ):
        if metal_name in tex_dic:
            metal = tex_dic[metal_name]
            metal.image.colorspace_settings.name = "Non-Color"
            tree.links.new(metal.outputs[0], principled.inputs["Metallic"])

    # alpha
    alpha = None
    # JWE billboard: Foliage_Billboard
    if "pdiffusealphatexture" in tex_dic:
        alpha = tex_dic["pdiffusealphatexture"]
        alpha_pass = alpha.outputs[1]
    elif "pdiffuse_alphatexture" in tex_dic:
        alpha = tex_dic["pdiffuse_alphatexture"]
        alpha_pass = alpha.outputs[1]
    # PZ penguin
    elif "popacitytexture" in tex_dic:
        alpha = tex_dic["popacitytexture"]
        alpha_pass = alpha.outputs[0]
    elif "proughnesspackedtexture_00" in tex_dic and "Foliage_Clip" in fgm_data.shader_name:
        alpha = tex_dic["proughnesspackedtexture_00"]
        alpha_pass = alpha.outputs[0]
    # parrot: Metallic_Roughness_Clip -> 03
    elif "proughnesspackedtexture_03" in tex_dic and "Foliage_Clip" not in fgm_data.shader_name:
        alpha = tex_dic["proughnesspackedtexture_03"]
        alpha_pass = alpha.outputs[0]
    if alpha:
        # transparency
        b_mat.blend_method = "CLIP"
        b_mat.shadow_method = "CLIP"
        for attrib in fgm_data.attributes:
            if attrib.name.lower() == "palphatestref":
                b_mat.alpha_threshold = attrib.value[0]
                break
        transp = tree.nodes.new('ShaderNodeBsdfTransparent')
        alpha_mixer = tree.nodes.new('ShaderNodeMixShader')
        tree.links.new(alpha_pass, alpha_mixer.inputs[0])

        tree.links.new(transp.outputs[0], alpha_mixer.inputs[1])
        tree.links.new(principled.outputs[0], alpha_mixer.inputs[2])
        tree.links.new(alpha_mixer.outputs[0], output.inputs[0])
        alpha_mixer.update()
    # no alpha
    else:
        b_mat.blend_method = "OPAQUE"
        tree.links.new(principled.outputs[0], output.inputs[0])

    nodes_iterate(tree, output)
    return b_mat
Example #7
0
	def _get_data(file_path):
		fgm_data = FgmFile()
		fgm_data.load(file_path)
		return fgm_data
Example #8
0
class MainWindow(widgets.MainWindow):
    def __init__(self):
        widgets.MainWindow.__init__(
            self,
            "FGM Editor",
        )

        self.resize(800, 600)

        self.fgm_data = FgmFile()
        self.widgets = []
        self.tooltips = config.read_config("ovl_util/tooltips/fgm.txt")
        self.shaders = {}
        for game in games:
            self.shaders[game] = config.read_list(
                f"ovl_util/tooltips/fgm-shaders-{game.lower().replace(' ', '-')}.txt"
            )

        self.cleaner = QtCore.QObjectCleanupHandler()

        self.scrollarea = QtWidgets.QScrollArea(self)
        self.scrollarea.setWidgetResizable(True)
        self.setCentralWidget(self.scrollarea)

        # the actual scrollable stuff
        self.widget = QtWidgets.QWidget()
        self.scrollarea.setWidget(self.widget)

        self.game_container = widgets.LabelCombo("Game:", games)
        self.game_container.entry.currentIndexChanged.connect(
            self.game_changed)
        self.game_container.entry.setEditable(False)
        self.file_widget = widgets.FileWidget(self, self.cfg, dtype="FGM")
        self.shader_container = widgets.LabelCombo("Shader:", ())
        self.shader_container.entry.activated.connect(self.shader_changed)
        self.tex_container = QtWidgets.QGroupBox("Textures")
        self.attrib_container = QtWidgets.QGroupBox("Attributes")

        vbox = QtWidgets.QVBoxLayout()
        vbox.addWidget(self.file_widget)
        vbox.addWidget(self.game_container)
        vbox.addWidget(self.shader_container)
        vbox.addWidget(self.tex_container)
        vbox.addWidget(self.attrib_container)
        vbox.addStretch(1)
        self.widget.setLayout(vbox)

        self.tex_grid = self.create_grid()
        self.attrib_grid = self.create_grid()

        self.tex_container.setLayout(self.tex_grid)
        self.attrib_container.setLayout(self.attrib_grid)

        mainMenu = self.menuBar()
        fileMenu = mainMenu.addMenu('File')
        helpMenu = mainMenu.addMenu('Help')
        button_data = ( (fileMenu, "Open", self.file_widget.ask_open, "CTRL+O", ""), \
            (fileMenu, "Save", self.save_fgm, "CTRL+S", ""), \
            (fileMenu, "Exit", self.close, "", ""), \
            (helpMenu, "Report Bug", self.report_bug, "", ""), \
            (helpMenu, "Documentation", self.online_support, "", ""), \
            )
        self.add_to_menu(button_data)

    def game_changed(self, ):
        if self.file_widget.filepath:
            self.shader_container.entry.clear()
            game = self.game_container.entry.currentText()
            self.shader_container.entry.addItems(self.shaders[game])

    def shader_changed(self, ):
        """Change the fgm data shader name if gui changes"""
        if self.file_widget.filepath:
            self.fgm_data.shader_name = self.shader_container.entry.currentText(
            )

    @property
    def fgm_name(self, ):
        return self.file_widget.entry.text()

    def create_grid(self, ):
        g = QtWidgets.QGridLayout()
        g.setHorizontalSpacing(3)
        g.setVerticalSpacing(0)
        return g

    def clear_layout(self, layout):
        w = QtWidgets.QWidget()
        w.setLayout(layout)
        # while layout.count():
        # 	item = layout.takeAt(0)
        # 	widget = item.widget()
        # 	# if widget has some id attributes you need to
        # 	# save in a list to maintain order, you can do that here
        # 	# i.e.:   aList.append(widget.someId)
        # 	widget.deleteLater()

    def load(self):
        if self.file_widget.filepath:
            for w in self.widgets:
                w.deleteLater()
            try:
                self.fgm_data.load(self.file_widget.filepath)
                print(self.fgm_data)
                game = get_game(self.fgm_data)
                print("from game", game)
                self.game_container.entry.setText(game)
                # also for
                self.game_changed()
                self.shader_container.entry.setText(self.fgm_data.shader_name)

                # delete existing widgets
                self.clear_layout(self.tex_grid)
                self.clear_layout(self.attrib_grid)

                self.tex_grid = self.create_grid()
                self.tex_grid.setColumnStretch(1, 3)
                self.tex_grid.setColumnStretch(2, 1)
                self.attrib_grid = self.create_grid()

                self.tex_container.setLayout(self.tex_grid)
                self.attrib_container.setLayout(self.attrib_grid)
                for line_i, tex in enumerate(self.fgm_data.textures):
                    w = widgets.VectorEntry(tex, self.tooltips)
                    self.tex_grid.addWidget(w.delete, line_i, 0)
                    self.tex_grid.addWidget(w.entry, line_i, 1)
                    self.tex_grid.addWidget(w.data, line_i, 2)

                for line_i, attrib in enumerate(self.fgm_data.attributes):
                    w = widgets.VectorEntry(attrib, self.tooltips)
                    self.attrib_grid.addWidget(w.delete, line_i, 0)
                    self.attrib_grid.addWidget(w.entry, line_i, 1)
                    self.attrib_grid.addWidget(w.data, line_i, 2)

            except Exception as ex:
                traceback.print_exc()
                ovl_util.interaction.showdialog(str(ex))
                print(ex)
            print("Done!")

    def save_fgm(self):
        if self.file_widget.filepath:
            file_out = QtWidgets.QFileDialog.getSaveFileName(
                self,
                'Save FGM',
                os.path.join(self.cfg.get("dir_fgms_out", "C://"),
                             self.fgm_name),
                "FGM files (*.fgm)",
            )[0]
            if file_out:
                self.cfg["dir_fgms_out"], fgm_name = os.path.split(file_out)
                self.fgm_data.save(file_out)
                print("Done!")
Example #9
0
def create_material(in_dir, matname):

    logging.info(f"Importing material {matname}")
    b_mat = bpy.data.materials.new(matname)

    fgm_path = os.path.join(in_dir, matname + ".fgm")
    # print(fgm_path)
    try:
        fgm_data = FgmFile()
        fgm_data.load(fgm_path)
    except FileNotFoundError:
        logging.warning(f"{fgm_path} does not exist!")
        return b_mat
    # base_index = fgm_data.textures[0].layers[1]
    # height_index = fgm_data.textures[1].layers[1]
    tree = get_tree(b_mat)
    output = tree.nodes.new('ShaderNodeOutputMaterial')
    principled = tree.nodes.new('ShaderNodeBsdfPrincipled')

    color_ramp = fgm_data.get_color_ramp("colourKey", "RGB")
    opacity_ramp = fgm_data.get_color_ramp("opacityKey", "Value")
    if color_ramp and opacity_ramp:
        # print(color_ramp, list(zip(color_ramp)))
        positions, colors = zip(*color_ramp)
        positions_2, opacities = zip(*color_ramp)
        ramp = tree.nodes.new('ShaderNodeValToRGB')
        for position, color, opacity in zip(positions, colors, opacities):
            print(position, color, opacity)
            pos_relative = (position - min(positions)) / (max(positions) -
                                                          min(positions))
            e = ramp.color_ramp.elements.new(pos_relative)
            e.color[:3] = color
            e.alpha = opacity[0]

    all_textures = [
        file for file in os.listdir(in_dir) if file.lower().endswith(".png")
    ]
    # map texture names to node
    tex_dic = {}
    for fgm_texture in fgm_data.textures:
        if not fgm_texture.file:
            continue
        png_base = fgm_texture.file.lower()
        if "blendweights" in png_base or "warpoffset" in png_base:
            continue
        textures = [
            file for file in all_textures if file.lower().startswith(png_base)
        ]
        if not textures:
            png_base = png_base.lower().replace("_eyes", "").replace(
                "_fin", "").replace("_shell", "")
            textures = [
                file for file in all_textures
                if file.lower().startswith(png_base)
            ]
        if not textures:
            textures = [
                png_base + ".png",
            ]
        # print(textures)
        for png_name in textures:
            png_path = os.path.join(in_dir, png_name)
            b_tex = load_tex(tree, png_path)
            k = png_name.lower().split(".")[1]
            tex_dic[k] = b_tex

    # get diffuse and AO
    for diffuse_name in ("pdiffusetexture", "pbasediffusetexture",
                         "pbasecolourtexture", "pbasecolourandmasktexture",
                         "pdiffusealphatexture", "pdiffuse_alphatexture",
                         "palbinobasecolourandmasktexture"):
        # get diffuse
        if diffuse_name in tex_dic:
            diffuse = tex_dic[diffuse_name]
            # get AO
            for ao_name in ("paotexture", "pbasepackedtexture_[03]",
                            "pbaseaotexture"):
                if ao_name in tex_dic:
                    ao = tex_dic[ao_name]
                    ao.image.colorspace_settings.name = "Non-Color"

                    # apply AO to diffuse
                    diffuse_premix = tree.nodes.new('ShaderNodeMixRGB')
                    diffuse_premix.blend_type = "MULTIPLY"
                    diffuse_premix.inputs["Fac"].default_value = .25
                    tree.links.new(diffuse.outputs[0],
                                   diffuse_premix.inputs["Color1"])
                    tree.links.new(ao.outputs[0],
                                   diffuse_premix.inputs["Color2"])
                    diffuse = diffuse_premix
                    break
            # get marking
            fur_names = [
                k for k in tex_dic.keys()
                if "marking" in k and "noise" not in k and "patchwork" not in k
            ]
            lut_names = [k for k in tex_dic.keys() if "pclut" in k]
            if fur_names and lut_names:
                marking = tex_dic[sorted(fur_names)[0]]
                lut = tex_dic[sorted(lut_names)[0]]
                marking.image.colorspace_settings.name = "Non-Color"

                # PZ LUTs usually occupy half of the texture, so scale the incoming greyscale coordinates so that
                # 1 lands in the center of the LUT
                scaler = tree.nodes.new('ShaderNodeMath')
                scaler.operation = "MULTIPLY"
                tree.links.new(marking.outputs[0], scaler.inputs[0])
                scaler.inputs[1].default_value = 0.5
                tree.links.new(scaler.outputs[0], lut.inputs[0])

                # apply AO to diffuse
                diffuse_premix = tree.nodes.new('ShaderNodeMixRGB')
                diffuse_premix.blend_type = "MIX"
                tree.links.new(diffuse.outputs[0],
                               diffuse_premix.inputs["Color1"])
                tree.links.new(lut.outputs[0], diffuse_premix.inputs["Color2"])
                tree.links.new(marking.outputs[0],
                               diffuse_premix.inputs["Fac"])
                diffuse = diffuse_premix
            #  link finished diffuse to shader
            tree.links.new(diffuse.outputs[0], principled.inputs["Base Color"])
            break

    for normal_name in ("pnormaltexture", "pbasenormaltexture_[0]"):
        # get diffuse
        if normal_name in tex_dic:
            normal = tex_dic[normal_name]
            normal.image.colorspace_settings.name = "Non-Color"
            normal_map = tree.nodes.new('ShaderNodeNormalMap')
            tree.links.new(normal.outputs[0], normal_map.inputs[1])
            # normal_map.inputs["Strength"].default_value = 1.0
            tree.links.new(normal_map.outputs[0], principled.inputs["Normal"])

    # PZ - specularity?
    for spec_name in (
            "proughnesspackedtexture_[02]",
            "pspecularmaptexture_[00]",
    ):
        if spec_name in tex_dic:
            specular = tex_dic[spec_name]
            specular.image.colorspace_settings.name = "Non-Color"
            tree.links.new(specular.outputs[0], principled.inputs["Specular"])

    # PZ - roughness?
    for roughness_name in (
            "proughnesspackedtexture_[01]",
            "pbasenormaltexture_[2]"):  # "pspecularmaptexture_[01]" ?
        if roughness_name in tex_dic:
            roughness = tex_dic[roughness_name]
            roughness.image.colorspace_settings.name = "Non-Color"
            tree.links.new(roughness.outputs[0],
                           principled.inputs["Roughness"])

    # JWE dinos - metalness
    for metal_name in ("pbasepackedtexture_[02]", ):
        if metal_name in tex_dic:
            metal = tex_dic[metal_name]
            metal.image.colorspace_settings.name = "Non-Color"
            tree.links.new(metal.outputs[0], principled.inputs["Metallic"])

    # alpha
    alpha = None
    # JWE billboard: Foliage_Billboard
    if "pdiffusealphatexture" in tex_dic:
        alpha = tex_dic["pdiffusealphatexture"]
        alpha_pass = alpha.outputs[1]
    elif "pdiffuse_alphatexture" in tex_dic:
        alpha = tex_dic["pdiffuse_alphatexture"]
        alpha_pass = alpha.outputs[1]
    # PZ penguin
    elif "popacitytexture" in tex_dic:
        alpha = tex_dic["popacitytexture"]
        alpha_pass = alpha.outputs[0]
    elif is_jwe(
            fgm_data
    ) and "proughnesspackedtexture_[00]" in tex_dic and "Foliage_Clip" in fgm_data.shader_name:
        alpha = tex_dic["proughnesspackedtexture_[00]"]
        alpha_pass = alpha.outputs[0]
    elif "proughnesspackedtexture_[03]" in tex_dic:
        alpha = tex_dic["proughnesspackedtexture_[03]"]
        alpha_pass = alpha.outputs[0]
    if alpha:
        # transparency
        b_mat.blend_method = "CLIP"
        b_mat.shadow_method = "CLIP"
        attr_dict = fgm_data.get_attr_dict()
        if "palphatestref" in attr_dict:
            b_mat.alpha_threshold = attr_dict["palphatestref"].value[0]
        transp = tree.nodes.new('ShaderNodeBsdfTransparent')
        alpha_mixer = tree.nodes.new('ShaderNodeMixShader')
        tree.links.new(alpha_pass, alpha_mixer.inputs[0])

        tree.links.new(transp.outputs[0], alpha_mixer.inputs[1])
        tree.links.new(principled.outputs[0], alpha_mixer.inputs[2])
        tree.links.new(alpha_mixer.outputs[0], output.inputs[0])
        alpha_mixer.update()
    # no alpha
    else:
        b_mat.blend_method = "OPAQUE"
        tree.links.new(principled.outputs[0], output.inputs[0])

    nodes_iterate(tree, output)
    return b_mat
Example #10
0
def create_material(in_dir, matname):

    print(f"Importing material {matname}")
    # only create the material if it doesn't exist in the blend file, then just grab it
    # but we overwrite its contents anyway
    if matname not in bpy.data.materials:
        mat = bpy.data.materials.new(matname)
    else:
        mat = bpy.data.materials[matname]

    fgm_path = os.path.join(in_dir, matname + ".fgm")
    # print(fgm_path)
    try:
        fgm_data = FgmFile()
        fgm_data.load(fgm_path)
    except FileNotFoundError:
        print(f"{fgm_path} does not exist!")
        return mat
    # base_index = fgm_data.textures[0].layers[1]
    # height_index = fgm_data.textures[1].layers[1]
    tree = get_tree(mat)
    output = tree.nodes.new('ShaderNodeOutputMaterial')
    principled = tree.nodes.new('ShaderNodeBsdfPrincipled')

    all_textures = [
        file for file in os.listdir(in_dir) if file.lower().endswith(".png")
    ]
    # map texture names to node
    tex_dic = {}
    for fgm_texture in fgm_data.textures:
        png_base = f"{matname}.{fgm_texture.name}".lower()
        if "blendweights" in png_base or "warpoffset" in png_base:
            continue
        textures = [
            file for file in all_textures if file.lower().startswith(png_base)
        ]
        if not textures:
            png_base = png_base.lower().replace("_eyes", "").replace(
                "_fin", "").replace("_shell", "")
            textures = [
                file for file in all_textures
                if file.lower().startswith(png_base)
            ]
        if not textures:
            textures = [
                png_base + ".png",
            ]
        # print(textures)
        for png_name in textures:
            png_path = os.path.join(in_dir, png_name)
            b_tex = load_tex(tree, png_path)
            k = png_name.lower().split(".")[1]
            tex_dic[k] = b_tex

    # get diffuse and AO
    for diffuse_name in ("pbasediffusetexture", "pbasecolourtexture",
                         "pbasecolourandmasktexture"):
        # get diffuse
        if diffuse_name in tex_dic:
            diffuse = tex_dic[diffuse_name]
            # get AO
            for ao_name in ("paotexture", "pbasepackedtexture_03"):
                if ao_name in tex_dic:
                    ao = tex_dic[ao_name]
                    ao.image.colorspace_settings.name = "Non-Color"

                    # apply AO to diffuse
                    diffuse_premix = tree.nodes.new('ShaderNodeMixRGB')
                    diffuse_premix.blend_type = "MULTIPLY"
                    diffuse_premix.inputs["Fac"].default_value = .25
                    tree.links.new(diffuse.outputs[0],
                                   diffuse_premix.inputs["Color1"])
                    tree.links.new(ao.outputs[0],
                                   diffuse_premix.inputs["Color2"])
                    diffuse = diffuse_premix
                    break
            #  link finished diffuse to shader
            tree.links.new(diffuse.outputs[0], principled.inputs["Base Color"])
            break

    if "pnormaltexture" in tex_dic:
        normal = tex_dic["pnormaltexture"]
        normal.image.colorspace_settings.name = "Non-Color"
        normal_map = tree.nodes.new('ShaderNodeNormalMap')
        tree.links.new(normal.outputs[0], normal_map.inputs[1])
        # normal_map.inputs["Strength"].default_value = 1.0
        tree.links.new(normal_map.outputs[0], principled.inputs["Normal"])

    # PZ - specularity?
    for spec_name in ("proughnesspackedtexture_02", ):
        if spec_name in tex_dic:
            specular = tex_dic[spec_name]
            specular.image.colorspace_settings.name = "Non-Color"
            tree.links.new(specular.outputs[0], principled.inputs["Specular"])

    # PZ - roughness?
    for roughness_name in ("proughnesspackedtexture_01", ):
        if roughness_name in tex_dic:
            roughness = tex_dic[roughness_name]
            roughness.image.colorspace_settings.name = "Non-Color"
            tree.links.new(roughness.outputs[0],
                           principled.inputs["Roughness"])

    # JWE dinos - metalness
    for metal_name in ("pbasepackedtexture_02", ):
        if metal_name in tex_dic:
            metal = tex_dic[metal_name]
            metal.image.colorspace_settings.name = "Non-Color"
            tree.links.new(metal.outputs[0], principled.inputs["Metallic"])

    # alpha
    if "proughnesspackedtexture_03" in tex_dic:
        # transparency
        mat.blend_method = "CLIP"
        mat.shadow_method = "CLIP"
        for attrib in fgm_data.attributes:
            if attrib.name.lower() == "palphatestref":
                mat.alpha_threshold = attrib.value[0]
                break
        # if material.AlphaBlendEnable:
        # 	mat.blend_method = "BLEND"
        transp = tree.nodes.new('ShaderNodeBsdfTransparent')
        alpha_mixer = tree.nodes.new('ShaderNodeMixShader')
        alpha = tex_dic["proughnesspackedtexture_03"]
        tree.links.new(alpha.outputs[0], alpha_mixer.inputs[0])

        tree.links.new(transp.outputs[0], alpha_mixer.inputs[1])
        tree.links.new(principled.outputs[0], alpha_mixer.inputs[2])
        tree.links.new(alpha_mixer.outputs[0], output.inputs[0])
        alpha_mixer.update()
    # no alpha
    else:
        mat.blend_method = "OPAQUE"
        tree.links.new(principled.outputs[0], output.inputs[0])

    nodes_iterate(tree, output)
    return mat
Example #11
0
	def __init__(self):
		widgets.MainWindow.__init__(self, "FGM Editor", )
		
		self.resize(800, 600)

		self.fgm_data = FgmFile()
		self.tooltips = config.read_config("ovl_util/tooltips/fgm.txt")
		self.games = [g.value for g in games]
		self.fgm_dict = None

		self.cleaner = QtCore.QObjectCleanupHandler()

		self.scrollarea = QtWidgets.QScrollArea(self)
		self.scrollarea.setWidgetResizable(True)
		self.setCentralWidget(self.scrollarea)

		# the actual scrollable stuff
		self.widget = QtWidgets.QWidget()
		self.scrollarea.setWidget(self.widget)

		self.game_container = widgets.LabelCombo("Game:", self.games)
		self.game_container.entry.currentIndexChanged.connect(self.game_changed)
		self.game_container.entry.setEditable(False)
		self.file_widget = widgets.FileWidget(self, self.cfg, dtype="FGM")

		self.shader_choice = widgets.LabelCombo("Shader:", ())
		self.shader_choice.entry.activated.connect(self.shader_changed)
		self.attribute_choice = widgets.LabelCombo("Attribute:", ())
		self.texture_choice = widgets.LabelCombo("Texture:", ())
		self.attribute_add = QtWidgets.QPushButton("Add Attribute")
		self.attribute_add.clicked.connect(self.add_attribute)
		self.texture_add = QtWidgets.QPushButton("Add Texture")
		self.texture_add.clicked.connect(self.add_texture)

		self.tex_container = ProptertyContainer(self, "Textures")
		self.attrib_container = ProptertyContainer(self, "Attributes")

		self.game_changed()
		# self.populate_choices()
		self.shader_changed()

		vbox = QtWidgets.QVBoxLayout()
		vbox.addWidget(self.file_widget)
		vbox.addWidget(self.game_container)
		vbox.addWidget(self.shader_choice)
		vbox.addWidget(self.attribute_choice)
		vbox.addWidget(self.attribute_add)
		vbox.addWidget(self.texture_choice)
		vbox.addWidget(self.texture_add)
		vbox.addWidget(self.tex_container)
		vbox.addWidget(self.attrib_container)
		vbox.addStretch(1)
		self.widget.setLayout(vbox)

		main_menu = self.menuBar()
		file_menu = main_menu.addMenu('File')
		help_menu = main_menu.addMenu('Help')
		button_data = (
			(file_menu, "Open", self.file_widget.ask_open, "CTRL+O", "dir"),
			(file_menu, "Save", self.save_fgm, "CTRL+S", "save"),
			(file_menu, "Save As", self.save_as_fgm, "CTRL+SHIFT+S", "save"),
			(file_menu, "Exit", self.close, "", "exit"),
			(help_menu, "Report Bug", self.report_bug, "", "report"),
			(help_menu, "Documentation", self.online_support, "", "manual")
		)
		self.add_to_menu(button_data)
Example #12
0
class MainWindow(widgets.MainWindow):

	def __init__(self):
		widgets.MainWindow.__init__(self, "FGM Editor", )
		
		self.resize(800, 600)

		self.fgm_data = FgmFile()
		self.tooltips = config.read_config("ovl_util/tooltips/fgm.txt")
		self.games = [g.value for g in games]
		self.fgm_dict = None

		self.cleaner = QtCore.QObjectCleanupHandler()

		self.scrollarea = QtWidgets.QScrollArea(self)
		self.scrollarea.setWidgetResizable(True)
		self.setCentralWidget(self.scrollarea)

		# the actual scrollable stuff
		self.widget = QtWidgets.QWidget()
		self.scrollarea.setWidget(self.widget)

		self.game_container = widgets.LabelCombo("Game:", self.games)
		self.game_container.entry.currentIndexChanged.connect(self.game_changed)
		self.game_container.entry.setEditable(False)
		self.file_widget = widgets.FileWidget(self, self.cfg, dtype="FGM")

		self.shader_choice = widgets.LabelCombo("Shader:", ())
		self.shader_choice.entry.activated.connect(self.shader_changed)
		self.attribute_choice = widgets.LabelCombo("Attribute:", ())
		self.texture_choice = widgets.LabelCombo("Texture:", ())
		self.attribute_add = QtWidgets.QPushButton("Add Attribute")
		self.attribute_add.clicked.connect(self.add_attribute)
		self.texture_add = QtWidgets.QPushButton("Add Texture")
		self.texture_add.clicked.connect(self.add_texture)

		self.tex_container = ProptertyContainer(self, "Textures")
		self.attrib_container = ProptertyContainer(self, "Attributes")

		self.game_changed()
		# self.populate_choices()
		self.shader_changed()

		vbox = QtWidgets.QVBoxLayout()
		vbox.addWidget(self.file_widget)
		vbox.addWidget(self.game_container)
		vbox.addWidget(self.shader_choice)
		vbox.addWidget(self.attribute_choice)
		vbox.addWidget(self.attribute_add)
		vbox.addWidget(self.texture_choice)
		vbox.addWidget(self.texture_add)
		vbox.addWidget(self.tex_container)
		vbox.addWidget(self.attrib_container)
		vbox.addStretch(1)
		self.widget.setLayout(vbox)

		main_menu = self.menuBar()
		file_menu = main_menu.addMenu('File')
		help_menu = main_menu.addMenu('Help')
		button_data = (
			(file_menu, "Open", self.file_widget.ask_open, "CTRL+O", "dir"),
			(file_menu, "Save", self.save_fgm, "CTRL+S", "save"),
			(file_menu, "Save As", self.save_as_fgm, "CTRL+SHIFT+S", "save"),
			(file_menu, "Exit", self.close, "", "exit"),
			(help_menu, "Report Bug", self.report_bug, "", "report"),
			(help_menu, "Documentation", self.online_support, "", "manual")
		)
		self.add_to_menu(button_data)

	def game_changed(self,):
		game = self.game_container.entry.currentText()
		logging.info(f"Changed game to {game}")
		try:
			set_game(self.fgm_data.context, game)
			set_game(self.fgm_data, game)
		except BaseException as err:
			print(err)

		if is_jwe2(self.fgm_data):
			self.fgm_dict = fgm_jwe2
		elif is_pz16(self.fgm_data) or is_pz(self.fgm_data):
			self.fgm_dict = fgm_pz
		else:
			self.fgm_dict = None
		self.populate_choices()

	def populate_choices(self):
		if self.fgm_dict:
			self.shader_choice.entry.clear()
			self.shader_choice.entry.addItems(sorted(self.fgm_dict.shaders))
			self.attribute_choice.entry.clear()
			self.attribute_choice.entry.addItems(sorted(self.fgm_dict.attributes))
			self.texture_choice.entry.clear()
			self.texture_choice.entry.addItems(sorted(self.fgm_dict.textures))
		
	def shader_changed(self,):
		self.fgm_data.shader_name = self.shader_choice.entry.currentText()

	def add_attribute(self,):
		if self.fgm_dict:
			attrib_name = self.attribute_choice.entry.currentText()
			self.fgm_data.add_attrib(attrib_name, self.fgm_dict.attributes[attrib_name])
			self.attrib_container.update_gui(self.fgm_data.attributes)

	def add_texture(self,):
		tex_name = self.texture_choice.entry.currentText()
		self.fgm_data.add_texture(tex_name)
		self.tex_container.update_gui(self.fgm_data.textures)

	@property
	def fgm_name(self,):
		return self.file_widget.entry.text()

	def create_grid(self,):
		g = QtWidgets.QGridLayout()
		g.setHorizontalSpacing(3)
		g.setVerticalSpacing(0)
		return g

	def clear_layout(self, layout):
		w = QtWidgets.QWidget()
		w.setLayout(layout)
		# while layout.count():
		# 	item = layout.takeAt(0)
		# 	widget = item.widget()
		# 	# if widget has some id attributes you need to
		# 	# save in a list to maintain order, you can do that here
		# 	# i.e.:   aList.append(widget.someId)
		# 	widget.deleteLater()

	def load(self):
		if self.file_widget.filepath:
			try:
				self.fgm_data.load(self.file_widget.filepath)
				game = get_game(self.fgm_data)[0]
				logging.debug(f"from game {game}")
				self.game_container.entry.setText(game.value)
				self.game_changed()
				self.shader_choice.entry.setText(self.fgm_data.shader_name)
				self.tex_container.update_gui(self.fgm_data.textures)
				self.attrib_container.update_gui(self.fgm_data.attributes)

			except Exception as ex:
				traceback.print_exc()
				ovl_util.interaction.showdialog(str(ex))
				logging.warning(ex)
			logging.info("Done!")

	def save_fgm(self):
		if self.file_widget.filepath:
			self.fgm_data.save(self.file_widget.filepath)

	def save_as_fgm(self):
		file_out = QtWidgets.QFileDialog.getSaveFileName(self, 'Save FGM', os.path.join(self.cfg.get("dir_fgms_out", "C://"), self.fgm_name), "FGM files (*.fgm)",)[0]
		if file_out:
			self.cfg["dir_fgms_out"], fgm_name = os.path.split(file_out)
			try:
				self.fgm_data.save(file_out)
				print(self.fgm_data)
			except BaseException as err:
				traceback.print_exc()
				interaction.showdialog(str(err))
				logging.error(err)
			logging.info("Done!")