From 82587cbff546e274b3e1304a6de3b4326805f8f0 Mon Sep 17 00:00:00 2001 From: Lucretia Date: Sat, 1 Aug 2026 16:44:11 +1000 Subject: [PATCH 1/5] Add option to include CI format details in texture filenames --- __init__.py | 8 +++++++- fast64_internal/f3d/f3d_texture_writer.py | 19 +++++++++++++------ fast64_internal/repo_settings.py | 2 ++ fast64_internal/utility.py | 19 +++++++++++++++---- 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/__init__.py b/__init__.py index 9ff8a8eb5..e3d9b4248 100644 --- a/__init__.py +++ b/__init__.py @@ -113,6 +113,8 @@ def draw(self, context): if context.scene.f3d_type in {"F3DEX3", "T3D"}: prop_split(col, context.scene, "packed_normals_algorithm", "Packed normals alg") col.prop(context.scene, "saveTextures") + if context.scene.saveTextures: + col.prop(context.scene, "textureNameIncludesCiFormat") col.prop(context.scene, "exportInlineF3D", text="Bleed and Inline Material Exports") if context.scene.exportInlineF3D: multilineLabel( @@ -492,7 +494,10 @@ def register(): bpy.types.Scene.gameEditorMode = bpy.props.EnumProperty( name="Game", default="SM64", items=gameEditorEnum, update=gameEditorUpdate ) - bpy.types.Scene.saveTextures = bpy.props.BoolProperty(name="Save Textures As PNGs (Breaks CI Textures)") + bpy.types.Scene.saveTextures = bpy.props.BoolProperty(name="Save Textures As PNGs (May Break CI Textures)") + bpy.types.Scene.textureNameIncludesCiFormat = bpy.props.BoolProperty( + name="Include CI Format In File Name", default=False + ) bpy.types.Scene.exportHiddenGeometry = bpy.props.BoolProperty(name="Export Hidden Geometry", default=True) bpy.types.Scene.exportInlineF3D = bpy.props.BoolProperty( name="Bleed and Inline Material Exports", @@ -529,6 +534,7 @@ def unregister(): del bpy.types.Scene.fullTraceback del bpy.types.Scene.ignoreTextureRestrictions del bpy.types.Scene.saveTextures + del bpy.types.Scene.textureNameIncludesCiFormat del bpy.types.Scene.gameEditorMode del bpy.types.Scene.exportHiddenGeometry del bpy.types.Scene.blenderF3DScale diff --git a/fast64_internal/f3d/f3d_texture_writer.py b/fast64_internal/f3d/f3d_texture_writer.py index 599b65560..2b22cd7da 100644 --- a/fast64_internal/f3d/f3d_texture_writer.py +++ b/fast64_internal/f3d/f3d_texture_writer.py @@ -277,8 +277,10 @@ def loadOneOrTwoS(tmemBase, tidxBase, TL, TH): # Functions for texture and palette definitions -def getTextureNamesFromBasename(baseName: str, texOrPalFormat: str, parent: Union[FModel, FTexRect], isPalette: bool): - suffix = getTextureSuffixFromFormat(texOrPalFormat) +def getTextureNamesFromBasename( + baseName: str, texFmt: str, ciFmt: Union[str, None], parent: Union[FModel, FTexRect], isPalette: bool +): + suffix = getTextureSuffixFromFormat(texFmt, ciFmt, isPalette) imageName = parent.name + "_" + baseName + "_" if isPalette: imageName += "pal_" @@ -297,14 +299,19 @@ def getImageName(image: bpy.types.Image): return getNameFromPath(image.filepath, True) -def getTextureNamesFromImage(image: bpy.types.Image, texFormat: str, parent: Union[FModel, FTexRect]): - return getTextureNamesFromBasename(getImageName(image), texFormat, parent, False) +def getTextureNamesFromImage( + image: bpy.types.Image, texFmt: str, ciFmt: Union[str, None], parent: Union[FModel, FTexRect] +): + return getTextureNamesFromBasename(getImageName(image), texFmt, ciFmt, parent, False) def getTextureNamesFromProp(texProp: TextureProperty, parent: Union[FModel, FTexRect]): if texProp.use_tex_reference: raise PluginError("Internal error, invalid use of getTextureNamesFromProp") - return getTextureNamesFromImage(texProp.tex, texProp.tex_format, parent) + + return getTextureNamesFromImage( + texProp.tex, texProp.tex_format, texProp.ci_format if texProp.is_ci else None, parent + ) def checkDuplicateTextureName(parent: Union[FModel, FTexRect], name): @@ -340,7 +347,7 @@ def saveOrGetPaletteDefinition( # print(f"Palette already exists") return paletteKey, fPalette - paletteName, filename = getTextureNamesFromBasename(palBaseName, palFmt, parent, True) + paletteName, filename = getTextureNamesFromBasename(palBaseName, texFmt, palFmt, parent, True) fPalette = FImage(paletteName, palFormat, "G_IM_SIZ_16b", 1, palLen, filename) parent.addTexture(paletteKey, fPalette, fMaterial) diff --git a/fast64_internal/repo_settings.py b/fast64_internal/repo_settings.py index 6faf3c494..206b11ee3 100644 --- a/fast64_internal/repo_settings.py +++ b/fast64_internal/repo_settings.py @@ -84,6 +84,7 @@ def load_repo_settings(scene: Scene, path: os.PathLike, skip_if_no_auto_load=Fal fast64_settings.from_repo_settings(data) set_prop_if_in_data(scene, "f3d_type", data, "microcode") set_prop_if_in_data(scene, "saveTextures", data, "saveTextures") + set_prop_if_in_data(scene, "textureNameIncludesCiFormat", data, "textureNameIncludesCiFormat") set_prop_if_in_data(scene, "exportInlineF3D", data, "bleedAndInline") set_prop_if_in_data(scene, "ignoreTextureRestrictions", data, "ignoreTextureRestrictions") set_prop_if_in_data(scene, "packed_normals_algorithm", data, "packedNormalsAlgorithm") @@ -107,6 +108,7 @@ def save_repo_settings(scene: Scene, path: os.PathLike, game: str = ""): data["packedNormalsAlgorithm"] = scene.packed_normals_algorithm data["bleedAndInline"] = scene.exportInlineF3D data["saveTextures"] = scene.saveTextures + data["textureNameIncludesCiFormat"] = scene.textureNameIncludesCiFormat data["ignoreTextureRestrictions"] = scene.ignoreTextureRestrictions rdp_defaults: RDPSettings = scene.world.rdp_defaults data["rdpDefaults"] = rdp_defaults.to_dict() diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index cccd9bea2..00e0e9026 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1813,10 +1813,21 @@ def ootGetBaseOrCustomLight(prop, idx, toExport: bool, errIfMissing: bool): return col, dir -def getTextureSuffixFromFormat(texFmt): - # if texFmt == "RGBA16": - # return "rgb5a1" - return texFmt.lower() +def getTextureSuffixFromFormat(texFmt: str, ciFmt: str | None, isPalette: bool): + if bpy.context.scene.textureNameIncludesCiFormat: + if ciFmt: + fmtName = f"{texFmt}-{ciFmt}" + else: + fmtName = texFmt + else: + if isPalette: + if ciFmt is None: + raise PluginError("Internal error, getTextureSuffixFromFormat required ciFmt but wasn't specified") + fmtName = ciFmt + else: + fmtName = texFmt + + return fmtName.lower() # https://stackoverflow.com/a/241506 From 146fb3ecaff417e6fbf62c8d70f8bed2f9a9cc04 Mon Sep 17 00:00:00 2001 From: Lucretia Date: Sat, 1 Aug 2026 17:06:23 +1000 Subject: [PATCH 2/5] Call getTextureNamesFromImage using correct params for OoT flipbooks --- fast64_internal/z64/model_classes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fast64_internal/z64/model_classes.py b/fast64_internal/z64/model_classes.py index a1611e7db..a2e2702b0 100644 --- a/fast64_internal/z64/model_classes.py +++ b/fast64_internal/z64/model_classes.py @@ -231,7 +231,7 @@ def processTexRefCITextures(self, fMaterial: FMaterial, material: bpy.types.Mate allImages = self.validateImages(material, index) for flipbookTexture in flipbookProp.textures: # print(f"Texture: {str(flipbookTexture.image)}") - imageName, filename = getTextureNamesFromImage(flipbookTexture.image, texProp.tex_format, model) + imageName, filename = getTextureNamesFromImage(flipbookTexture.image, texProp.tex_format, texProp.ci_format, model) if flipbookProp.exportMode == "Individual": imageName = flipbookTexture.name @@ -300,7 +300,7 @@ def processTexRefNonCITextures(self, fMaterial: FMaterial, material: bpy.types.M imageKey = FImageKey(flipbookTexture.image, texProp.tex_format, texProp.ci_format, [flipbookTexture.image]) fImage = model.getTextureAndHandleShared(imageKey) if fImage is None: - imageName, filename = getTextureNamesFromImage(flipbookTexture.image, texProp.tex_format, model) + imageName, filename = getTextureNamesFromImage(flipbookTexture.image, texProp.tex_format, None, model) if flipbookProp.exportMode == "Individual": imageName = flipbookTexture.name fImage = FImage( From 9f6394dce72b6ede3c8b6791819455e9a1147401 Mon Sep 17 00:00:00 2001 From: Lucretia Date: Sat, 1 Aug 2026 17:58:24 +1000 Subject: [PATCH 3/5] Reformat w/ Black --- fast64_internal/z64/model_classes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fast64_internal/z64/model_classes.py b/fast64_internal/z64/model_classes.py index a2e2702b0..d63c01898 100644 --- a/fast64_internal/z64/model_classes.py +++ b/fast64_internal/z64/model_classes.py @@ -231,7 +231,9 @@ def processTexRefCITextures(self, fMaterial: FMaterial, material: bpy.types.Mate allImages = self.validateImages(material, index) for flipbookTexture in flipbookProp.textures: # print(f"Texture: {str(flipbookTexture.image)}") - imageName, filename = getTextureNamesFromImage(flipbookTexture.image, texProp.tex_format, texProp.ci_format, model) + imageName, filename = getTextureNamesFromImage( + flipbookTexture.image, texProp.tex_format, texProp.ci_format, model + ) if flipbookProp.exportMode == "Individual": imageName = flipbookTexture.name From 63ec1ca9eac7ad76e823e82bb7491d457f8853af Mon Sep 17 00:00:00 2001 From: Lucretia Date: Sun, 2 Aug 2026 00:45:26 +1000 Subject: [PATCH 4/5] Use underscore for full CI format suffix, move property to Fast64Settings_Properties --- __init__.py | 11 ++++++----- fast64_internal/repo_settings.py | 2 -- fast64_internal/utility.py | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/__init__.py b/__init__.py index e3d9b4248..12180176d 100644 --- a/__init__.py +++ b/__init__.py @@ -103,6 +103,8 @@ def draw(self, context): col = self.layout.column() col.scale_y = 1.1 # extra padding + fast64_settings: Fast64Settings_Properties = context.scene.fast64.settings + col.prop(context.scene, "f3d_simple", text="Simple Material UI") col.separator() @@ -114,7 +116,7 @@ def draw(self, context): prop_split(col, context.scene, "packed_normals_algorithm", "Packed normals alg") col.prop(context.scene, "saveTextures") if context.scene.saveTextures: - col.prop(context.scene, "textureNameIncludesCiFormat") + col.prop(fast64_settings, "texture_name_includes_ci_format") col.prop(context.scene, "exportInlineF3D", text="Bleed and Inline Material Exports") if context.scene.exportInlineF3D: multilineLabel( @@ -230,6 +232,7 @@ class Fast64Settings_Properties(bpy.types.PropertyGroup): description="When enabled, fast64 will default colored textures's format to RGBA even if they fit CI requirements, with the exception of textures that would not fit into TMEM otherwise", ) dont_ask_color_management: bpy.props.BoolProperty(name="Don't ask to set color management properties") + texture_name_includes_ci_format: bpy.props.BoolProperty(name="Include CI Format In File Name", default=False) repo_settings_tab: bpy.props.BoolProperty(default=True, name="Repo Settings") repo_settings_path: bpy.props.StringProperty(name="Path", subtype="FILE_PATH", update=repo_path_update) @@ -246,6 +249,7 @@ def to_repo_settings(self): data = {} data["autoLoad"] = self.auto_repo_load_settings data["autoPickTextureFormat"] = self.auto_pick_texture_format + data["textureNameIncludesCiFormat"] = self.texture_name_includes_ci_format if self.auto_pick_texture_format: data["preferRGBAOverCI"] = self.prefer_rgba_over_ci return data @@ -253,6 +257,7 @@ def to_repo_settings(self): def from_repo_settings(self, data: dict): set_prop_if_in_data(self, "auto_repo_load_settings", data, "autoLoad") set_prop_if_in_data(self, "auto_pick_texture_format", data, "autoPickTextureFormat") + set_prop_if_in_data(self, "texture_name_includes_ci_format", data, "textureNameIncludesCiFormat") set_prop_if_in_data(self, "prefer_rgba_over_ci", data, "preferRGBAOverCI") @@ -495,9 +500,6 @@ def register(): name="Game", default="SM64", items=gameEditorEnum, update=gameEditorUpdate ) bpy.types.Scene.saveTextures = bpy.props.BoolProperty(name="Save Textures As PNGs (May Break CI Textures)") - bpy.types.Scene.textureNameIncludesCiFormat = bpy.props.BoolProperty( - name="Include CI Format In File Name", default=False - ) bpy.types.Scene.exportHiddenGeometry = bpy.props.BoolProperty(name="Export Hidden Geometry", default=True) bpy.types.Scene.exportInlineF3D = bpy.props.BoolProperty( name="Bleed and Inline Material Exports", @@ -534,7 +536,6 @@ def unregister(): del bpy.types.Scene.fullTraceback del bpy.types.Scene.ignoreTextureRestrictions del bpy.types.Scene.saveTextures - del bpy.types.Scene.textureNameIncludesCiFormat del bpy.types.Scene.gameEditorMode del bpy.types.Scene.exportHiddenGeometry del bpy.types.Scene.blenderF3DScale diff --git a/fast64_internal/repo_settings.py b/fast64_internal/repo_settings.py index 206b11ee3..6faf3c494 100644 --- a/fast64_internal/repo_settings.py +++ b/fast64_internal/repo_settings.py @@ -84,7 +84,6 @@ def load_repo_settings(scene: Scene, path: os.PathLike, skip_if_no_auto_load=Fal fast64_settings.from_repo_settings(data) set_prop_if_in_data(scene, "f3d_type", data, "microcode") set_prop_if_in_data(scene, "saveTextures", data, "saveTextures") - set_prop_if_in_data(scene, "textureNameIncludesCiFormat", data, "textureNameIncludesCiFormat") set_prop_if_in_data(scene, "exportInlineF3D", data, "bleedAndInline") set_prop_if_in_data(scene, "ignoreTextureRestrictions", data, "ignoreTextureRestrictions") set_prop_if_in_data(scene, "packed_normals_algorithm", data, "packedNormalsAlgorithm") @@ -108,7 +107,6 @@ def save_repo_settings(scene: Scene, path: os.PathLike, game: str = ""): data["packedNormalsAlgorithm"] = scene.packed_normals_algorithm data["bleedAndInline"] = scene.exportInlineF3D data["saveTextures"] = scene.saveTextures - data["textureNameIncludesCiFormat"] = scene.textureNameIncludesCiFormat data["ignoreTextureRestrictions"] = scene.ignoreTextureRestrictions rdp_defaults: RDPSettings = scene.world.rdp_defaults data["rdpDefaults"] = rdp_defaults.to_dict() diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 00e0e9026..843105e1e 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1814,9 +1814,9 @@ def ootGetBaseOrCustomLight(prop, idx, toExport: bool, errIfMissing: bool): def getTextureSuffixFromFormat(texFmt: str, ciFmt: str | None, isPalette: bool): - if bpy.context.scene.textureNameIncludesCiFormat: + if bpy.context.scene.fast64.settings.texture_name_includes_ci_format: if ciFmt: - fmtName = f"{texFmt}-{ciFmt}" + fmtName = f"{texFmt}_{ciFmt}" else: fmtName = texFmt else: From 1609e504fa61d515c2b7bc5e7a1f9404b7d4a9a4 Mon Sep 17 00:00:00 2001 From: Lucretia Date: Sun, 2 Aug 2026 00:52:23 +1000 Subject: [PATCH 5/5] Tidy F3D_GlobalSettingsPanel to match Fast64_GlobalSettingsPanel --- __init__.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/__init__.py b/__init__.py index 12180176d..efaac76a3 100644 --- a/__init__.py +++ b/__init__.py @@ -103,29 +103,30 @@ def draw(self, context): col = self.layout.column() col.scale_y = 1.1 # extra padding - fast64_settings: Fast64Settings_Properties = context.scene.fast64.settings + scene = context.scene + fast64_settings: Fast64Settings_Properties = scene.fast64.settings - col.prop(context.scene, "f3d_simple", text="Simple Material UI") + col.prop(scene, "f3d_simple", text="Simple Material UI") col.separator() col.label(text="Saved to Repo Settings file", icon="PROPERTIES") - prop_split(col, context.scene, "f3d_type", "Microcode") + prop_split(col, scene, "f3d_type", "Microcode") gbi = get_F3D_GBI() - if context.scene.f3d_type in {"F3DEX3", "T3D"}: - prop_split(col, context.scene, "packed_normals_algorithm", "Packed normals alg") - col.prop(context.scene, "saveTextures") - if context.scene.saveTextures: + if scene.f3d_type in {"F3DEX3", "T3D"}: + prop_split(col, scene, "packed_normals_algorithm", "Packed normals alg") + col.prop(scene, "saveTextures") + if scene.saveTextures: col.prop(fast64_settings, "texture_name_includes_ci_format") - col.prop(context.scene, "exportInlineF3D", text="Bleed and Inline Material Exports") - if context.scene.exportInlineF3D: + col.prop(scene, "exportInlineF3D", text="Bleed and Inline Material Exports") + if scene.exportInlineF3D: multilineLabel( col.box(), "While inlining, all meshes will be restored to world default values.\n You can configure these values in the world properties tab.", icon="INFO", ) - col.prop(context.scene, "ignoreTextureRestrictions") - if context.scene.ignoreTextureRestrictions: + col.prop(scene, "ignoreTextureRestrictions") + if scene.ignoreTextureRestrictions: col.box().label(text="Width/height must be < 1024. Must be png format.")