From cba98cde7cac28210021403fccefa67d38b6c235 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:30:09 +0200 Subject: [PATCH 1/9] fix #742 --- fast64_internal/f3d/f3d_texture_writer.py | 41 ++++++++++++++--------- fast64_internal/f3d/f3d_writer.py | 5 +++ 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/fast64_internal/f3d/f3d_texture_writer.py b/fast64_internal/f3d/f3d_texture_writer.py index 2b22cd7da..b7609b427 100644 --- a/fast64_internal/f3d/f3d_texture_writer.py +++ b/fast64_internal/f3d/f3d_texture_writer.py @@ -131,7 +131,7 @@ def initWithFace(self, obj, face): raise PluginError( f"Large texture material {self.materialName} has a face that needs" + f" to cover texels {self.sl}-{self.sh} x {self.tl}-{self.th}" - + f" ({self.sh-self.sl+1} x {self.th-self.tl+1} texels) " + + f" ({self.sh - self.sl + 1} x {self.th - self.tl + 1} texels) " + f"in format {self.texFormat}, which can't fit in TMEM." ) self.faces.append(face) @@ -635,23 +635,34 @@ def __init__( if self.ti0.useTex and self.ti1.useTex: if self.ti0.isTexCI != self.ti1.isTexCI: raise PluginError("N64 does not support CI + non-CI texture. Must be both CI or neither CI.") - if ( - self.ti0.isTexRef - and self.ti1.isTexRef - and self.ti0.texProp.tex_reference == self.ti1.texProp.tex_reference - and self.ti0.texProp.tex_reference_size != self.ti1.texProp.tex_reference_size - ): - raise PluginError("Two textures with the same reference must have the same size.") + + def assert_size(is_pal: bool): + p_str = " palette " if is_pal else " " + + is_ref_enabled = ( + self.ti0.texProp.pal_reference == self.ti1.texProp.pal_reference + if is_pal + else self.ti0.texProp.tex_reference == self.ti1.texProp.tex_reference + ) + + size_1 = ( + list(self.ti0.texProp.pal_reference_size) if is_pal else list(self.ti0.texProp.tex_reference_size) + ) + size_2 = ( + list(self.ti1.texProp.pal_reference_size) if is_pal else list(self.ti1.texProp.tex_reference_size) + ) + is_same_size = size_1[0] == size_2[0] and size_1[0] == size_2[0] + + if self.ti0.isTexRef and self.ti1.isTexRef and is_ref_enabled and not is_same_size: + raise PluginError(f"Two textures with the same{p_str}reference must have the same{p_str}size.") + + assert_size(False) + if self.isCI: if self.ti0.palFormat != self.ti1.palFormat: raise PluginError("Both CI textures must use the same palette format (usually RGBA16).") - if ( - self.ti0.isTexRef - and self.ti1.isTexRef - and self.ti0.texProp.pal_reference == self.ti1.texProp.pal_reference - and self.ti0.texProp.pal_reference_size != self.ti1.texProp.pal_reference_size - ): - raise PluginError("Two textures with the same palette reference must have the same palette size.") + + assert_size(True) self.palFormat = self.ti0.palFormat if self.ti0.useTex else self.ti1.palFormat diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index f6f2c5b1c..018a27493 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -259,6 +259,11 @@ def fixLargeUVs(obj): " that are assigned to an empty material slot." ) + # ignore materials using texture references (avoids getTexDimensions throwing an error) + # TODO: find a better way + if material.f3d_mat.tex0.use_tex_reference and material.f3d_mat.tex1.use_tex_reference: + continue + if material not in texSizeDict: texSizeDict[material] = getTexDimensions(material) if material.f3d_mat.use_large_textures: From dae5c6605b9b4bc183af1308e77d76382e49306b Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Mon, 28 Sep 2026 15:07:16 +0200 Subject: [PATCH 2/9] impl #743 --- fast64_internal/z64/exporter/__init__.py | 2 +- fast64_internal/z64/exporter/scene/rooms.py | 9 +-- .../z64/exporter/skeleton/functions.py | 2 +- fast64_internal/z64/f3d/operators.py | 4 +- fast64_internal/z64/f3d/properties.py | 4 ++ fast64_internal/z64/importer/scene.py | 2 +- fast64_internal/z64/model_classes.py | 66 ++++++++++++++----- fast64_internal/z64/scene/properties.py | 4 ++ .../z64/skeleton/importer/functions.py | 2 +- fast64_internal/z64/skeleton/properties.py | 4 ++ 10 files changed, 67 insertions(+), 32 deletions(-) diff --git a/fast64_internal/z64/exporter/__init__.py b/fast64_internal/z64/exporter/__init__.py index f88205c18..9edfc8b18 100644 --- a/fast64_internal/z64/exporter/__init__.py +++ b/fast64_internal/z64/exporter/__init__.py @@ -76,7 +76,7 @@ def create_scene(originalSceneObj: Object, transform: Matrix, exportInfo: Export sceneObj, transform, exportInfo, - OOTModel(f"{sceneName}_dl", DLFormat.Static, False), + OOTModel(f"{sceneName}_dl", DLFormat.Static, False, context="scene"), ) newScene.validateScene() diff --git a/fast64_internal/z64/exporter/scene/rooms.py b/fast64_internal/z64/exporter/scene/rooms.py index 02a75d7f4..9b29b3a12 100644 --- a/fast64_internal/z64/exporter/scene/rooms.py +++ b/fast64_internal/z64/exporter/scene/rooms.py @@ -50,14 +50,7 @@ def new( original_room_obj, roomObj, roomHeader.roomShape, - model.addSubModel( - OOTModel( - f"{roomName}_dl", - model.DLFormat, - None, - model.draw_config, - ) - ), + model.addSubModel(OOTModel(f"{roomName}_dl", model.DLFormat, None, model.draw_config, context="scene")), roomIndex, sceneName, exportInfo, diff --git a/fast64_internal/z64/exporter/skeleton/functions.py b/fast64_internal/z64/exporter/skeleton/functions.py index 8c2e35724..48e3269c3 100644 --- a/fast64_internal/z64/exporter/skeleton/functions.py +++ b/fast64_internal/z64/exporter/skeleton/functions.py @@ -264,7 +264,7 @@ def ootConvertArmatureToC( removeVanillaData = settings.removeVanillaData optimize = settings.optimize - fModel = OOTModel(skeletonName, DLFormat, drawLayer) + fModel = OOTModel(skeletonName, DLFormat, drawLayer, context="skeleton") skeleton, fModel = ootConvertArmatureToSkeletonWithMesh( originalArmatureObj, convertTransformMatrix, fModel, skeletonName, not savePNG, drawLayer, optimize ) diff --git a/fast64_internal/z64/f3d/operators.py b/fast64_internal/z64/f3d/operators.py index fd25af0f5..61a543132 100644 --- a/fast64_internal/z64/f3d/operators.py +++ b/fast64_internal/z64/f3d/operators.py @@ -69,7 +69,7 @@ def ootConvertMeshToC( try: obj, allObjs = ootDuplicateHierarchy(originalObj, None, False, OOTObjectCategorizer()) - fModel = OOTModel(obj_name, DLFormat, None) + fModel = OOTModel(obj_name, DLFormat, None, context="dl") triConverterInfo = TriangleConverterInfo(obj, None, fModel.f3d, finalTransform, getInfoDict(obj)) fMeshes = saveStaticModel( triConverterInfo, fModel, obj, finalTransform, fModel.name, not saveTextures, False, "oot" @@ -171,7 +171,7 @@ def execute(self, context): ] filedata = getImportData(paths) - f3dContext = OOTF3DContext(get_F3D_GBI(), [name], basePath) + f3dContext = OOTF3DContext(get_F3D_GBI(), [name], basePath, context="dl") # Test for "new" (post-ZAPD) assets system. is_2025_assets_system = "{\n#include" in filedata diff --git a/fast64_internal/z64/f3d/properties.py b/fast64_internal/z64/f3d/properties.py index e57fa50e3..d9fbdace7 100644 --- a/fast64_internal/z64/f3d/properties.py +++ b/fast64_internal/z64/f3d/properties.py @@ -27,6 +27,7 @@ class OOTDLExportSettings(PropertyGroup): default="assets/objects/gameplay_keep", description="Used in #include for including image files", ) + ignore_segments: BoolProperty(name="Ignore DL Segments", default=False) def draw_props(self, layout: UILayout): layout.label(text="Object name used for export.", icon="INFO") @@ -46,6 +47,7 @@ def draw_props(self, layout: UILayout): layout.prop(self, "isCustom") layout.prop(self, "removeVanillaData") + layout.prop(self, "ignore_segments") class OOTDLImportSettings(PropertyGroup): @@ -61,6 +63,7 @@ class OOTDLImportSettings(PropertyGroup): flipbookArrayIndex2D: IntProperty(name="Index if 2D Array", default=0, min=0) autoDetectActorScale: BoolProperty(name="Auto Detect Actor Scale", default=True) actorScale: FloatProperty(name="Actor Scale", min=0, default=10) + ignore_segments: BoolProperty(name="Ignore DL Segments", default=False) def draw_props(self, layout: UILayout): prop_split(layout, self, "name", "DL") @@ -82,6 +85,7 @@ def draw_props(self, layout: UILayout): layout.prop(self, "isCustom") layout.prop(self, "removeDoubles") layout.prop(self, "importNormals") + layout.prop(self, "ignore_segments") class OOTDynamicMaterialDrawLayerProperty(PropertyGroup): diff --git a/fast64_internal/z64/importer/scene.py b/fast64_internal/z64/importer/scene.py index f350dfcd4..bcb1b5601 100644 --- a/fast64_internal/z64/importer/scene.py +++ b/fast64_internal/z64/importer/scene.py @@ -175,7 +175,7 @@ def parseScene( ) # set scene default registers (see sDefaultDisplayList) - f3dContext = OOTF3DContext(get_F3D_GBI(), [], bpy.path.abspath(bpy.context.scene.ootDecompPath)) + f3dContext = OOTF3DContext(get_F3D_GBI(), [], bpy.path.abspath(bpy.context.scene.ootDecompPath), context="scene") f3dContext.mat().prim_color = (0.5, 0.5, 0.5, 0.5) f3dContext.mat().env_color = (0.5, 0.5, 0.5, 0.5) diff --git a/fast64_internal/z64/model_classes.py b/fast64_internal/z64/model_classes.py index dc1809383..97abf14cd 100644 --- a/fast64_internal/z64/model_classes.py +++ b/fast64_internal/z64/model_classes.py @@ -563,10 +563,13 @@ def getBufferVert(self, loop: MeshLoop, face: MeshLoopTriangle, groupIndex: int class OOTModel(FModel): - def __init__(self, name, DLFormat, drawLayerOverride, draw_config: Optional[str] = None): + def __init__( + self, name, DLFormat, drawLayerOverride, draw_config: Optional[str] = None, context: str = "undefined" + ): self.drawLayerOverride = drawLayerOverride self.flipbooks: list[TextureFlipbook] = [] self.draw_config = draw_config + self.context = context # ["skeleton", "scene", "dl"] FModel.__init__(self, name, DLFormat, GfxMatWriteMethod.WriteAll) @@ -754,18 +757,31 @@ def onMaterialCommandsBuilt(self, fMaterial, material, drawLayer): gfxList = fMaterial.material matDrawLayer = getattr(material.ootMaterial, drawLayer.lower()) - for i in range(8, 14): - if getattr(matDrawLayer, f"segment{i:X}"): - is_animated_material = False - - if self.draw_config is not None and "mat_anim" in self.draw_config: - is_animated_material = True - - gfxList.commands.append( - DynamicMaterialDL( - GfxList(f"0x0{i:X}000000", GfxListTag.Material, DLFormat.Static), is_animated_material + ignore_segments = False + + match self.context: + case "skeleton": + ignore_segments = bpy.context.scene.fast64.oot.skeletonExportSettings.ignore_segments + case "scene": + ignore_segments = bpy.context.scene.ootSceneExportSettings.ignore_segments + case "dl": + ignore_segments = bpy.context.scene.fast64.oot.DLExportSettings.ignore_segments + case _: + pass + + if not ignore_segments: + for i in range(8, 14): + if getattr(matDrawLayer, f"segment{i:X}"): + is_animated_material = False + + if self.draw_config is not None and "mat_anim" in self.draw_config: + is_animated_material = True + + gfxList.commands.append( + DynamicMaterialDL( + GfxList(f"0x0{i:X}000000", GfxListTag.Material, DLFormat.Static), is_animated_material + ) ) - ) for i in range(0, 2): p = f"customCall{i}" @@ -836,11 +852,12 @@ def __init__(self): class OOTF3DContext(F3DContext): - def __init__(self, f3d, limbList, basePath): + def __init__(self, f3d, limbList, basePath, context="undefined"): self.limbList = limbList self.dlList = [] # in the order they are rendered self.isBillboard = False self.flipbooks = {} # {(segment, draw layer) : TextureFlipbook} + self.context = context # ["skeleton", "scene", "dl"] # the new assets system extracts CI textures as PNGs with the TLUT already applied # so we need to avoid reading TLUTs as the files don't exist outside the build folder @@ -912,11 +929,24 @@ def processDLName(self, name): return None return name else: - segment = pointer >> 24 - if segment >= 0x08 and segment <= 0x0D: - setattr(self.materialContext.ootMaterial.opaque, "segment" + format(segment, "1X"), True) - setattr(self.materialContext.ootMaterial.transparent, "segment" + format(segment, "1X"), True) - self.materialChanged = True + ignore_segments = False + + match self.context: + case "skeleton": + ignore_segments = bpy.context.scene.fast64.oot.skeletonImportSettings.ignore_segments + case "scene": + ignore_segments = bpy.context.scene.ootSceneImportSettings.ignore_segments + case "dl": + ignore_segments = bpy.context.scene.fast64.oot.DLImportSettings.ignore_segments + case _: + pass + + if not ignore_segments: + segment = pointer >> 24 + if segment >= 0x08 and segment <= 0x0D: + setattr(self.materialContext.ootMaterial.opaque, "segment" + format(segment, "1X"), True) + setattr(self.materialContext.ootMaterial.transparent, "segment" + format(segment, "1X"), True) + self.materialChanged = True return None return name diff --git a/fast64_internal/z64/scene/properties.py b/fast64_internal/z64/scene/properties.py index 5338de3cb..7742a737f 100644 --- a/fast64_internal/z64/scene/properties.py +++ b/fast64_internal/z64/scene/properties.py @@ -608,6 +608,7 @@ class OOTExportSceneSettingsProperty(PropertyGroup): ) option: EnumProperty(items=ootEnumSceneID, default="SCENE_DEKU_TREE") auto_add_room_objects: BoolProperty(name="Auto-add Missing Room Objects", default=True) + ignore_segments: BoolProperty(name="Ignore DL Segments", default=False) # keeping this on purpose, will be removed once old code is cleaned-up useNewExporter: BoolProperty(name="Use New Exporter", default=True) @@ -627,6 +628,7 @@ def draw_props(self, layout: UILayout): layout.prop(self, "singleFile") layout.prop(self, "customExport") layout.prop(self, "auto_add_room_objects") + layout.prop(self, "ignore_segments") # layout.prop(self, "useNewExporter") @@ -646,6 +648,7 @@ class OOTImportSceneSettingsProperty(PropertyGroup): includeCutscenes: BoolProperty(name="Cutscenes", default=False) includeAnimatedMats: BoolProperty(name="Animated Materials", default=False) option: EnumProperty(items=ootEnumSceneID, default="SCENE_DEKU_TREE") + ignore_segments: BoolProperty(name="Ignore DL Segments", default=False) def draw_props(self, layout: UILayout, sceneOption: str): col = layout.column() @@ -669,6 +672,7 @@ def draw_props(self, layout: UILayout, sceneOption: str): includeButtons4.prop(self, "includeAnimatedMats", toggle=1) col.prop(self, "isCustomDest") + col.prop(self, "ignore_segments") if self.isCustomDest: prop_split(col, self, "destPath", "Directory") diff --git a/fast64_internal/z64/skeleton/importer/functions.py b/fast64_internal/z64/skeleton/importer/functions.py index c911f17a0..5c449fe0b 100644 --- a/fast64_internal/z64/skeleton/importer/functions.py +++ b/fast64_internal/z64/skeleton/importer/functions.py @@ -376,7 +376,7 @@ def ootImportSkeletonC(basePath: str, importSettings: OOTSkeletonImportSettings) limbs_info = ootGetLimbs(skeletonData, skel_info.limbs_name, False) - f3dContext = OOTF3DContext(get_F3D_GBI(), limbs_info.limb_list, basePath) + f3dContext = OOTF3DContext(get_F3D_GBI(), limbs_info.limb_list, basePath, context="skeleton") f3dContext.mat().draw_layer.oot = drawLayer f3dContext.ignore_tlut = ignore_tlut diff --git a/fast64_internal/z64/skeleton/properties.py b/fast64_internal/z64/skeleton/properties.py index 079dacfd7..37b633309 100644 --- a/fast64_internal/z64/skeleton/properties.py +++ b/fast64_internal/z64/skeleton/properties.py @@ -81,6 +81,7 @@ class OOTSkeletonExportSettings(PropertyGroup): + "If enabled, the skeleton limbs must be drawn in their normal order, " + "with nothing in between and no culling, otherwise the mesh will be corrupted.", ) + ignore_segments: BoolProperty(name="Ignore DL Segments", default=False) def draw_props(self, layout: UILayout): layout.prop(self, "removeVanillaData") @@ -91,6 +92,7 @@ def draw_props(self, layout: UILayout): b.label(text="callbacks or cull limbs, will be corrupted.") layout.prop(self, "isCustom") layout.label(text="Object name used for export.", icon="INFO") + layout.prop(self, "ignore_segments") layout.prop(self, "isCustomFilename") if self.isCustomFilename: prop_split(layout, self, "filename", "Filename") @@ -128,12 +130,14 @@ class OOTSkeletonImportSettings(PropertyGroup): flipbookArrayIndex2D: IntProperty(name="Index if 2D Array", default=0, min=0) autoDetectActorScale: BoolProperty(name="Auto Detect Actor Scale", default=True) actorScale: FloatProperty(name="Actor Scale", min=0, default=10) + ignore_segments: BoolProperty(name="Ignore DL Segments", default=False) def draw_props(self, layout: UILayout): prop_split(layout, self, "drawLayer", "Import Draw Layer") layout.prop(self, "removeDoubles") layout.prop(self, "importNormals") layout.prop(self, "import_animations") + layout.prop(self, "ignore_segments") layout.prop(self, "isCustom") if self.isCustom: prop_split(layout, self, "name", "Skeleton") From dac72fde9880486b2b937f86ba5d96ff2186c958 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:42:37 +0200 Subject: [PATCH 3/9] impl #748 and fix remaining size check issue --- fast64_internal/f3d/f3d_gbi.py | 2 ++ fast64_internal/f3d/f3d_material.py | 32 ++++++++++++------- fast64_internal/f3d/f3d_texture_writer.py | 4 +-- fast64_internal/f3d/f3d_writer.py | 18 +++++++++++ fast64_internal/z64/exporter/room/__init__.py | 3 ++ 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py index 860f3a43e..6f3ed9f32 100644 --- a/fast64_internal/f3d/f3d_gbi.py +++ b/fast64_internal/f3d/f3d_gbi.py @@ -2450,6 +2450,8 @@ def __init__( self.global_data: FGlobalData = FGlobalData() self.texturesSavedLastExport: int = 0 # hacky + self.reference_to_extern: list[str] = [] + def processTexRefNonCITextures(self, fMaterial: FMaterial, material: bpy.types.Material, index: int): """ For non CI textures that use a texture reference, process additional textures that will possibly be loaded here. diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index bf1a739b6..7efb842a3 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -780,17 +780,15 @@ def rendermode_presets_checks(material: "F3DMaterialProperty"): no_flags_2 = rdp.rendermode_preset_cycle_2 in f3d.rendermodePresetsWithoutFlags if no_flags_1 and no_flags_2: raise PluginError( - "Invalid combination of rendermode presets.\n" "Neither of these presets sets the rendermode flags." + "Invalid combination of rendermode presets.\nNeither of these presets sets the rendermode flags." ) elif not no_flags_1 and not no_flags_2: raise PluginError( - "Invalid combination of rendermode presets.\n" "Both of these presets set the rendermode flags." + "Invalid combination of rendermode presets.\nBoth of these presets set the rendermode flags." ) else: if no_flags_1: - raise PluginError( - "Invalid rendermode preset in 1-cycle.\n" "This preset does not set the rendermode flags." - ) + raise PluginError("Invalid rendermode preset in 1-cycle.\nThis preset does not set the rendermode flags.") # UI Assumptions: @@ -2986,6 +2984,10 @@ class TextureProperty(PropertyGroup): min=1, default=16, ) + extern_refs: bpy.props.BoolProperty( + name="Extern Texture Reference", + description="Adds externs to the exported source, useful if the texture or palette reference is a symbol.", + ) menu: bpy.props.BoolProperty() tex_set: bpy.props.BoolProperty( @@ -3146,13 +3148,20 @@ def ui_image( prop_input.prop(textureProp, "use_tex_reference") if textureProp.use_tex_reference: + flipbook = getattr(material.flipbookGroup, "flipbook" + texIndex) + can_pal_ref = flipbook is None or not flipbook.enable + + if (len(textureProp.tex_reference) > 0 and not textureProp.tex_reference.startswith("0x")) or ( + can_pal_ref and len(textureProp.pal_reference) > 0 and not textureProp.pal_reference.startswith("0x") + ): + prop_input.prop(textureProp, "extern_refs") + prop_split(prop_input, textureProp, "tex_reference", "Texture Reference") prop_split(prop_input, textureProp, "tex_reference_size", "Texture Size") - if textureProp.tex_format[:2] == "CI": - flipbook = getattr(material.flipbookGroup, "flipbook" + texIndex) - if flipbook is None or not flipbook.enable: - prop_split(prop_input, textureProp, "pal_reference", "Palette Reference") - prop_split(prop_input, textureProp, "pal_reference_size", "Palette Size") + + if textureProp.tex_format[:2] == "CI" and can_pal_ref: + prop_split(prop_input, textureProp, "pal_reference", "Palette Reference") + prop_split(prop_input, textureProp, "pal_reference_size", "Palette Size") else: prop_input.template_ID( @@ -3996,8 +4005,7 @@ def draw_rdp_world_defaults(layout: UILayout, scene: Scene): col.label(text="Saved to Repo Settings file", icon="PROPERTIES") multilineLabel( col, - text="If a material setting is the same as the default setting\n" - "it won't be set, otherwise a revert will be added.", + text="If a material setting is the same as the default setting\nit won't be set, otherwise a revert will be added.", ) if scene.gameEditorMode == "Homebrew": multilineLabel( diff --git a/fast64_internal/f3d/f3d_texture_writer.py b/fast64_internal/f3d/f3d_texture_writer.py index b7609b427..0d44ea5bc 100644 --- a/fast64_internal/f3d/f3d_texture_writer.py +++ b/fast64_internal/f3d/f3d_texture_writer.py @@ -814,12 +814,12 @@ def writeAll( or ( # reference self.ti0.isTexRef and self.ti0.texProp.tex_reference == self.ti1.texProp.tex_reference - and self.ti0.texProp.tex_reference_size == self.ti1.texProp.tex_reference_size + and list(self.ti0.texProp.tex_reference_size) == list(self.ti1.texProp.tex_reference_size) and ( # ci format reference not self.isCI or ( self.ti0.texProp.pal_reference == self.ti1.texProp.pal_reference - and self.ti0.texProp.pal_reference_size == self.ti1.texProp.pal_reference_size + and list(self.ti0.texProp.pal_reference_size) == list(self.ti1.texProp.pal_reference_size) ) ) ) diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 018a27493..0d0813889 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -456,6 +456,24 @@ def saveStaticModel( for material_index, faces in faces_by_mat.items(): material = obj.material_slots[material_index].material + if ( + material.f3d_mat.tex0.tex_set + and material.f3d_mat.tex0.use_tex_reference + and material.f3d_mat.tex0.extern_refs + and len(material.f3d_mat.tex0.tex_reference) > 0 + and not material.f3d_mat.tex0.tex_reference.startswith("0x") + ): + fModel.reference_to_extern.append(material.f3d_mat.tex0.tex_reference) + + if ( + material.f3d_mat.tex1.tex_set + and material.f3d_mat.tex1.use_tex_reference + and material.f3d_mat.tex1.extern_refs + and len(material.f3d_mat.tex1.tex_reference) > 0 + and not material.f3d_mat.tex1.tex_reference.startswith("0x") + ): + fModel.reference_to_extern.append(material.f3d_mat.tex1.tex_reference) + if drawLayerField is not None and material.mat_ver > 3: drawLayer = getattr(material.f3d_mat.draw_layer, drawLayerField) drawLayerName = drawLayer diff --git a/fast64_internal/z64/exporter/room/__init__.py b/fast64_internal/z64/exporter/room/__init__.py index 38d297c2c..7f277c699 100644 --- a/fast64_internal/z64/exporter/room/__init__.py +++ b/fast64_internal/z64/exporter/room/__init__.py @@ -203,6 +203,9 @@ def getRoomShapeModelC(self, textureSettings: TextureExportSettings): """Returns the C data of the room model""" roomModel = CData() + for ref in self.roomShape.model.reference_to_extern: + roomModel.source += f"extern u64 {ref}[];\n\n" + for i, entry in enumerate(self.roomShape.dl_entries): if entry.opaque is not None: roomModel.append(entry.opaque.to_c(self.roomShape.model.f3d)) From bb5fa67997ba6f12063085c6a88404a781eb7473 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:56:13 +0200 Subject: [PATCH 4/9] better check --- fast64_internal/f3d/f3d_writer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 0d0813889..590ee940e 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -261,7 +261,9 @@ def fixLargeUVs(obj): # ignore materials using texture references (avoids getTexDimensions throwing an error) # TODO: find a better way - if material.f3d_mat.tex0.use_tex_reference and material.f3d_mat.tex1.use_tex_reference: + if (material.f3d_mat.tex0.tex_set and material.f3d_mat.tex0.use_tex_reference) or ( + material.f3d_mat.tex1.tex_set and material.f3d_mat.tex1.use_tex_reference + ): continue if material not in texSizeDict: From 00230ab70d93d90721491fd2dae641bf863873c1 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:33:31 +0200 Subject: [PATCH 5/9] forgot to add externs for dls and skeletons --- fast64_internal/z64/exporter/skeleton/functions.py | 3 +++ fast64_internal/z64/f3d/operators.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/fast64_internal/z64/exporter/skeleton/functions.py b/fast64_internal/z64/exporter/skeleton/functions.py index 48e3269c3..1400eba02 100644 --- a/fast64_internal/z64/exporter/skeleton/functions.py +++ b/fast64_internal/z64/exporter/skeleton/functions.py @@ -326,6 +326,9 @@ def ootConvertArmatureToC( ) skeletonC = skeleton.toC() + for ref in fModel.reference_to_extern: + data.source += f"extern u64 {ref}[];\n\n" + data.append(exportData.all()) data.append(skeletonC) diff --git a/fast64_internal/z64/f3d/operators.py b/fast64_internal/z64/f3d/operators.py index 61a543132..72b318c09 100644 --- a/fast64_internal/z64/f3d/operators.py +++ b/fast64_internal/z64/f3d/operators.py @@ -108,6 +108,9 @@ def ootConvertMeshToC( OOTF3DGfxFormatter(ScrollMethod.Vertex, use_draw_layer_suffix=use_draw_layer_suffix), ) + for ref in fModel.reference_to_extern: + data.source += f"extern u64 {ref}[];\n\n" + data.append(exportData.all()) if isCustomExport: From 4b00c6cef265218e790397b4d249e0472f288698 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:52:26 +0200 Subject: [PATCH 6/9] improve formatting and I forgot palette ref externs --- fast64_internal/f3d/f3d_writer.py | 27 +++++++------------ fast64_internal/z64/exporter/room/__init__.py | 5 +++- .../z64/exporter/skeleton/functions.py | 5 +++- fast64_internal/z64/f3d/operators.py | 5 +++- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 590ee940e..cc8299ed0 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -458,23 +458,16 @@ def saveStaticModel( for material_index, faces in faces_by_mat.items(): material = obj.material_slots[material_index].material - if ( - material.f3d_mat.tex0.tex_set - and material.f3d_mat.tex0.use_tex_reference - and material.f3d_mat.tex0.extern_refs - and len(material.f3d_mat.tex0.tex_reference) > 0 - and not material.f3d_mat.tex0.tex_reference.startswith("0x") - ): - fModel.reference_to_extern.append(material.f3d_mat.tex0.tex_reference) - - if ( - material.f3d_mat.tex1.tex_set - and material.f3d_mat.tex1.use_tex_reference - and material.f3d_mat.tex1.extern_refs - and len(material.f3d_mat.tex1.tex_reference) > 0 - and not material.f3d_mat.tex1.tex_reference.startswith("0x") - ): - fModel.reference_to_extern.append(material.f3d_mat.tex1.tex_reference) + def try_append_refs(tex): + if tex.tex_set and tex.use_tex_reference and tex.extern_refs: + if len(tex.tex_reference) > 0 and not tex.tex_reference.startswith("0x"): + fModel.reference_to_extern.append(tex.tex_reference) + + if len(tex.pal_reference) > 0 and not tex.pal_reference.startswith("0x"): + fModel.reference_to_extern.append(tex.pal_reference) + + try_append_refs(material.f3d_mat.tex0) + try_append_refs(material.f3d_mat.tex1) if drawLayerField is not None and material.mat_ver > 3: drawLayer = getattr(material.f3d_mat.draw_layer, drawLayerField) diff --git a/fast64_internal/z64/exporter/room/__init__.py b/fast64_internal/z64/exporter/room/__init__.py index 7f277c699..51cae85a8 100644 --- a/fast64_internal/z64/exporter/room/__init__.py +++ b/fast64_internal/z64/exporter/room/__init__.py @@ -204,7 +204,10 @@ def getRoomShapeModelC(self, textureSettings: TextureExportSettings): roomModel = CData() for ref in self.roomShape.model.reference_to_extern: - roomModel.source += f"extern u64 {ref}[];\n\n" + roomModel.source += f"extern u64 {ref}[];\n" + + if len(self.roomShape.model.reference_to_extern) > 0: + roomModel.source += "\n" for i, entry in enumerate(self.roomShape.dl_entries): if entry.opaque is not None: diff --git a/fast64_internal/z64/exporter/skeleton/functions.py b/fast64_internal/z64/exporter/skeleton/functions.py index 1400eba02..2424ea539 100644 --- a/fast64_internal/z64/exporter/skeleton/functions.py +++ b/fast64_internal/z64/exporter/skeleton/functions.py @@ -327,7 +327,10 @@ def ootConvertArmatureToC( skeletonC = skeleton.toC() for ref in fModel.reference_to_extern: - data.source += f"extern u64 {ref}[];\n\n" + data.source += f"extern u64 {ref}[];\n" + + if len(fModel.reference_to_extern) > 0: + data.source += "\n" data.append(exportData.all()) data.append(skeletonC) diff --git a/fast64_internal/z64/f3d/operators.py b/fast64_internal/z64/f3d/operators.py index 72b318c09..7fdb522d0 100644 --- a/fast64_internal/z64/f3d/operators.py +++ b/fast64_internal/z64/f3d/operators.py @@ -109,7 +109,10 @@ def ootConvertMeshToC( ) for ref in fModel.reference_to_extern: - data.source += f"extern u64 {ref}[];\n\n" + data.source += f"extern u64 {ref}[];\n" + + if len(fModel.reference_to_extern) > 0: + data.source += "\n" data.append(exportData.all()) From 33e4a012aabbb8369afde4a59165eaa56829bea2 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Tue, 29 Sep 2026 00:57:23 +0200 Subject: [PATCH 7/9] make the C exporter a function and make sure the thing isn't already externed --- fast64_internal/f3d/f3d_gbi.py | 9 +++++++++ fast64_internal/f3d/f3d_writer.py | 1 + fast64_internal/z64/exporter/room/__init__.py | 6 +----- fast64_internal/z64/exporter/skeleton/functions.py | 7 +------ fast64_internal/z64/f3d/operators.py | 7 +------ 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py index 6f3ed9f32..5f2db687e 100644 --- a/fast64_internal/f3d/f3d_gbi.py +++ b/fast64_internal/f3d/f3d_gbi.py @@ -2851,6 +2851,15 @@ def save_textures(self, exportPath): def freePalettes(self): pass + def add_refs_externs(self, data: CData): + for ref in self.reference_to_extern: + extern = f"extern u64 {ref}[];\n" + + if extern not in data.source: + data.source += extern + + if len(self.reference_to_extern) > 0: + data.source += "\n" class FTexRect(FModel): def __init__(self, name, matWriteMethod): diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index cc8299ed0..ec05dfa34 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -468,6 +468,7 @@ def try_append_refs(tex): try_append_refs(material.f3d_mat.tex0) try_append_refs(material.f3d_mat.tex1) + fModel.reference_to_extern = list(set(fModel.reference_to_extern)) # remove duplicates if drawLayerField is not None and material.mat_ver > 3: drawLayer = getattr(material.f3d_mat.draw_layer, drawLayerField) diff --git a/fast64_internal/z64/exporter/room/__init__.py b/fast64_internal/z64/exporter/room/__init__.py index 51cae85a8..418e10ce2 100644 --- a/fast64_internal/z64/exporter/room/__init__.py +++ b/fast64_internal/z64/exporter/room/__init__.py @@ -203,11 +203,7 @@ def getRoomShapeModelC(self, textureSettings: TextureExportSettings): """Returns the C data of the room model""" roomModel = CData() - for ref in self.roomShape.model.reference_to_extern: - roomModel.source += f"extern u64 {ref}[];\n" - - if len(self.roomShape.model.reference_to_extern) > 0: - roomModel.source += "\n" + self.roomShape.model.add_refs_externs(roomModel) for i, entry in enumerate(self.roomShape.dl_entries): if entry.opaque is not None: diff --git a/fast64_internal/z64/exporter/skeleton/functions.py b/fast64_internal/z64/exporter/skeleton/functions.py index 2424ea539..895836eb2 100644 --- a/fast64_internal/z64/exporter/skeleton/functions.py +++ b/fast64_internal/z64/exporter/skeleton/functions.py @@ -326,12 +326,7 @@ def ootConvertArmatureToC( ) skeletonC = skeleton.toC() - for ref in fModel.reference_to_extern: - data.source += f"extern u64 {ref}[];\n" - - if len(fModel.reference_to_extern) > 0: - data.source += "\n" - + fModel.add_refs_externs(data) data.append(exportData.all()) data.append(skeletonC) diff --git a/fast64_internal/z64/f3d/operators.py b/fast64_internal/z64/f3d/operators.py index 7fdb522d0..6ccd19ac4 100644 --- a/fast64_internal/z64/f3d/operators.py +++ b/fast64_internal/z64/f3d/operators.py @@ -108,12 +108,7 @@ def ootConvertMeshToC( OOTF3DGfxFormatter(ScrollMethod.Vertex, use_draw_layer_suffix=use_draw_layer_suffix), ) - for ref in fModel.reference_to_extern: - data.source += f"extern u64 {ref}[];\n" - - if len(fModel.reference_to_extern) > 0: - data.source += "\n" - + fModel.add_refs_externs(data) data.append(exportData.all()) if isCustomExport: From b2b7948d78ac1196fda7cfb6b071318ef742615c Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Tue, 29 Sep 2026 02:57:04 +0200 Subject: [PATCH 8/9] fix issue where when not using references it throws an error because somehow the type is different... --- fast64_internal/f3d/f3d_gbi.py | 1 + fast64_internal/f3d/f3d_texture_writer.py | 13 +++++++------ fast64_internal/f3d/f3d_writer.py | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/fast64_internal/f3d/f3d_gbi.py b/fast64_internal/f3d/f3d_gbi.py index 5f2db687e..ab93150ef 100644 --- a/fast64_internal/f3d/f3d_gbi.py +++ b/fast64_internal/f3d/f3d_gbi.py @@ -2861,6 +2861,7 @@ def add_refs_externs(self, data: CData): if len(self.reference_to_extern) > 0: data.source += "\n" + class FTexRect(FModel): def __init__(self, name, matWriteMethod): self.draw = GfxList(name, GfxListTag.Draw, DLFormat.Dynamic) diff --git a/fast64_internal/f3d/f3d_texture_writer.py b/fast64_internal/f3d/f3d_texture_writer.py index 0d44ea5bc..4ba293d0f 100644 --- a/fast64_internal/f3d/f3d_texture_writer.py +++ b/fast64_internal/f3d/f3d_texture_writer.py @@ -645,12 +645,12 @@ def assert_size(is_pal: bool): else self.ti0.texProp.tex_reference == self.ti1.texProp.tex_reference ) - size_1 = ( - list(self.ti0.texProp.pal_reference_size) if is_pal else list(self.ti0.texProp.tex_reference_size) - ) - size_2 = ( - list(self.ti1.texProp.pal_reference_size) if is_pal else list(self.ti1.texProp.tex_reference_size) - ) + data_1 = self.ti0.texProp.pal_reference_size if is_pal else self.ti0.texProp.tex_reference_size + size_1 = list(data_1) if not isinstance(data_1, int) else [data_1] + + data_2 = self.ti1.texProp.pal_reference_size if is_pal else self.ti1.texProp.tex_reference_size + size_2 = list(data_2) if not isinstance(data_2, int) else [data_2] + is_same_size = size_1[0] == size_2[0] and size_1[0] == size_2[0] if self.ti0.isTexRef and self.ti1.isTexRef and is_ref_enabled and not is_same_size: @@ -825,6 +825,7 @@ def writeAll( ) ) ) + useLargeTextures = material.mat_ver > 3 and f3dMat.use_large_textures tmemSize = 256 if self.isCI else 512 self.ti1.texAddr = None # must be set whenever tex 1 used (and loaded or tiled) diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index ec05dfa34..31255fdea 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -468,7 +468,7 @@ def try_append_refs(tex): try_append_refs(material.f3d_mat.tex0) try_append_refs(material.f3d_mat.tex1) - fModel.reference_to_extern = list(set(fModel.reference_to_extern)) # remove duplicates + fModel.reference_to_extern = list(set(fModel.reference_to_extern)) # remove duplicates if drawLayerField is not None and material.mat_ver > 3: drawLayer = getattr(material.f3d_mat.draw_layer, drawLayerField) From b340687ad0ad61eb58bbee5f3bb98e7f1ad38d71 Mon Sep 17 00:00:00 2001 From: Yanis002 <35189056+Yanis002@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:45:00 +0200 Subject: [PATCH 9/9] review --- fast64_internal/f3d/f3d_material.py | 7 +++---- fast64_internal/f3d/f3d_writer.py | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index 7efb842a3..f08b0e151 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -3149,17 +3149,16 @@ def ui_image( prop_input.prop(textureProp, "use_tex_reference") if textureProp.use_tex_reference: flipbook = getattr(material.flipbookGroup, "flipbook" + texIndex) - can_pal_ref = flipbook is None or not flipbook.enable - if (len(textureProp.tex_reference) > 0 and not textureProp.tex_reference.startswith("0x")) or ( - can_pal_ref and len(textureProp.pal_reference) > 0 and not textureProp.pal_reference.startswith("0x") + if (len(textureProp.tex_reference) > 0 and not textureProp.tex_reference.lower().startswith("0x")) or ( + not flipbook.enable and len(textureProp.pal_reference) > 0 and not textureProp.pal_reference.lower().startswith("0x") ): prop_input.prop(textureProp, "extern_refs") prop_split(prop_input, textureProp, "tex_reference", "Texture Reference") prop_split(prop_input, textureProp, "tex_reference_size", "Texture Size") - if textureProp.tex_format[:2] == "CI" and can_pal_ref: + if textureProp.tex_format[:2] == "CI" and not flipbook.enable: prop_split(prop_input, textureProp, "pal_reference", "Palette Reference") prop_split(prop_input, textureProp, "pal_reference_size", "Palette Size") diff --git a/fast64_internal/f3d/f3d_writer.py b/fast64_internal/f3d/f3d_writer.py index 31255fdea..3161cac95 100644 --- a/fast64_internal/f3d/f3d_writer.py +++ b/fast64_internal/f3d/f3d_writer.py @@ -460,10 +460,10 @@ def saveStaticModel( def try_append_refs(tex): if tex.tex_set and tex.use_tex_reference and tex.extern_refs: - if len(tex.tex_reference) > 0 and not tex.tex_reference.startswith("0x"): + if len(tex.tex_reference) > 0 and not tex.tex_reference.lower().startswith("0x"): fModel.reference_to_extern.append(tex.tex_reference) - if len(tex.pal_reference) > 0 and not tex.pal_reference.startswith("0x"): + if len(tex.pal_reference) > 0 and not tex.pal_reference.lower().startswith("0x"): fModel.reference_to_extern.append(tex.pal_reference) try_append_refs(material.f3d_mat.tex0)