Improved nodes preview: F3DEX3 and OoT lighting (#382)

* Draft of shader changes

* Almost draft of new version

* Seems to be working

* Fixed gamma on lighting

* Fixed fog issues

* blacked

* Manually added in changes for color wrapping

* Fixes OoT light directions and an OoT light previewing error

* Fixed more issues and applied review suggestions

* 4.2 support

Add CLIP output group node, no longer uses CLIP blend mode because it was deprecated

* Changed variable name

* Addressing comments

* Fixed upgrade props

---------

Co-authored-by: Lila <[email protected]>
This commit is contained in:
Sauraen
2024-07-26 09:01:05 +01:00
committed by GitHub
co-authored by Lila
parent 79fa1a89d5
commit 435ca9681e
9 changed files with 244 additions and 164 deletions
+17 -7
View File
@@ -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")
+129 -87
View File
@@ -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")
Binary file not shown.
+6 -9
View File
@@ -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.")
@@ -113,7 +113,7 @@ class SceneLighting:
light2[1],
exportColor(lightProp.fogColor),
lightProp.fogNear,
lightProp.fogFar,
lightProp.z_far,
lightProp.transitionSpeed,
)
)
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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")
+78 -53
View File
@@ -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(
+8 -2
View File
@@ -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))