Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix merge conflicts from mat_bleed PR. #1

Merged
merged 18 commits into from
Mar 19, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a44b1c8
added hashing to gbi base classes
Nov 28, 2022
c470358
Merge remote-tracking branch 'fast64/main' into fast64_main
Dec 4, 2022
88dd3d6
Merge remote-tracking branch 'fast64/main' into fast64_main
Dec 17, 2022
32bfeaf
Merge remote-tracking branch 'fast64/main' into fast64_main
Dec 23, 2022
8dbb18f
Merge remote-tracking branch 'fast64/main' into fast64_main
Jan 19, 2023
9bf6e9b
Merge remote-tracking branch 'fast64/main' into fast64_main
Jan 27, 2023
eb5a964
Updated FModel.save_textures() with FImageKey re-factor
kurethedead Jan 27, 2023
5fbfcf3
Fixed issue while upgrading materials if a vert has groups but the me…
thecozies Feb 18, 2023
f5ffd2b
[F3D] Add material bleeding to FMesh GfxLists (#186)
jesusyoshi54 Feb 18, 2023
2d9f0c2
Merge pull request #213 from kurethedead/f3d_image_key_bug
thecozies Feb 18, 2023
7259fd1
Merge remote-tracking branch 'fast64/main' into fast64_main
Feb 19, 2023
80fffe8
merge fixes for mat bleed PR
Feb 19, 2023
5006586
fixed issue with area object being none during object geo layout expo…
jesusyoshi54 Feb 19, 2023
57a7124
Merge remote-tracking branch 'fast64/main' into tex_writer_fixes
Feb 19, 2023
a096c1f
updated instances of use large textures with checking large texture u…
Feb 19, 2023
c3962ed
re-wrote tile bleed to work with new texture writer, and made all tex…
Mar 17, 2023
2238313
tile scroll gfx tag only will apply if tile scroll exists in material
Mar 17, 2023
c0089b1
Fixed superfluous passing of loadGfx to writeAll
sauraen Mar 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ def draw(self, context):
col.prop(context.scene, "saveTextures")
col.prop(context.scene, "f3d_simple", text="Simple Material UI")
col.prop(context.scene, "generateF3DNodeGraph", text="Generate F3D Node Graph For Materials")
col.prop(context.scene, "exportInlineF3D", text="Export Mesh F3D as single DL")
col.prop(context.scene, "decomp_compatible", invert_checkbox=True, text="Homebrew Compatibility")
col.prop(context.scene, "ignoreTextureRestrictions")
if context.scene.ignoreTextureRestrictions:
Expand Down Expand Up @@ -471,6 +472,9 @@ def register():
bpy.types.Scene.saveTextures = bpy.props.BoolProperty(name="Save Textures As PNGs (Breaks CI Textures)")
bpy.types.Scene.generateF3DNodeGraph = bpy.props.BoolProperty(name="Generate F3D Node Graph", default=True)
bpy.types.Scene.exportHiddenGeometry = bpy.props.BoolProperty(name="Export Hidden Geometry", default=True)
bpy.types.Scene.exportInlineF3D = bpy.props.BoolProperty(name="Export Inline F3D", \
description = "F3D for each mesh will be one Gfx list instead of having different Gfx lists for each component of the mesh.\n\
This will cause repeated F3D cmds. This option does not work on armature exports.", default=False)
bpy.types.Scene.blenderF3DScale = bpy.props.FloatProperty(
name="F3D Blender Scale", default=100, update=on_update_render_settings
)
Expand Down
310 changes: 310 additions & 0 deletions fast64_internal/f3d/f3d_bleed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
from __future__ import annotations
from dataclasses import dataclass, field
import copy

from .f3d_gbi import (
GfxTag,
SPMatrix,
SPVertex,
SPViewport,
SPDisplayList,
SPBranchList,
SP1Triangle,
SPLine3D,
SPLineW3D,
SP2Triangles,
SPCullDisplayList,
SPSegment,
SPBranchLessZraw,
SPModifyVertex,
SPEndDisplayList,
SPGeometryMode,
SPSetOtherMode,
DPLoadBlock,
DPLoadTLUTCmd,
DPFullSync,
DPSetRenderMode,
DPSetTextureLUT,
DPSetCycleType,
DPSetTextureImage,
DPPipeSync,
DPLoadSync,
DPTileSync,
DPSetTile,
DPLoadTile,
)


class BleedGraphics:

# bleed_state "enums"
bleed_start = 1
bleed_in_progress = 2

def __init__(self):
self.bled_gfx_lists = dict()

def bleed_fModel(self, fModel: FModel, fMeshes: dict[FMesh]):
# walk fModel, no order to drawing is observed, so lastMat is not kept track of
for drawLayer, fMesh in fMeshes.items():
self.bleed_fmesh(fModel.f3d, fMesh, None, fMesh.draw, fModel.getRenderMode(drawLayer))
self.clear_gfx_lists(fModel)

# clear the gfx lists so they don't export
def clear_gfx_lists(self, fModel: FModel):
for (fMaterial, texDimensions) in fModel.materials.values():
fMaterial.material = None
fMaterial.revert = None
for fMesh in fModel.meshes.values():
for tri_list in fMesh.triangleGroups:
tri_list.triList = None

def bleed_fmesh(self, f3d: F3D, fMesh: FMesh, lastMat: FMaterial, cmd_list: GfxList, default_render_mode=None):
if bled_mat := self.bled_gfx_lists.get(cmd_list, None):
return bled_mat
self.on_bleed_start(f3d, lastMat, cmd_list)
bleed_state = self.bleed_start
for triGroup in fMesh.triangleGroups:
# bleed mat and tex
bleed_gfx_lists = BleedGfxLists()
if triGroup.fMaterial:
bleed_gfx_lists.bled_mats = self.bleed_mat(triGroup.fMaterial, lastMat, cmd_list, bleed_state)
if not triGroup.fMaterial.largeTexFmt:
bleed_gfx_lists.bled_tex = self.bleed_textures(triGroup.fMaterial, lastMat, cmd_list, bleed_state)
lastMat = triGroup.fMaterial
# bleed tri group (for large textures) and to remove other unnecessary cmds
self.bleed_tri_group(f3d, triGroup, bleed_gfx_lists, cmd_list, bleed_state)
self.inline_triGroup(f3d, triGroup, bleed_gfx_lists, cmd_list)
self.on_tri_group_bleed_end(f3d, triGroup, lastMat, bleed_gfx_lists)
bleed_state = self.bleed_in_progress
self.on_bleed_end(f3d, lastMat, bleed_gfx_lists, cmd_list, default_render_mode)
return lastMat

def bleed_textures(self, curMat: FMaterial, lastMat: FMaterial, cmd_list: GfxList, bleed_state: int):
if lastMat:
bled_tex = []
# bleed cmds if matching tile has duplicate cmds
for j, (LastTex, TexCmds) in enumerate(zip(lastMat.texture_DLs, curMat.texture_DLs)):
# deep copy breaks on Image objects so I will only copy the levels needed
commands_bled = copy.copy(TexCmds)
commands_bled.commands = copy.copy(TexCmds.commands) # copy the commands also
lastList = LastTex.commands
# eliminate set tex images
set_tex = (c for c in TexCmds.commands if type(c) == DPSetTextureImage)
removed_tex = [c for c in set_tex if c in lastList] # needs to be a list to check "in" multiple times
rm_load = None # flag to elim loads once
for j, cmd in enumerate(TexCmds.commands):
# remove set tex explicitly
if cmd in removed_tex:
commands_bled.commands.remove(cmd)
rm_load = True
continue
if rm_load and type(cmd) in (DPLoadTLUTCmd, DPLoadTile, DPLoadBlock):
commands_bled.commands.remove(cmd)
rm_load = None
continue
# now eval as normal conditionals
iter_cmds = copy.copy(commands_bled.commands) # need extra list to iterate with
for j, cmd in enumerate(iter_cmds):
if self.bleed_individual_cmd(commands_bled, cmd, bleed_state):
if cmd in lastList:
commands_bled.commands[j] = None
# remove Nones from list
while None in commands_bled.commands:
commands_bled.commands.remove(None)
bled_tex.append(commands_bled)
else:
bled_tex = curMat.texture_DLs
return bled_tex

def bleed_mat(self, curMat: FMaterial, lastMat: FMaterial, cmd_list: GfxList, bleed_state: int):
if lastMat:
gfx = curMat.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
LastList = lastMat.mat_only_DL.commands
for j, cmd in enumerate(gfx.commands):
if self.bleed_individual_cmd(commands_bled, cmd, bleed_state):
if cmd in LastList:
commands_bled.commands[j] = None
# remove Nones from list
while None in commands_bled.commands:
commands_bled.commands.remove(None)
else:
commands_bled = curMat.mat_only_DL
# remove SPEndDisplayList
while SPEndDisplayList() in commands_bled.commands:
commands_bled.commands.remove(SPEndDisplayList())
return commands_bled

def bleed_tri_group(
self, f3d: F3D, triGroup: FTriGroup, bleed_gfx_lists: BleedGfxLists, cmd_list: GfxList, bleed_state: int
):
# remove SPEndDisplayList from triGroup
while SPEndDisplayList() in triGroup.triList.commands:
triGroup.triList.commands.remove(SPEndDisplayList())
if triGroup.fMaterial.largeTexFmt:
triGroup.triList = self.bleed_cmd_list(triGroup.triList, 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):
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):
if not self.bleed_individual_cmd(commands_bled, cmd, bleed_state):
continue
last_use = usage_dict.get((type(cmd), getattr(cmd, "tile", None)), None)
usage_dict[(type(cmd), getattr(cmd, "tile", None))] = cmd
if last_use == cmd:
commands_bled.commands[j] = None
# remove Nones from list
while None in commands_bled.commands:
commands_bled.commands.remove(None)
return commands_bled

