Skip to content

Commit

Permalink
[SM64/F3D] Use WriteDifferingAndRevert for bleed
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Lilaa3 committed Sep 15, 2024
1 parent ddd6969 commit 8ef2b02
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 80 deletions.
125 changes: 105 additions & 20 deletions fast64_internal/f3d/f3d_bleed.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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):
Expand Down Expand Up @@ -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:
Expand All @@ -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
):
Expand Down Expand Up @@ -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
Expand Down
87 changes: 37 additions & 50 deletions fast64_internal/f3d/f3d_gbi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
5 changes: 0 additions & 5 deletions fast64_internal/f3d/f3d_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion fast64_internal/sm64/sm64_f3d_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading

0 comments on commit 8ef2b02

Please sign in to comment.