From 8ef2b020fa268417ae05f4508d52586ccd5b281e Mon Sep 17 00:00:00 2001 From: Lila Date: Sun, 15 Sep 2024 16:04:46 +0100 Subject: [PATCH] [SM64/F3D] Use WriteDifferingAndRevert for bleed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Write all logic remains unaffected, obviously. A new dict was added to map each mode cmd to its default. For the actual bleed part, we fIrst need to add geo mode reverts of the last material to the geo mode of the current material, since the current material may not set them now. For othermodes we do something different, get all reverts and add any that doesn´t get set again by the current material ignoring bleeding. For the geo mode revert, we keep track of one geo mode command for set and clear, updating it as we go. For othermodes we keep track of all of the command types and then revert them using the new dict. --- fast64_internal/f3d/f3d_bleed.py | 125 +++++++++++++++--- fast64_internal/f3d/f3d_gbi.py | 87 ++++++------ fast64_internal/f3d/f3d_writer.py | 5 - fast64_internal/sm64/sm64_f3d_writer.py | 2 +- fast64_internal/sm64/sm64_geolayout_writer.py | 4 +- fast64_internal/sm64/sm64_level_writer.py | 3 +- 6 files changed, 146 insertions(+), 80 deletions(-) diff --git a/fast64_internal/f3d/f3d_bleed.py b/fast64_internal/f3d/f3d_bleed.py index 0e6faaa9b..adb1d6861 100644 --- a/fast64_internal/f3d/f3d_bleed.py +++ b/fast64_internal/f3d/f3d_bleed.py @@ -7,9 +7,24 @@ from ..utility import create_or_get_world from .f3d_gbi import ( + DPPipelineMode, + DPSetAlphaCompare, + DPSetAlphaDither, + DPSetColorDither, + DPSetCombineKey, + DPSetCycleType, + DPSetDepthSource, + DPSetTextureConvert, + DPSetTextureDetail, + DPSetTextureFilter, + DPSetTextureLOD, + DPSetTextureLUT, + DPSetTexturePersp, GfxTag, GfxListTag, + SPGeometryMode, SPMatrix, + SPSetOtherModeSub, SPVertex, SPViewport, SPDisplayList, @@ -94,27 +109,30 @@ def place_in_flaglist(flag: bool, enum: str, set_list: SPSetGeometryMode, clear_ def build_default_othermodes(self): defaults = create_or_get_world(bpy.context.scene).rdp_defaults - othermode_H = SPSetOtherMode("G_SETOTHERMODE_H", 4, 20 - self.is_f3d_old, []) + othermode_L: dict[SPSetOtherModeSub:str] = {} + othermode_L[DPSetAlphaCompare] = defaults.g_mdsft_alpha_compare + othermode_L[DPSetDepthSource] = defaults.g_mdsft_zsrcsel + + othermode_H: dict[SPSetOtherModeSub:str] = {} + othermode_H[DPSetColorDither] = defaults.g_mdsft_rgb_dither + othermode_H[DPSetAlphaDither] = defaults.g_mdsft_alpha_dither + othermode_H[DPSetCombineKey] = defaults.g_mdsft_combkey + othermode_H[DPSetTextureConvert] = defaults.g_mdsft_textconv + othermode_H[DPSetTextureFilter] = defaults.g_mdsft_text_filt + othermode_H[DPSetTextureLUT] = defaults.g_mdsft_textlut + othermode_H[DPSetTextureLOD] = defaults.g_mdsft_textlod + othermode_H[DPSetTextureDetail] = defaults.g_mdsft_textdetail + othermode_H[DPSetTexturePersp] = defaults.g_mdsft_textpersp + othermode_H[DPSetCycleType] = defaults.g_mdsft_cycletype + othermode_H[DPPipelineMode] = defaults.g_mdsft_pipeline + self.default_othermode_dict = othermode_L | othermode_H + self.default_othermode_H = SPSetOtherMode( + "G_SETOTHERMODE_H", 4, 20 - self.is_f3d_old, list(othermode_H.values()) + ) # if the render mode is set, it will be consider non-default a priori - othermode_L = SPSetOtherMode("G_SETOTHERMODE_L", 0, 3 - self.is_f3d_old, []) - - othermode_L.flagList.append(defaults.g_mdsft_alpha_compare) - othermode_L.flagList.append(defaults.g_mdsft_zsrcsel) - - othermode_H.flagList.append(defaults.g_mdsft_rgb_dither) - othermode_H.flagList.append(defaults.g_mdsft_alpha_dither) - othermode_H.flagList.append(defaults.g_mdsft_combkey) - othermode_H.flagList.append(defaults.g_mdsft_textconv) - othermode_H.flagList.append(defaults.g_mdsft_text_filt) - othermode_H.flagList.append(defaults.g_mdsft_textlut) - othermode_H.flagList.append(defaults.g_mdsft_textlod) - othermode_H.flagList.append(defaults.g_mdsft_textdetail) - othermode_H.flagList.append(defaults.g_mdsft_textpersp) - othermode_H.flagList.append(defaults.g_mdsft_cycletype) - othermode_H.flagList.append(defaults.g_mdsft_pipeline) - - self.default_othermode_L = othermode_L - self.default_othermode_H = othermode_H + self.default_othermode_L = SPSetOtherMode( + "G_SETOTHERMODE_L", 0, 3 - self.is_f3d_old, list(othermode_L.values()) + ) def bleed_fModel(self, fModel: FModel, fMeshes: dict[FMesh]): # walk fModel, no order to drawing is observed, so last_mat is not kept track of @@ -243,12 +261,32 @@ def bleed_mat(self, cur_fmat: FMaterial, last_mat: FMaterial, bleed_state: int): commands_bled = copy.copy(gfx) commands_bled.commands = copy.copy(gfx.commands) # copy the commands also last_cmd_list = last_mat.mat_only_DL.commands + + # handle write diff, save pre bleed cmds + geo_cmd = next((cmd for cmd in commands_bled.commands if type(cmd) == SPGeometryMode), None) + othermode_cmds = [cmd for cmd in commands_bled.commands if isinstance(cmd, SPSetOtherModeSub)] + for j, cmd in enumerate(gfx.commands): if self.bleed_individual_cmd(commands_bled, cmd, bleed_state, last_cmd_list): commands_bled.commands[j] = None # remove Nones from list while None in commands_bled.commands: commands_bled.commands.remove(None) + + # handle write diff + revert_geo_cmd = next((cmd for cmd in last_mat.revert.commands if type(cmd) == SPGeometryMode), None) + geo_cmd_bleeded = geo_cmd is not None and geo_cmd not in commands_bled.commands + # if there was a geo command, and it wasnt bleeded, add revert's modes to it + if not geo_cmd_bleeded and geo_cmd and revert_geo_cmd: + geo_cmd.extend(revert_geo_cmd.clearFlagList, revert_geo_cmd.setFlagList) + # if there was no geo command but there was a revert, add the revert command + elif geo_cmd is None and revert_geo_cmd: + commands_bled.commands.insert(0, revert_geo_cmd) + + for revert_cmd in [cmd for cmd in last_mat.revert.commands if isinstance(cmd, SPSetOtherModeSub)]: + othermode_cmd = next((cmd for cmd in othermode_cmds if type(cmd) == type(revert_cmd)), None) + if othermode_cmd is None: # if there is no equivelent cmd, it must be using the revert + commands_bled.commands.insert(0, revert_cmd) else: commands_bled = self.bleed_cmd_list(cur_fmat.mat_only_DL, bleed_state) # some syncs may become redundant after bleeding @@ -388,6 +426,17 @@ def create_reset_cmds(self, reset_cmd_dict: dict[GbiMacro], default_render_mode: elif cmd_type == SPClearGeometryMode and cmd_use != self.default_clear_geo: reset_cmds.append(self.default_clear_geo) + elif cmd_type == SPGeometryMode: # revert cmd includes everything from the start + # First, figure out what needs to be cleared or set + set_list, clear_list = {}, {} + if cmd_use.setFlagList != self.default_set_geo.flagList: + clear_list = set(cmd_use.setFlagList) - set(self.default_set_geo.flagList) + if cmd_use.clearFlagList != self.default_clear_geo.flagList: + set_list = set(cmd_use.clearFlagList) - set(self.default_clear_geo.flagList) + + if set_list or clear_list: + reset_cmds.append(SPGeometryMode(list(clear_list), list(set_list))) + elif cmd_type == "G_SETOTHERMODE_H": if cmd_use != self.default_othermode_H: reset_cmds.append(self.default_othermode_H) @@ -407,6 +456,11 @@ def create_reset_cmds(self, reset_cmd_dict: dict[GbiMacro], default_render_mode: elif cmd_type == "G_SETOTHERMODE_L": if cmd_use != self.default_othermode_L: reset_cmds.append(self.default_othermode_L) + + elif isinstance(cmd_use, SPSetOtherModeSub): + default = self.default_othermode_dict[cmd_type] + if cmd_use.mode != default: + reset_cmds.append(cmd_type(default)) return reset_cmds def bleed_individual_cmd(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None): @@ -438,6 +492,11 @@ def bleed_individual_cmd(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: in if not last_cmd_list: return self.bleed_self_conflict + if isinstance(cmd, SPSetOtherModeSub): + if bleed_state != self.bleed_start: + return cmd in last_cmd_list + else: + return cmd.mode == self.default_othermode_dict[type(cmd)] # apply specific logic to these cmds, see functions below, otherwise default behavior is to bleed if cmd is in the last list bleed_func = getattr(self, (f"bleed_{type(cmd).__name__}"), None) if bleed_func: @@ -454,6 +513,12 @@ def bleed_SPLoadGeometryMode( else: return cmd == self.default_load_geo + def bleed_SPGeometryMode(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None): + if bleed_state != self.bleed_start: + return cmd in last_cmd_list + else: + return cmd.clearFlagList == self.default_clear_geo and cmd.setFlagList == self.default_set_geo + def bleed_SPSetGeometryMode( self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None ): @@ -531,6 +596,26 @@ def add_reset_cmd(self, cmd: GbiMacro, reset_cmd_dict: dict[GbiMacro]): SPClearGeometryMode, DPSetRenderMode, ) + if type(cmd) == SPGeometryMode: + if SPGeometryMode not in reset_cmd_dict: + reset_cmd_dict[SPGeometryMode] = SPGeometryMode([], []) + reset_cmd_dict[SPGeometryMode].extend(cmd.clearFlagList, cmd.setFlagList) + elif isinstance(cmd, SPSetOtherModeSub): + l: SPSetOtherMode = reset_cmd_dict.get("G_SETOTHERMODE_L") + h: SPSetOtherMode = reset_cmd_dict.get("G_SETOTHERMODE_H") + if l or h: # should never be reached, but if we reach it we are prepared + existing_mode = next((mode.startswith(cmd.mode_prefix) for mode in h.flagList + l.flagList), None) + if h and cmd.is_othermodeh: + if existing_mode: + h.flagList.remove(existing_mode) + h.flagList.append(cmd.mode) + if l and not cmd.is_othermodeh: + if existing_mode: + l.flagList.remove(existing_mode) + l.flagList.append(cmd.mode) + else: + reset_cmd_dict[type(cmd)] = cmd + # separate other mode H and othermode L if type(cmd) == SPSetOtherMode: reset_cmd_dict[cmd.cmd] = cmd diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py index 1eb5b7d80..a1186260b 100644 --- a/fast64_internal/f3d/f3d_gbi.py +++ b/fast64_internal/f3d/f3d_gbi.py @@ -4266,6 +4266,12 @@ class SPGeometryMode(GbiMacro): clearFlagList: list setFlagList: list + def extend(self, clear_list: list, set_list: list): + clear_list = set(self.clearFlagList + clear_list) + set_list = set(self.setFlagList + set_list) + self.setFlagList = list(set_list - clear_list) + self.clearFlagList = list(clear_list - set_list) + def to_binary(self, f3d, segments): if f3d.F3DEX_GBI_2: wordClear = geoFlagListToWord(self.clearFlagList, f3d) @@ -4339,10 +4345,27 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPPipelineMode(GbiMacro): - # mode is a string +class SPSetOtherModeSub(GbiMacro): mode: str + is_othermodeh = False + + @property + def mode_prefix(self): + return "_".join(self.mode.split("_")[:2]) + + +@dataclass(unsafe_hash=True) +class SPSetOtherModeLSub(SPSetOtherModeSub): + is_othermodeh = False + + +@dataclass(unsafe_hash=True) +class SPSetOtherModeHSub(SPSetOtherModeSub): + is_othermodeh = True + +@dataclass(unsafe_hash=True) +class DPPipelineMode(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_PM_1PRIMITIVE": modeVal = f3d.G_PM_1PRIMITIVE @@ -4352,10 +4375,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetCycleType(GbiMacro): - # mode is a string - mode: str - +class DPSetCycleType(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_CYC_1CYCLE": modeVal = f3d.G_CYC_1CYCLE @@ -4369,10 +4389,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetTexturePersp(GbiMacro): - # mode is a string - mode: str - +class DPSetTexturePersp(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_TP_NONE": modeVal = f3d.G_TP_NONE @@ -4382,10 +4399,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetTextureDetail(GbiMacro): - # mode is a string - mode: str - +class DPSetTextureDetail(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_TD_CLAMP": modeVal = f3d.G_TD_CLAMP @@ -4397,10 +4411,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetTextureLOD(GbiMacro): - # mode is a string - mode: str - +class DPSetTextureLOD(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_TL_TILE": modeVal = f3d.G_TL_TILE @@ -4410,10 +4421,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetTextureLUT(GbiMacro): - # mode is a string - mode: str - +class DPSetTextureLUT(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_TT_NONE": modeVal = f3d.G_TT_NONE @@ -4427,10 +4435,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetTextureFilter(GbiMacro): - # mode is a string - mode: str - +class DPSetTextureFilter(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_TF_POINT": modeVal = f3d.G_TF_POINT @@ -4442,10 +4447,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetTextureConvert(GbiMacro): - # mode is a string - mode: str - +class DPSetTextureConvert(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_TC_CONV": modeVal = f3d.G_TC_CONV @@ -4457,10 +4459,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetCombineKey(GbiMacro): - # mode is a string - mode: str - +class DPSetCombineKey(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_CK_NONE": modeVal = f3d.G_CK_NONE @@ -4470,10 +4469,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetColorDither(GbiMacro): - # mode is a string - mode: str - +class DPSetColorDither(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_CD_MAGICSQ": modeVal = f3d.G_CD_MAGICSQ @@ -4489,10 +4485,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetAlphaDither(GbiMacro): - # mode is a string - mode: str - +class DPSetAlphaDither(SPSetOtherModeHSub): def to_binary(self, f3d, segments): if self.mode == "G_AD_PATTERN": modeVal = f3d.G_AD_PATTERN @@ -4506,10 +4499,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetAlphaCompare(GbiMacro): - # mask is a string - mode: str - +class DPSetAlphaCompare(SPSetOtherModeLSub): def to_binary(self, f3d, segments): if self.mode == "G_AC_NONE": maskVal = f3d.G_AC_NONE @@ -4521,10 +4511,7 @@ def to_binary(self, f3d, segments): @dataclass(unsafe_hash=True) -class DPSetDepthSource(GbiMacro): - # src is a string - src: str - +class DPSetDepthSource(SPSetOtherModeLSub): def to_binary(self, f3d, segments): if self.src == "G_ZS_PIXEL": srcVal = f3d.G_ZS_PIXEL diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index c73a6e50d..bad95d3d4 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -1635,11 +1635,6 @@ def saveGeoModeDefinitionF3DEX2(fMaterial, settings, defaults, matWriteMethod): saveGeoModeCommon(saveBitGeoF3DEX2, settings, defaults, (geo, matWriteMethod)) if len(geo.clearFlagList) != 0 or len(geo.setFlagList) != 0: - if len(geo.clearFlagList) == 0: - geo.clearFlagList.append("0") - elif len(geo.setFlagList) == 0: - geo.setFlagList.append("0") - if matWriteMethod == GfxMatWriteMethod.WriteAll: fMaterial.mat_only_DL.commands.append(SPLoadGeometryMode(geo.setFlagList)) else: diff --git a/fast64_internal/sm64/sm64_f3d_writer.py b/fast64_internal/sm64/sm64_f3d_writer.py index ab69b095f..4ce300a6b 100644 --- a/fast64_internal/sm64/sm64_f3d_writer.py +++ b/fast64_internal/sm64/sm64_f3d_writer.py @@ -367,7 +367,7 @@ def sm64ExportF3DtoC( fModel = SM64Model( name, DLFormat, - GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll, + GfxMatWriteMethod.WriteDifferingAndRevert, ) fMeshes = exportF3DCommon(obj, fModel, transformMatrix, includeChildren, name, DLFormat, not savePNG) diff --git a/fast64_internal/sm64/sm64_geolayout_writer.py b/fast64_internal/sm64/sm64_geolayout_writer.py index 7a3fb7abd..971a9112e 100644 --- a/fast64_internal/sm64/sm64_geolayout_writer.py +++ b/fast64_internal/sm64/sm64_geolayout_writer.py @@ -393,7 +393,7 @@ def convertArmatureToGeolayout(armatureObj, obj, convertTransformMatrix, camera, fModel = SM64Model( name, DLFormat, - GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll, + GfxMatWriteMethod.WriteDifferingAndRevert, ) if len(armatureObj.children) == 0: @@ -461,7 +461,7 @@ def convertObjectToGeolayout( fModel = SM64Model( name, DLFormat, - GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll, + GfxMatWriteMethod.WriteDifferingAndRevert, ) # convertTransformMatrix = convertTransformMatrix @ \ diff --git a/fast64_internal/sm64/sm64_level_writer.py b/fast64_internal/sm64/sm64_level_writer.py index 1587b43fe..0613db594 100644 --- a/fast64_internal/sm64/sm64_level_writer.py +++ b/fast64_internal/sm64/sm64_level_writer.py @@ -873,11 +873,10 @@ def exportLevelC(obj, transformMatrix, level_name, exportDir, savePNG, customExp level_data = LevelData(camera_data=f"struct CameraTrigger {levelCameraVolumeName}[] = {{\n") - inline = bpy.context.scene.exportInlineF3D fModel = SM64Model( level_name + "_dl", DLFormat, - GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll, + GfxMatWriteMethod.WriteDifferingAndRevert, ) childAreas = [child for child in obj.children if child.type == "EMPTY" and child.sm64_obj_type == "Area Root"] if len(childAreas) == 0: