[SM64/F3D] Use WriteDifferingAndRevert for bleed (#461)

* [SM64/F3D] Use WriteDifferingAndRevert for bleed

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.

* remove this

* scut sabotage

* Port over #462

We cannot just assume that othermodeh or othermodel are always setting the same bits, so instead we write the diff of the current cmd to the one already in the reset dict, this means that if a materials sets rendermode and another sets alpha compare (for example) it will still revert the rendermode because those bits will be carried over in the reset dict

* make write different optional, as it can lead to bigger dls

* clean up sm64 repo settings

* im gonna have to rework a bunch of othermode stuff i actually dispise fast64 so much

* RendermodeBlender class

* Update f3d_bleed.py

* i just want to be done with this

* [Repo Settings] Only set if different

Fixes lag at start up from draw layers being set unnecessarily.
Some clean up that I already did in #461

* Update utility.py

* is_rendermode_cmd to def

* fix rendermode

* return RendermodeBlender instead of temp list

* instead of using a revert, set when no render mode

* make rendermode command hashable, check for default

* pick appropriate geo cmd, make geo cmds use sets

makes dls just a bit cleaner and makes ex1 work correctly

* implement render mode reset fix

* Update f3d_bleed.py

* undo dumb change

* respect write method

* only revert as needed, i feel like a god

* make switchs work super well

* fix setting rendermode

* remove repeated resets in switch options and add options for reverts in bones

* use last_mat by default

* comment out occlusion plane code to show bleed indep

* functions already work

* fix load othermodes

these work more like write diffs, so we do that

* someone forgot

* fix prev node issue

* create our own pipe syncs

* Update f3d_bleed.py

* fix disgusting broken late night code

* Revert "fix disgusting broken late night code"

This reverts commit 9ece1511595678a551fae02f906a998fbde1ffba.

* Update sm64_geolayout_classes.py

* something is wrong but i cant figure it out

* dont remove lighting

* fix DPSetRenderMode

* fix write all rendermode

* fix this bug once and for all

* write all get_flags impl

* get_flags

* forgot to do diff in reverts in write all

* add back prim depth, accidentally removed

* comments (not ready)

* not a union

* not a tuple

Co-authored-by: Dragorn421 <[email protected]>

* proper formatting

Co-authored-by: Dragorn421 <[email protected]>

* use load sync

* oopsie dasie

* use pipe syncs again

---------

Co-authored-by: Dragorn421 <[email protected]>
This commit is contained in:
Lila
2025-05-23 19:48:29 +01:00
committed by GitHub
co-authored by Dragorn421
parent 552bdb797c
commit f221e640d3
12 changed files with 667 additions and 476 deletions
+341 -187
View File
@@ -7,9 +7,25 @@ from dataclasses import dataclass, field
from ..utility import create_or_get_world
from .f3d_gbi import (
DPPipelineMode,
DPSetAlphaCompare,
DPSetAlphaDither,
DPSetColorDither,
DPSetCombineKey,
DPSetCycleType,
DPSetDepthSource,
DPSetTextureConvert,
DPSetTextureDetail,
DPSetTextureFilter,
DPSetTextureLOD,
DPSetTextureLUT,
DPSetTexturePersp,
GfxMatWriteMethod,
GfxTag,
GfxListTag,
SPGeometryMode,
SPMatrix,
SPSetOtherModeSub,
SPVertex,
SPViewport,
SPDisplayList,
@@ -36,6 +52,7 @@ from .f3d_gbi import (
DPLoadSync,
DPTileSync,
DPSetTile,
DPSetTileSize,
DPLoadTile,
FModel,
FMesh,
@@ -45,9 +62,61 @@ from .f3d_gbi import (
GfxList,
FTriGroup,
GbiMacro,
get_F3D_GBI,
)
def get_geo_cmds(
clear_modes: set[str], set_modes: set[str], is_ex2: bool, matWriteMethod: GfxMatWriteMethod
) -> tuple[
list[SPLoadGeometryMode | SPGeometryMode | SPSetGeometryMode | SPClearGeometryMode],
list[SPGeometryMode | SPSetGeometryMode | SPClearGeometryMode],
]:
set_modes, clear_modes = set(set_modes), set(clear_modes)
if len(clear_modes) == 0 and len(set_modes) == 0:
return ([], [])
if is_ex2:
if matWriteMethod == GfxMatWriteMethod.WriteAll:
return ([SPLoadGeometryMode(set_modes)], [])
elif len(set_modes) > 0 and len(clear_modes) > 0:
return ([SPGeometryMode(clear_modes, set_modes)], [SPGeometryMode(set_modes, clear_modes)])
material, revert = [], []
if len(set_modes) > 0:
material.append(SPSetGeometryMode(set_modes))
revert.append(SPClearGeometryMode(set_modes))
if len(clear_modes) > 0:
material.append(SPClearGeometryMode(clear_modes))
revert.append(SPSetGeometryMode(clear_modes))
return (material, revert)
GEO_CMDS = (SPGeometryMode, SPSetGeometryMode, SPClearGeometryMode, SPLoadGeometryMode)
WRITE_DIFF_OTHERMODE_CMDS = (SPSetOtherModeSub, DPSetRenderMode)
def get_flags(
set_modes: set[str], clear_modes: set[str], cmd: GEO_CMDS, default_clear: SPClearGeometryMode | None = None
):
if type(cmd) == SPGeometryMode:
set_modes.update(cmd.setFlagList)
clear_modes.update(cmd.clearFlagList)
clear_modes.difference_update(set_modes)
set_modes.difference_update(clear_modes)
elif type(cmd) == SPSetGeometryMode:
set_modes.update(cmd.flagList)
clear_modes.difference_update(set_modes)
elif type(cmd) == SPClearGeometryMode:
clear_modes.update(cmd.flagList)
set_modes.difference_update(clear_modes)
elif type(cmd) == SPLoadGeometryMode:
clear_modes.update(set_modes)
clear_modes.difference_update(cmd.flagList)
if default_clear is not None:
clear_modes.update(default_clear.flagList - cmd.flagList)
set_modes.clear()
set_modes.update(cmd.flagList)
class BleedGraphics:
# bleed_state "enums"
bleed_start = 1
@@ -57,7 +126,9 @@ class BleedGraphics:
def __init__(self):
self.bled_gfx_lists = dict()
self.reset_gfx_lists = set()
# build world default cmds to compare against, f3d types needed for reset cmd building
self.f3d = get_F3D_GBI()
self.is_f3d_old = bpy.context.scene.f3d_type == "F3D"
self.is_f3dex2 = "F3DEX2" in bpy.context.scene.f3d_type
self.build_default_geo()
@@ -66,14 +137,14 @@ class BleedGraphics:
def build_default_geo(self):
defaults = create_or_get_world(bpy.context.scene).rdp_defaults
setGeo = SPSetGeometryMode([])
clearGeo = SPClearGeometryMode([])
setGeo = SPSetGeometryMode()
clearGeo = SPClearGeometryMode()
def place_in_flaglist(flag: bool, enum: str, set_list: SPSetGeometryMode, clear_list: SPClearGeometryMode):
if flag:
set_list.flagList.append(enum)
set_list.flagList.add(enum)
else:
clear_list.flagList.append(enum)
clear_list.flagList.add(enum)
place_in_flaglist(defaults.g_zbuffer, "G_ZBUFFER", setGeo, clearGeo)
place_in_flaglist(defaults.g_shade, "G_SHADE", setGeo, clearGeo)
@@ -94,32 +165,42 @@ class BleedGraphics:
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, set(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, set(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
for drawLayer, fMesh in fMeshes.items():
self.bleed_fmesh(fMesh, None, fMesh.draw, fModel.getAllMaterials().items(), fModel.getRenderMode(drawLayer))
reset_cmd_dict = {}
self.bleed_fmesh(
None,
reset_cmd_dict,
fMesh.draw,
fModel.getAllMaterials().items(),
fModel.matWriteMethod,
fModel.getRenderMode(drawLayer),
)
self.add_reset_cmds(fMesh.draw, reset_cmd_dict, fModel.matWriteMethod, fModel.getRenderMode(drawLayer))
self.clear_gfx_lists(fModel)
# clear the gfx lists so they don't export
@@ -132,28 +213,63 @@ class BleedGraphics:
for tri_list in fMesh.triangleGroups:
tri_list.triList.tag |= GfxListTag.NoExport
def add_reset_cmd(
self, f3d: F3D, cmd: GbiMacro, reset_cmd_dict: dict[GbiMacro], mat_write_method: GfxMatWriteMethod
):
reset_cmd_list = (DPSetRenderMode,)
if SPGeometryMode not in reset_cmd_dict:
if mat_write_method == GfxMatWriteMethod.WriteAll:
reset_cmd_dict[SPGeometryMode] = (
self.default_set_geo.flagList.copy(),
self.default_clear_geo.flagList.copy(),
)
else:
reset_cmd_dict[SPGeometryMode] = set(), set()
get_flags(*reset_cmd_dict[SPGeometryMode], cmd)
if 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
if h and cmd.is_othermodeh:
for existing_mode in [mode for mode in h.flagList if str(mode).startswith(cmd.mode_prefix)]:
h.flagList.remove(existing_mode)
h.flagList.add(cmd.mode)
if l and not cmd.is_othermodeh:
for existing_mode in [mode for mode in l.flagList if str(mode).startswith(cmd.mode_prefix)]:
l.flagList.remove(existing_mode)
l.flagList.add(cmd.mode)
else:
reset_cmd_dict[type(cmd)] = cmd
# separate other mode H and othermode L
elif type(cmd) == SPSetOtherMode:
if cmd.cmd in reset_cmd_dict:
reset_cmd_dict[cmd.cmd].add_other(f3d, cmd)
else:
reset_cmd_dict[cmd.cmd] = copy.deepcopy(cmd)
elif type(cmd) in reset_cmd_list:
reset_cmd_dict[type(cmd)] = cmd
def bleed_fmesh(
self,
fMesh: FMesh,
last_mat: FMaterial,
reset_cmd_dict: dict[type, GbiMacro],
cmd_list: GfxList,
fmodel_materials,
default_render_mode: list[str] = None,
mat_write_method: GfxMatWriteMethod,
default_render_mode: tuple[str] = None,
):
if bled_mat := self.bled_gfx_lists.get(cmd_list, None):
if bled_mat := self.bled_gfx_lists.get(id(cmd_list)):
return bled_mat
bleed_state = self.bleed_start
cur_fmat = None
reset_cmd_dict = dict()
bleed_gfx_lists = BleedGfxLists()
fmesh_static_cmds, fmesh_jump_cmds = self.on_bleed_start(cmd_list)
start_cmds = cmd_list.commands # commands that preceed any jump list
for jump_list_cmd in fmesh_jump_cmds:
# bleed mat and tex
if jump_list_cmd.displayList.tag & GfxListTag.Material:
# update last_mat
if cur_fmat:
last_mat = cur_fmat
_, cur_fmat = find_material_from_jump_cmd(fmodel_materials, jump_list_cmd)
if not cur_fmat:
# make better error msg
@@ -163,22 +279,26 @@ class BleedGraphics:
bleed_gfx_lists.bled_tex = self.bleed_textures(cur_fmat, last_mat, bleed_state)
else:
bleed_gfx_lists.bled_tex = cur_fmat.texture_DL.commands
bleed_gfx_lists.bled_mats = self.bleed_mat(cur_fmat, last_mat, bleed_state)
# some syncs may become redundant after bleeding
self.optimize_syncs(bleed_gfx_lists, bleed_state)
bleed_gfx_lists.bled_mats = self.bleed_mat(
cur_fmat, last_mat, start_cmds, mat_write_method, default_render_mode, bleed_state
)
start_cmds = []
# bleed tri group (for large textures) and to remove other unnecessary cmds
if jump_list_cmd.displayList.tag & GfxListTag.Geometry:
tri_list = jump_list_cmd.displayList
self.bleed_tri_group(tri_list, cur_fmat, bleed_state)
self.inline_triGroup(tri_list, bleed_gfx_lists, cmd_list, reset_cmd_dict)
self.inline_triGroup(tri_list, bleed_gfx_lists, cmd_list)
self.on_tri_group_bleed_end(tri_list, cur_fmat, bleed_gfx_lists)
# reset bleed gfx lists after inlining
bleed_gfx_lists = BleedGfxLists()
# set bleed state for cmd reverts
bleed_state = self.bleed_in_progress
last_mat = cur_fmat
self.on_bleed_end(last_mat, cmd_list, fmesh_static_cmds, reset_cmd_dict, default_render_mode)
last_mat = cur_fmat
cmd_list.commands.extend(fmesh_static_cmds) # this is troublesome
cmd_list.commands.append(SPEndDisplayList())
self.optimize_syncs(cmd_list) # some syncs may become redundant after bleeding
[self.add_reset_cmd(self.f3d, cmd, reset_cmd_dict, mat_write_method) for cmd in cmd_list.commands]
self.bled_gfx_lists[id(cmd_list)] = cur_fmat
return last_mat
def build_tmem_dict(self, cmd_list: GfxList):
@@ -228,7 +348,7 @@ class BleedGraphics:
for j, cmd in enumerate(cur_fmat.texture_DL.commands):
if not cmd:
continue # some cmds are None from previous step
if self.bleed_individual_cmd(commands_bled, cmd, bleed_state, last_mat.texture_DL.commands) is True:
if self.bleed_individual_cmd(commands_bled, cmd, last_mat.texture_DL.commands) is True:
commands_bled.commands[j] = None
# remove Nones from list
while None in commands_bled.commands:
@@ -238,21 +358,98 @@ class BleedGraphics:
bled_tex = cur_fmat.texture_DL
return bled_tex.commands
def bleed_mat(self, cur_fmat: FMaterial, last_mat: FMaterial, bleed_state: int):
def bleed_mat(
self,
cur_fmat: FMaterial,
last_mat: FMaterial,
start_cmds: list[GbiMacro],
mat_write_method: GfxMatWriteMethod,
default_render_mode: list[str],
bleed_state: int,
):
if mat_write_method == GfxMatWriteMethod.WriteAll:
new_sets, new_clears = self.default_set_geo.flagList.copy(), self.default_clear_geo.flagList.copy()
previous_sets, previous_clears = (
self.default_set_geo.flagList.copy(),
self.default_clear_geo.flagList.copy(),
)
revert_sets, revert_clears = self.default_set_geo.flagList.copy(), self.default_clear_geo.flagList.copy()
else:
new_sets, new_clears = set(), set()
previous_sets, previous_clears = set(), set()
revert_sets, revert_clears = set(), set()
revert_other_diff_cmd, revert_other_load_cmd, othermode_diff_cmds, last_cmd_list = [], [], [], []
[get_flags(new_sets, new_clears, cmd, self.default_clear_geo) for cmd in cur_fmat.mat_only_DL.commands]
if last_mat:
gfx = cur_fmat.mat_only_DL
# deep copy breaks on Image objects so I will only copy the levels needed
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
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)
last_cmd_list = last_mat.mat_only_DL.commands + start_cmds
[get_flags(previous_sets, previous_clears, cmd, self.default_clear_geo) for cmd in last_cmd_list]
# handle write diff reverts
othermode_diff_cmds = [c for c in commands_bled.commands if isinstance(c, WRITE_DIFF_OTHERMODE_CMDS)]
if last_mat.revert:
[get_flags(revert_sets, revert_clears, cmd, self.default_clear_geo) for cmd in last_mat.revert.commands]
revert_other_diff_cmd = [
c for c in last_mat.revert.commands if isinstance(c, WRITE_DIFF_OTHERMODE_CMDS)
]
revert_other_load_cmd = [
copy.deepcopy(c) for c in last_mat.revert.commands if isinstance(c, SPSetOtherMode)
]
# while load mode is always written, they may not set the same range of values and therefor need revert
for revert_cmd in revert_other_load_cmd:
othermode_cmd = next(
(c for c in commands_bled.commands if type(c) == type(revert_cmd) and c.cmd == revert_cmd.cmd), None
)
if othermode_cmd is None:
commands_bled.commands.insert(0, revert_cmd)
else:
index = commands_bled.commands.index(othermode_cmd)
revert_cmd.add_other(self.f3d, othermode_cmd)
commands_bled.commands[index] = revert_cmd
commands_bled.commands = [
cmd
for cmd in commands_bled.commands
if not self.bleed_individual_cmd(commands_bled, cmd, last_cmd_list, default_render_mode)
]
else:
commands_bled = self.bleed_cmd_list(cur_fmat.mat_only_DL, bleed_state)
[get_flags(previous_sets, previous_clears, cmd, self.default_clear_geo) for cmd in start_cmds]
commands_bled = self.bleed_cmd_list(cur_fmat.mat_only_DL, default_render_mode, bleed_state)
# remove all geo cmds to add later
commands_bled.commands = [cmd for cmd in commands_bled.commands if not isinstance(cmd, GEO_CMDS)]
# remove clears and sets from revert if they will be set later in start or this material
revert_clears, revert_sets = (
revert_clears - previous_clears - new_sets,
revert_sets - previous_sets - new_clears,
)
if mat_write_method == GfxMatWriteMethod.WriteAll:
if previous_clears != new_clears or previous_sets != new_sets:
set_modes, clear_modes = new_sets | revert_sets, new_clears | revert_clears
# add back removed geo cmds, reverts and start cmds
for cmd in get_geo_cmds(clear_modes, set_modes, self.f3d.F3DEX_GBI_2, mat_write_method)[0]:
commands_bled.commands.insert(0, cmd)
else:
# remove clears and sets from the material if set in start
new_clears, new_sets = new_clears - previous_clears, new_sets - previous_sets
# combine
set_modes, clear_modes = new_sets | revert_sets, new_clears | revert_clears
clear_modes, set_modes = clear_modes - set_modes, set_modes - clear_modes
# add back removed geo cmds and reverts
for cmd in get_geo_cmds(clear_modes, set_modes, self.f3d.F3DEX_GBI_2, mat_write_method)[0]:
commands_bled.commands.insert(0, cmd)
# if there is no equivelent othermode cmd, it must be using the revert
for revert_cmd in revert_other_diff_cmd:
othermode_cmd = next((cmd for cmd in othermode_diff_cmds if type(cmd) == type(revert_cmd)), None)
if othermode_cmd is None:
commands_bled.commands.insert(0, revert_cmd)
# remove SPEndDisplayList
while SPEndDisplayList() in commands_bled.commands:
commands_bled.commands.remove(SPEndDisplayList())
@@ -263,16 +460,16 @@ class BleedGraphics:
while SPEndDisplayList() in tri_list.commands:
tri_list.commands.remove(SPEndDisplayList())
if not cur_fmat or (cur_fmat.isTexLarge[0] or cur_fmat.isTexLarge[1]):
tri_list = self.bleed_cmd_list(tri_list, bleed_state)
tri_list = self.bleed_cmd_list(tri_list, None, bleed_state)
# this is a little less versatile than comparing by last used material
def bleed_cmd_list(self, target_cmd_list: GfxList, bleed_state: int):
def bleed_cmd_list(self, target_cmd_list: GfxList, default_render_mode: list[str], bleed_state: int):
usage_dict = dict()
commands_bled = copy.copy(target_cmd_list) # copy the commands
commands_bled.commands = copy.copy(target_cmd_list.commands) # copy the commands
for j, cmd in enumerate(target_cmd_list.commands):
# some cmds you can bleed vs world defaults, others only if they repeat within this gfx list
bleed_cmd_status = self.bleed_individual_cmd(commands_bled, cmd, bleed_state)
bleed_cmd_status = self.bleed_individual_cmd(commands_bled, cmd, default_render_mode=default_render_mode)
if not bleed_cmd_status:
continue
last_use = usage_dict.get((type(cmd), getattr(cmd, "tile", None)), None)
@@ -285,20 +482,13 @@ class BleedGraphics:
return commands_bled
# Put triGroup bleed gfx in the FMesh.draw object
def inline_triGroup(
self, tri_list: GfxList, bleed_gfx_lists: BleedGfxLists, cmd_list: GfxList, reset_cmd_dict: dict[GbiMacro]
):
def inline_triGroup(self, tri_list: GfxList, bleed_gfx_lists: BleedGfxLists, cmd_list: GfxList):
# add material
cmd_list.commands.extend(bleed_gfx_lists.bled_mats)
# add textures
cmd_list.commands.extend(bleed_gfx_lists.bled_tex)
# add in triangles
cmd_list.commands.extend(tri_list.commands)
# skinned meshes don't draw tris sometimes, use this opportunity to save a sync
tri_cmds = [c for c in tri_list.commands if type(c) == SP1Triangle or type(c) == SP2Triangles]
if tri_cmds:
reset_cmd_dict[DPPipeSync] = DPPipeSync()
[bleed_gfx_lists.add_reset_cmd(cmd, reset_cmd_dict) for cmd in bleed_gfx_lists.bled_mats]
# pre processes cmd_list and removes cmds deemed useless. subclass and override if this causes a game specific issue
def on_bleed_start(self, cmd_list: GfxList):
@@ -334,82 +524,104 @@ class BleedGraphics:
def on_tri_group_bleed_end(self, triGroup: FTriGroup, last_mat: FMaterial, bleed_gfx_lists: BleedGfxLists):
return
def on_bleed_end(
def add_reset_cmds(
self,
last_mat: FMaterial,
cmd_list: GfxList,
fmesh_static_cmds: list[GbiMacro],
reset_cmd_dict: dict[GbiMacro],
default_render_mode: list[str] = None,
mat_write_method: GfxMatWriteMethod,
default_render_mode: tuple[str] = None,
):
if not cmd_list or not reset_cmd_dict or id(cmd_list) in self.reset_gfx_lists:
return False
# revert certain cmds for extra safety
reset_cmds = self.create_reset_cmds(reset_cmd_dict, default_render_mode)
# if pipe sync in reset list, make sure it is the first cmd
if DPPipeSync in reset_cmds:
reset_cmds.remove(DPPipeSync)
reset_cmds.insert(0, DPPipeSync)
reset_cmds = self.create_reset_cmds(reset_cmd_dict, mat_write_method, default_render_mode)
while SPEndDisplayList() in cmd_list.commands:
cmd_list.commands.remove(SPEndDisplayList())
cmd_list.commands.extend(reset_cmds)
cmd_list.commands.extend(fmesh_static_cmds) # this is troublesome
cmd_list.commands.append(SPEndDisplayList())
self.bled_gfx_lists[cmd_list] = last_mat
self.optimize_syncs(cmd_list)
self.reset_gfx_lists.add(id(cmd_list))
return True
# remove syncs if first material, or if no gsDP cmds in material
def optimize_syncs(self, bleed_gfx_lists: BleedGfxLists, bleed_state: int):
def optimize_syncs(self, cmd_list: GfxList):
no_syncs_needed = {"DPSetPrimColor", "DPSetPrimDepth"} # will not affect rdp
syncs_needed = {"SPSetOtherMode"} # will affect rdp
if bleed_state == self.bleed_start:
while DPPipeSync() in bleed_gfx_lists.bled_mats:
bleed_gfx_lists.bled_mats.remove(DPPipeSync())
for cmd in (*bleed_gfx_lists.bled_mats, *bleed_gfx_lists.bled_tex):
cmd_name = type(cmd).__name__
if cmd == DPPipeSync():
continue
if "DP" in cmd_name and cmd_name not in no_syncs_needed:
return
if cmd_name in syncs_needed:
return
while DPPipeSync() in bleed_gfx_lists.bled_mats:
bleed_gfx_lists.bled_mats.remove(DPPipeSync())
syncs_needed = {"SPSetOtherMode", "SPTexture"} # will affect rdp
def create_reset_cmds(self, reset_cmd_dict: dict[GbiMacro], default_render_mode: list[str]):
tri_buffered = True
last_load_sync = None
old_cmds = cmd_list.commands
new_cmds = []
cmd_list.commands = new_cmds
for cmd in old_cmds:
cmd_name = type(cmd).__name__
is_dp_cmd = ("DP" in cmd_name and cmd_name not in no_syncs_needed) or cmd_name in syncs_needed
if isinstance(cmd, (DPPipeSync, DPLoadSync, DPTileSync)):
continue
elif isinstance(cmd, (DPLoadBlock, DPLoadTile, DPLoadTLUTCmd, DPSetTile, DPSetTileSize)) and tri_buffered:
last_load_sync = len(new_cmds)
new_cmds.append(DPLoadSync())
tri_buffered = False
elif tri_buffered and is_dp_cmd:
tri_buffered = False
if last_load_sync is not None:
new_cmds[last_load_sync] = DPPipeSync()
last_load_sync = None
else:
new_cmds.append(DPPipeSync())
elif not is_dp_cmd and isinstance(cmd, (SP2Triangles, SP1Triangle, SPLine3D, SPLineW3D)):
tri_buffered = True
last_load_sync = None
new_cmds.append(cmd)
def create_reset_cmds(
self, reset_cmd_dict: dict[GbiMacro], mat_write_method: GfxMatWriteMethod, default_render_mode: list[str]
):
reset_cmds = []
for cmd_type, cmd_use in reset_cmd_dict.items():
if cmd_type == DPPipeSync:
reset_cmds.append(DPPipeSync())
# generally either loadgeo, or a combo of set/clear is used based on microcode selected
# if you are in f3d, any selection different from the default will add a set/clear
if cmd_type == SPLoadGeometryMode and cmd_use != self.default_load_geo:
reset_cmds.append(self.default_load_geo)
elif cmd_type == SPSetGeometryMode and cmd_use != self.default_set_geo:
reset_cmds.append(self.default_set_geo)
elif cmd_type == SPClearGeometryMode and cmd_use != self.default_clear_geo:
reset_cmds.append(self.default_clear_geo)
if cmd_type == SPGeometryMode: # revert cmd includes everything from the start
set_list, clear_list = cmd_use
if mat_write_method == GfxMatWriteMethod.WriteDifferingAndRevert:
clear_list = clear_list - self.default_clear_geo.flagList
set_list = set_list - self.default_set_geo.flagList
reset_cmds.extend(get_geo_cmds(clear_list, set_list, self.f3d.F3DEX_GBI_2, mat_write_method)[1])
elif clear_list != self.default_clear_geo.flagList or set_list != self.default_set_geo.flagList:
reset_cmds.append(self.default_load_geo)
elif cmd_type == "G_SETOTHERMODE_H":
if cmd_use != self.default_othermode_H:
reset_cmds.append(self.default_othermode_H)
# render mode takes up most bits of the lower half, so seeing high bit usage is enough to determine render mode was used
elif cmd_type == DPSetRenderMode or (cmd_type == "G_SETOTHERMODE_L" and cmd_use.length >= 31):
if default_render_mode:
reset_cmds.append(
SPSetOtherMode(
"G_SETOTHERMODE_L",
0,
32 - self.is_f3d_old,
[*self.default_othermode_L.flagList, *default_render_mode],
)
)
elif cmd_type == DPSetRenderMode:
if default_render_mode and cmd_use.flagList != default_render_mode:
reset_cmds.append(DPSetRenderMode(tuple(default_render_mode)))
elif cmd_type == "G_SETOTHERMODE_L":
if cmd_use != self.default_othermode_L:
reset_cmds.append(self.default_othermode_L)
flag_list = copy.copy(self.default_othermode_L.flagList)
if cmd_use.sets_rendermode(self.f3d):
flag_list.update(default_render_mode)
default_othermode_l = SPSetOtherMode(
"G_SETOTHERMODE_L",
0,
(32 if cmd_use.sets_rendermode(self.f3d) else 3) - self.is_f3d_old,
flag_list,
)
if cmd_use != default_othermode_l:
reset_cmds.append(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):
def bleed_individual_cmd(
self,
cmd_list: GfxList,
cmd: GbiMacro,
last_cmd_list: GfxList = None,
default_render_mode: tuple[str] = None,
):
# never bleed these cmds
if type(cmd) in [
SPMatrix,
@@ -434,44 +646,28 @@ class BleedGraphics:
]:
return False
# if no last list then calling func will own behavior of bleeding
if not last_cmd_list:
return self.bleed_self_conflict
if last_cmd_list is None:
if isinstance(cmd, SPSetOtherModeSub):
return cmd.mode == self.default_othermode_dict[type(cmd)]
elif isinstance(cmd, DPSetRenderMode):
return cmd.flagList == default_render_mode and cmd.blender is None
# 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:
return bleed_func(cmd_list, cmd, bleed_state, last_cmd_list)
return bleed_func(cmd_list, cmd, last_cmd_list)
else:
return cmd in last_cmd_list
return last_cmd_list is not None and cmd in last_cmd_list
# bleed these cmds only if it is the second call and cmd was in the last use list, or if they match world defaults and it is the first call
def bleed_SPLoadGeometryMode(
self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None
):
if bleed_state != self.bleed_start:
def bleed_SPLoadGeometryMode(self, cmd_list: GfxList, cmd: GbiMacro, last_cmd_list: GfxList = None):
if last_cmd_list is not None:
return cmd in last_cmd_list
else:
return cmd == self.default_load_geo
def bleed_SPSetGeometryMode(
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 == self.default_set_geo
def bleed_SPClearGeometryMode(
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 == self.default_clear_geo
def bleed_SPSetOtherMode(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None):
if bleed_state != self.bleed_start:
def bleed_SPSetOtherMode(self, cmd_list: GfxList, cmd: GbiMacro, last_cmd_list: GfxList = None):
if last_cmd_list is not None:
return cmd in last_cmd_list
else:
if cmd.cmd == "G_SETOTHERMODE_H":
@@ -480,43 +676,15 @@ class BleedGraphics:
return cmd == self.default_othermode_L
# Don´t bleed if the cmd is used for scrolling or if the last cmd's tags are not the same (those are not hashed)
def bleed_DPSetTileSize(self, _cmd_list: GfxList, cmd: GbiMacro, _bleed_state: int, last_cmd_list: GfxList = None):
def bleed_DPSetTileSize(self, _cmd_list: GfxList, cmd: GbiMacro, last_cmd_list: GfxList = None):
if cmd.tags == GfxTag.TileScroll0 or cmd.tags == GfxTag.TileScroll1:
return False
if cmd in last_cmd_list:
if last_cmd_list is not None and cmd in last_cmd_list:
last_size_cmd = last_cmd_list[last_cmd_list.index(cmd)]
if last_size_cmd.tags == cmd.tags:
return True
return False
# At most, only one sync is needed after drawing tris. The f3d writer should
# already have placed the appropriate sync type required. If a second sync is
# detected between drawing cmds, then remove that sync. Remove the latest sync
# not the first seen sync.
def bleed_DPTileSync(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None):
return self.bleed_between_tris(cmd_list, cmd, bleed_state, [DPLoadSync, DPPipeSync, DPTileSync])
def bleed_DPPipeSync(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None):
return self.bleed_between_tris(cmd_list, cmd, bleed_state, [DPLoadSync, DPPipeSync, DPTileSync])
def bleed_DPLoadSync(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, last_cmd_list: GfxList = None):
return self.bleed_between_tris(cmd_list, cmd, bleed_state, [DPLoadSync, DPPipeSync, DPTileSync])
def bleed_between_tris(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int, conflict_cmds: list[GbiMacro]):
tri_buffered = False
for parse_cmd in cmd_list.commands:
if parse_cmd is cmd:
return tri_buffered
if type(parse_cmd) in [SP2Triangles, SP1Triangle, SPLine3D, SPLineW3D]:
tri_buffered = False
continue
if type(parse_cmd) in conflict_cmds:
if not tri_buffered:
tri_buffered = True
else:
return True
return False
# small containers for data used in inline Gfx
@dataclass
@@ -524,20 +692,6 @@ class BleedGfxLists:
bled_mats: GfxList = field(default_factory=list)
bled_tex: GfxList = field(default_factory=list)
def add_reset_cmd(self, cmd: GbiMacro, reset_cmd_dict: dict[GbiMacro]):
reset_cmd_list = (
SPLoadGeometryMode,
SPSetGeometryMode,
SPClearGeometryMode,
DPSetRenderMode,
)
# separate other mode H and othermode L
if type(cmd) == SPSetOtherMode:
reset_cmd_dict[cmd.cmd] = cmd
if type(cmd) in reset_cmd_list:
reset_cmd_dict[type(cmd)] = cmd
# helper function used for sm64
def find_material_from_jump_cmd(
+107 -119
View File
@@ -3416,7 +3416,7 @@ class GbiMacro:
else:
return field.name
if hasattr(field, "__iter__") and type(field) is not str:
return " | ".join(field) if len(field) else "0"
return " | ".join(map(str, field)) if len(field) else "0"
if self._hex > 0 and isinstance(field, int):
temp = field if field >= 0 else (1 << (self._hex * 4)) + field
return f"{temp:#0{self._hex + 2}x}" # + 2 for the 0x part
@@ -4275,9 +4275,9 @@ def gsSPGeometryMode_Non_F3DEX_GBI_2(word, f3d):
return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big")
def geoFlagListToWord(flagList, f3d):
def geoFlagListToWord(flags: tuple, f3d: F3D):
word = 0
for name in flagList:
for name in flags:
if name in f3d.allGeomModeFlags:
word += getattr(f3d, name)
else:
@@ -4291,8 +4291,8 @@ def geoFlagListToWord(flagList, f3d):
@dataclass(unsafe_hash=True)
class SPGeometryMode(GbiMacro):
clearFlagList: list
setFlagList: list
clearFlagList: set[str] = field(default_factory=set)
setFlagList: set[str] = field(default_factory=set)
def to_binary(self, f3d, segments):
if f3d.F3DEX_GBI_2:
@@ -4306,7 +4306,7 @@ class SPGeometryMode(GbiMacro):
@dataclass(unsafe_hash=True)
class SPSetGeometryMode(GbiMacro):
flagList: list
flagList: set[str] = field(default_factory=set)
def to_binary(self, f3d, segments):
word = geoFlagListToWord(self.flagList, f3d)
@@ -4319,7 +4319,7 @@ class SPSetGeometryMode(GbiMacro):
@dataclass(unsafe_hash=True)
class SPClearGeometryMode(GbiMacro):
flagList: list
flagList: set[str] = field(default_factory=set)
def to_binary(self, f3d, segments):
word = geoFlagListToWord(self.flagList, f3d)
@@ -4332,7 +4332,7 @@ class SPClearGeometryMode(GbiMacro):
@dataclass(unsafe_hash=True)
class SPLoadGeometryMode(GbiMacro):
flagList: list
flagList: set[str]
def to_binary(self, f3d, segments):
word = geoFlagListToWord(self.flagList, f3d)
@@ -4350,12 +4350,54 @@ def gsSPSetOtherMode(cmd, sft, length, data, f3d):
return words[0].to_bytes(4, "big") + words[1].to_bytes(4, "big")
@dataclass(unsafe_hash=True)
class RendermodeBlender:
cycle1: tuple
cycle2: tuple
def __str__(self):
return f"GBL_c1({', '.join(self.cycle1)}) | GBL_c2({', '.join(self.cycle2)})"
def to_c(self, _static=True):
return str(self)
def to_binary(self, f3d):
return GBL_c1(*[getattr(f3d, str(x), x) for x in self.cycle1]) | GBL_c2(
*[getattr(f3d, str(x), x) for x in self.cycle2]
)
@dataclass(unsafe_hash=True)
class SPSetOtherMode(GbiMacro):
cmd: str
sft: int
length: int
flagList: list
flagList: set
def sets_rendermode(self, f3d):
return self.cmd == "G_SETOTHERMODE_L" and (self.sft + self.length) > (3 - f3d.F3D_OLD_GBI)
def extend(self, flags: Iterable | str):
flags = {flags} if isinstance(flags, str) else set(flags)
self.flagList = self.flagList | flags
def add_other(self, f3d, other: SPSetOtherMode):
min_max = min(self.sft, other.sft), max(self.sft + self.length, other.sft + other.length)
self.sft = min_max[0]
self.length = min_max[1] - min_max[0]
for flag in self.flagList.copy(): # remove any flag overriden by other
value = flag
if isinstance(flag, RendermodeBlender):
value = flag.to_binary(f3d)
elif isinstance(flag, str):
value = getattr(f3d, flag, None)
if value is None:
raise ValueError(f"Flag {flag} not found in {f3d}")
if not value or value >> other.sft < (2**other.length):
self.flagList.remove(flag)
# add other's flags
self.extend(other.flagList)
def to_binary(self, f3d, segments):
data = 0
@@ -4367,10 +4409,27 @@ class SPSetOtherMode(GbiMacro):
@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
@@ -4380,10 +4439,7 @@ class DPPipelineMode(GbiMacro):
@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
@@ -4397,10 +4453,7 @@ class DPSetCycleType(GbiMacro):
@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
@@ -4410,10 +4463,7 @@ class DPSetTexturePersp(GbiMacro):
@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
@@ -4425,10 +4475,7 @@ class DPSetTextureDetail(GbiMacro):
@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
@@ -4438,10 +4485,7 @@ class DPSetTextureLOD(GbiMacro):
@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
@@ -4455,10 +4499,7 @@ class DPSetTextureLUT(GbiMacro):
@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
@@ -4470,10 +4511,7 @@ class DPSetTextureFilter(GbiMacro):
@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
@@ -4485,10 +4523,7 @@ class DPSetTextureConvert(GbiMacro):
@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
@@ -4498,10 +4533,7 @@ class DPSetCombineKey(GbiMacro):
@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
@@ -4517,10 +4549,7 @@ class DPSetColorDither(GbiMacro):
@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
@@ -4534,10 +4563,7 @@ class DPSetAlphaDither(GbiMacro):
@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
@@ -4549,14 +4575,11 @@ class DPSetAlphaCompare(GbiMacro):
@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":
if self.mode == "G_ZS_PIXEL":
srcVal = f3d.G_ZS_PIXEL
elif self.src == "G_ZS_PRIM":
elif self.mode == "G_ZS_PRIM":
srcVal = f3d.G_ZS_PRIM
return gsSPSetOtherMode(f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_ZSRCSEL, 1, srcVal, f3d)
@@ -4579,37 +4602,20 @@ def GBL_c2(m1a, m1b, m2a, m2b):
@dataclass(unsafe_hash=True)
class DPSetRenderMode(GbiMacro):
flagList: set[str]
blender: Optional[RendermodeBlender] = None
# bl0-3 are string for each blender enum
def __init__(self, flagList, blendList):
self.flagList = flagList
self.use_preset = blendList is None
if not self.use_preset:
self.bl00 = blendList[0]
self.bl01 = blendList[1]
self.bl02 = blendList[2]
self.bl03 = blendList[3]
self.bl10 = blendList[4]
self.bl11 = blendList[5]
self.bl12 = blendList[6]
self.bl13 = blendList[7]
def getGBL_c(self, f3d):
bl00 = getattr(f3d, self.bl00)
bl01 = getattr(f3d, self.bl01)
bl02 = getattr(f3d, self.bl02)
bl03 = getattr(f3d, self.bl03)
bl10 = getattr(f3d, self.bl10)
bl11 = getattr(f3d, self.bl11)
bl12 = getattr(f3d, self.bl12)
bl13 = getattr(f3d, self.bl13)
return GBL_c1(bl00, bl01, bl02, bl03) | GBL_c2(bl10, bl11, bl12, bl13)
@property
def use_preset(self):
return self.blender is None
def to_binary(self, f3d, segments):
flagWord = renderFlagListToWord(self.flagList, f3d)
if not self.use_preset:
return gsSPSetOtherMode(
f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_RENDERMODE, 29, flagWord | self.getGBL_c(f3d), f3d
f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_RENDERMODE, 29, flagWord | self.blender.to_binary(f3d), f3d
)
else:
return gsSPSetOtherMode(f3d.G_SETOTHERMODE_L, f3d.G_MDSFT_RENDERMODE, 29, flagWord, f3d)
@@ -4618,25 +4624,7 @@ class DPSetRenderMode(GbiMacro):
data = "gsDPSetRenderMode(" if static else "gDPSetRenderMode(glistp++, "
if not self.use_preset:
data += (
"GBL_c1("
+ self.bl00
+ ", "
+ self.bl01
+ ", "
+ self.bl02
+ ", "
+ self.bl03
+ ") | GBL_c2("
+ self.bl10
+ ", "
+ self.bl11
+ ", "
+ self.bl12
+ ", "
+ self.bl13
+ "), "
)
data += self.blender.to_c(static) + ", "
for name in self.flagList:
data += name + " | "
return data[:-3] + ")"
@@ -4859,8 +4847,8 @@ class SPLightToFogColor(GbiMacro):
@dataclass(unsafe_hash=True)
class DPSetOtherMode(GbiMacro):
mode0: list
mode1: list
mode0: set[str]
mode1: set[str]
def to_binary(self, f3d, segments):
mode0 = mode1 = 0
@@ -4914,10 +4902,10 @@ class DPSetTile(GbiMacro):
tmem: int
tile: int
palette: int
cmt: list
cmt: tuple[str, str]
maskt: int
shiftt: int
cms: list
cms: tuple[str, str]
masks: int
shifts: int
@@ -4982,8 +4970,8 @@ class DPLoadTextureBlock(GbiMacro):
width: int
height: int
pal: int
cms: list
cmt: list
cms: tuple[str, str]
cmt: tuple[str, str]
masks: int
maskt: int
shifts: int
@@ -5055,8 +5043,8 @@ class DPLoadTextureBlockYuv(GbiMacro):
width: int
height: int
pal: int
cms: list
cmt: list
cms: tuple[str, str]
cmt: tuple[str, str]
masks: int
maskt: int
shifts: int
@@ -5134,8 +5122,8 @@ class _DPLoadTextureBlock(GbiMacro):
width: int
height: int
pal: int
cms: list
cmt: list
cms: tuple[str, str]
cmt: tuple[str, str]
masks: int
maskt: int
shifts: int
@@ -5212,8 +5200,8 @@ class DPLoadTextureBlock_4b(GbiMacro):
width: int
height: int
pal: int
cms: list
cmt: list
cms: tuple[str, str]
cmt: tuple[str, str]
masks: int
maskt: int
shifts: int
@@ -5287,8 +5275,8 @@ class DPLoadTextureTile(GbiMacro):
lrs: int
lrt: int
pal: int
cms: list
cmt: list
cms: tuple[str, str]
cmt: tuple[str, str]
masks: int
maskt: int
shifts: int
@@ -5363,8 +5351,8 @@ class DPLoadTextureTile_4b(GbiMacro):
lrs: int
lrt: int
pal: int
cms: list
cmt: list
cms: tuple[str, str]
cmt: tuple[str, str]
masks: int
maskt: int
shifts: int
+3 -3
View File
@@ -406,7 +406,7 @@ def getTileSize(value, f3d):
def getTileClampMirror(value, f3d):
data = math_eval(value, f3d)
return [(data & f3d.G_TX_CLAMP) != 0, (data & f3d.G_TX_MIRROR) != 0]
return ((data & f3d.G_TX_CLAMP) != 0, (data & f3d.G_TX_MIRROR) != 0)
def getTileMask(value, f3d):
@@ -496,7 +496,7 @@ class F3DContext:
# This macro has all the tile setting properties, so we reuse it
self.tileSettings: list[DPSetTile] = [
DPSetTile("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 5, 0, i, 0, [False, False], 0, 0, [False, False], 0, 0)
DPSetTile("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 5, 0, i, 0, (False, False), 0, 0, (False, False), 0, 0)
for i in range(8)
]
self.tileSizes: list[DPSetTileSize] = [DPSetTileSize(i, 0, 0, 32, 32) for i in range(8)]
@@ -579,7 +579,7 @@ class F3DContext:
self.tmemDict = {}
self.tileSettings = [
DPSetTile("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 5, 0, i, 0, [False, False], 0, 0, [False, False], 0, 0)
DPSetTile("G_IM_FMT_RGBA", "G_IM_SIZ_16b", 5, 0, i, 0, (False, False), 0, 0, (False, False), 0, 0)
for i in range(8)
]
+5 -5
View File
@@ -204,7 +204,7 @@ def maybeSaveSingleLargeTextureSetup(
# SL, SH is * 2 for 4 bit and * 4 otherwise, because actually loading
# 8 bit pairs of texels. Also written using f3d.G_TEXTURE_IMAGE_FRAC.
sm = 2 if is4bit else 4
nocm = ["G_TX_WRAP", "G_TX_NOMIRROR"]
nocm = ("G_TX_WRAP", "G_TX_NOMIRROR")
if curImgSet != i:
gfxOut.commands.append(DPSetTextureImage(fmt, siz, wid, fImage))
@@ -970,7 +970,7 @@ def saveTextureLoadOnly(
):
fmt = texFormatOf[texProp.tex_format]
siz = texBitSizeF3D[texProp.tex_format]
nocm = ["G_TX_WRAP", "G_TX_NOMIRROR"]
nocm = ("G_TX_WRAP", "G_TX_NOMIRROR")
SL, TL, SH, TH, sl, tl, sh, th = getTileSizeSettings(texProp, tileSettings, f3d)
# LoadTile will pad rows to 64 bit word alignment, while
@@ -1040,8 +1040,8 @@ def saveTextureTile(
mask_T = texProp.T.mask
shift_S = texProp.S.shift
shift_T = texProp.T.shift
cms = [("G_TX_CLAMP" if clamp_S else "G_TX_WRAP"), ("G_TX_MIRROR" if mirror_S else "G_TX_NOMIRROR")]
cmt = [("G_TX_CLAMP" if clamp_T else "G_TX_WRAP"), ("G_TX_MIRROR" if mirror_T else "G_TX_NOMIRROR")]
cms = (("G_TX_CLAMP" if clamp_S else "G_TX_WRAP"), ("G_TX_MIRROR" if mirror_S else "G_TX_NOMIRROR"))
cmt = (("G_TX_CLAMP" if clamp_T else "G_TX_WRAP"), ("G_TX_MIRROR" if mirror_T else "G_TX_NOMIRROR"))
masks = mask_S
maskt = mask_T
shifts = shift_S if shift_S >= 0 else (shift_S + 16)
@@ -1081,7 +1081,7 @@ def savePaletteLoad(
):
assert 0 <= palAddr < 256 and (palAddr & 0xF) == 0
palFmt = texFormatOf[palFormat]
nocm = ["G_TX_WRAP", "G_TX_NOMIRROR"]
nocm = ("G_TX_WRAP", "G_TX_NOMIRROR")
gfxOut.commands.extend(
[
DPSetTextureImage(palFmt, "G_IM_SIZ_16b", 1, fPalette),
+61 -108
View File
@@ -16,7 +16,7 @@ from .f3d_material import (
)
from .f3d_texture_writer import MultitexManager, TileLoad, maybeSaveSingleLargeTextureSetup
from .f3d_gbi import *
from .f3d_bleed import BleedGraphics
from .f3d_bleed import BleedGraphics, get_geo_cmds
from ..utility import *
@@ -518,18 +518,18 @@ def addCullCommand(obj, fMesh, transformMatrix, matWriteMethod):
defaults = create_or_get_world(bpy.context.scene).rdp_defaults
if defaults.g_lighting:
cullCommands = [
SPClearGeometryMode(["G_LIGHTING"]),
SPClearGeometryMode({"G_LIGHTING"}),
SPVertex(fMesh.cullVertexList, 0, 8, 0),
SPSetGeometryMode(["G_LIGHTING"]),
SPSetGeometryMode({"G_LIGHTING"}),
SPCullDisplayList(0, 7),
]
else:
cullCommands = [SPVertex(fMesh.cullVertexList, 0, 8, 0), SPCullDisplayList(0, 7)]
elif matWriteMethod == GfxMatWriteMethod.WriteAll:
cullCommands = [
SPClearGeometryMode(["G_LIGHTING"]),
SPClearGeometryMode({"G_LIGHTING"}),
SPVertex(fMesh.cullVertexList, 0, 8, 0),
SPSetGeometryMode(["G_LIGHTING"]),
SPSetGeometryMode({"G_LIGHTING"}),
SPCullDisplayList(0, 7),
]
else:
@@ -1056,10 +1056,10 @@ class TriangleConverter:
if usesDecal:
if not wroteOpaque:
wroteOpaque = True
self.triList.commands.append(SPSetOtherMode("G_SETOTHERMODE_L", 10, 2, ["ZMODE_OPA"]))
self.triList.commands.append(SPSetOtherMode("G_SETOTHERMODE_L", 10, 2, {"ZMODE_OPA"}))
if not wroteDecal and (darker and wroteDarker or not darker and wroteLighter):
wroteDecal = True
self.triList.commands.append(SPSetOtherMode("G_SETOTHERMODE_L", 10, 2, ["ZMODE_DEC"]))
self.triList.commands.append(SPSetOtherMode("G_SETOTHERMODE_L", 10, 2, {"ZMODE_DEC"}))
if darker:
wroteDarker = True
else:
@@ -1323,10 +1323,7 @@ def saveOrGetF3DMaterial(material, fModel, obj, drawLayer, convertTextureData):
useDict = all_combiner_uses(f3dMat)
defaults = create_or_get_world(bpy.context.scene).rdp_defaults
if fModel.f3d.F3DEX_GBI_2:
saveGeoModeDefinitionF3DEX2(fMaterial, f3dMat.rdp_settings, defaults, fModel.matWriteMethod)
else:
saveGeoModeDefinition(fMaterial, f3dMat.rdp_settings, defaults, fModel.matWriteMethod)
saveGeoModeDefinition(fMaterial, f3dMat.rdp_settings, defaults, fModel.matWriteMethod, fModel.f3d.F3DEX_GBI_2)
# Checking for f3dMat.rdp_settings.g_lighting here will prevent accidental exports,
# There may be some edge case where this isn't desired.
@@ -1585,20 +1582,12 @@ def addLightDefinition(f3d_light, fLights):
)
def saveBitGeoF3DEX2(value, defaultValue, flagName, geo, matWriteMethod):
def saveBitGeo(value, defaultValue, flagName, set_modes: list[str], clear_modes: list[str], matWriteMethod):
if value != defaultValue or matWriteMethod == GfxMatWriteMethod.WriteAll:
if value:
geo.setFlagList.append(flagName)
set_modes.append(flagName)
else:
geo.clearFlagList.append(flagName)
def saveBitGeo(value, defaultValue, flagName, setGeo, clearGeo, matWriteMethod):
if value != defaultValue or matWriteMethod == GfxMatWriteMethod.WriteAll:
if value:
setGeo.flagList.append(flagName)
else:
clearGeo.flagList.append(flagName)
clear_modes.append(flagName)
def saveGeoModeCommon(saveFunc: Callable, settings: RDPSettings, defaults: RDPSettings, args: Any):
@@ -1625,37 +1614,15 @@ def saveGeoModeCommon(saveFunc: Callable, settings: RDPSettings, defaults: RDPSe
saveFunc(settings.g_clipping, defaults.g_clipping, "G_CLIPPING", *args)
def saveGeoModeDefinitionF3DEX2(fMaterial, settings, defaults, matWriteMethod):
geo = SPGeometryMode([], [])
saveGeoModeCommon(saveBitGeoF3DEX2, settings, defaults, (geo, matWriteMethod))
def saveGeoModeDefinition(fMaterial, settings, defaults, matWriteMethod, is_ex2: bool):
set_modes = []
clear_modes = []
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")
saveGeoModeCommon(saveBitGeo, settings, defaults, (set_modes, clear_modes, matWriteMethod))
if matWriteMethod == GfxMatWriteMethod.WriteAll:
fMaterial.mat_only_DL.commands.append(SPLoadGeometryMode(geo.setFlagList))
else:
fMaterial.mat_only_DL.commands.append(geo)
fMaterial.revert.commands.append(SPGeometryMode(geo.setFlagList, geo.clearFlagList))
def saveGeoModeDefinition(fMaterial, settings, defaults, matWriteMethod):
setGeo = SPSetGeometryMode([])
clearGeo = SPClearGeometryMode([])
saveGeoModeCommon(saveBitGeo, settings, defaults, (setGeo, clearGeo, matWriteMethod))
if len(setGeo.flagList) > 0:
fMaterial.mat_only_DL.commands.append(setGeo)
if matWriteMethod == GfxMatWriteMethod.WriteDifferingAndRevert:
fMaterial.revert.commands.append(SPClearGeometryMode(setGeo.flagList))
if len(clearGeo.flagList) > 0:
fMaterial.mat_only_DL.commands.append(clearGeo)
if matWriteMethod == GfxMatWriteMethod.WriteDifferingAndRevert:
fMaterial.revert.commands.append(SPSetGeometryMode(clearGeo.flagList))
material, revert = get_geo_cmds(clear_modes, set_modes, is_ex2, matWriteMethod)
fMaterial.mat_only_DL.commands.extend(material)
fMaterial.revert.commands.extend(revert)
def saveModeSetting(fMaterial, value, defaultValue, cmdClass):
@@ -1674,18 +1641,18 @@ def saveOtherModeHDefinition(fMaterial, settings, tlut, defaults, matWriteMethod
def saveOtherModeHDefinitionAll(fMaterial, settings, tlut, defaults, f3d):
cmd = SPSetOtherMode("G_SETOTHERMODE_H", 4, 20 - f3d.F3D_OLD_GBI, [])
cmd.flagList.append(settings.g_mdsft_alpha_dither)
cmd.flagList.append(settings.g_mdsft_rgb_dither)
cmd.flagList.append(settings.g_mdsft_combkey)
cmd.flagList.append(settings.g_mdsft_textconv)
cmd.flagList.append(settings.g_mdsft_text_filt)
cmd.flagList.append(tlut)
cmd.flagList.append(settings.g_mdsft_textlod)
cmd.flagList.append(settings.g_mdsft_textdetail)
cmd.flagList.append(settings.g_mdsft_textpersp)
cmd.flagList.append(settings.g_mdsft_cycletype)
cmd.flagList.append(settings.g_mdsft_pipeline)
cmd = SPSetOtherMode("G_SETOTHERMODE_H", 4, 20 - f3d.F3D_OLD_GBI, set())
cmd.flagList.add(settings.g_mdsft_alpha_dither)
cmd.flagList.add(settings.g_mdsft_rgb_dither)
cmd.flagList.add(settings.g_mdsft_combkey)
cmd.flagList.add(settings.g_mdsft_textconv)
cmd.flagList.add(settings.g_mdsft_text_filt)
cmd.flagList.add(tlut)
cmd.flagList.add(settings.g_mdsft_textlod)
cmd.flagList.add(settings.g_mdsft_textdetail)
cmd.flagList.add(settings.g_mdsft_textpersp)
cmd.flagList.add(settings.g_mdsft_cycletype)
cmd.flagList.add(settings.g_mdsft_pipeline)
fMaterial.mat_only_DL.commands.append(cmd)
@@ -1706,39 +1673,40 @@ def saveOtherModeHDefinitionIndividual(fMaterial, settings, tlut, defaults):
def saveOtherModeLDefinition(fMaterial, settings, defaults, defaultRenderMode, matWriteMethod, f3d):
if matWriteMethod == GfxMatWriteMethod.WriteAll:
saveOtherModeLDefinitionAll(fMaterial, settings, defaults, f3d)
saveOtherModeLDefinitionAll(fMaterial, settings, defaults, defaultRenderMode, f3d)
elif matWriteMethod == GfxMatWriteMethod.WriteDifferingAndRevert:
saveOtherModeLDefinitionIndividual(fMaterial, settings, defaults, defaultRenderMode)
else:
raise PluginError("Unhandled material write method: " + str(matWriteMethod))
def saveOtherModeLDefinitionAll(fMaterial: FMaterial, settings, defaults, f3d):
baseLength = 3 if not settings.set_rendermode else 32
cmd = SPSetOtherMode("G_SETOTHERMODE_L", 0, baseLength - f3d.F3D_OLD_GBI, [])
cmd.flagList.append(settings.g_mdsft_alpha_compare)
cmd.flagList.append(settings.g_mdsft_zsrcsel)
if settings.set_rendermode:
flagList, blendList = getRenderModeFlagList(settings, fMaterial)
cmd.flagList.extend(flagList)
if blendList is not None:
cmd.flagList.extend(
[
"GBL_c1(" + blendList[0] + ", " + blendList[1] + ", " + blendList[2] + ", " + blendList[3] + ")",
"GBL_c2(" + blendList[4] + ", " + blendList[5] + ", " + blendList[6] + ", " + blendList[7] + ")",
]
)
fMaterial.mat_only_DL.commands.append(cmd)
def saveOtherModeLDefinitionAll(fMaterial: FMaterial, settings, defaults, defaultRenderMode, f3d):
cmd = SPSetOtherMode("G_SETOTHERMODE_L", 0, (32 if settings.set_rendermode else 3) - f3d.F3D_OLD_GBI, set())
cmd.flagList.add(settings.g_mdsft_alpha_compare)
cmd.flagList.add(settings.g_mdsft_zsrcsel)
if settings.g_mdsft_zsrcsel == "G_ZS_PRIM":
fMaterial.mat_only_DL.commands.append(DPSetPrimDepth(z=settings.prim_depth.z, dz=settings.prim_depth.dz))
if settings.set_rendermode:
if defaultRenderMode:
revert_cmd = SPSetOtherMode(
"G_SETOTHERMODE_L",
0,
32 - f3d.F3D_OLD_GBI,
{*defaultRenderMode, defaults.g_mdsft_alpha_compare, defaults.g_mdsft_zsrcsel},
)
fMaterial.revert.commands.append(revert_cmd)
flagList, blender = getRenderModeFlagList(settings, fMaterial)
cmd.flagList.update(flagList)
if blender is not None:
cmd.flagList.add(blender)
fMaterial.mat_only_DL.commands.append(cmd)
def saveOtherModeLDefinitionIndividual(fMaterial, settings, defaults, defaultRenderMode):
saveModeSetting(fMaterial, settings.g_mdsft_alpha_compare, defaults.g_mdsft_alpha_compare, DPSetAlphaCompare)
saveModeSetting(fMaterial, settings.g_mdsft_zsrcsel, defaults.g_mdsft_zsrcsel, DPSetDepthSource)
if settings.g_mdsft_zsrcsel == "G_ZS_PRIM":
@@ -1746,8 +1714,8 @@ def saveOtherModeLDefinitionIndividual(fMaterial, settings, defaults, defaultRen
fMaterial.revert.commands.append(DPSetPrimDepth())
if settings.set_rendermode:
flagList, blendList = getRenderModeFlagList(settings, fMaterial)
renderModeSet = DPSetRenderMode(flagList, blendList)
flagList, blender = getRenderModeFlagList(settings, fMaterial)
renderModeSet = DPSetRenderMode(flagList, blender)
fMaterial.mat_only_DL.commands.append(renderModeSet)
if defaultRenderMode is not None:
@@ -1756,7 +1724,7 @@ def saveOtherModeLDefinitionIndividual(fMaterial, settings, defaults, defaultRen
def getRenderModeFlagList(settings, fMaterial):
flagList = []
blendList = None
blender = None
# cycle independent
if not settings.rendermode_advanced_enabled:
@@ -1773,28 +1741,13 @@ def getRenderModeFlagList(settings, fMaterial):
cycle2 = "G_RM_NOOP"
flagList = [settings.rendermode_preset_cycle_1, cycle2]
else:
cycle1 = (settings.blend_p1, settings.blend_a1, settings.blend_m1, settings.blend_b1)
if settings.g_mdsft_cycletype == "G_CYC_2CYCLE":
blendList = [
settings.blend_p1,
settings.blend_a1,
settings.blend_m1,
settings.blend_b1,
settings.blend_p2,
settings.blend_a2,
settings.blend_m2,
settings.blend_b2,
]
blender = RendermodeBlender(
cycle1, (settings.blend_p2, settings.blend_a2, settings.blend_m2, settings.blend_b2)
)
else:
blendList = [
settings.blend_p1,
settings.blend_a1,
settings.blend_m1,
settings.blend_b1,
settings.blend_p1,
settings.blend_a1,
settings.blend_m1,
settings.blend_b1,
]
blender = RendermodeBlender(cycle1, cycle1)
if settings.aa_en:
flagList.append("AA_EN")
@@ -1817,7 +1770,7 @@ def getRenderModeFlagList(settings, fMaterial):
if settings.force_bl:
flagList.append("FORCE_BL")
return flagList, blendList
return tuple(flagList), blender
def saveOtherDefinition(fMaterial, material, defaults):
@@ -1854,7 +1807,7 @@ def getWriteMethodFromEnum(enumVal):
def exportF3DtoC(dirPath, obj, DLFormat, transformMatrix, texDir, savePNG, texSeparate, name, matWriteMethod):
inline = bpy.context.scene.exportInlineF3D
fModel = FModel(name, DLFormat, matWriteMethod if not inline else GfxMatWriteMethod.WriteAll)
fModel = FModel(name, DLFormat, matWriteMethod)
fMeshes = exportF3DCommon(obj, fModel, transformMatrix, True, name, DLFormat, not savePNG)
if inline:
+1 -1
View File
@@ -121,7 +121,7 @@ class OOTModel(FModel):
defaultRenderModes = create_or_get_world(bpy.context.scene).ootDefaultRenderModes
cycle1 = getattr(defaultRenderModes, drawLayerUsed.lower() + "Cycle1")
cycle2 = getattr(defaultRenderModes, drawLayerUsed.lower() + "Cycle2")
return [cycle1, cycle2]
return (cycle1, cycle2)
def addFlipbookWithRepeatCheck(self, flipbook: TextureFlipbook):
model = self.getFlipbookOwner()
@@ -81,6 +81,10 @@ class SM64_Properties(PropertyGroup):
name="Matstack Fix",
description="Exports account for matstack fix requirements",
)
write_all: BoolProperty(
name="Write All",
description="Write single load geo and set othermode commands instead of writting the difference to defaults. Can result in smaller displaylists but may introduce issues",
)
@property
def binary_export(self):
@@ -90,6 +94,12 @@ class SM64_Properties(PropertyGroup):
def abs_decomp_path(self) -> Path:
return Path(abspath(self.decomp_path))
@property
def gfx_write_method(self):
from ...f3d.f3d_gbi import GfxMatWriteMethod
return GfxMatWriteMethod.WriteAll if self.write_all else GfxMatWriteMethod.WriteDifferingAndRevert
@staticmethod
def upgrade_changed_props():
old_scene_props_to_new = {
@@ -145,6 +155,7 @@ class SM64_Properties(PropertyGroup):
data["compression_format"] = self.compression_format
data["force_extended_ram"] = self.force_extended_ram
data["matstack_fix"] = self.matstack_fix
data["write_all"] = self.write_all
return data
def from_repo_settings(self, data: dict):
@@ -152,6 +163,7 @@ class SM64_Properties(PropertyGroup):
set_prop_if_in_data(self, "compression_format", data, "compression_format")
set_prop_if_in_data(self, "force_extended_ram", data, "force_extended_ram")
set_prop_if_in_data(self, "matstack_fix", data, "matstack_fix")
set_prop_if_in_data(self, "write_all", data, "write_all")
def draw_repo_settings(self, layout: UILayout):
col = layout.column()
@@ -161,6 +173,7 @@ class SM64_Properties(PropertyGroup):
prop_split(col, self, "refresh_version", "Refresh (Function Map)")
col.prop(self, "force_extended_ram")
col.prop(self, "matstack_fix")
col.prop(self, "write_all")
def draw_props(self, layout: UILayout, show_repo_settings: bool = True):
col = layout.column()
+7 -19
View File
@@ -119,7 +119,7 @@ class SM64Model(FModel):
world = create_or_get_world(bpy.context.scene)
cycle1 = getattr(world, "draw_layer_" + str(drawLayer) + "_cycle_1")
cycle2 = getattr(world, "draw_layer_" + str(drawLayer) + "_cycle_2")
return [cycle1, cycle2]
return (cycle1, cycle2)
class SM64GfxFormatter(GfxFormatter):
@@ -320,8 +320,8 @@ def exportTexRectCommon(texProp, name, convertTextureData):
saveModeSetting(fMaterial, "G_AC_THRESHOLD", defaults.g_mdsft_alpha_compare, DPSetAlphaCompare)
fMaterial.mat_only_DL.commands.append(DPSetBlendColor(0xFF, 0xFF, 0xFF, 0xFF))
fMaterial.mat_only_DL.commands.append(DPSetRenderMode(["G_RM_AA_XLU_SURF", "G_RM_AA_XLU_SURF2"], None))
fMaterial.revert.commands.append(DPSetRenderMode(["G_RM_AA_ZB_OPA_SURF", "G_RM_AA_ZB_OPA_SURF2"], None))
fMaterial.mat_only_DL.commands.append(DPSetRenderMode(("G_RM_AA_XLU_SURF", "G_RM_AA_XLU_SURF2"), None))
fMaterial.revert.commands.append(DPSetRenderMode(("G_RM_AA_ZB_OPA_SURF", "G_RM_AA_ZB_OPA_SURF2"), None))
saveModeSetting(fMaterial, texProp.tlut_mode, defaults.g_mdsft_textlut, DPSetTextureLUT)
ti = TexInfo()
@@ -370,7 +370,7 @@ def sm64ExportF3DtoC(
fModel = SM64Model(
name,
DLFormat,
GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll,
bpy.context.scene.fast64.sm64.gfx_write_method,
)
fMeshes = exportF3DCommon(obj, fModel, transformMatrix, includeChildren, name, DLFormat, not savePNG)
@@ -492,11 +492,7 @@ def sm64ExportF3DtoC(
def exportF3DtoBinary(romfile, exportRange, transformMatrix, obj, segmentData, includeChildren):
inline = bpy.context.scene.exportInlineF3D
fModel = SM64Model(
obj.name,
DLFormat.Static,
GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll,
)
fModel = SM64Model(obj.name, DLFormat, bpy.context.scene.fast64.sm64.gfx_write_method)
fMeshes = exportF3DCommon(obj, fModel, transformMatrix, includeChildren, obj.name, DLFormat.Static, True)
if inline:
@@ -522,11 +518,7 @@ def exportF3DtoBinary(romfile, exportRange, transformMatrix, obj, segmentData, i
def exportF3DtoBinaryBank0(romfile, exportRange, transformMatrix, obj, RAMAddr, includeChildren):
inline = bpy.context.scene.exportInlineF3D
fModel = SM64Model(
obj.name,
DLFormat.Static,
GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll,
)
fModel = SM64Model(obj.name, DLFormat, bpy.context.scene.fast64.sm64.gfx_write_method)
fMeshes = exportF3DCommon(obj, fModel, transformMatrix, includeChildren, obj.name, DLFormat.Static, True)
if inline:
@@ -554,11 +546,7 @@ def exportF3DtoBinaryBank0(romfile, exportRange, transformMatrix, obj, RAMAddr,
def exportF3DtoInsertableBinary(filepath, transformMatrix, obj, includeChildren):
inline = bpy.context.scene.exportInlineF3D
fModel = SM64Model(
obj.name,
DLFormat.Static,
GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll,
)
fModel = SM64Model(obj.name, DLFormat, bpy.context.scene.fast64.sm64.gfx_write_method)
fMeshes = exportF3DCommon(obj, fModel, transformMatrix, includeChildren, obj.name, DLFormat.Static, True)
if inline:
+25 -6
View File
@@ -82,6 +82,7 @@ enumMatOverrideOptions = [
def drawGeoInfo(panel: Panel, bone: Bone):
bone_props: "SM64_BoneProperties" = bone.fast64.sm64
panel.layout.box().label(text="Geolayout Inspector")
if bone is None:
panel.layout.label(text="Edit geolayout properties in Pose mode.")
@@ -91,6 +92,15 @@ def drawGeoInfo(panel: Panel, bone: Bone):
prop_split(col, bone, "geo_cmd", "Geolayout Command")
if bpy.context.scene.exportInlineF3D:
revert_split = col.split(factor=0.4)
revert_split.label(text="Revert Material")
revert_row = revert_split.row()
revert_row.prop(
bone_props,
"revert_before_func" if bone.geo_cmd in {"Function", "HeldObject"} else "revert_previous_mat",
text="Previous",
)
if bone.geo_cmd in [
"TranslateRotate",
"Translate",
@@ -102,6 +112,8 @@ def drawGeoInfo(panel: Panel, bone: Bone):
"CustomAnimated",
]:
drawLayerWarningBox(col, bone, "draw_layer")
if bpy.context.scene.exportInlineF3D:
revert_row.prop(bone_props, "revert_after_mat", text="After")
if bone.geo_cmd == "Scale":
prop_split(col, bone, "geo_scale", "Scale")
@@ -138,9 +150,9 @@ def drawGeoInfo(panel: Panel, bone: Bone):
prop_split(col, bone, "culling_radius", "Culling Radius")
elif bone.geo_cmd in {"CustomAnimated", "CustomNonAnimated"}:
prop_split(col, bone.fast64.sm64, "custom_geo_cmd_macro", "Geo Command Macro")
prop_split(col, bone_props, "custom_geo_cmd_macro", "Geo Command Macro")
if bone.geo_cmd == "CustomNonAnimated":
prop_split(col, bone.fast64.sm64, "custom_geo_cmd_args", "Geo Command Args")
prop_split(col, bone_props, "custom_geo_cmd_args", "Geo Command Args")
else: # It's animated
infobox = col.box()
infobox.label(text="Command's args will be filled with layer, translate, and rotate", icon="INFO")
@@ -251,10 +263,10 @@ class GeolayoutObjectPanel(Panel):
prop_split(col, geo_asm, "param", "Parameter")
col.prop(obj, "ignore_render")
col.prop(obj, "ignore_collision")
if bpy.context.scene.f3d_type == "F3DEX3":
box.prop(obj, "is_occlusion_planes")
if obj.is_occlusion_planes and (not obj.ignore_render or not obj.ignore_collision):
box.label(icon="INFO", text="Suggest Ignore Render & Ignore Collision.")
# if bpy.context.scene.f3d_type == "F3DEX3":
# box.prop(obj, "is_occlusion_planes")
# if obj.is_occlusion_planes and (not obj.ignore_render or not obj.ignore_collision):
# box.label(icon="INFO", text="Suggest Ignore Render & Ignore Collision.")
if context.scene.exportInlineF3D:
col.prop(obj, "bleed_independently")
if obj_scale_is_unified(obj) and len(obj.modifiers) == 0:
@@ -463,6 +475,13 @@ class SM64_BoneProperties(PropertyGroup):
custom_geo_cmd_macro: StringProperty(name="Geo Command Macro", default="GEO_BONE")
custom_geo_cmd_args: StringProperty(name="Geo Command Args", default="")
revert_previous_mat: BoolProperty(name="Revert Previous Material", default=False)
revert_after_mat: BoolProperty(
name="Revert After Material",
default=False,
description="If disabled the last material of each layer will still be reverted at the end",
)
revert_before_func: BoolProperty(name="Revert Before Function", default=True)
sm64_bone_classes = (
+71 -22
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import bpy
from struct import pack
from copy import copy
from copy import copy, deepcopy
from ..utility import (
PluginError,
@@ -19,7 +19,7 @@ from ..utility import (
geoNodeRotateOrder,
)
from ..f3d.f3d_bleed import BleedGraphics
from ..f3d.f3d_gbi import FModel
from ..f3d.f3d_gbi import FMaterial, FModel, GbiMacro, GfxList
from .sm64_geolayout_constants import (
nodeGroupCmds,
@@ -269,7 +269,6 @@ class BaseDisplayListNode:
"""Base displaylist node with common helper functions dealing with displaylists"""
dl_ext = "WITH_DL" # add dl_ext to geo command if command has a displaylist
bleed_independently = False # base behavior, can be changed with obj boolProp
def get_dl_address(self):
assert self.dlRef is None, "dlRef not implemented in binary"
@@ -306,6 +305,9 @@ class TransformNode:
self.parent = None
self.skinned = False
self.skinnedWithoutDL = False
# base behavior, can be changed with obj boolProp
self.revert_previous_mat = False
self.revert_after_mat = False
def convertToDynamic(self):
if self.node.hasDL:
@@ -470,46 +472,93 @@ class JumpNode:
return "GEO_BRANCH(" + ("1, " if self.storeReturn else "0, ") + geo_name + "),"
LastMaterials = dict[int, tuple[FMaterial | None, list[tuple[GfxList, dict[type, GbiMacro]]]]]
class GeoLayoutBleed(BleedGraphics):
def bleed_geo_layout_graph(self, fModel: FModel, geo_layout_graph: GeolayoutGraph, use_rooms: bool = False):
last_materials = dict() # last used material should be kept track of per layer
# last used material, last used cmd list and resets per layer
last_materials = {}
def walk(node, last_materials):
def copy_last(last_materials: LastMaterials) -> LastMaterials:
return {dl: [lm, [(c, deepcopy(r)) for c, r in lcr]] for dl, (lm, lcr) in last_materials.items()}
def reset_layer(last_materials: LastMaterials, draw_layer: int) -> LastMaterials:
_, cmds_resets = last_materials.get(draw_layer, (None, []))
for i, (cmd_list, reset_cmd_dict) in enumerate(copy(cmds_resets)):
# only discard reset if the reset was actually applied
if self.add_reset_cmds(
cmd_list, reset_cmd_dict, fModel.matWriteMethod, fModel.getRenderMode(draw_layer)
):
cmds_resets[i] = None
cmds_resets = [cr for cr in cmds_resets if cr is not None]
if not cmds_resets:
last_materials.pop(draw_layer, 0)
return last_materials
def reset_all_layers(last_materials: LastMaterials) -> LastMaterials:
for draw_layer in copy(list(last_materials.keys())):
last_materials = reset_layer(last_materials, draw_layer)
return {}
def walk(node, last_materials: LastMaterials) -> LastMaterials:
last_materials = copy_last(last_materials)
base_node = node.node
if type(base_node) == JumpNode:
if base_node.geolayout:
for node in base_node.geolayout.nodes:
last_materials = (
walk(node, last_materials if not use_rooms else dict()) if not use_rooms else dict()
)
else:
last_materials = dict()
last_materials = walk(node, last_materials)
fMesh = getattr(base_node, "fMesh", None)
last_mat, last_cmds_resets = None, []
if fMesh is not None:
last_mat, last_cmds_resets = last_materials.get(base_node.drawLayer, (None, []))
if node.revert_previous_mat:
if fMesh is not None:
# add reset commands to previous cmd lists, reset last mat and reset dict
last_materials = reset_layer(last_materials, base_node.drawLayer)
else:
last_materials = reset_all_layers(last_materials)
last_mat, last_cmds_resets = None, []
if fMesh:
base_node: BaseDisplayListNode
cmd_list = fMesh.drawMatOverrides.get(base_node.override_hash, None) or fMesh.draw
last_mat = last_materials.get(base_node.drawLayer, None)
default_render_mode = fModel.getRenderMode(base_node.drawLayer)
reset_cmd_dict = {typ: cmd for _, reset_cmds in last_cmds_resets for typ, cmd in reset_cmds.items()}
last_mat = self.bleed_fmesh(
fMesh,
last_mat if not base_node.bleed_independently else None,
last_mat,
reset_cmd_dict,
cmd_list,
fModel.getAllMaterials().items(),
fModel.matWriteMethod,
default_render_mode,
)
# if the mesh has culling, it can be culled, and create invalid combinations of f3d to represent the current full DL
if fMesh.cullVertexList:
last_materials[base_node.drawLayer] = None
else:
last_materials[base_node.drawLayer] = last_mat
# don't carry over last_mat if it is a switch node or geo asm node
last_materials[base_node.drawLayer] = [last_mat, [(cmd_list, reset_cmd_dict)]]
# if the mesh has culling, we must revert to avoid bleed issues
if fMesh.cullVertexList or node.revert_after_mat:
last_materials = reset_layer(last_materials, base_node.drawLayer)
elif node.revert_after_mat: # if no mesh but still forced revert, revert all
last_materials = reset_all_layers(last_materials)
cur_last_materials = copy_last(last_materials)
is_switch = type(base_node) in {SwitchNode}
for child in node.children:
if type(base_node) in [SwitchNode, FunctionNode]:
last_materials = dict()
last_materials = walk(child, last_materials)
if is_switch: # parent node is switch or function
new_materials = walk(child, cur_last_materials) # last material info from current switch option
# add switch option reverts, to either revert at the end or in the option itself
for draw_layer, (last_mat, cmds_resets) in new_materials.items():
last_materials.setdefault(draw_layer, [last_mat, []])[1].extend(cmds_resets)
last_materials[draw_layer][0] = None # reset last material
else:
last_materials = walk(child, last_materials)
return last_materials
for node in geo_layout_graph.startGeolayout.nodes:
last_materials = walk(node, last_materials)
reset_all_layers(last_materials)
self.clear_gfx_lists(fModel)
+32 -4
View File
@@ -1,4 +1,5 @@
from __future__ import annotations
import typing
import bpy, mathutils, math, copy, os, shutil, re
from bpy.utils import register_class, unregister_class
@@ -103,6 +104,7 @@ from ..f3d.f3d_gbi import (
)
from .sm64_geolayout_classes import (
BaseDisplayListNode,
DisplayListNode,
TransformNode,
StartNode,
@@ -136,6 +138,9 @@ from .sm64_constants import (
enumLevelNames,
)
if typing.TYPE_CHECKING:
from .sm64_geolayout_bone import SM64_BoneProperties
def appendSecondaryGeolayout(geoDirPath, geoName1, geoName2, additionalNode=""):
geoPath = os.path.join(geoDirPath, "geo.inc.c")
@@ -393,7 +398,7 @@ def convertArmatureToGeolayout(armatureObj, obj, convertTransformMatrix, camera,
fModel = SM64Model(
name,
DLFormat,
GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll,
bpy.context.scene.fast64.sm64.gfx_write_method,
)
if len(armatureObj.children) == 0:
@@ -460,7 +465,7 @@ def convertObjectToGeolayout(
fModel = SM64Model(
name,
DLFormat,
GfxMatWriteMethod.WriteDifferingAndRevert if not inline else GfxMatWriteMethod.WriteAll,
bpy.context.scene.fast64.sm64.gfx_write_method,
)
# convertTransformMatrix = convertTransformMatrix @ \
@@ -1548,6 +1553,9 @@ def processMesh(
additionalTransformNode = TransformNode(additionalNode)
transformNode.children.append(additionalTransformNode)
additionalTransformNode.parent = transformNode
additionalTransformNode.revert_previous_mat = (
additionalTransformNode.revert_after_mat
) = obj.bleed_independently
else:
triConverterInfo = TriangleConverterInfo(
@@ -1577,11 +1585,11 @@ def processMesh(
node.hasDL = False
else:
firstNodeProcessed = False
node: BaseDisplayListNode
for drawLayer, fMesh in fMeshes.items():
if not firstNodeProcessed:
node.DLmicrocode = fMesh.draw
node.fMesh = fMesh
node.bleed_independently = obj.bleed_independently
node.drawLayer = drawLayer # previous drawLayer assigments useless?
firstNodeProcessed = True
else:
@@ -1592,13 +1600,16 @@ def processMesh(
)
additionalNode.DLmicrocode = fMesh.draw
additionalNode.fMesh = fMesh
additionalNode.bleed_independently = obj.bleed_independently
additionalTransformNode = TransformNode(additionalNode)
additionalTransformNode.revert_previous_mat = (
additionalTransformNode.revert_after_mat
) = obj.bleed_independently
transformNode.children.append(additionalTransformNode)
additionalTransformNode.parent = transformNode
parentTransformNode.children.append(transformNode)
transformNode.parent = parentTransformNode
transformNode.revert_previous_mat = transformNode.revert_after_mat = obj.bleed_independently
alphabeticalChildren = sorted(obj.children, key=lambda childObj: childObj.original_name.lower())
for childObj in alphabeticalChildren:
@@ -1641,6 +1652,8 @@ def processBone(
convertTextureData,
):
bone = armatureObj.data.bones[boneName]
bone_props: "SM64_BoneProperties" = bone.fast64.sm64
poseBone = armatureObj.pose.bones[boneName]
final_transform = copy.deepcopy(transformMatrix)
materialOverrides = copy.copy(materialOverrides)
@@ -1862,6 +1875,16 @@ def processBone(
parentTransformNode.children.append(transformNode)
transformNode.parent = parentTransformNode
new_node: TransformNode
for new_node in additionalNodes + [transformNode]:
new_node.revert_previous_mat = (
bone_props.revert_before_func
if bone.geo_cmd in {"Function", "HeldObject"}
else bone_props.revert_previous_mat
)
if isinstance(new_node.node, BaseDisplayListNode):
new_node.revert_after_mat = bone_props.revert_after_mat
if not isinstance(transformNode.node, SwitchNode):
# print(boneGroup.name if boneGroup is not None else "Offset")
if len(bone.children) > 0:
@@ -2167,10 +2190,15 @@ def addSkinnedMeshNode(armatureObj, boneName, skinnedMesh, transformNode, parent
# Get skinned node
bone = armatureObj.data.bones[boneName]
bone_props: "SM64_BoneProperties" = bone.fast64.sm64
skinnedNode = DisplayListNode(drawLayer)
skinnedNode.fMesh = skinnedMesh
skinnedNode.DLmicrocode = skinnedMesh.draw
skinnedTransformNode = TransformNode(skinnedNode)
skinnedTransformNode.revert_previous_mat, skinnedTransformNode.revert_after_mat = (
bone_props.revert_previous_mat,
bone_props.revert_after_mat,
)
# Ascend heirarchy until reaching first node before a deform parent.
# We duplicate the hierarchy along the way to possibly use later.
+1 -2
View File
@@ -876,11 +876,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,
bpy.context.scene.fast64.sm64.gfx_write_method,
)
childAreas = [child for child in obj.children if child.type == "EMPTY" and child.sm64_obj_type == "Area Root"]
if len(childAreas) == 0: