From def06552e37544f0f79d47fd11144a3cfcee52b3 Mon Sep 17 00:00:00 2001 From: Lilaa3 <87947656+Lilaa3@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:14:01 +0000 Subject: [PATCH 1/7] Prefer CI over rgba toggle for automatic texture format --- __init__.py | 2 ++ fast64_internal/f3d/f3d_material.py | 14 +++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/__init__.py b/__init__.py index a4ef80e71..0da2ad8a4 100644 --- a/__init__.py +++ b/__init__.py @@ -212,6 +212,7 @@ def draw(self, context): prop_split(col, context.scene, "gameEditorMode", "Game") col.prop(context.scene, "exportHiddenGeometry") col.prop(context.scene, "fullTraceback") + col.prop(context.scene.fast64.settings, "prefer_ci_over_rgba") prop_split(col, context.scene.fast64.settings, "anim_range_choice", "Anim Range") @@ -267,6 +268,7 @@ class Fast64Settings_Properties(bpy.types.PropertyGroup): ], default="intersect_action_and_scene", ) + prefer_ci_over_rgba: bpy.props.BoolProperty(name="Prefer Color Indexed Over RGBA") class Fast64_Properties(bpy.types.PropertyGroup): diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index da1794e50..b66ab5ab4 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -3377,7 +3377,7 @@ def getOptimalFormat(tex, curFormat, isMultitexture): isGreyscale = True hasAlpha4bit = False hasAlpha1bit = False - pixelValues = [] + pixelValues = set() # N64 is -Y, Blender is +Y pixels = tex.pixels[:] @@ -3392,12 +3392,11 @@ def getOptimalFormat(tex, curFormat, isMultitexture): hasAlpha4bit = True if color[3] < 0.5: hasAlpha1bit = True - pixelColor = getRGBA16Tuple(color) - if pixelColor not in pixelValues: - pixelValues.append(pixelColor) + pixelValues.add(getRGBA16Tuple(color)) + tex_size = tex.size[0] * tex.size[1] if isGreyscale: - if tex.size[0] * tex.size[1] > 4096: + if tex_size > 4096: if not hasAlpha1bit: texFormat = "I4" else: @@ -3408,9 +3407,10 @@ def getOptimalFormat(tex, curFormat, isMultitexture): else: texFormat = "IA8" else: - if len(pixelValues) <= 16: + prefer_ci_over_rgba = bpy.context.scene.fast64.settings.prefer_ci_over_rgba + if (prefer_ci_over_rgba and len(pixelValues) <= 16) or (len(pixelValues) <= 16 and tex_size > 2048): texFormat = "CI4" - elif len(pixelValues) <= 256 and tex.size[0] * tex.size[1] <= 2048: + elif prefer_ci_over_rgba and len(pixelValues) <= 256 and tex_size <= 2048: texFormat = "CI8" else: texFormat = "RGBA16" From 514e5ce3287c0b2052377b090d2ede8090ad0c60 Mon Sep 17 00:00:00 2001 From: Lilaa3 <87947656+Lilaa3@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:40:49 +0000 Subject: [PATCH 2/7] Devwizard's suggestion --- fast64_internal/f3d/f3d_material.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index b66ab5ab4..29e70cdc9 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -3394,9 +3394,9 @@ def getOptimalFormat(tex, curFormat, isMultitexture): hasAlpha1bit = True pixelValues.add(getRGBA16Tuple(color)) - tex_size = tex.size[0] * tex.size[1] + n_size = tex.size[0] * tex.size[1] if isGreyscale: - if tex_size > 4096: + if n_size > 4096: if not hasAlpha1bit: texFormat = "I4" else: @@ -3408,9 +3408,9 @@ def getOptimalFormat(tex, curFormat, isMultitexture): texFormat = "IA8" else: prefer_ci_over_rgba = bpy.context.scene.fast64.settings.prefer_ci_over_rgba - if (prefer_ci_over_rgba and len(pixelValues) <= 16) or (len(pixelValues) <= 16 and tex_size > 2048): + if (prefer_ci_over_rgba and len(pixelValues) <= 16) or (len(pixelValues) <= 16 and n_size > 2048): texFormat = "CI4" - elif prefer_ci_over_rgba and len(pixelValues) <= 256 and tex_size <= 2048: + elif prefer_ci_over_rgba and len(pixelValues) <= 256 and n_size <= 2048: texFormat = "CI8" else: texFormat = "RGBA16" From 257b3b7caf2aee78f1ef6eed6dc9880079323422 Mon Sep 17 00:00:00 2001 From: Lilaa3 <87947656+Lilaa3@users.noreply.github.com> Date: Mon, 22 Jan 2024 18:44:31 +0000 Subject: [PATCH 3/7] First description attempt --- __init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0da2ad8a4..d37928e14 100644 --- a/__init__.py +++ b/__init__.py @@ -268,7 +268,10 @@ class Fast64Settings_Properties(bpy.types.PropertyGroup): ], default="intersect_action_and_scene", ) - prefer_ci_over_rgba: bpy.props.BoolProperty(name="Prefer Color Indexed Over RGBA") + prefer_ci_over_rgba: bpy.props.BoolProperty( + name="Prefer CI Over RGBA", + description="When enabled, fast64 will use ci instead of rgba even if the texture fits as an rgba16 for performance.", + ) class Fast64_Properties(bpy.types.PropertyGroup): From 5bee1cd9e7aaa348e1c178d6791532312004eda7 Mon Sep 17 00:00:00 2001 From: Lilaa3 <87947656+Lilaa3@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:00:27 +0000 Subject: [PATCH 4/7] Second attempt --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index d37928e14..b575bedf6 100644 --- a/__init__.py +++ b/__init__.py @@ -270,7 +270,7 @@ class Fast64Settings_Properties(bpy.types.PropertyGroup): ) prefer_ci_over_rgba: bpy.props.BoolProperty( name="Prefer CI Over RGBA", - description="When enabled, fast64 will use ci instead of rgba even if the texture fits as an rgba16 for performance.", + description="When enabled, fast64 will default colored textures that fit ci requirements to ci instead of rgba even if the texture fits as an rgba16 for the sake of performance", ) From 31a7a541d6e17015077a7e1621d47d3958859317 Mon Sep 17 00:00:00 2001 From: Lila <87947656+Lilaa3@users.noreply.github.com> Date: Sat, 6 Apr 2024 15:32:38 +0100 Subject: [PATCH 5/7] final commit (hopefully) Invert the logic, account for multi texture, hopefully improve code, toggle to skip auto pick, better descriptions (i think) --- __init__.py | 16 +++- fast64_internal/f3d/f3d_material.py | 136 ++++++++++++++++------------ 2 files changed, 87 insertions(+), 65 deletions(-) diff --git a/__init__.py b/__init__.py index b575bedf6..ceffb0a5a 100644 --- a/__init__.py +++ b/__init__.py @@ -212,7 +212,9 @@ def draw(self, context): prop_split(col, context.scene, "gameEditorMode", "Game") col.prop(context.scene, "exportHiddenGeometry") col.prop(context.scene, "fullTraceback") - col.prop(context.scene.fast64.settings, "prefer_ci_over_rgba") + col.separator() + col.prop(context.scene.fast64.settings, "auto_pick_texture_format") + col.prop(context.scene.fast64.settings, "prefer_rgba_over_ci") prop_split(col, context.scene.fast64.settings, "anim_range_choice", "Anim Range") @@ -268,11 +270,15 @@ class Fast64Settings_Properties(bpy.types.PropertyGroup): ], default="intersect_action_and_scene", ) - prefer_ci_over_rgba: bpy.props.BoolProperty( - name="Prefer CI Over RGBA", - description="When enabled, fast64 will default colored textures that fit ci requirements to ci instead of rgba even if the texture fits as an rgba16 for the sake of performance", + auto_pick_texture_format: bpy.props.BoolProperty( + name="Auto Pick Texture Format", + description="When enabled, fast64 will try to pick the best texture format whenever a texture is selected.", + default=True, + ) + prefer_rgba_over_ci: bpy.props.BoolProperty( + name="Prefer RGBA Over CI", + 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", ) - class Fast64_Properties(bpy.types.PropertyGroup): """ diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index 29e70cdc9..3af6079a9 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -1898,23 +1898,90 @@ def update_tex_values_index(self: Material, *, texProperty: "TextureProperty", t tex_I_node.node_tree = desired_node +def get_color_info_from_tex(tex: bpy.types.Image): + is_greyscale, has_alpha_4_bit, has_alpha_1_bit = True, False, False + rgba_colors: set[int] = set() + + pixels, channel_count = tex.pixels, tex.channels + + for x in range(tex.size[0]): + for y in range(tex.size[1]): # N64 is -Y, Blender is +Y, in this context this doesn´t matter + pixel_color = [1, 1, 1, 1] + for field in range(channel_count): + pixel_color[field] = pixels[(y * tex.size[0] + x) * channel_count + field] + rgba_colors.add(getRGBA16Tuple(pixel_color)) + + if not (pixel_color[0] == pixel_color[1] and pixel_color[1] == pixel_color[2]): + is_greyscale = False + + if pixel_color[3] < 0.9375: + has_alpha_4_bit = True + if pixel_color[3] < 0.5: + has_alpha_1_bit = True + + return is_greyscale, has_alpha_1_bit, has_alpha_4_bit, rgba_colors + + +def get_optimal_format(tex: bpy.types.Image | None, prefer_rgba_over_ci: bool): + if not tex: + return "RGBA16" + + n_size = tex.size[0] * tex.size[1] + if n_size > 8192: # Image is too big + return "RGBA16" + + is_greyscale, has_alpha_1_bit, has_alpha_4_bit, rgba_colors = get_color_info_from_tex(tex) + + if is_greyscale: + if n_size > 4096: + if has_alpha_1_bit: + return "IA4" + return "I4" + + if has_alpha_4_bit: + return "IA8" + + return "I8" + else: + if len(rgba_colors) <= 16 and (not prefer_rgba_over_ci or n_size > 2048): + return "CI4" + if not prefer_rgba_over_ci and len(rgba_colors) <= 256: + return "CI8" + + return "RGBA16" + + def update_tex_values_and_formats(self, context): with F3DMaterial_UpdateLock(get_material_from_context(context)) as material: if not material: return - f3d_mat = context.material.f3d_mat + settings_props = context.scene.fast64.settings + if not settings_props.auto_pick_texture_format: + update_tex_values_manual(material, context) + return + + f3d_mat: F3DMaterialProperty = material.f3d_mat useDict = all_combiner_uses(f3d_mat) - isMultiTexture = ( - useDict["Texture 0"] - and f3d_mat.tex0.tex is not None - and useDict["Texture 1"] - and f3d_mat.tex1.tex is not None + tex0_props = f3d_mat.tex0 + tex1_props = f3d_mat.tex1 + + tex0, tex1 = tex0_props.tex if useDict["Texture 0"] else None, ( + tex1_props.tex if useDict["Texture 1"] else None ) - if self.tex is not None: - self.tex_format = getOptimalFormat(self.tex, self.tex_format, isMultiTexture) - update_tex_values_manual(context.material, context) + if tex0: + tex0_props.tex_format = get_optimal_format(tex0, settings_props.prefer_rgba_over_ci) + if tex1: + tex1_props.tex_format = get_optimal_format(tex1, settings_props.prefer_rgba_over_ci) + + if tex0 and tex1: + if tex0_props.tex_format.startswith("CI") and not tex1_props.tex_format.startswith("CI"): + tex0_props.tex_format = "RGBA16" + elif tex1_props.tex_format.startswith("CI") and not tex0_props.tex_format.startswith("CI"): + tex1_props.tex_format = "RGBA16" + + update_tex_values_manual(material, context) def update_tex_values(self, context): @@ -3367,57 +3434,6 @@ def execute(self, context): return {"FINISHED"} -def getOptimalFormat(tex, curFormat, isMultitexture): - texFormat = "RGBA16" - if isMultitexture: - return curFormat - if tex.size[0] * tex.size[1] > 8192: # Image too big - return curFormat - - isGreyscale = True - hasAlpha4bit = False - hasAlpha1bit = False - pixelValues = set() - - # N64 is -Y, Blender is +Y - pixels = tex.pixels[:] - for j in reversed(range(tex.size[1])): - for i in range(tex.size[0]): - color = [1, 1, 1, 1] - for field in range(tex.channels): - color[field] = pixels[(j * tex.size[0] + i) * tex.channels + field] - if not (color[0] == color[1] and color[1] == color[2]): - isGreyscale = False - if color[3] < 0.9375: - hasAlpha4bit = True - if color[3] < 0.5: - hasAlpha1bit = True - pixelValues.add(getRGBA16Tuple(color)) - - n_size = tex.size[0] * tex.size[1] - if isGreyscale: - if n_size > 4096: - if not hasAlpha1bit: - texFormat = "I4" - else: - texFormat = "IA4" - else: - if not hasAlpha4bit: - texFormat = "I8" - else: - texFormat = "IA8" - else: - prefer_ci_over_rgba = bpy.context.scene.fast64.settings.prefer_ci_over_rgba - if (prefer_ci_over_rgba and len(pixelValues) <= 16) or (len(pixelValues) <= 16 and n_size > 2048): - texFormat = "CI4" - elif prefer_ci_over_rgba and len(pixelValues) <= 256 and n_size <= 2048: - texFormat = "CI8" - else: - texFormat = "RGBA16" - - return texFormat - - def getCurrentPresetDir(): return "f3d/" + bpy.context.scene.gameEditorMode.lower() From 4d513a1e4e67252271b13c46eea373028c1011d8 Mon Sep 17 00:00:00 2001 From: Lila <87947656+Lilaa3@users.noreply.github.com> Date: Sat, 6 Apr 2024 15:39:23 +0100 Subject: [PATCH 6/7] Weird that this didn't autoformat --- __init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/__init__.py b/__init__.py index ceffb0a5a..f18e6db20 100644 --- a/__init__.py +++ b/__init__.py @@ -280,6 +280,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", ) + class Fast64_Properties(bpy.types.PropertyGroup): """ Properties in scene.fast64. From e2b43a0164196f670e56e86bf063894b52c93ab1 Mon Sep 17 00:00:00 2001 From: Lila <87947656+Lilaa3@users.noreply.github.com> Date: Sat, 6 Apr 2024 18:42:06 +0100 Subject: [PATCH 7/7] changes to ui draw put anim range above texture format settings, dynamic ui, reduced repetition of context.scene.fast64.settings --- __init__.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/__init__.py b/__init__.py index f18e6db20..805d62157 100644 --- a/__init__.py +++ b/__init__.py @@ -209,13 +209,20 @@ def poll(cls, context): def draw(self, context): col = self.layout.column() col.scale_y = 1.1 # extra padding - prop_split(col, context.scene, "gameEditorMode", "Game") - col.prop(context.scene, "exportHiddenGeometry") - col.prop(context.scene, "fullTraceback") + + scene = context.scene + fast64_settings: Fast64Settings_Properties = scene.fast64.settings + + prop_split(col, scene, "gameEditorMode", "Game") + col.prop(scene, "exportHiddenGeometry") + col.prop(scene, "fullTraceback") + prop_split(col, fast64_settings, "anim_range_choice", "Anim Range") + col.separator() - col.prop(context.scene.fast64.settings, "auto_pick_texture_format") - col.prop(context.scene.fast64.settings, "prefer_rgba_over_ci") - prop_split(col, context.scene.fast64.settings, "anim_range_choice", "Anim Range") + + col.prop(fast64_settings, "auto_pick_texture_format") + if fast64_settings.auto_pick_texture_format: + col.prop(fast64_settings, "prefer_rgba_over_ci") class Fast64_GlobalToolsPanel(bpy.types.Panel):