From 3fe2654e6758c8ca56147448b0a93462cb058516 Mon Sep 17 00:00:00 2001 From: Lila Date: Fri, 19 Jul 2024 05:20:52 +0100 Subject: [PATCH] [F3D] Upgrade improvements (+related) (#375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [F3D] Upgrade improvements (+related) Changes: 1. Upgrades now copy over the current f3d library node tree onto the existing material, this has a lot of benifits such as no longer needing to purge materials, no naming changes, carying over all props, removal of hacks for material pointer propreties and the speed of upgrades. Orphan data text is removed from upgrade popup. 2. Removed generateF3DNodeGraph 3. Added warning for old materials and an early return for such 4. Added "Recreate F3D Shader Nodes" operator, copies over the node tree from the f3d library and updates everything, useful for broken nodes of any kind 5. Fixed f3d_light upgrading, along with now popping the props instead of just getting them 6. Fixed recursiveCopyOldPropertyGroup which was interperting non set data (for example something set by auto prop) as a non group, and trying to set new prop to it, it can also now fail and continue 7. Failed material upgrades now print traceback 8. Fixed all the errors during draw layer updates by locking the updates and passing in a correct material context, fixed auto props error * black * Make sure update flag gets disabled * HOW IN HELL DID THIS NOT FAIL BEFORE THIS PR?? * black * Don´t show popup for redo * fixed ordering * Fix blender 4.2 --- __init__.py | 21 +-- fast64_internal/f3d/f3d_material.py | 147 ++++++++++++-------- fast64_internal/f3d/f3d_material_helpers.py | 59 ++++++++ fast64_internal/f3d_material_converter.py | 103 ++++++-------- fast64_internal/sm64/sm64_collision.py | 26 ---- fast64_internal/utility.py | 10 +- 6 files changed, 202 insertions(+), 164 deletions(-) diff --git a/__init__.py b/__init__.py index 7623a5148..21037e64d 100644 --- a/__init__.py +++ b/__init__.py @@ -80,7 +80,6 @@ def draw(self, context): prop_split(col, context.scene, "f3d_type", "F3D Microcode") col.prop(context.scene, "saveTextures") col.prop(context.scene, "f3d_simple", text="Simple Material UI") - col.prop(context.scene, "generateF3DNodeGraph", text="Generate F3D Node Graph For Materials") col.prop(context.scene, "exportInlineF3D", text="Bleed and Inline Material Exports") if context.scene.exportInlineF3D: multilineLabel( @@ -243,22 +242,7 @@ def draw(self, context): layout = self.layout if self.done: layout.label(text="Success!") - box = layout.box() - box.label(text="Materials were successfully upgraded.") - box.label(text="Please purge your remaining materials.") - - purge_box = box.box() - purge_box.scale_y = 0.25 - purge_box.separator(factor=0.5) - purge_box.label(text="How to purge:") - purge_box.separator(factor=0.5) - purge_box.label(text="Go to the outliner, change the display mode") - purge_box.label(text='to "Orphan Data" (broken heart icon)') - purge_box.separator(factor=0.25) - purge_box.label(text='Click "Purge" in the top right corner.') - purge_box.separator(factor=0.25) - purge_box.label(text="Purge multiple times until the node groups") - purge_box.label(text="are gone.") + layout.label(text="Materials were successfully upgraded.") layout.separator(factor=0.25) layout.label(text="You may click anywhere to close this dialog.") return @@ -282,7 +266,6 @@ def execute(self, context: "bpy.types.Context"): upgradeF3DVersionAll( [obj for obj in bpy.data.objects if obj.type == "MESH"], - list(bpy.data.armatures), MatUpdateConvert.version, ) self.done = True @@ -408,7 +391,6 @@ def register(): 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.generateF3DNodeGraph = bpy.props.BoolProperty(name="Generate F3D Node Graph", default=True) 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", @@ -444,7 +426,6 @@ def unregister(): del bpy.types.Scene.ignoreTextureRestrictions del bpy.types.Scene.saveTextures del bpy.types.Scene.gameEditorMode - del bpy.types.Scene.generateF3DNodeGraph del bpy.types.Scene.exportHiddenGeometry del bpy.types.Scene.blenderF3DScale diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index 957430d49..e5edce3c1 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -35,7 +35,7 @@ from .f3d_material_presets import * from ..utility import * from ..render_settings import Fast64RenderSettings_Properties, update_scene_props_from_render_settings -from .f3d_material_helpers import F3DMaterial_UpdateLock +from .f3d_material_helpers import F3DMaterial_UpdateLock, node_tree_copy from bpy.app.handlers import persistent from typing import Generator, Optional, Tuple, Any, Dict, Union @@ -1246,6 +1246,15 @@ def draw(self, context): titleCol = layout.column() titleCol.box().label(text="F3D Material Inspector") + if material.mat_ver < 5: + box = layout.box().column() + box.label( + text=f"Material version is outdated (V{material.mat_ver})", + icon="ORPHAN_DATA", + ) + box.operator("object.convert_f3d_update") + return + presetCol = layout.column() split = presetCol.split(factor=0.33) split.label(text="Preset") @@ -1275,6 +1284,8 @@ def draw(self, context): r.enabled = False r.label(text="Use Cel Shading (requires F3DEX3)", icon="TRIA_RIGHT") + layout.operator(RecreateF3DNodes.bl_idname) + class F3DMeshPanel(Panel): bl_label = "F3D Mesh Inspector" @@ -1395,6 +1406,12 @@ def update_node_values(self, context, update_preset): material.f3d_mat.presetName = "Custom" +def update_all_node_values(material, context): + update_node_values_without_preset(material, context) + update_tex_values_and_formats(material, context) + update_rendermode_preset(material, context) + + def update_node_values_with_preset(self, context): update_node_values(self, context, update_preset=True) @@ -1923,9 +1940,6 @@ def update_tex_values_index(self: Material, *, texProperty: "TextureProperty", t if tex_size: # only returns tex size if a texture is being set if tex_size[0] > 0 and tex_size[1] > 0: - if texProperty.autoprop: - setAutoProp(texProperty.S, tex_size[0]) - setAutoProp(texProperty.T, tex_size[1]) update_tex_values_field(self, texProperty, tex_size, texIndex) texFormat = texProperty.tex_format @@ -2371,6 +2385,15 @@ def link_f3d_material_library(): bpy.ops.object.mode_set(mode=get_mode_set_from_context_mode(prevMode)) +def get_f3d_node_tree() -> bpy.types.NodeTree: + try: + link_f3d_material_library() + mat = bpy.data.materials["fast64_f3d_material_library_beefwashere"] + return mat.node_tree.copy() + finally: + bpy.data.materials.remove(mat) + + def shouldConvOrCreateColorAttribute(mesh: Mesh, attr_name="Col"): has_attr, conv_attr = False, False if attr_name in mesh.attributes: @@ -2537,6 +2560,24 @@ def execute(self, context): UpdateColorManagementPopup.already_invoked = False +class RecreateF3DNodes(Operator): + bl_idname = "material.recreate_f3d_nodes" + bl_label = "Recreate F3D Shader Nodes" + bl_options = {"REGISTER", "UNDO", "PRESET"} + bl_description = "Recreates the node tree for f3d materials, use if material preview is broken." + + def execute(self, context): + material = context.material + if material is None: + self.report({"ERROR"}, "No active material.") + else: + node_tree_copy(get_f3d_node_tree(), material.node_tree) + createScenePropertiesForMaterial(material) + update_all_node_values(material, context) + self.report({"INFO"}, "Success!") + return {"FINISHED"} + + class CreateFast3DMaterial(Operator): bl_idname = "object.create_f3d_mat" bl_label = "Create Fast3D Material" @@ -2598,10 +2639,8 @@ def toggle_auto_prop(self, context: Context): tex_property, tex_index = get_tex_prop_from_path(material, prop_path) if tex_property.autoprop: tex_size = tuple([s for s in tex_property.get_tex_size()]) - - setAutoProp(tex_property.S, tex_size[0]) - setAutoProp(tex_property.T, tex_size[1]) - update_tex_values_field(material, tex_property, tex_size, tex_index) + if tex_size[0] > 0 and tex_size[1] > 0: + update_tex_values_field(material, tex_property, tex_size, tex_index) set_texture_settings_node(material) @@ -3997,38 +4036,34 @@ def rna_recursive_attr_expand(value, rna_path_step, level): def convertToNewMat(material, oldMat): - material.f3d_mat.presetName = oldMat.get("presetName", "Custom") + material.f3d_mat.presetName = oldMat.pop("presetName", "Custom") - material.f3d_mat.scale_autoprop = oldMat.get("scale_autoprop", material.f3d_mat.scale_autoprop) - material.f3d_mat.uv_basis = oldMat.get("uv_basis", material.f3d_mat.uv_basis) + material.f3d_mat.scale_autoprop = oldMat.pop("scale_autoprop", material.f3d_mat.scale_autoprop) + material.f3d_mat.uv_basis = oldMat.pop("uv_basis", material.f3d_mat.uv_basis) # Combiners - if "combiner1" in oldMat: - recursiveCopyOldPropertyGroup(oldMat["combiner1"], material.f3d_mat.combiner1) - if "combiner2" in oldMat: - recursiveCopyOldPropertyGroup(oldMat["combiner2"], material.f3d_mat.combiner2) + recursiveCopyOldPropertyGroup(oldMat.pop("combiner1", {}), material.f3d_mat.combiner1) + recursiveCopyOldPropertyGroup(oldMat.pop("combiner2", {}), material.f3d_mat.combiner2) # Texture animation - material.f3d_mat.menu_procAnim = oldMat.get("menu_procAnim", material.f3d_mat.menu_procAnim) - if "UVanim" in oldMat: - recursiveCopyOldPropertyGroup(oldMat["UVanim"], material.f3d_mat.UVanim0) - if "UVanim_tex1" in oldMat: - recursiveCopyOldPropertyGroup(oldMat["UVanim_tex1"], material.f3d_mat.UVanim1) + material.f3d_mat.menu_procAnim = oldMat.pop("menu_procAnim", material.f3d_mat.menu_procAnim) + recursiveCopyOldPropertyGroup(oldMat.pop("UVanim", {}), material.f3d_mat.UVanim0) + recursiveCopyOldPropertyGroup(oldMat.pop("UVanim_tex1", {}), material.f3d_mat.UVanim1) # material textures - material.f3d_mat.tex_scale = oldMat.get("tex_scale", material.f3d_mat.tex_scale) - recursiveCopyOldPropertyGroup(oldMat["tex0"], material.f3d_mat.tex0) - recursiveCopyOldPropertyGroup(oldMat["tex1"], material.f3d_mat.tex1) + material.f3d_mat.tex_scale = oldMat.pop("tex_scale", material.f3d_mat.tex_scale) + recursiveCopyOldPropertyGroup(oldMat.pop("tex0", {}), material.f3d_mat.tex0) + recursiveCopyOldPropertyGroup(oldMat.pop("tex1", {}), material.f3d_mat.tex1) # Should Set? - material.f3d_mat.set_prim = oldMat.get("set_prim", material.f3d_mat.set_prim) - material.f3d_mat.set_lights = oldMat.get("set_lights", material.f3d_mat.set_lights) - material.f3d_mat.set_env = oldMat.get("set_env", material.f3d_mat.set_env) - material.f3d_mat.set_blend = oldMat.get("set_blend", material.f3d_mat.set_blend) - material.f3d_mat.set_key = oldMat.get("set_key", material.f3d_mat.set_key) - material.f3d_mat.set_k0_5 = oldMat.get("set_k0_5", material.f3d_mat.set_k0_5) - material.f3d_mat.set_combiner = oldMat.get("set_combiner", material.f3d_mat.set_combiner) - material.f3d_mat.use_default_lighting = oldMat.get("use_default_lighting", material.f3d_mat.use_default_lighting) + material.f3d_mat.set_prim = oldMat.pop("set_prim", material.f3d_mat.set_prim) + material.f3d_mat.set_lights = oldMat.pop("set_lights", material.f3d_mat.set_lights) + material.f3d_mat.set_env = oldMat.pop("set_env", material.f3d_mat.set_env) + material.f3d_mat.set_blend = oldMat.pop("set_blend", material.f3d_mat.set_blend) + material.f3d_mat.set_key = oldMat.pop("set_key", material.f3d_mat.set_key) + material.f3d_mat.set_k0_5 = oldMat.pop("set_k0_5", material.f3d_mat.set_k0_5) + material.f3d_mat.set_combiner = oldMat.pop("set_combiner", material.f3d_mat.set_combiner) + material.f3d_mat.use_default_lighting = oldMat.pop("use_default_lighting", material.f3d_mat.use_default_lighting) # Colors nodes = oldMat.node_tree.nodes @@ -4040,52 +4075,51 @@ def convertToNewMat(material, oldMat): prim = nodes["Primitive Color"].outputs[0].default_value env = nodes["Environment Color"].outputs[0].default_value - material.f3d_mat.blend_color = oldMat.get("blend_color", material.f3d_mat.blend_color) + material.f3d_mat.blend_color = oldMat.pop("blend_color", material.f3d_mat.blend_color) material.f3d_mat.prim_color = prim material.f3d_mat.env_color = env if "Chroma Key Center" in nodes: material.f3d_mat.key_center = nodes["Chroma Key Center"].outputs[0].default_value # Chroma - material.f3d_mat.key_scale = oldMat.get("key_scale", material.f3d_mat.key_scale) - material.f3d_mat.key_width = oldMat.get("key_width", material.f3d_mat.key_width) + material.f3d_mat.key_scale = oldMat.pop("key_scale", material.f3d_mat.key_scale) + material.f3d_mat.key_width = oldMat.pop("key_width", material.f3d_mat.key_width) # Convert - material.f3d_mat.k0 = oldMat.get("k0", material.f3d_mat.k0) - material.f3d_mat.k1 = oldMat.get("k1", material.f3d_mat.k1) - material.f3d_mat.k2 = oldMat.get("k2", material.f3d_mat.k2) - material.f3d_mat.k3 = oldMat.get("k3", material.f3d_mat.k3) - material.f3d_mat.k4 = oldMat.get("k4", material.f3d_mat.k4) - material.f3d_mat.k5 = oldMat.get("k5", material.f3d_mat.k5) + material.f3d_mat.k0 = oldMat.pop("k0", material.f3d_mat.k0) + material.f3d_mat.k1 = oldMat.pop("k1", material.f3d_mat.k1) + material.f3d_mat.k2 = oldMat.pop("k2", material.f3d_mat.k2) + material.f3d_mat.k3 = oldMat.pop("k3", material.f3d_mat.k3) + material.f3d_mat.k4 = oldMat.pop("k4", material.f3d_mat.k4) + material.f3d_mat.k5 = oldMat.pop("k5", material.f3d_mat.k5) # Prim - material.f3d_mat.prim_lod_frac = oldMat.get("prim_lod_frac", material.f3d_mat.prim_lod_frac) - material.f3d_mat.prim_lod_min = oldMat.get("prim_lod_min", material.f3d_mat.prim_lod_min) + material.f3d_mat.prim_lod_frac = oldMat.pop("prim_lod_frac", material.f3d_mat.prim_lod_frac) + material.f3d_mat.prim_lod_min = oldMat.pop("prim_lod_min", material.f3d_mat.prim_lod_min) # lights - material.f3d_mat.default_light_color = oldMat.get("default_light_color", material.f3d_mat.default_light_color) - material.f3d_mat.ambient_light_color = oldMat.get("ambient_light_color", material.f3d_mat.ambient_light_color) + material.f3d_mat.default_light_color = oldMat.pop("default_light_color", material.f3d_mat.default_light_color) + material.f3d_mat.ambient_light_color = oldMat.pop("ambient_light_color", material.f3d_mat.ambient_light_color) for i in range(1, 8): - old_light = oldMat.get(f"f3d_light{str(i)}") + old_light = oldMat.pop(f"f3d_light{str(i)}", None) # can be a broken property with V1 materials (IDPropertyGroup), thankfully this isnt typical to see when upgrading but # this method is safer if type(old_light) is Light: setattr(material.f3d_mat, f"f3d_light{str(i)}", old_light) # Fog Properties - material.f3d_mat.fog_color = oldMat.get("fog_color", material.f3d_mat.fog_color) - material.f3d_mat.fog_position = oldMat.get("fog_position", material.f3d_mat.fog_position) - material.f3d_mat.set_fog = oldMat.get("set_fog", material.f3d_mat.set_fog) - material.f3d_mat.use_global_fog = oldMat.get("use_global_fog", material.f3d_mat.use_global_fog) + material.f3d_mat.fog_color = oldMat.pop("fog_color", material.f3d_mat.fog_color) + material.f3d_mat.fog_position = oldMat.pop("fog_position", material.f3d_mat.fog_position) + material.f3d_mat.set_fog = oldMat.pop("set_fog", material.f3d_mat.set_fog) + material.f3d_mat.use_global_fog = oldMat.pop("use_global_fog", material.f3d_mat.use_global_fog) # geometry mode - material.f3d_mat.menu_geo = oldMat.get("menu_geo", material.f3d_mat.menu_geo) - material.f3d_mat.menu_upper = oldMat.get("menu_upper", material.f3d_mat.menu_upper) - material.f3d_mat.menu_lower = oldMat.get("menu_lower", material.f3d_mat.menu_lower) - material.f3d_mat.menu_other = oldMat.get("menu_other", material.f3d_mat.menu_other) - material.f3d_mat.menu_lower_render = oldMat.get("menu_lower_render", material.f3d_mat.menu_lower_render) - if "rdp_settings" in oldMat: - recursiveCopyOldPropertyGroup(oldMat["rdp_settings"], material.f3d_mat.rdp_settings) + material.f3d_mat.menu_geo = oldMat.pop("menu_geo", material.f3d_mat.menu_geo) + material.f3d_mat.menu_upper = oldMat.pop("menu_upper", material.f3d_mat.menu_upper) + material.f3d_mat.menu_lower = oldMat.pop("menu_lower", material.f3d_mat.menu_lower) + material.f3d_mat.menu_other = oldMat.pop("menu_other", material.f3d_mat.menu_other) + material.f3d_mat.menu_lower_render = oldMat.pop("menu_lower_render", material.f3d_mat.menu_lower_render) + recursiveCopyOldPropertyGroup(oldMat.pop("rdp_settings", {}), material.f3d_mat.rdp_settings) class F3DMaterialProperty(PropertyGroup): @@ -4642,6 +4676,7 @@ def draw_f3d_render_settings(self, context): AddPresetF3D, F3DPanel, CreateFast3DMaterial, + RecreateF3DNodes, TextureFieldProperty, SetTileSizeScrollProperty, TextureProperty, diff --git a/fast64_internal/f3d/f3d_material_helpers.py b/fast64_internal/f3d/f3d_material_helpers.py index 66c0b791b..1ab707851 100644 --- a/fast64_internal/f3d/f3d_material_helpers.py +++ b/fast64_internal/f3d/f3d_material_helpers.py @@ -1,4 +1,5 @@ import bpy +from bpy.types import NodeTree class F3DMaterial_UpdateLock: @@ -34,3 +35,61 @@ def lock_material(self): def unlock_material(self): if hasattr(self.material, "f3d_update_flag"): self.material.f3d_update_flag = False + + +EXCLUDE_FROM_NODE = ( + "rna_type", + "type", + "inputs", + "outputs", + "dimensions", + "interface", + "internal_links", + "texture_mapping", + "color_mapping", + "image_user", +) +EXCLUDE_FROM_INPUT_OUTPUT = ( + "rna_type", + "label", + "identifier", + "is_output", + "is_linked", + "is_multi_input", + "node", + "bl_idname", + "default_value", + "is_unavailable", +) + + +def node_tree_copy(src: NodeTree, dst: NodeTree): + def copy_attributes(src, dst, excludes=None): + fails, excludes = [], excludes if excludes else [] + attributes = (attr.identifier for attr in src.bl_rna.properties if attr.identifier not in excludes) + for attr in attributes: + try: + setattr(dst, attr, getattr(src, attr)) + except Exception as exc: # pylint: disable=broad-except + fails.append(exc) + if fails: + raise AttributeError("Failed to copy all attributes: " + str(fails)) + + dst.nodes.clear() + dst.links.clear() + + node_mapping = {} # To not have to look up the new node for linking + for src_node in src.nodes: # Copy all nodes + new_node = dst.nodes.new(src_node.bl_idname) + copy_attributes(src_node, new_node, excludes=EXCLUDE_FROM_NODE) + node_mapping[src_node] = new_node + for src_node, dst_node in node_mapping.items(): + for i, src_input in enumerate(src_node.inputs): # Link all nodes + for link in src_input.links: + connected_node = dst.nodes[link.from_node.name] + dst.links.new(connected_node.outputs[link.from_socket.name], dst_node.inputs[i]) + + for src_input, dst_input in zip(src_node.inputs, dst_node.inputs): # Copy all inputs + copy_attributes(src_input, dst_input, excludes=EXCLUDE_FROM_INPUT_OUTPUT) + for src_output, dst_output in zip(src_node.outputs, dst_node.outputs): # Copy all outputs + copy_attributes(src_output, dst_output, excludes=EXCLUDE_FROM_INPUT_OUTPUT) diff --git a/fast64_internal/f3d_material_converter.py b/fast64_internal/f3d_material_converter.py index 584d806cc..a9850c087 100644 --- a/fast64_internal/f3d_material_converter.py +++ b/fast64_internal/f3d_material_converter.py @@ -1,14 +1,16 @@ # This is not in the f3d package since copying materials requires copying collision settings from all games as well. -import bpy, math +import bpy from bpy.utils import register_class, unregister_class from .f3d.f3d_material import * -from .sm64.sm64_collision import CollisionSettings +from .f3d.f3d_material_helpers import node_tree_copy from .utility import * from bl_operators.presets import AddPresetBase -def upgradeF3DVersionAll(objs, armatures, version): +def upgradeF3DVersionAll(objs, version): + f3d_node_tree = get_f3d_node_tree() + # Remove original v2 node groups so that they can be recreated. deleteGroups = [] for node_tree in bpy.data.node_groups: @@ -23,33 +25,15 @@ def upgradeF3DVersionAll(objs, armatures, version): # handles cases where materials are used in multiple objects materialDict = {} for obj in objs: - upgradeF3DVersionOneObject(obj, materialDict, version) + upgradeF3DVersionOneObject(obj, materialDict, f3d_node_tree) - for armature in armatures: - for bone in armature.bones: - if bone.geo_cmd == "Switch": - for switchOption in bone.switch_options: - if switchOption.switchType == "Material": - if switchOption.materialOverride in materialDict: - switchOption.materialOverride = materialDict[switchOption.materialOverride] - for i in range(len(switchOption.specificOverrideArray)): - material = switchOption.specificOverrideArray[i].material - if material in materialDict: - switchOption.specificOverrideArray[i].material = materialDict[material] - for i in range(len(switchOption.specificIgnoreArray)): - material = switchOption.specificIgnoreArray[i].material - if material in materialDict: - switchOption.specificIgnoreArray[i].material = materialDict[material] - - -def upgradeF3DVersionOneObject(obj, materialDict, version): + +def upgradeF3DVersionOneObject(obj, materialDict, f3d_node_tree: bpy.types.NodeTree): for index in range(len(obj.material_slots)): material = obj.material_slots[index].material - if material is not None and material.is_f3d: - if material in materialDict: - obj.material_slots[index].material = materialDict[material] - else: - convertF3DtoNewVersion(obj, index, material, materialDict, version) + if material is not None and material.is_f3d and material not in materialDict: + convertF3DtoNewVersion(obj, index, material, f3d_node_tree) + materialDict[material] = material V4PresetName = { @@ -113,9 +97,10 @@ def set_best_draw_layer_for_materials(): mat: bpy.types.Material = obj.material_slots[p.material_index].material if not has_valid_mat_ver(mat) or mat.mat_ver >= 4 or mat.name in finished_mats: continue - + mat.f3d_update_flag = True # default to object's draw layer - mat.f3d_mat.draw_layer.sm64 = obj.draw_layer_static + with bpy.context.temp_override(material=mat): + mat.f3d_mat.draw_layer.sm64 = obj.draw_layer_static if len(obj.vertex_groups) == 0: continue # object doesn't have vertex groups @@ -126,8 +111,10 @@ def set_best_draw_layer_for_materials(): # check for matching bone from group name bone = bone_map.get(group.name) if bone is not None: + mat.f3d_update_flag = True # override material draw later with bone's draw layer - mat.f3d_mat.draw_layer.sm64 = bone.draw_layer + with bpy.context.temp_override(material=mat): + mat.f3d_mat.draw_layer.sm64 = bone.draw_layer finished_mats.add(mat.name) for obj in objects: @@ -138,11 +125,15 @@ def set_best_draw_layer_for_materials(): if not has_valid_mat_ver(mat) or mat.mat_ver >= 4 or mat.name in finished_mats: continue - mat.f3d_mat.draw_layer.sm64 = obj.draw_layer_static + mat.f3d_update_flag = True + with bpy.context.temp_override(material=mat): + mat.f3d_mat.draw_layer.sm64 = obj.draw_layer_static finished_mats.add(mat.name) -def convertF3DtoNewVersion(obj: bpy.types.Object | bpy.types.Bone, index, material, materialDict, version): +def convertF3DtoNewVersion( + obj: bpy.types.Object | bpy.types.Bone, index: int, material, f3d_node_tree: bpy.types.NodeTree +): try: if not has_valid_mat_ver(material): return @@ -151,27 +142,25 @@ def convertF3DtoNewVersion(obj: bpy.types.Object | bpy.types.Bone, index, materi else: oldPreset = material.get("f3d_preset") - newMat = createF3DMat(obj, preset=getV4PresetName(oldPreset), index=index) - - if material.mat_ver > 3: - copyPropertyGroup(material.f3d_mat, newMat.f3d_mat) - else: - convertToNewMat(newMat, material) + update_preset_manual_v4(material, getV4PresetName(oldPreset)) + # HACK: We can´t just lock, so make is_f3d temporarly false + material.is_f3d, material.f3d_update_flag = False, True + # Convert before node tree changes, as old materials store some values in the actual nodes + if material.mat_ver <= 3: + convertToNewMat(material, material) - newMat.f3d_mat.draw_layer.sm64 = material.f3d_mat.draw_layer.sm64 + node_tree_copy(f3d_node_tree, material.node_tree) - copyPropertyGroup(material.ootMaterial, newMat.ootMaterial) - copyPropertyGroup(material.flipbookGroup, newMat.flipbookGroup) - copyPropertyGroup(material.ootCollisionProperty, newMat.ootCollisionProperty) + material.is_f3d, material.f3d_update_flag = True, False + material.mat_ver = 5 - colSettings = CollisionSettings() - colSettings.load(material) - colSettings.apply(newMat) + createScenePropertiesForMaterial(material) + with bpy.context.temp_override(material=material): + update_all_node_values(material, bpy.context) # Reload everything - updateMatWithNewVersionName(newMat, material, materialDict, version) except Exception as exc: print("Failed to upgrade", material.name) - print(exc) + traceback.print_exc() def convertAllBSDFtoF3D(objs, renameUV): @@ -238,14 +227,6 @@ def updateMatWithName(f3dMat, oldMat, materialDict): materialDict[oldMat] = f3dMat -def updateMatWithNewVersionName(f3dMat, oldMat, materialDict, version): - name = oldMat.name - f3dMat.name = name - oldMat.name = name + "_v" + str(oldMat.mat_ver) - update_preset_manual(f3dMat, bpy.context) - materialDict[oldMat] = f3dMat - - class BSDFConvert(bpy.types.Operator): # set bl_ properties bl_idname = "object.convert_bsdf" @@ -286,7 +267,9 @@ class MatUpdateConvert(bpy.types.Operator): version = 5 bl_idname = "object.convert_f3d_update" bl_label = "Recreate F3D Materials As v" + str(version) - bl_options = {"REGISTER", "UNDO", "PRESET"} + bl_options = {"UNDO"} + + update_conv_all: bpy.props.BoolProperty(default=True) # Called on demand (i.e. button press, menu item) # Can also be called from operator search menu (Spacebar) @@ -295,10 +278,9 @@ def execute(self, context): if context.mode != "OBJECT": raise PluginError("Operator can only be used in object mode.") - if context.scene.update_conv_all: + if self.update_conv_all: upgradeF3DVersionAll( [obj for obj in bpy.data.objects if obj.type == "MESH"], - bpy.data.armatures, self.version, ) else: @@ -308,7 +290,7 @@ def execute(self, context): raise PluginError("Mesh not selected.") obj = context.selected_objects[0] - upgradeF3DVersionOneObject(obj, {}, self.version) + upgradeF3DVersionOneObject(obj, {}, get_f3d_node_tree()) except Exception as e: raisePluginError(self, e) @@ -336,7 +318,8 @@ def draw(self, context): self.layout.operator(BSDFConvert.bl_idname) self.layout.prop(context.scene, "bsdf_conv_all") self.layout.prop(context.scene, "rename_uv_maps") - self.layout.operator(MatUpdateConvert.bl_idname) + op = self.layout.operator(MatUpdateConvert.bl_idname) + op.update_conv_all = context.scene.update_conv_all self.layout.prop(context.scene, "update_conv_all") self.layout.operator(ReloadDefaultF3DPresets.bl_idname) diff --git a/fast64_internal/sm64/sm64_collision.py b/fast64_internal/sm64/sm64_collision.py index e70134e6c..728b52a10 100644 --- a/fast64_internal/sm64/sm64_collision.py +++ b/fast64_internal/sm64/sm64_collision.py @@ -470,32 +470,6 @@ def collisionVertIndex(vert, vertArray): return None -class CollisionSettings: - def __init__(self): - self.collision_type = "SURFACE_DEFAULT" - self.collision_type_simple = "SURFACE_DEFAULT" - self.collision_custom = "SURFACE_DEFAULT" - self.collision_all_options = False - self.use_collision_param = False - self.collision_param = "0x0000" - - def load(self, material): - self.collision_type = material.collision_type - self.collision_type_simple = material.collision_type_simple - self.collision_custom = material.collision_custom - self.collision_all_options = material.collision_all_options - self.use_collision_param = material.use_collision_param - self.collision_param = material.collision_param - - def apply(self, material): - material.collision_type = self.collision_type - material.collision_type_simple = self.collision_type_simple - material.collision_custom = self.collision_custom - material.collision_all_options = self.collision_all_options - material.use_collision_param = self.use_collision_param - material.collision_param = self.collision_param - - class SM64_ExportCollision(bpy.types.Operator): # set bl_ properties bl_idname = "object.sm64_export_collision" diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index ff40a5912..45aac222b 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -251,6 +251,8 @@ def get_attr_or_property(prop: dict | object, attr: str, newProp: dict | object) # change type hint to proper type newPropDef: bpy.types.EnumProperty = newPropDef return newPropDef.enum_items[val].identifier + elif "Bool" in newPropDef.bl_rna.name: # Should be "Boolean Definition" + return bool(val) except Exception as e: pass return val @@ -274,15 +276,19 @@ def recursiveCopyOldPropertyGroup(oldProp, newProp): if sub_value_attr == "rna_type": continue sub_value = get_attr_or_property(oldProp, sub_value_attr, newProp) + new_value = get_attr_or_property(newProp, sub_value_attr, newProp) - if isinstance(sub_value, bpy.types.PropertyGroup) or type(sub_value).__name__ in ( + if isinstance(new_value, bpy.types.PropertyGroup) or type(new_value).__name__ in ( "bpy_prop_collection_idprop", "IDPropertyGroup", ): newCollection = getattr(newProp, sub_value_attr) recursiveCopyOldPropertyGroup(sub_value, newCollection) else: - setattr(newProp, sub_value_attr, sub_value) + try: + setattr(newProp, sub_value_attr, sub_value) + except Exception as e: + print(e) def propertyCollectionEquals(oldProp, newProp):