# Put triGroup bleed gfx in the FMesh.draw object
def inline_triGroup(self, f3d: F3D, triGroup: FTriGroup, bleed_gfx_lists: BleedGfxLists, cmd_list: GfxList):
# add material
cmd_list.commands.extend(bleed_gfx_lists.bled_mats.commands)
# add textures
for tile, texGfx in enumerate(bleed_gfx_lists.bled_tex):
cmd_list.commands.extend(texGfx.commands)
# add in triangles
cmd_list.commands.extend(triGroup.triList.commands)
# skinned meshes don't draw tris sometimes, use this opportunity to save a sync
tri_cmds = [c for c in triGroup.triList.commands if type(c) == SP1Triangle or type(c) == SP2Triangles]
if tri_cmds:
bleed_gfx_lists.reset_cmds.add(DPPipeSync)

def on_bleed_start(self, f3d: F3D, lastMat: FMaterial, cmd_list: GfxList):
# remove SPDisplayList and SPEndDisplayList from FMesh.draw
iter_cmds = copy.copy(cmd_list.commands)
spDLCmds = (c for c in iter_cmds if type(c) == SPDisplayList)
spEndCmds = (c for c in iter_cmds if type(c) == SPEndDisplayList)
for spDL in spDLCmds:
cmd_list.commands.remove(spDL)
for spEnd in spEndCmds:
cmd_list.commands.remove(spEnd)

