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: