|
29 | 29 | import mmap |
30 | 30 | import cProfile |
31 | 31 | import json |
| 32 | +from enum import Enum |
32 | 33 |
|
33 | 34 | BLENDER_30 = bpy.app.version[0] >= 3 |
34 | 35 | BLENDER_29 = (bpy.app.version[0] == 2 and bpy.app.version[1] >= 90) \ |
@@ -167,39 +168,124 @@ def get_filename_without_extension(file_path): |
167 | 168 |
|
168 | 169 | # src\helpers\export_tex.py |
169 | 170 |
|
170 | | -def export_tex(texture, out_folder, use_wimgt): |
171 | | - if not texture.image: |
172 | | - return |
| 171 | +# Enum for selecting the export mode |
| 172 | +class ExportMode(Enum): |
| 173 | + LEGACY = 1 |
| 174 | + WIMGT = 2 |
| 175 | + RSZST = 3 |
| 176 | + |
| 177 | +# Doesn't support encoding settings. Just passes a PNG in |
| 178 | +def export_tex_legacy(texture, out_folder): |
| 179 | + if not texture.image: |
| 180 | + return |
| 181 | + |
| 182 | + tex_name = get_filename_without_extension(texture.image.name) if BLENDER_28 else texture.name |
| 183 | + print("ExportTex: %s" % tex_name) |
| 184 | + |
| 185 | + # Force PNG |
| 186 | + texture.image.file_format = 'PNG' |
| 187 | + |
| 188 | + # Save image as PNG |
| 189 | + texture_outpath = os.path.join(out_folder, tex_name) + ".png" |
| 190 | + texture.image.save_render(texture_outpath) |
| 191 | + |
| 192 | +# Uses WIMGT to encode the PNG first, with some settings |
| 193 | +def export_tex_wimgt(texture, out_folder): |
| 194 | + if not texture.image: |
| 195 | + return |
173 | 196 |
|
174 | | - tex_name = get_filename_without_extension(texture.image.name) if BLENDER_28 else texture.name |
175 | | - print("ExportTex: %s" % tex_name) |
176 | | - # Force PNG |
177 | | - texture.image.file_format = 'PNG' |
178 | | - # save image as PNNG |
179 | | - texture_outpath = os.path.join(out_folder, tex_name) + ".png" |
180 | | - if use_wimgt: |
| 197 | + tex_name = get_filename_without_extension(texture.image.name) if BLENDER_28 else texture.name |
| 198 | + print("ExportTex: %s" % tex_name) |
| 199 | + |
| 200 | + # Force PNG |
| 201 | + texture.image.file_format = 'PNG' |
| 202 | + |
| 203 | + # Save image as PNG with a temporary name |
181 | 204 | temp_output_name = str(binascii.b2a_hex(os.urandom(15))) |
182 | 205 | texture_outpath = os.path.join(out_folder, temp_output_name) + ".png" |
183 | | - tex0_outpath = os.path.join(out_folder, tex_name) + ".tex0" |
184 | | - texture.image.save_render(texture_outpath) |
185 | | - # determine format |
186 | | - # print(best_tex_format(texture)) |
187 | | - tformat_string = ( |
188 | | - texture.brres_manual_format if texture.brres_mode == 'manual' else best_tex_format( |
189 | | - texture)).upper() |
190 | | - # determine mipmaps |
191 | | - mm_string = "--mipmap-size=%s" % texture.brres_mipmap_minsize |
192 | | - if texture.brres_mipmap_mode == 'manual': |
193 | | - mm_string = "--n-mm=%s" % texture.brres_mipmap_manual |
194 | | - elif texture.brres_mipmap_mode == 'none': |
195 | | - mm_string = "--n-mm=0" |
196 | | - #Encode textures |
197 | | - if use_wimgt: |
| 206 | + tex0_outpath = os.path.join(out_folder, tex_name) + ".tex0" |
| 207 | + texture.image.save_render(texture_outpath) |
| 208 | + |
| 209 | + # Determine format |
| 210 | + tformat_string = ( |
| 211 | + texture.brres_manual_format if texture.brres_mode == 'manual' else best_tex_format( |
| 212 | + texture)).upper() |
| 213 | + |
| 214 | + # Determine mipmaps |
| 215 | + mm_string = "--mipmap-size=%s" % texture.brres_mipmap_minsize |
| 216 | + if texture.brres_mipmap_mode == 'manual': |
| 217 | + mm_string = "--n-mm=%s" % texture.brres_mipmap_manual |
| 218 | + elif texture.brres_mipmap_mode == 'none': |
| 219 | + mm_string = "--n-mm=0" |
| 220 | + |
| 221 | + # Encode textures using wimgt |
198 | 222 | print("EncodeTex: %s" % tex_name) |
199 | 223 | wimgt = r'wimgt encode "{0}" --transform {1} {2} --dest "{3}" -o'.format(texture_outpath, tformat_string, mm_string, tex0_outpath) |
200 | 224 | os.system(wimgt) |
201 | 225 | os.remove(texture_outpath) |
202 | 226 |
|
| 227 | +# Uses RSZST to encode the PNG first, with options for mipmaps and format |
| 228 | +def export_tex_rszst(texture, out_folder): |
| 229 | + if not texture.image: |
| 230 | + return |
| 231 | + |
| 232 | + tex_name = get_filename_without_extension(texture.image.name) if BLENDER_28 else texture.name |
| 233 | + print("ExportTex: %s" % tex_name) |
| 234 | + |
| 235 | + # Force PNG |
| 236 | + texture.image.file_format = 'PNG' |
| 237 | + |
| 238 | + # Save image as PNG with a temporary name |
| 239 | + temp_output_name = str(binascii.b2a_hex(os.urandom(15))) |
| 240 | + texture_outpath = os.path.join(out_folder, temp_output_name) + ".png" |
| 241 | + tex0_outpath = os.path.join(out_folder, tex_name) + ".tex0" |
| 242 | + texture.image.save_render(texture_outpath) |
| 243 | + |
| 244 | + # Determine format |
| 245 | + tformat_string = ( |
| 246 | + texture.brres_manual_format if texture.brres_mode == 'manual' else best_tex_format( |
| 247 | + texture)).upper() |
| 248 | + |
| 249 | + tformat_idx = str({ |
| 250 | + 'I4': 0, |
| 251 | + 'I8': 1, |
| 252 | + 'IA4': 2, |
| 253 | + 'IA8': 3, |
| 254 | + 'RGB565': 4, |
| 255 | + 'RGB5A3': 5, |
| 256 | + 'RGBA8': 6, |
| 257 | + 'C4': 8, |
| 258 | + 'C8': 9, |
| 259 | + # C14 not supported by NW4R g3d |
| 260 | + 'CMPR': 14, |
| 261 | + }[tformat_string]) |
| 262 | + |
| 263 | + # Determine mipmaps |
| 264 | + mipmaps_flag = "--mipmaps" if texture.brres_mipmap_mode != 'none' else "" |
| 265 | + min_mip = "--min-mip=%s" % texture.brres_mipmap_minsize |
| 266 | + max_mip = "--max-mip=%s" % texture.brres_mipmap_manual if texture.brres_mipmap_mode == 'manual' else "--max-mip=5" |
| 267 | + |
| 268 | + # Construct the RSZST command |
| 269 | + print("EncodeTex: %s" % tex_name) |
| 270 | + context = bpy.context |
| 271 | + bin_root = os.path.abspath(get_rs_prefs(context).riistudio_directory) |
| 272 | + rszst = os.path.join(bin_root, "rszst.exe") |
| 273 | + rszst_command = f'{rszst} import-tex0 {mipmaps_flag} {min_mip} {max_mip} --format {tformat_idx} "{texture_outpath}" "{tex0_outpath}"' |
| 274 | + os.system(rszst_command) |
| 275 | + |
| 276 | + # Clean up the temporary PNG file |
| 277 | + os.remove(texture_outpath) |
| 278 | + |
| 279 | +def export_tex(texture, out_folder, mode): |
| 280 | + if mode == ExportMode.LEGACY: |
| 281 | + export_tex_legacy(texture, out_folder) |
| 282 | + elif mode == ExportMode.WIMGT: |
| 283 | + export_tex_wimgt(texture, out_folder) |
| 284 | + elif mode == ExportMode.RSZST: |
| 285 | + export_tex_rszst(texture, out_folder) |
| 286 | + else: |
| 287 | + raise ValueError("Invalid export mode selected") |
| 288 | + |
203 | 289 | # src\panels\BRRESTexturePanel.py |
204 | 290 |
|
205 | 291 | class BRRESTexturePanel(bpy.types.Panel): |
@@ -944,8 +1030,13 @@ def export_textures(textures_path,selection,params): |
944 | 1030 | os.makedirs(textures_path) |
945 | 1031 |
|
946 | 1032 | for tex in all_textures(selection): |
947 | | - use_wimgt = wimgt_installed and params.flags.texture_encoder == 'wimgt' |
948 | | - export_tex(tex, textures_path, use_wimgt) |
| 1033 | + if params.flags.texture_encoder == 'rszst': |
| 1034 | + mode = ExportMode.RSZST |
| 1035 | + elif wimgt_installed and params.flags.texture_encoder == 'wimgt': |
| 1036 | + mode = ExportMode.WIMGT |
| 1037 | + else: |
| 1038 | + mode = ExportMode.LEGACY |
| 1039 | + export_tex(tex, textures_path, mode) |
949 | 1040 |
|
950 | 1041 | def build_rs_mat(mat, texture_name): |
951 | 1042 | # Swap Table |
@@ -1305,7 +1396,7 @@ def __init__(self, pos="float", nrm="float", uv="float", clr="auto"): |
1305 | 1396 |
|
1306 | 1397 | class ConverterFlags: |
1307 | 1398 | def __init__(self, split_mesh_by_material=True, mesh_conversion_mode='PREVIEW', |
1308 | | - add_dummy_colors = True, ignore_cache = False, texture_encoder='wimgt', write_metadata = False): |
| 1399 | + add_dummy_colors = True, ignore_cache = False, texture_encoder='rszst', write_metadata = False): |
1309 | 1400 |
|
1310 | 1401 | self.split_mesh_by_material = split_mesh_by_material |
1311 | 1402 | self.mesh_conversion_mode = mesh_conversion_mode |
@@ -1575,12 +1666,13 @@ class RHST_RNA: |
1575 | 1666 | if BLENDER_29: verbose : verbose |
1576 | 1667 |
|
1577 | 1668 | texture_encoder = EnumProperty( |
1578 | | - name="Encoder", |
| 1669 | + name="Texture Codec", |
1579 | 1670 | items=( |
1580 | | - ('wimgt',"wimgt","Use wimgt to encode textures"), |
1581 | | - ('rs',"RiiStudio","Use RiiStudio to encode textures") |
| 1671 | + ('rszst', "rszst (RiiStudio)", "Use RiiStudio CLI to encode textures"), |
| 1672 | + ('wimgt', "wimgt (Wiimm)", "Use wimgt to encode textures"), |
| 1673 | + ('rs', "Legacy", "Don't pre-encode textures") |
1582 | 1674 | ), |
1583 | | - default="wimgt", |
| 1675 | + default="rszst", |
1584 | 1676 | description="Select which encoder to use", |
1585 | 1677 | ) |
1586 | 1678 | if BLENDER_29: texture_encoder : texture_encoder |
@@ -1663,9 +1755,11 @@ def draw_rhst_options(self, context): |
1663 | 1755 | box.prop(self, 'verbose') |
1664 | 1756 |
|
1665 | 1757 | # Textures |
1666 | | - if(self.get_wimgt_installed): |
| 1758 | + # This method was never actually called, replacing with True |
| 1759 | + # if(self.get_wimgt_installed): |
| 1760 | + if True: |
1667 | 1761 | box = layout.box() |
1668 | | - box.label(text="Textures", icon='TEXTURE') |
| 1762 | + box.label(text="Texture Codec", icon='TEXTURE') |
1669 | 1763 | row = box.row(align=True) |
1670 | 1764 | row.label(text="Encoder") |
1671 | 1765 | row.prop(self, "texture_encoder", expand=True) |
@@ -2529,7 +2623,7 @@ def register_tex(): |
2529 | 2623 | ) |
2530 | 2624 | tex_type.brres_mipmap_mode = EnumProperty( |
2531 | 2625 | items=( |
2532 | | - ('auto', "Auto", "Allow the conversion tool to determine best mipmapping level (currently wimgt)"), |
| 2626 | + ('auto', "Auto", "Allow the conversion tool to determine best mipmapping level"), |
2533 | 2627 | ('manual', "Manual", "Specify the number of mipmaps"), |
2534 | 2628 | ('none', "None", "Do not perform mipmapping (the same as manual > 0)") |
2535 | 2629 | ), |
|
0 commit comments