def on_tri_group_bleed_end(self, f3d: F3D, triGroup: FTriGroup, lastMat: FMaterial, bleed_gfx_lists: BleedGfxLists):
return

def on_bleed_end(
self, f3d: F3D, lastMat: FMaterial, bleed_gfx_lists: BleedGfxLists, cmd_list: GfxList, default_render_mode=None
):
[bleed_gfx_lists.add_reset_cmd(cmd) for cmd in cmd_list.commands]
# revert certain cmds for extra safety
reset_cmds = [reset_cmd for cmd in bleed_gfx_lists.reset_cmds if (reset_cmd := bleed_gfx_lists.create_reset_cmd(cmd, default_render_mode))]
# if pipe sync in rest list, make sure it is the first cmd
if DPPipeSync in reset_cmds:
reset_cmds.remove(DPPipeSync)
reset_cmds.insert(0, DPPipeSync)
cmd_list.commands.extend(reset_cmds)
cmd_list.commands.append(SPEndDisplayList())
self.bled_gfx_lists[cmd_list] = lastMat

def bleed_individual_cmd(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
if type(cmd) in [
SPMatrix,
SPVertex,
SPViewport,
SPDisplayList,
SPBranchList,
SP1Triangle,
SPLine3D,
SPLineW3D,
SP2Triangles,
SPCullDisplayList,
SPSegment,
SPBranchLessZraw,
SPModifyVertex,
SPEndDisplayList,
DPLoadBlock,
DPLoadTLUTCmd,
DPFullSync,
]:
return False

bleed_func = getattr(self, (f"bleed_{type(cmd).__name__}"), None)
if bleed_func:
return bleed_func(cmd_list, cmd, bleed_state)

return True

def bleed_SPSetOtherMode(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
if bleed_state != self.bleed_start:
return True

def bleed_DPSetTileSize(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
return cmd.tags != GfxTag.TileScroll0 and cmd.tags != GfxTag.TileScroll1

def bleed_DPSetTile(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
# should only be removed if there is no other set tile in this list
# that is on the same tile, and has different settings
for parse_cmd in cmd_list.commands:
if type(parse_cmd) is DPSetTile and parse_cmd is not cmd:
if cmd != self:
return False
return True

def bleed_DPTileSync(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
# will be bled if there are two of these syncs, at most only one tilesync
# is ever required after rendering triangles, and before subsequent cmds rdp attr changes
for parse_cmd in cmd_list.commands:
if type(parse_cmd) is DPTileSync and parse_cmd is not cmd:
return True
return False

def bleed_DPPipeSync(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
# will be bled if there are two of these syncs, at most only one pipesync
# is ever required after rendering triangles, and before subsequent cmds rdp attr changes
for parse_cmd in cmd_list.commands:
if type(parse_cmd) is DPPipeSync and parse_cmd is not cmd:
return True
return False

def bleed_DPLoadSync(self, cmd_list: GfxList, cmd: GbiMacro, bleed_state: int):
# will be bled if there are two of these syncs, at most only one pipesync
# is ever required after rendering triangles, and before subsequent cmds rdp attr changes
for parse_cmd in cmd_list.commands:
if type(parse_cmd) is DPLoadSync and parse_cmd is not cmd:
return True
return False


# small containers for data used in inline Gfx
@dataclass
class BleedGfxLists:
bled_mats: GfxList = field(default_factory=list)
bled_tex: list[GfxList] = field(default_factory=list) # list of GfxList
reset_cmds: set[GbiMacro] = field(default_factory=set) # set of cmds to reset

def __post_init__(self):
# this cmds always reset
self.reset_cmds.add(DPSetCycleType)

def create_reset_cmd(self, cmd, default_render_mode):
if cmd == DPSetRenderMode:
if not default_render_mode:
return
else:
return cmd(default_render_mode, None)
return cmd(*self.reset_command_dict.get(cmd, []))

@property
def reset_command_dict(self):
return {
SPGeometryMode: (["G_TEXTURE_GEN"], ["G_LIGHTING"]),
DPSetCycleType: ("G_CYC_1CYCLE",),
DPSetTextureLUT: ("G_TT_NONE",),
DPSetRenderMode: (None, None),
}

def add_reset_cmd(self, cmd: GbiMacro):
if type(cmd) in self.reset_command_dict.keys():
self.reset_cmds.add(type(cmd))
if type(cmd) is SPSetOtherMode:
if any(["G_RM" in flag for flag in cmd.flagList]):
self.reset_cmds.add(DPSetRenderMode)
Loading