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

[SM64/F3D] Fix and update Ui Image Exporter #452

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
17 changes: 14 additions & 3 deletions fast64_internal/f3d/f3d_material.py
Original file line number Diff line number Diff line change
Expand Up @@ -2143,9 +2143,9 @@ def get_textlut_mode(f3d_mat: "F3DMaterialProperty", inherit_from_tex: bool = Fa
use_dict = all_combiner_uses(f3d_mat)
textures = [f3d_mat.tex0] if use_dict["Texture 0"] and f3d_mat.tex0.tex_set else []
textures += [f3d_mat.tex1] if use_dict["Texture 1"] and f3d_mat.tex1.tex_set else []
tlut_modes = [tex.ci_format if tex.tex_format.startswith("CI") else "NONE" for tex in textures]
tlut_modes = [tex.tlut_mode for tex in textures]
if tlut_modes and tlut_modes[0] == tlut_modes[-1]:
return "G_TT_" + tlut_modes[0]
return tlut_modes[0]
return None if inherit_from_tex else f3d_mat.rdp_settings.g_mdsft_textlut


Expand Down Expand Up @@ -2858,6 +2858,15 @@ class TextureProperty(PropertyGroup):
)
tile_scroll: bpy.props.PointerProperty(type=SetTileSizeScrollProperty)

@property
def is_ci(self):
self.tex_format: str
return self.tex_format.startswith("CI")

@property
def tlut_mode(self):
return f"G_TT_{self.ci_format if self.is_ci else 'NONE'}"

def get_tex_size(self) -> list[int]:
if self.tex or self.use_tex_reference:
if self.tex is not None:
Expand Down Expand Up @@ -2919,6 +2928,7 @@ def ui_image(
textureProp: TextureProperty,
name: str,
showCheckBox: bool,
hide_lowhigh=False,
):
inputGroup = layout.box().column()

Expand Down Expand Up @@ -3025,7 +3035,8 @@ def ui_image(
shift = prop_input.row()
shift.prop(textureProp.S, "shift", text="Shift S")
shift.prop(textureProp.T, "shift", text="Shift T")

if hide_lowhigh:
return
low = prop_input.row()
low.prop(textureProp.S, "low", text="S Low")
low.prop(textureProp.T, "low", text="T Low")
Expand Down
56 changes: 53 additions & 3 deletions fast64_internal/f3d/f3d_texture_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,10 +420,10 @@ def fromMat(self, index: int, f3dMat: F3DMaterialProperty) -> bool:
texProp = getattr(f3dMat, "tex" + str(index))
return self.fromProp(texProp, index)

def fromProp(self, texProp: TextureProperty, index: int) -> bool:
def fromProp(self, texProp: TextureProperty, index: int, ignore_tex_set=False) -> bool:
self.indexInMat = index
self.texProp = texProp
if not texProp.tex_set:
if not texProp.tex_set and not ignore_tex_set:
return True

self.useTex = True
Expand Down Expand Up @@ -463,6 +463,33 @@ def fromProp(self, texProp: TextureProperty, index: int) -> bool:

return True

def materialless_setup(self) -> None:
"""moreSetupFromModel equivelent that does not handle material properties like OOT flipbooks"""
Lilaa3 marked this conversation as resolved.
Show resolved Hide resolved
if not self.useTex:
return

if self.isTexCI:
self.imDependencies, self.flipbook, self.pal = (
[] if self.texProp.tex is None else [self.texProp.tex],
None,
None,
)
if self.isTexRef:
self.palLen = self.texProp.pal_reference_size
else:
assert self.flipbook is None
self.pal = getColorsUsedInImage(self.texProp.tex, self.palFormat)
self.palLen = len(self.pal)
if self.palLen > (16 if self.texFormat == "CI4" else 256):
raise PluginError(
f"Error in Texture {self.indexInMat} uses too many unique colors to fit in format {self.texFormat}."
)
else:
self.imDependencies, self.flipbook = [] if self.texProp.tex is None else [self.texProp.tex], None
Lilaa3 marked this conversation as resolved.
Show resolved Hide resolved

self.isPalRef = self.isTexRef and self.flipbook is None
self.palDependencies = self.imDependencies

def moreSetupFromModel(
self,
material: bpy.types.Material,
Expand Down Expand Up @@ -504,6 +531,26 @@ def getPaletteName(self):
return self.flipbook.name
return getImageName(self.texProp.tex)

def setup_single_tex(self, is_ci: bool, use_large_tex: bool):
is_large = False
tmem_size = 256 if is_ci else 512
if is_ci:
assert self.useTex # should this be here?
if self.useTex:
self.loadPal = True
self.palBaseName = self.getPaletteName()
if self.tmemSize >= tmem_size:
if use_large_tex:
self.doTexLoad = False
return True
elif not bpy.context.scene.ignoreTextureRestrictions:
raise PluginError(
"Textures are too big. Max TMEM size is 4k "
"bytes, ex. 2 32x32 RGBA 16 bit textures.\n"
"Note that texture width will be internally padded to 64 bit boundaries."
)
return is_large

def writeAll(
self,
fMaterial: FMaterial,
Expand All @@ -514,7 +561,7 @@ def writeAll(
return
assert (
self.imDependencies is not None
) # Must be set manually if didn't use moreSetupFromModel, e.g. ti.imDependencies = [tex]
), "self.imDependencies is None, either moreSetupFromModel or materialless_setup must be called beforehand"

# Get definitions
imageKey, fImage = saveOrGetTextureDefinition(
Expand Down Expand Up @@ -551,6 +598,9 @@ def writeAll(
fModel.writeTexRefNonCITextures(self.flipbook, self.texFormat)
else:
if self.isTexCI:
assert (
self.pal is not None
), "self.pal is None, either moreSetupFromModel or materialless_setup must be called beforehand"
writeCITextureData(self.texProp.tex, fImage, self.pal, self.palFormat, self.texFormat)
else:
writeNonCITextureData(self.texProp.tex, fImage, self.texFormat)
Expand Down
117 changes: 40 additions & 77 deletions fast64_internal/sm64/sm64_f3d_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from mathutils import Matrix, Vector
from bpy.utils import register_class, unregister_class
from ..panels import SM64_Panel
from ..f3d.f3d_writer import exportF3DCommon
from ..f3d.f3d_writer import exportF3DCommon, saveModeSetting
from ..f3d.f3d_texture_writer import TexInfo
from ..f3d.f3d_material import (
TextureProperty,
tmemUsageUI,
all_combiner_uses,
ui_image,
ui_procAnim,
update_world_default_rendermode,
)
Expand All @@ -22,6 +22,8 @@
from ..f3d.f3d_bleed import BleedGraphics

from ..f3d.f3d_gbi import (
DPSetCombineMode,
DPSetTextureLUT,
get_F3D_GBI,
GbiMacro,
GfxTag,
Expand Down Expand Up @@ -99,7 +101,7 @@
# filepath, function to insert before
enumHUDPaths = {
"HUD": ("src/game/hud.c", "void render_hud(void)"),
"Menu": ("src/game/ingame_menu.c", "s16 render_menus_and_dialogs()"),
"Menu": ("src/game/ingame_menu.c", "s16 render_menus_and_dialogs("),
}


Expand Down Expand Up @@ -186,7 +188,7 @@ def exportTexRectToC(dirPath, texProp, texDir, savePNG, name, exportToProject, p
checkIfPathExists(seg2TexDir)
checkIfPathExists(hudPath)

fTexRect.save_textures(seg2TexDir, not savePNG)
fTexRect.save_textures(seg2TexDir)

textures = []
for _, fImage in fTexRect.textures.items():
Expand Down Expand Up @@ -279,69 +281,45 @@ def modifyDLForHUD(data):


def exportTexRectCommon(texProp, name, convertTextureData):
tex = texProp.tex
if tex is None:
raise PluginError("No texture is selected.")
use_copy_mode = texProp.tlut_mode == "G_TT_RGBA16" or texProp.tex_format == "RGBA16"

texProp.S.low = 0
texProp.S.high = texProp.tex.size[0] - 1
texProp.S.mask = ceil(log(texProp.tex.size[0], 2) - 0.001)
texProp.S.shift = 0

texProp.T.low = 0
texProp.T.high = texProp.tex.size[1] - 1
texProp.T.mask = ceil(log(texProp.tex.size[1], 2) - 0.001)
texProp.T.shift = 0
defaults = create_or_get_world(bpy.context.scene).rdp_defaults

fTexRect = FTexRect(toAlnum(name), GfxMatWriteMethod.WriteDifferingAndRevert)
fMaterial = FMaterial(toAlnum(name) + "_mat", DLFormat.Dynamic)

# dl_hud_img_begin
fTexRect.draw.commands.extend(
[
DPPipeSync(),
DPSetCycleType("G_CYC_COPY"),
DPSetTexturePersp("G_TP_NONE"),
DPSetAlphaCompare("G_AC_THRESHOLD"),
DPSetBlendColor(0xFF, 0xFF, 0xFF, 0xFF),
DPSetRenderMode(["G_RM_AA_XLU_SURF", "G_RM_AA_XLU_SURF2"], None),
]
)
fMaterial = fTexRect.addMaterial(toAlnum(name) + "_mat")

drawEndCommands = GfxList("temp", GfxListTag.Draw, DLFormat.Dynamic)
# use_copy_mode is based on dl_hud_img_begin and dl_hud_img_end
if use_copy_mode:
saveModeSetting(fMaterial, "G_CYC_COPY", defaults.g_mdsft_cycletype, DPSetCycleType)
else:
saveModeSetting(fMaterial, "G_CYC_1CYCLE", defaults.g_mdsft_cycletype, DPSetCycleType)
fMaterial.mat_only_DL.commands.append(
DPSetCombineMode(*fTexRect.f3d.G_CC_DECALRGBA, *fTexRect.f3d.G_CC_DECALRGBA)
)
fMaterial.revert.commands.append(DPSetCombineMode(*fTexRect.f3d.G_CC_SHADE, *fTexRect.f3d.G_CC_SHADE))
saveModeSetting(fMaterial, "G_TP_NONE", defaults.g_mdsft_textpersp, DPSetTexturePersp)
saveModeSetting(fMaterial, "G_AC_THRESHOLD", defaults.g_mdsft_alpha_compare, DPSetAlphaCompare)
fMaterial.mat_only_DL.commands.append(DPSetBlendColor(0xFF, 0xFF, 0xFF, 0xFF))

ti = TexInfo()
if not ti.fromProp(texProp, 0):
raise PluginError(f"In {name}: {texProp.errorMsg}.")
if not ti.useTex:
raise PluginError(f"In {name}: texture disabled.")
if ti.isTexCI:
raise PluginError(f"In {name}: CI textures not compatible with exportTexRectCommon (because copy mode).")
if ti.tmemSize > 512:
raise PluginError(f"In {name}: texture is too big (> 4 KiB).")
if ti.texFormat != "RGBA16":
raise PluginError(f"In {name}: texture format must be RGBA16 (because copy mode).")
ti.imDependencies = [tex]
ti.writeAll(fTexRect.draw, fMaterial, fTexRect, convertTextureData)
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()
ti.fromProp(texProp, index=0, ignore_tex_set=True)
ti.materialless_setup()
ti.setup_single_tex(texProp.is_ci, False)
ti.writeAll(fMaterial, fTexRect, convertTextureData)
fTexRect.materials[texProp] = (fMaterial, ti.imageDims)

fTexRect.draw.commands.extend(fMaterial.mat_only_DL.commands)
fTexRect.draw.commands.extend(fMaterial.texture_DL.commands)
fTexRect.draw.commands.append(
SPScisTextureRectangle(0, 0, (texDimensions[0] - 1) << 2, (texDimensions[1] - 1) << 2, 0, 0, 0)
)

fTexRect.draw.commands.extend(drawEndCommands.commands)

# dl_hud_img_end
fTexRect.draw.commands.extend(
[
DPPipeSync(),
DPSetCycleType("G_CYC_1CYCLE"),
SPTexture(0xFFFF, 0xFFFF, 0, "G_TX_RENDERTILE", "G_OFF"),
DPSetTexturePersp("G_TP_PERSP"),
DPSetAlphaCompare("G_AC_NONE"),
DPSetRenderMode(["G_RM_AA_ZB_OPA_SURF", "G_RM_AA_ZB_OPA_SURF2"], None),
SPEndDisplayList(),
]
SPScisTextureRectangle(0, 0, (ti.imageDims[0] - 1) << 2, (ti.imageDims[1] - 1) << 2, 0, 0, 0)
)
fTexRect.draw.commands.append(DPPipeSync())
fTexRect.draw.commands.extend(fMaterial.revert.commands)
fTexRect.draw.commands.append(SPEndDisplayList())

return fTexRect

Expand Down Expand Up @@ -822,25 +800,7 @@ class ExportTexRectDrawPanel(SM64_Panel):
# called every frame
def draw(self, context):
col = self.layout.column()
propsTexRectE = col.operator(ExportTexRectDraw.bl_idname)

textureProp = context.scene.texrect
tex = textureProp.tex
col.label(text="This is for decomp only.")
col.template_ID(textureProp, "tex", new="image.new", open="image.open", unlink="image.texrect_unlink")
# col.prop(textureProp, 'tex')

tmemUsageUI(col, textureProp)
if tex is not None and tex.size[0] > 0 and tex.size[1] > 0:
col.prop(textureProp, "tex_format", text="Format")
if textureProp.tex_format[:2] == "CI":
col.prop(textureProp, "ci_format", text="CI Format")
col.prop(textureProp.S, "clamp", text="Clamp S")
col.prop(textureProp.T, "clamp", text="Clamp T")
col.prop(textureProp.S, "mirror", text="Mirror S")
col.prop(textureProp.T, "mirror", text="Mirror T")

prop_split(col, context.scene, "TexRectName", "Name")
col.prop(context.scene, "TexRectCustomExport")
if context.scene.TexRectCustomExport:
col.prop(context.scene, "TexRectExportPath")
Expand All @@ -857,6 +817,9 @@ def draw(self, context):
infoBox.label(text="After export, call your hud's draw function in ")
infoBox.label(text=enumHUDPaths[context.scene.TexRectExportType][0] + ": ")
infoBox.label(text=enumHUDPaths[context.scene.TexRectExportType][1] + ".")
prop_split(col, context.scene, "TexRectName", "Name")
ui_image(False, col, None, context.scene.texrect, context.scene.TexRectName, False, hide_lowhigh=True)
col.operator(ExportTexRectDraw.bl_idname)


class SM64_DrawLayersPanel(bpy.types.Panel):
Expand Down
Loading