diff --git a/__init__.py b/__init__.py index 21037e6..a22f345 100644 --- a/__init__.py +++ b/__init__.py @@ -22,7 +22,12 @@ from .fast64_internal.oot import OOT_Properties, oot_register, oot_unregister from .fast64_internal.oot.props_panel_main import OOT_ObjectProperties from .fast64_internal.utility_anim import utility_anim_register, utility_anim_unregister, ArmatureApplyWithMeshOperator -from .fast64_internal.f3d.f3d_material import mat_register, mat_unregister, check_or_ask_color_management +from .fast64_internal.f3d.f3d_material import ( + F3D_MAT_CUR_VERSION, + mat_register, + mat_unregister, + check_or_ask_color_management, +) from .fast64_internal.f3d.f3d_render_engine import render_engine_register, render_engine_unregister from .fast64_internal.f3d.f3d_writer import f3d_writer_register, f3d_writer_unregister from .fast64_internal.f3d.f3d_parser import f3d_parser_register, f3d_parser_unregister @@ -31,7 +36,7 @@ from .fast64_internal.f3d.op_largetexture import op_largetexture_register, op_la from .fast64_internal.f3d_material_converter import ( MatUpdateConvert, - upgradeF3DVersionAll, + upgrade_f3d_version_all_meshes, bsdf_conv_register, bsdf_conv_unregister, bsdf_conv_panel_regsiter, @@ -264,10 +269,7 @@ class UpgradeF3DMaterialsDialog(bpy.types.Operator): if context.mode != "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") - upgradeF3DVersionAll( - [obj for obj in bpy.data.objects if obj.type == "MESH"], - MatUpdateConvert.version, - ) + upgrade_f3d_version_all_meshes() self.done = True return {"FINISHED"} @@ -320,10 +322,18 @@ def upgrade_changed_props(): scene.gameEditorMode = "Homebrew" del scene["decomp_compatible"] + settings = scene.fast64.renderSettings + light0Color = settings.pop("lightColor", None) + if light0Color is not None: + settings.light0Color = light0Color + light0Direction = settings.pop("lightDirection", None) + if light0Direction is not None: + settings.light0Direction = light0Direction + def upgrade_scene_props_node(): """update f3d materials with SceneProperties node""" - has_old_f3d_mats = any(mat.is_f3d and mat.mat_ver < MatUpdateConvert.version for mat in bpy.data.materials) + has_old_f3d_mats = any(mat.is_f3d and mat.mat_ver < F3D_MAT_CUR_VERSION for mat in bpy.data.materials) if has_old_f3d_mats: bpy.ops.dialog.upgrade_f3d_materials("INVOKE_DEFAULT") diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index 71ea1ab..dc7f5a0 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -39,6 +39,8 @@ 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 +F3D_MAT_CUR_VERSION = 6 # Increment this when changing the nodes + F3DMaterialHash = Any # giant tuple logging.basicConfig(format="%(asctime)s: %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p") @@ -114,14 +116,14 @@ drawLayerOOTtoSM64 = { } drawLayerSM64Alpha = { - "0": "OPAQUE", - "1": "OPAQUE", - "2": "OPAQUE", - "3": "OPAQUE", + "0": "OPA", + "1": "OPA", + "2": "OPA", + "3": "OPA", "4": "CLIP", - "5": "BLEND", - "6": "BLEND", - "7": "BLEND", + "5": "XLU", + "6": "XLU", + "7": "XLU", } enumF3DMenu = [ @@ -270,7 +272,7 @@ def is_blender_doing_fog(settings: "RDPSettings", default_for_no_rendermode: boo ) -def get_blend_method(material: bpy.types.Material) -> str: +def get_output_method(material: bpy.types.Material) -> str: settings = material.f3d_mat.rdp_settings if not settings.set_rendermode: return drawLayerSM64Alpha[material.f3d_mat.draw_layer.sm64] @@ -279,14 +281,17 @@ def get_blend_method(material: bpy.types.Material) -> str: if settings.force_bl and is_blender_equation_equal( settings, -1, "G_BL_CLR_IN", "G_BL_A_IN", "G_BL_CLR_MEM", "G_BL_1MA" ): - return "BLEND" - return "OPAQUE" + return "XLU" + return "OPA" def update_blend_method(material: Material, context): - material.blend_method = get_blend_method(material) - if material.blend_method == "CLIP": - material.alpha_threshold = 0.125 + if bpy.app.version >= (4, 2, 0): + material.surface_render_method = "BLENDED" + elif get_output_method(material) == "OPA": + material.blend_method = "OPAQUE" + else: + material.blend_method = "BLEND" class DrawLayerProperty(PropertyGroup): @@ -1571,8 +1576,6 @@ def update_fog_nodes(material: Material, context: Context): f3dMat: "F3DMaterialProperty" = material.f3d_mat shade_alpha_is_fog = material.f3d_mat.rdp_settings.g_fog - nodes["Shade Color"].inputs["Fog"].default_value = int(shade_alpha_is_fog) - fogBlender: ShaderNodeGroup = nodes["FogBlender"] # if NOT setting rendermode, it is more likely that the user is setting # rendermodes in code, so to be safe we'll enable fog. Plus we are checking @@ -1599,7 +1602,7 @@ def update_fog_nodes(material: Material, context: Context): remove_first_link_if_exists(material, nodes["CalcFog"].inputs["FogNear"].links) remove_first_link_if_exists(material, nodes["CalcFog"].inputs["FogFar"].links) - fogBlender.inputs["Fog Color"].default_value = f3dMat.fog_color + fogBlender.inputs["Fog Color"].default_value = s_rgb_alpha_1_tuple(f3dMat.fog_color) nodes["CalcFog"].inputs["FogNear"].default_value = f3dMat.fog_position[0] nodes["CalcFog"].inputs["FogFar"].default_value = f3dMat.fog_position[1] @@ -1649,20 +1652,28 @@ def update_combiner_connections(material: Material, context: Context, combiner: def set_output_node_groups(material: Material): nodes = material.node_tree.nodes - f3dMat: "F3DMaterialProperty" = material.f3d_mat - is_two_cycle = f3dMat.rdp_settings.g_mdsft_cycletype == "G_CYC_2CYCLE" - output_node = nodes["OUTPUT"] - if is_two_cycle: - if material.blend_method == "OPAQUE": - output_node.node_tree = bpy.data.node_groups["OUTPUT_2CYCLE_OPA"] - else: - output_node.node_tree = bpy.data.node_groups["OUTPUT_2CYCLE_XLU"] - else: - if material.blend_method == "OPAQUE": - output_node.node_tree = bpy.data.node_groups["OUTPUT_1CYCLE_OPA"] - else: - output_node.node_tree = bpy.data.node_groups["OUTPUT_1CYCLE_XLU"] + f3dMat: "F3DMaterialProperty" = material.f3d_mat + cycle = f3dMat.rdp_settings.g_mdsft_cycletype.lstrip("G_CYC_").rstrip("_CYCLE") + output_method = get_output_method(material) + + output_group_name = f"OUTPUT_{cycle}CYCLE_{output_method}" + output_group = bpy.data.node_groups[output_group_name] + output_node.node_tree = output_group + + for inp in output_node.inputs: + remove_first_link_if_exists(material, inp.links) + output_node.inputs["Cycle_C_1"].default_value = (0.0, 0.0, 0.0, 1.0) + output_node.inputs["Cycle_A_1"].default_value = 0.5 + output_node.inputs["Cycle_C_2"].default_value = (0.0, 0.0, 0.0, 1.0) + output_node.inputs["Cycle_A_2"].default_value = 0.5 + if output_method == "CLIP": + output_node.inputs["Alpha Threshold"].default_value = 0.125 + material.node_tree.links.new(nodes["Cycle_1"].outputs["Color"], output_node.inputs["Cycle_C_1"]) + material.node_tree.links.new(nodes["Cycle_1"].outputs["Alpha"], output_node.inputs["Cycle_A_1"]) + material.node_tree.links.new(nodes["FogBlender"].outputs["Color"], output_node.inputs["Cycle_C_2"]) + material.node_tree.links.new(nodes["Cycle_2"].outputs["Alpha"], output_node.inputs["Cycle_A_2"]) + material.node_tree.links.new(output_node.outputs[0], nodes["Material Output F3D"].inputs[0]) def update_light_colors(material, context): @@ -1680,43 +1691,33 @@ def update_light_colors(material, context): f3dMat.ambient_light_color = new_amb if f3dMat.set_lights: - remove_first_link_if_exists(material, nodes["Shade Color"].inputs["Shade Color"].links) - remove_first_link_if_exists(material, nodes["Shade Color"].inputs["Ambient Color"].links) + remove_first_link_if_exists(material, nodes["Shade Color"].inputs["AmbientColor"].links) + remove_first_link_if_exists(material, nodes["Shade Color"].inputs["Light0Color"].links) + remove_first_link_if_exists(material, nodes["Shade Color"].inputs["Light1Color"].links) # TODO: feature to toggle gamma correction - light = f3dMat.default_light_color + light0 = f3dMat.default_light_color + light1 = [0.0, 0.0, 0.0, 1.0] if not f3dMat.use_default_lighting: - if f3dMat.f3d_light1 is not None: - light = f3dMat.f3d_light1.color - else: - light = [1.0, 1.0, 1.0, 1.0] + light0 = f3dMat.f3d_light1.color if f3dMat.f3d_light1 is not None else [1.0, 1.0, 1.0, 1.0] + light1 = f3dMat.f3d_light2.color if f3dMat.f3d_light2 is not None else light1 - corrected_col = gammaCorrect(light) - corrected_col.append(1.0) - corrected_amb = gammaCorrect(f3dMat.ambient_light_color) - corrected_amb.append(1.0) - - nodes["Shade Color"].inputs["Shade Color"].default_value = tuple(c for c in corrected_col) - nodes["Shade Color"].inputs["Ambient Color"].default_value = tuple(c for c in corrected_amb) + nodes["Shade Color"].inputs["AmbientColor"].default_value = s_rgb_alpha_1_tuple(f3dMat.ambient_light_color) + nodes["Shade Color"].inputs["Light0Color"].default_value = s_rgb_alpha_1_tuple(light0) + nodes["Shade Color"].inputs["Light1Color"].default_value = s_rgb_alpha_1_tuple(light1) else: - col = [1.0, 1.0, 1.0, 1.0] - amb_col = [0.5, 0.5, 0.5, 1.0] - nodes["Shade Color"].inputs["Shade Color"].default_value = tuple(c for c in col) - nodes["Shade Color"].inputs["Ambient Color"].default_value = tuple(c for c in amb_col) - link_if_none_exist(material, nodes["ShadeColOut"].outputs[0], nodes["Shade Color"].inputs["Shade Color"]) - link_if_none_exist(material, nodes["AmbientColOut"].outputs[0], nodes["Shade Color"].inputs["Ambient Color"]) + nodes["Shade Color"].inputs["AmbientColor"].default_value = (0.5, 0.5, 0.5, 1.0) + nodes["Shade Color"].inputs["Light0Color"].default_value = (1.0, 1.0, 1.0, 1.0) + nodes["Shade Color"].inputs["Light1Color"].default_value = (0.0, 0.0, 0.0, 1.0) + link_if_none_exist(material, nodes["AmbientColorOut"].outputs[0], nodes["Shade Color"].inputs["AmbientColor"]) + link_if_none_exist(material, nodes["Light0ColorOut"].outputs[0], nodes["Shade Color"].inputs["Light0Color"]) + link_if_none_exist(material, nodes["Light1ColorOut"].outputs[0], nodes["Shade Color"].inputs["Light1Color"]) def update_color_node(combiner_inputs, color: Color, prefix: str): """Function for updating either Prim or Env colors""" # TODO: feature to toggle gamma correction - corrected_prim = gammaCorrect(color) - combiner_inputs[f"{prefix} Color"].default_value = ( - corrected_prim[0], - corrected_prim[1], - corrected_prim[2], - 1.0, - ) + combiner_inputs[f"{prefix} Color"].default_value = s_rgb_alpha_1_tuple(color) combiner_inputs[f"{prefix} Alpha"].default_value = color[3] @@ -1758,13 +1759,24 @@ def update_node_values_of_material(material: Material, context): else: nodes["UV"].node_tree = bpy.data.node_groups["UV"] - # This is a temporary measure to be able to see the vertex colors in packed materials. - if bpy.context.scene.f3d_type == "F3DEX3" and f3dMat.rdp_settings.g_packed_normals: - nodes["Shade Color"].node_tree = bpy.data.node_groups["ShdCol_V"] - elif f3dMat.rdp_settings.g_lighting: - nodes["Shade Color"].node_tree = bpy.data.node_groups["ShdCol_L"] - else: - nodes["Shade Color"].node_tree = bpy.data.node_groups["ShdCol_V"] + shdcol_inputs = nodes["Shade Color"].inputs + for propName in [ + "g_ambocclusion", + "g_packed_normals", + "g_lighttoalpha", + "g_lighting_specular", + "g_fresnel_color", + "g_fresnel_alpha", + "g_fog", + "g_lighting", + ]: + shdcol_inputs[propName.upper()].default_value = getattr(f3dMat.rdp_settings, propName) + + shdcol_inputs["AO Ambient"].default_value = f3dMat.ao_ambient + shdcol_inputs["AO Directional"].default_value = f3dMat.ao_directional + shdcol_inputs["AO Point"].default_value = f3dMat.ao_point + shdcol_inputs["Fresnel Lo"].default_value = f3dMat.fresnel_lo + shdcol_inputs["Fresnel Hi"].default_value = f3dMat.fresnel_hi update_light_colors(material, context) @@ -2245,7 +2257,7 @@ def load_handler(dummy): bpy.app.handlers.load_post.append(load_handler) -SCENE_PROPERTIES_VERSION = 1 +SCENE_PROPERTIES_VERSION = 2 def createOrUpdateSceneProperties(): @@ -2300,14 +2312,27 @@ def createOrUpdateSceneProperties(): _nodeFogFar: NodeSocketFloat = tree_interface.new_socket( "FogFar", socket_type="NodeSocketFloat", in_out="OUTPUT" ) - _nodeShadeColor: NodeSocketColor = tree_interface.new_socket( - "ShadeColor", socket_type="NodeSocketColor", in_out="OUTPUT" - ) + _nodeAmbientColor: NodeSocketColor = tree_interface.new_socket( "AmbientColor", socket_type="NodeSocketColor", in_out="OUTPUT" ) - _nodeLightDirection: NodeSocketVector = tree_interface.new_socket( - "LightDirection", socket_type="NodeSocketVector", in_out="OUTPUT" + _nodeLight0Color: NodeSocketColor = tree_interface.new_socket( + "Light0Color", socket_type="NodeSocketColor", in_out="OUTPUT" + ) + _nodeLight0Dir: NodeSocketVector = tree_interface.new_socket( + "Light0Dir", socket_type="NodeSocketVector", in_out="OUTPUT" + ) + _nodeLight0Size: NodeSocketFloat = tree_interface.new_socket( + "Light0Size", socket_type="NodeSocketFloat", in_out="OUTPUT" + ) + _nodeLight1Color: NodeSocketColor = tree_interface.new_socket( + "Light1Color", socket_type="NodeSocketColor", in_out="OUTPUT" + ) + _nodeLight1Dir: NodeSocketVector = tree_interface.new_socket( + "Light1Dir", socket_type="NodeSocketVector", in_out="OUTPUT" + ) + _nodeLight1Size: NodeSocketFloat = tree_interface.new_socket( + "Light1Size", socket_type="NodeSocketFloat", in_out="OUTPUT" ) else: @@ -2318,11 +2343,14 @@ def createOrUpdateSceneProperties(): _nodeBlender_Game_Scale: NodeSocketFloat = new_group.outputs.new("NodeSocketFloat", "Blender_Game_Scale") _nodeFogNear: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogNear") _nodeFogFar: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "FogFar") - _nodeShadeColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "ShadeColor") + _nodeAmbientColor: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "AmbientColor") - _nodeLightDirection: NodeSocketVectorDirection = new_group.outputs.new( - "NodeSocketVectorDirection", "LightDirection" - ) + _nodeLight0Color: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "Light0Color") + _nodeLight0Dir: NodeSocketVectorDirection = new_group.outputs.new("NodeSocketVectorDirection", "Light0Dir") + _nodeLight0Size: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "Light0Size") + _nodeLight1Color: NodeSocketColor = new_group.outputs.new("NodeSocketColor", "Light1Color") + _nodeLight1Dir: NodeSocketVectorDirection = new_group.outputs.new("NodeSocketVectorDirection", "Light1Dir") + _nodeLight1Size: NodeSocketInt = new_group.outputs.new("NodeSocketInt", "Light1Size") # Set outputs from render settings sceneOutputs: NodeGroupOutput = new_group.nodes["Group Output"] @@ -2340,22 +2368,29 @@ def createScenePropertiesForMaterial(material: Material): # create a new group node to hold the tree scene_props = node_tree.nodes.new(type="ShaderNodeGroup") scene_props.name = "SceneProperties" - scene_props.location = (-420, -360) + scene_props.location = (-320, -23) scene_props.node_tree = bpy.data.node_groups["SceneProperties"] - # link the new node to correct socket + + # Fog links to reroutes and the CalcFog block node_tree.links.new(scene_props.outputs["FogEnable"], node_tree.nodes["FogEnable"].inputs[0]) node_tree.links.new(scene_props.outputs["FogColor"], node_tree.nodes["FogColor"].inputs[0]) - node_tree.links.new(scene_props.outputs["FogNear"], node_tree.nodes["CalcFog"].inputs["FogNear"]) - node_tree.links.new(scene_props.outputs["FogFar"], node_tree.nodes["CalcFog"].inputs["FogFar"]) + node_tree.links.new(scene_props.outputs["F3D_NearClip"], node_tree.nodes["CalcFog"].inputs["F3D_NearClip"]) + node_tree.links.new(scene_props.outputs["F3D_FarClip"], node_tree.nodes["CalcFog"].inputs["F3D_FarClip"]) node_tree.links.new( scene_props.outputs["Blender_Game_Scale"], node_tree.nodes["CalcFog"].inputs["Blender_Game_Scale"] ) - node_tree.links.new(scene_props.outputs["F3D_NearClip"], node_tree.nodes["CalcFog"].inputs["F3D_NearClip"]) - node_tree.links.new(scene_props.outputs["F3D_FarClip"], node_tree.nodes["CalcFog"].inputs["F3D_FarClip"]) + node_tree.links.new(scene_props.outputs["FogNear"], node_tree.nodes["CalcFog"].inputs["FogNear"]) + node_tree.links.new(scene_props.outputs["FogFar"], node_tree.nodes["CalcFog"].inputs["FogFar"]) - node_tree.links.new(scene_props.outputs["ShadeColor"], node_tree.nodes["ShadeColor"].inputs[0]) + # Lighting links to reroutes. The colors are connected to other reroutes for update_light_colors, + # the others go directly to the Shade Color block. node_tree.links.new(scene_props.outputs["AmbientColor"], node_tree.nodes["AmbientColor"].inputs[0]) - node_tree.links.new(scene_props.outputs["LightDirection"], node_tree.nodes["LightDirection"].inputs[0]) + node_tree.links.new(scene_props.outputs["Light0Color"], node_tree.nodes["Light0Color"].inputs[0]) + node_tree.links.new(scene_props.outputs["Light0Dir"], node_tree.nodes["Light0Dir"].inputs[0]) + node_tree.links.new(scene_props.outputs["Light0Size"], node_tree.nodes["Light0Size"].inputs[0]) + node_tree.links.new(scene_props.outputs["Light1Color"], node_tree.nodes["Light1Color"].inputs[0]) + node_tree.links.new(scene_props.outputs["Light1Dir"], node_tree.nodes["Light1Dir"].inputs[0]) + node_tree.links.new(scene_props.outputs["Light1Size"], node_tree.nodes["Light1Size"].inputs[0]) def link_f3d_material_library(): @@ -2474,7 +2509,7 @@ def createF3DMat(obj: Object | None, preset="Shaded Solid", index=None): add_f3d_mat_to_obj(obj, material, index) material.is_f3d = True - material.mat_ver = 5 + material.mat_ver = F3D_MAT_CUR_VERSION update_preset_manual_v4(material, preset) @@ -4577,6 +4612,7 @@ class F3DRenderSettingsPanel(Panel): layout = self.layout layout.ui_units_x = 16 renderSettings = context.scene.fast64.renderSettings + isF3DEX3 = bpy.context.scene.f3d_type == "F3DEX3" globalSettingsBox = layout.box() labelbox = globalSettingsBox.box() @@ -4591,8 +4627,14 @@ class F3DRenderSettingsPanel(Panel): globalSettingsBox.separator(factor=0.125) # TODO: (v5) add headings prop_split(globalSettingsBox, renderSettings, "ambientColor", "Ambient Light") - prop_split(globalSettingsBox, renderSettings, "lightColor", "Light Color") - prop_split(globalSettingsBox, renderSettings, "lightDirection", "Light Direction") + prop_split(globalSettingsBox, renderSettings, "light0Color", "Light 0 Color") + prop_split(globalSettingsBox, renderSettings, "light0Direction", "Light 0 Direction") + if isF3DEX3: + prop_split(globalSettingsBox, renderSettings, "light0SpecSize", "Light 0 Specular Size") + prop_split(globalSettingsBox, renderSettings, "light1Color", "Light 1 Color") + prop_split(globalSettingsBox, renderSettings, "light1Direction", "Light 1 Direction") + if isF3DEX3: + prop_split(globalSettingsBox, renderSettings, "light1SpecSize", "Light 1 Specular Size") prop_split(globalSettingsBox, renderSettings, "useWorldSpaceLighting", "Use World Space Lighting") if context.scene.gameEditorMode in ["SM64", "OOT"]: @@ -4627,16 +4669,16 @@ class F3DRenderSettingsPanel(Panel): else: numLightsNeeded = 1 if header.skyboxLighting == "Custom": - r2 = b.row() + r2 = b.row().split(factor=0.4) r2.prop(renderSettings, "ootForceTimeOfDay") if renderSettings.ootForceTimeOfDay: r2.label(text="Light Index sets first of four lights.", icon="INFO") numLightsNeeded = 4 - if header.skyboxLighting != "0x00": + if header.skyboxLighting != "LIGHT_MODE_TIME": r.prop(renderSettings, "ootLightIdx") if renderSettings.ootLightIdx + numLightsNeeded > len(header.lightList): b.label(text="Light does not exist.", icon="QUESTION") - if header.skyboxLighting == "0x00" or ( + if header.skyboxLighting == "LIGHT_MODE_TIME" or ( header.skyboxLighting == "Custom" and renderSettings.ootForceTimeOfDay ): r.prop(renderSettings, "ootTime") diff --git a/fast64_internal/f3d/f3d_material_library.blend b/fast64_internal/f3d/f3d_material_library.blend index 5d6f1d5..d9edfb8 100644 Binary files a/fast64_internal/f3d/f3d_material_library.blend and b/fast64_internal/f3d/f3d_material_library.blend differ diff --git a/fast64_internal/f3d_material_converter.py b/fast64_internal/f3d_material_converter.py index 9c6efef..fffb903 100644 --- a/fast64_internal/f3d_material_converter.py +++ b/fast64_internal/f3d_material_converter.py @@ -8,13 +8,14 @@ from .utility import * from bl_operators.presets import AddPresetBase -def upgradeF3DVersionAll(objs, version): +def upgrade_f3d_version_all_meshes() -> None: + objs = [obj for obj in bpy.data.objects if obj.type == "MESH"] 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: - if node_tree.name[-6:] == "F3D v" + str(version): + if node_tree.name[-6:] == "F3D v" + str(F3D_MAT_CUR_VERSION): deleteGroups.append(node_tree) for deleteGroup in deleteGroups: bpy.data.node_groups.remove(deleteGroup) @@ -152,7 +153,7 @@ def convertF3DtoNewVersion( node_tree_copy(f3d_node_tree, material.node_tree) material.is_f3d, material.f3d_update_flag = True, False - material.mat_ver = 5 + material.mat_ver = F3D_MAT_CUR_VERSION createScenePropertiesForMaterial(material) with bpy.context.temp_override(material=material): @@ -264,9 +265,8 @@ class BSDFConvert(bpy.types.Operator): class MatUpdateConvert(bpy.types.Operator): # set bl_ properties - version = 5 bl_idname = "object.convert_f3d_update" - bl_label = "Recreate F3D Materials As v" + str(version) + bl_label = "Recreate F3D Materials As v" + str(F3D_MAT_CUR_VERSION) bl_options = {"UNDO"} update_conv_all: bpy.props.BoolProperty(default=True) @@ -279,10 +279,7 @@ class MatUpdateConvert(bpy.types.Operator): raise PluginError("Operator can only be used in object mode.") if self.update_conv_all: - upgradeF3DVersionAll( - [obj for obj in bpy.data.objects if obj.type == "MESH"], - self.version, - ) + upgrade_f3d_version_all_meshes() else: if len(context.selected_objects) == 0: raise PluginError("Mesh not selected.") diff --git a/fast64_internal/oot/exporter/scene/general.py b/fast64_internal/oot/exporter/scene/general.py index 9d8949e..2a1638d 100644 --- a/fast64_internal/oot/exporter/scene/general.py +++ b/fast64_internal/oot/exporter/scene/general.py @@ -113,7 +113,7 @@ class SceneLighting: light2[1], exportColor(lightProp.fogColor), lightProp.fogNear, - lightProp.fogFar, + lightProp.z_far, lightProp.transitionSpeed, ) ) diff --git a/fast64_internal/oot/oot_level_parser.py b/fast64_internal/oot/oot_level_parser.py index 50137c0..c5a9939 100644 --- a/fast64_internal/oot/oot_level_parser.py +++ b/fast64_internal/oot/oot_level_parser.py @@ -1089,7 +1089,7 @@ def parseLightList( blendFogShort = hexOrDecInt(lightParams[18]) fogNear = blendFogShort & ((1 << 10) - 1) transitionSpeed = blendFogShort >> 10 - fogFar = hexOrDecInt(lightParams[19]) + z_far = hexOrDecInt(lightParams[19]) lightHeader = sceneHeader.lightList.add() lightHeader.ambient = ambientColor + (1,) @@ -1106,7 +1106,7 @@ def parseLightList( lightHeader.fogColor = fogColor + (1,) lightHeader.fogNear = fogNear - lightHeader.fogFar = fogFar + lightHeader.z_far = z_far lightHeader.transitionSpeed = transitionSpeed index += 1 diff --git a/fast64_internal/oot/scene/properties.py b/fast64_internal/oot/scene/properties.py index 6e47596..b48948e 100644 --- a/fast64_internal/oot/scene/properties.py +++ b/fast64_internal/oot/scene/properties.py @@ -178,7 +178,7 @@ class OOTLightProperty(PropertyGroup): ) fogNear: IntProperty(name="", default=993, min=0, max=2**10 - 1, update=on_update_oot_render_settings) transitionSpeed: IntProperty(name="", default=1, min=0, max=63, update=on_update_oot_render_settings) - fogFar: IntProperty(name="", default=0x3200, min=0, max=2**16 - 1, update=on_update_oot_render_settings) + z_far: IntProperty(name="", default=0x3200, min=0, max=2**15 - 1, update=on_update_oot_render_settings) expandTab: BoolProperty(name="Expand Tab") def draw_props( @@ -212,8 +212,8 @@ class OOTLightProperty(PropertyGroup): box.prop(self, "useCustomDiffuse1") prop_split(box, self, "fogColor", "Fog Color") - prop_split(box, self, "fogNear", "Fog Near") - prop_split(box, self, "fogFar", "Fog Far") + prop_split(box, self, "fogNear", "Fog Near (Fog Far=1000)") + prop_split(box, self, "z_far", "Z Far (Draw Distance)") prop_split(box, self, "transitionSpeed", "Transition Speed") diff --git a/fast64_internal/render_settings.py b/fast64_internal/render_settings.py index 3fa6104..6476f8f 100644 --- a/fast64_internal/render_settings.py +++ b/fast64_internal/render_settings.py @@ -10,7 +10,6 @@ def on_update_sm64_render_settings(self, context: bpy.types.Context): area: bpy.types.Object = renderSettings.sm64Area renderSettings.fogPreviewColor = tuple(c for c in area.area_fog_color) renderSettings.fogPreviewPosition = tuple(round(p) for p in area.area_fog_position) - renderSettings.clippingPlanes = tuple(float(p) for p in area.clipPlanes) @@ -26,22 +25,22 @@ def on_update_oot_render_settings(self, context: bpy.types.Context): if header is None: return lMode = header.skyboxLighting - if lMode == "0x01" or (lMode == "Custom" and not renderSettings.ootForceTimeOfDay): + if lMode == "LIGHT_MODE_SETTINGS" or (lMode == "Custom" and not renderSettings.ootForceTimeOfDay): if renderSettings.ootLightIdx >= len(header.lightList): return l = header.lightList[renderSettings.ootLightIdx] renderSettings.ambientColor = tuple(c for c in l.ambient) col0, dir0 = ootGetBaseOrCustomLight(l, 0, False, False) - renderSettings.lightColor = tuple(c for c in col0) - renderSettings.lightDirection = tuple(d for d in dir0) - # TODO: Implement light1 into shader nodes - # col1, dir1 = ootGetBaseOrCustomLight(l, 1, False, False) - # renderSettings.light1Color = tuple(c for c in col1) - # renderSettings.light1Direction = tuple(d for d in dir1) + renderSettings.light0Color = tuple(c for c in col0) + renderSettings.light0Direction = tuple(d for d in dir0) + col1, dir1 = ootGetBaseOrCustomLight(l, 1, False, False) + renderSettings.light1Color = tuple(c for c in col1) + renderSettings.light1Direction = tuple(d for d in dir1) renderSettings.fogPreviewColor = tuple(c for c in l.fogColor) - renderSettings.fogPreviewPosition = (l.fogNear, l.fogFar) + renderSettings.fogPreviewPosition = (l.fogNear, 1000) # fogFar is always 1000 in OoT + renderSettings.clippingPlanes = (10.0, l.z_far) # zNear seems to always be 10 in OoT else: - if header.skyboxLighting == "0x00": + if lMode == "LIGHT_MODE_TIME": tod = header.timeOfDayLights lights = [tod.dawn, tod.day, tod.dusk, tod.night] else: @@ -69,37 +68,34 @@ def on_update_oot_render_settings(self, context: bpy.types.Context): renderSettings.ambientColor = interpColors(la.ambient, lb.ambient, fade) col0a, _ = ootGetBaseOrCustomLight(la, 0, False, False) col0b, _ = ootGetBaseOrCustomLight(lb, 0, False, False) - renderSettings.lightColor = col0a + (col0b - col0a) * fade - # TODO: Implement light1 into shader nodes - # col1a, _ = ootGetBaseOrCustomLight(la, 1, False, False) - # col1b, _ = ootGetBaseOrCustomLight(lb, 1, False, False) - # renderSettings.light1Color = col1a * fa + col1b * fb + renderSettings.light0Color = col0a + (col0b - col0a) * fade + col1a, _ = ootGetBaseOrCustomLight(la, 1, False, False) + col1b, _ = ootGetBaseOrCustomLight(lb, 1, False, False) + renderSettings.light1Color = col1a + (col1b - col1a) * fade sint, cost = math.sin(math.tau * t / 24.0), math.cos(math.tau * t / 24.0) - renderSettings.lightDirection = mathutils.Vector( + renderSettings.light0Direction = mathutils.Vector( ( sint * 120.0 / 127.0, + cost * 20.0 / 127.0, -cost * 120.0 / 127.0, - -cost * 20.0 / 127.0, ) ).normalized() - # TODO: Implement light1 into shader nodes - # renderSettings.light1Direction = -renderSettings.lightDirection - renderSettings.fogColor = interpColors(la.fogColor, lb.fogColor, fade) - renderSettings.fogPreviewPosition = ( + renderSettings.light1Direction = -renderSettings.light0Direction + renderSettings.fogPreviewColor = interpColors(la.fogColor, lb.fogColor, fade) + renderSettings.fogPreviewPosition = ( # fogFar is always 1000 in OoT la.fogNear + int(float(lb.fogNear - la.fogNear) * fade), - la.fogFar + int(float(lb.fogFar - la.fogFar) * fade), + 1000, + ) + renderSettings.clippingPlanes = ( # zNear seems to always be 10 in OoT + 10.0, + la.z_far + float(lb.z_far - la.z_far) * fade, ) def update_lighting_space(renderSettings: "Fast64RenderSettings_Properties"): - if renderSettings.useWorldSpaceLighting: - bpy.data.node_groups["ShdCol_L"].nodes["GeometryNormal"].node_tree = bpy.data.node_groups[ - "GeometryNormal_WorldSpace" - ] - else: - bpy.data.node_groups["ShdCol_L"].nodes["GeometryNormal"].node_tree = bpy.data.node_groups[ - "GeometryNormal_ViewSpace" - ] + bpy.data.node_groups["GetSpecularNormal"].nodes["GeometryNormal"].node_tree = bpy.data.node_groups[ + "GeometryNormal_WorldSpace" if renderSettings.useWorldSpaceLighting else "GeometryNormal_ViewSpace" + ] def update_scene_props_from_render_settings( @@ -107,26 +103,24 @@ def update_scene_props_from_render_settings( sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties", ): - enableFog = int(renderSettings.enableFogPreview) - sceneOutputs.inputs["FogEnable"].default_value = enableFog - - sceneOutputs.inputs["FogColor"].default_value = tuple(c for c in renderSettings.fogPreviewColor) + sceneOutputs.inputs["FogEnable"].default_value = int(renderSettings.enableFogPreview) + sceneOutputs.inputs["FogColor"].default_value = s_rgb_alpha_1_tuple(renderSettings.fogPreviewColor) + sceneOutputs.inputs["F3D_NearClip"].default_value = float(renderSettings.clippingPlanes[0]) + sceneOutputs.inputs["F3D_FarClip"].default_value = float(renderSettings.clippingPlanes[1]) + sceneOutputs.inputs["Blender_Game_Scale"].default_value = float(get_blender_to_game_scale(context)) sceneOutputs.inputs["FogNear"].default_value = renderSettings.fogPreviewPosition[0] sceneOutputs.inputs["FogFar"].default_value = renderSettings.fogPreviewPosition[1] - sceneOutputs.inputs["F3D_NearClip"].default_value = float(renderSettings.clippingPlanes[0]) - sceneOutputs.inputs["F3D_FarClip"].default_value = float(renderSettings.clippingPlanes[1]) - - sceneOutputs.inputs["ShadeColor"].default_value = tuple(c for c in renderSettings.lightColor) - sceneOutputs.inputs["AmbientColor"].default_value = tuple(c for c in renderSettings.ambientColor) - sceneOutputs.inputs["LightDirection"].default_value = tuple( - d for d in (mathutils.Vector(renderSettings.lightDirection) @ transform_mtx_blender_to_n64()) - ) + sceneOutputs.inputs["AmbientColor"].default_value = s_rgb_alpha_1_tuple(renderSettings.ambientColor) + sceneOutputs.inputs["Light0Color"].default_value = s_rgb_alpha_1_tuple(renderSettings.light0Color) + sceneOutputs.inputs["Light0Dir"].default_value = renderSettings.light0Direction + sceneOutputs.inputs["Light0Size"].default_value = renderSettings.light0SpecSize + sceneOutputs.inputs["Light1Color"].default_value = s_rgb_alpha_1_tuple(renderSettings.light1Color) + sceneOutputs.inputs["Light1Dir"].default_value = renderSettings.light1Direction + sceneOutputs.inputs["Light1Size"].default_value = renderSettings.light1SpecSize update_lighting_space(renderSettings) - sceneOutputs.inputs["Blender_Game_Scale"].default_value = float(get_blender_to_game_scale(context)) - def on_update_render_preview_nodes(self, context: bpy.types.Context): sceneProps = bpy.data.node_groups.get("SceneProperties") @@ -165,10 +159,9 @@ def poll_oot_scene(self, object): def resync_scene_props(): - if "ShdCol_L" in bpy.data.node_groups and "GeometryNormal_WorldSpace" in bpy.data.node_groups: - renderSettings: "Fast64RenderSettings_Properties" = bpy.context.scene.fast64.renderSettings + if "GetSpecularNormal" in bpy.data.node_groups: # Lighting space needs to be updated due to the nodes being shared and reloaded - update_lighting_space(renderSettings) + update_lighting_space(bpy.context.scene.fast64.renderSettings) class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): @@ -191,8 +184,8 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): default=(0.5, 0.5, 0.5, 1), update=on_update_render_preview_nodes, ) - lightColor: bpy.props.FloatVectorProperty( - name="Light Color", + light0Color: bpy.props.FloatVectorProperty( + name="Light 0 Color", subtype="COLOR", size=4, min=0, @@ -200,13 +193,45 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): default=(1, 1, 1, 1), update=on_update_render_preview_nodes, ) - lightDirection: bpy.props.FloatVectorProperty( - name="Light Direction", + light0Direction: bpy.props.FloatVectorProperty( + name="Light 0 Direction", subtype="DIRECTION", size=3, min=-1, max=1, - default=mathutils.Vector((0.5, 0.5, 1)).normalized(), # pre normalized + default=mathutils.Vector((1.0, -1.0, 1.0)).normalized(), # pre normalized + update=on_update_render_preview_nodes, + ) + light0SpecSize: bpy.props.IntProperty( + name="Light 0 Specular Size", + min=1, + max=255, + default=3, + update=on_update_render_preview_nodes, + ) + light1Color: bpy.props.FloatVectorProperty( + name="Light 1 Color", + subtype="COLOR", + size=4, + min=0, + max=1, + default=(0, 0, 0, 1), + update=on_update_render_preview_nodes, + ) + light1Direction: bpy.props.FloatVectorProperty( + name="Light 1 Direction", + subtype="DIRECTION", + size=3, + min=-1, + max=1, + default=mathutils.Vector((-1.0, 1.0, -1.0)).normalized(), # pre normalized + update=on_update_render_preview_nodes, + ) + light1SpecSize: bpy.props.IntProperty( + name="Light 1 Specular Size", + min=1, + max=255, + default=3, update=on_update_render_preview_nodes, ) useWorldSpaceLighting: bpy.props.BoolProperty( @@ -214,7 +239,7 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): ) # Fog Preview is int because values reflect F3D values fogPreviewPosition: bpy.props.IntVectorProperty( - name="Fog Position", size=2, min=0, max=0x7FFFFFFF, default=(985, 1000), update=on_update_render_preview_nodes + name="Fog Position", size=2, min=0, max=1000, default=(985, 1000), update=on_update_render_preview_nodes ) # Clipping planes are float because values reflect F3D values clippingPlanes: bpy.props.FloatVectorProperty( diff --git a/fast64_internal/utility.py b/fast64_internal/utility.py index 087b566..5f4751f 100644 --- a/fast64_internal/utility.py +++ b/fast64_internal/utility.py @@ -1321,6 +1321,12 @@ def gammaCorrect(linearColor): return list(mathutils.Color(linearColor[:3]).from_scene_linear_to_srgb()) +def s_rgb_alpha_1_tuple(linearColor): + s_rgb = gammaCorrect(linearColor) + s_rgb.append(1.0) + return tuple(s for s in s_rgb) + + def gammaCorrectValue(linearValue): # doesn't need to use `colorToLuminance` since all values are the same return mathutils.Color((linearValue, linearValue, linearValue)).from_scene_linear_to_srgb().v @@ -1687,14 +1693,14 @@ def ootGetBaseOrCustomLight(prop, idx, toExport: bool, errIfMissing: bool): # code without circular dependencies. assert idx in {0, 1} col = getattr(prop, "diffuse" + str(idx)) - dir = (mathutils.Vector((1.0, 1.0, 1.0)) * (1.0 if idx == 0 else -1.0)).normalized() + dir = (mathutils.Vector((1.0, -1.0, 1.0)) * (1.0 if idx == 0 else -1.0)).normalized() if getattr(prop, "useCustomDiffuse" + str(idx)): light = getattr(prop, "diffuse" + str(idx) + "Custom") if light is None: if errIfMissing: raise PluginError("Error: Diffuse " + str(idx) + " light object not set in a scene lighting property.") else: - col = light.color + col = tuple(c for c in light.color) + (1.0,) lightObj = lightDataToObj(light) dir = getObjDirectionVec(lightObj, toExport) col = mathutils.Vector(tuple(c for c in col))