From 12681bf9b78bcdafb8606d3d748db801d4cc3f48 Mon Sep 17 00:00:00 2001 From: Dragorn421 Date: Thu, 12 Sep 2024 20:18:43 +0200 Subject: [PATCH] Speed up render settings update and add toggle for doing it manually (#429) * Speed up render settings update and add toggle for doing it manually * make callbacks with function * add description to autoupdate prop and op * shorten funcs names --- __init__.py | 2 + fast64_internal/f3d/f3d_material.py | 20 ++- fast64_internal/render_settings.py | 211 +++++++++++++++++++++++----- 3 files changed, 199 insertions(+), 34 deletions(-) diff --git a/__init__.py b/__init__.py index 7473759da..d3e5c548c 100644 --- a/__init__.py +++ b/__init__.py @@ -49,6 +49,7 @@ from .fast64_internal.render_settings import ( Fast64RenderSettings_Properties, + ManualUpdatePreviewOperator, resync_scene_props, on_update_render_settings, ) @@ -311,6 +312,7 @@ def draw(self, context): classes = ( Fast64Settings_Properties, Fast64RenderSettings_Properties, + ManualUpdatePreviewOperator, Fast64_Properties, Fast64_BoneProperties, Fast64_ObjectProperties, diff --git a/fast64_internal/f3d/f3d_material.py b/fast64_internal/f3d/f3d_material.py index f4be233ed..1f0970553 100644 --- a/fast64_internal/f3d/f3d_material.py +++ b/fast64_internal/f3d/f3d_material.py @@ -34,7 +34,11 @@ from .f3d_gbi import get_F3D_GBI, enumTexScroll, isUcodeF3DEX1, default_draw_layers from .f3d_material_presets import * from ..utility import * -from ..render_settings import Fast64RenderSettings_Properties, update_scene_props_from_render_settings +from ..render_settings import ( + Fast64RenderSettings_Properties, + update_scene_props_from_render_settings, + ManualUpdatePreviewOperator, +) 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 @@ -2425,7 +2429,7 @@ def createOrUpdateSceneProperties(): sceneOutputs: NodeGroupOutput = new_group.nodes["Group Output"] renderSettings: "Fast64RenderSettings_Properties" = bpy.context.scene.fast64.renderSettings - update_scene_props_from_render_settings(bpy.context, sceneOutputs, renderSettings) + update_scene_props_from_render_settings(sceneOutputs, renderSettings) def createScenePropertiesForMaterial(material: Material): @@ -4709,6 +4713,18 @@ def draw(self, context): labelbox.label(text="Global Settings") labelbox.ui_units_x = 6 + # Only show the update preview UI if the render engine is EEVEE, + # as there's no point in updating the nodes otherwise. + if context.scene.render.engine in { + "BLENDER_EEVEE", # <4.2 + "BLENDER_EEVEE_NEXT", # 4.2+ + }: + updatePreviewRow = globalSettingsBox.row() + updatePreviewRow.prop(renderSettings, "enableAutoUpdatePreview") + if not renderSettings.enableAutoUpdatePreview: + updatePreviewRow.operator(ManualUpdatePreviewOperator.bl_idname) + globalSettingsBox.separator() + globalSettingsBox.prop(renderSettings, "enableFogPreview") prop_split(globalSettingsBox, renderSettings, "fogPreviewColor", "Fog Color") prop_split(globalSettingsBox, renderSettings, "fogPreviewPosition", "Fog Position") diff --git a/fast64_internal/render_settings.py b/fast64_internal/render_settings.py index 6476f8f1f..29e5a7385 100644 --- a/fast64_internal/render_settings.py +++ b/fast64_internal/render_settings.py @@ -92,45 +92,155 @@ def interpColors(cola, colb, fade): ) -def update_lighting_space(renderSettings: "Fast64RenderSettings_Properties"): - 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_rs_enableFogPreview( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): + sceneOutputs.inputs["FogEnable"].default_value = int(renderSettings.enableFogPreview) -def update_scene_props_from_render_settings( - context: bpy.types.Context, - sceneOutputs: bpy.types.NodeGroupOutput, - renderSettings: "Fast64RenderSettings_Properties", +def update_scene_props_from_rs_fogPreviewColor( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" ): - sceneOutputs.inputs["FogEnable"].default_value = int(renderSettings.enableFogPreview) sceneOutputs.inputs["FogColor"].default_value = s_rgb_alpha_1_tuple(renderSettings.fogPreviewColor) + + +def update_scene_props_from_rs_clippingPlanes( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): 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)) + + +def update_scene_props_from_rs_fogPreviewPosition( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["FogNear"].default_value = renderSettings.fogPreviewPosition[0] sceneOutputs.inputs["FogFar"].default_value = renderSettings.fogPreviewPosition[1] + +def update_scene_props_from_rs_ambientColor( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["AmbientColor"].default_value = s_rgb_alpha_1_tuple(renderSettings.ambientColor) + + +def update_scene_props_from_rs_light0Color( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["Light0Color"].default_value = s_rgb_alpha_1_tuple(renderSettings.light0Color) + + +def update_scene_props_from_rs_light0Direction( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["Light0Dir"].default_value = renderSettings.light0Direction + + +def update_scene_props_from_rs_light0SpecSize( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["Light0Size"].default_value = renderSettings.light0SpecSize + + +def update_scene_props_from_rs_light1Color( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["Light1Color"].default_value = s_rgb_alpha_1_tuple(renderSettings.light1Color) + + +def update_scene_props_from_rs_light1Direction( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["Light1Dir"].default_value = renderSettings.light1Direction + + +def update_scene_props_from_rs_light1SpecSize( + sceneOutputs: bpy.types.NodeGroupOutput, renderSettings: "Fast64RenderSettings_Properties" +): sceneOutputs.inputs["Light1Size"].default_value = renderSettings.light1SpecSize - update_lighting_space(renderSettings) + +def update_scene_props_from_rs_useWorldSpaceLighting(renderSettings: "Fast64RenderSettings_Properties"): + bpy.data.node_groups["GetSpecularNormal"].nodes["GeometryNormal"].node_tree = bpy.data.node_groups[ + "GeometryNormal_WorldSpace" if renderSettings.useWorldSpaceLighting else "GeometryNormal_ViewSpace" + ] -def on_update_render_preview_nodes(self, context: bpy.types.Context): +def update_scene_props_from_render_settings( + sceneOutputs: bpy.types.NodeGroupOutput, + renderSettings: "Fast64RenderSettings_Properties", +): + update_scene_props_from_rs_enableFogPreview(sceneOutputs, renderSettings) + update_scene_props_from_rs_fogPreviewColor(sceneOutputs, renderSettings) + update_scene_props_from_rs_clippingPlanes(sceneOutputs, renderSettings) + update_scene_props_from_rs_fogPreviewPosition(sceneOutputs, renderSettings) + update_scene_props_from_rs_ambientColor(sceneOutputs, renderSettings) + update_scene_props_from_rs_light0Color(sceneOutputs, renderSettings) + update_scene_props_from_rs_light0Direction(sceneOutputs, renderSettings) + update_scene_props_from_rs_light0SpecSize(sceneOutputs, renderSettings) + update_scene_props_from_rs_light1Color(sceneOutputs, renderSettings) + update_scene_props_from_rs_light1Direction(sceneOutputs, renderSettings) + update_scene_props_from_rs_light1SpecSize(sceneOutputs, renderSettings) + update_scene_props_from_rs_useWorldSpaceLighting(renderSettings) + + # TODO use a callback on the scale props to set this value + sceneOutputs.inputs["Blender_Game_Scale"].default_value = float(get_blender_to_game_scale(bpy.context)) + + +def getSceneOutputs(): sceneProps = bpy.data.node_groups.get("SceneProperties") if sceneProps == None: print("Could not locate SceneProperties!") - return + return None sceneOutputs: bpy.types.NodeGroupOutput = sceneProps.nodes["Group Output"] - renderSettings: "Fast64RenderSettings_Properties" = context.scene.fast64.renderSettings - update_scene_props_from_render_settings(context, sceneOutputs, renderSettings) + return sceneOutputs + + +class ManualUpdatePreviewOperator(bpy.types.Operator): + bl_idname = "view3d.fast64_manual_update_preview" + bl_label = "Update Preview" + bl_description = "Apply the F3D Render Settings to the view" + + def execute(self, context): + sceneOutputs = getSceneOutputs() + renderSettings = bpy.context.scene.fast64.renderSettings + + if sceneOutputs is None: + return {"CANCELLED"} + + update_scene_props_from_render_settings(sceneOutputs, renderSettings) + return {"FINISHED"} + + +def make_callback(update_scene_props_from_rs_func): + def on_update_rs_func(self: "Fast64RenderSettings_Properties", context): + if not self.enableAutoUpdatePreview: + return + sceneOutputs = getSceneOutputs() + if sceneOutputs is not None: + update_scene_props_from_rs_func(sceneOutputs, self) + + return on_update_rs_func + + +# These are all the callbacks that modify values in the scene properties node group +# Since modifying node values turns out to be very slow, +# we need one callback per prop in order to update the specific associated value. +on_update_rs_enableFogPreview = make_callback(update_scene_props_from_rs_enableFogPreview) +on_update_rs_fogPreviewColor = make_callback(update_scene_props_from_rs_fogPreviewColor) +on_update_rs_clippingPlanes = make_callback(update_scene_props_from_rs_clippingPlanes) +on_update_rs_fogPreviewPosition = make_callback(update_scene_props_from_rs_fogPreviewPosition) +on_update_rs_ambientColor = make_callback(update_scene_props_from_rs_ambientColor) +on_update_rs_light0Color = make_callback(update_scene_props_from_rs_light0Color) +on_update_rs_light0Direction = make_callback(update_scene_props_from_rs_light0Direction) +on_update_rs_light0SpecSize = make_callback(update_scene_props_from_rs_light0SpecSize) +on_update_rs_light1Color = make_callback(update_scene_props_from_rs_light1Color) +on_update_rs_light1Direction = make_callback(update_scene_props_from_rs_light1Direction) +on_update_rs_light1SpecSize = make_callback(update_scene_props_from_rs_light1SpecSize) +on_update_rs_useWorldSpaceLighting = make_callback(update_scene_props_from_rs_useWorldSpaceLighting) + +del make_callback def on_update_render_settings(self, context: bpy.types.Context): @@ -147,7 +257,9 @@ def on_update_render_settings(self, context: bpy.types.Context): case _: pass - on_update_render_preview_nodes(self, context) + sceneOutputs = getSceneOutputs() + if sceneOutputs is not None: + update_scene_props_from_render_settings(sceneOutputs, self) def poll_sm64_area(self, object): @@ -161,11 +273,27 @@ def poll_oot_scene(self, object): def resync_scene_props(): if "GetSpecularNormal" in bpy.data.node_groups: # Lighting space needs to be updated due to the nodes being shared and reloaded - update_lighting_space(bpy.context.scene.fast64.renderSettings) + update_scene_props_from_rs_useWorldSpaceLighting(bpy.context.scene.fast64.renderSettings) + + +def on_update_render_settings_enableAutoUpdatePreview(self, context): + # Update on enabling but not disabling + if self.enableAutoUpdatePreview: + on_update_render_settings(self, context) class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): - enableFogPreview: bpy.props.BoolProperty(name="Enable Fog Preview", default=True, update=on_update_render_settings) + enableAutoUpdatePreview: bpy.props.BoolProperty( + name="Auto Update Preview", + description="If enabled, the view will update automatically when changing render settings", + default=True, + update=on_update_render_settings_enableAutoUpdatePreview, + ) + enableFogPreview: bpy.props.BoolProperty( + name="Enable Fog Preview", + default=True, + update=on_update_render_settings, + ) fogPreviewColor: bpy.props.FloatVectorProperty( name="Fog Color", subtype="COLOR", @@ -173,7 +301,7 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): min=0, max=1, default=(1, 1, 1, 1), - update=on_update_render_preview_nodes, + update=on_update_rs_fogPreviewColor, ) ambientColor: bpy.props.FloatVectorProperty( name="Ambient Light", @@ -182,7 +310,7 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): min=0, max=1, default=(0.5, 0.5, 0.5, 1), - update=on_update_render_preview_nodes, + update=on_update_rs_ambientColor, ) light0Color: bpy.props.FloatVectorProperty( name="Light 0 Color", @@ -191,7 +319,7 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): min=0, max=1, default=(1, 1, 1, 1), - update=on_update_render_preview_nodes, + update=on_update_rs_light0Color, ) light0Direction: bpy.props.FloatVectorProperty( name="Light 0 Direction", @@ -200,14 +328,14 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): min=-1, max=1, default=mathutils.Vector((1.0, -1.0, 1.0)).normalized(), # pre normalized - update=on_update_render_preview_nodes, + update=on_update_rs_light0Direction, ) light0SpecSize: bpy.props.IntProperty( name="Light 0 Specular Size", min=1, max=255, default=3, - update=on_update_render_preview_nodes, + update=on_update_rs_light0SpecSize, ) light1Color: bpy.props.FloatVectorProperty( name="Light 1 Color", @@ -216,7 +344,7 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): min=0, max=1, default=(0, 0, 0, 1), - update=on_update_render_preview_nodes, + update=on_update_rs_light1Color, ) light1Direction: bpy.props.FloatVectorProperty( name="Light 1 Direction", @@ -225,36 +353,55 @@ class Fast64RenderSettings_Properties(bpy.types.PropertyGroup): min=-1, max=1, default=mathutils.Vector((-1.0, 1.0, -1.0)).normalized(), # pre normalized - update=on_update_render_preview_nodes, + update=on_update_rs_light1Direction, ) light1SpecSize: bpy.props.IntProperty( name="Light 1 Specular Size", min=1, max=255, default=3, - update=on_update_render_preview_nodes, + update=on_update_rs_light1SpecSize, ) useWorldSpaceLighting: bpy.props.BoolProperty( - name="Use World Space Lighting", default=True, update=on_update_render_settings + name="Use World Space Lighting", + default=True, + update=on_update_render_settings, ) # Fog Preview is int because values reflect F3D values fogPreviewPosition: bpy.props.IntVectorProperty( - name="Fog Position", size=2, min=0, max=1000, default=(985, 1000), update=on_update_render_preview_nodes + name="Fog Position", + size=2, + min=0, + max=1000, + default=(985, 1000), + update=on_update_rs_fogPreviewPosition, ) # Clipping planes are float because values reflect F3D values clippingPlanes: bpy.props.FloatVectorProperty( - name="Clipping Planes", size=2, min=0, default=(100, 30000), update=on_update_render_preview_nodes + name="Clipping Planes", + size=2, + min=0, + default=(100, 30000), + update=on_update_rs_clippingPlanes, ) useObjectRenderPreview: bpy.props.BoolProperty( - name="Use Object Preview", default=True, update=on_update_render_settings + name="Use Object Preview", + default=True, + update=on_update_render_settings, ) # SM64 sm64Area: bpy.props.PointerProperty( - name="Area Object", type=bpy.types.Object, update=on_update_sm64_render_settings, poll=poll_sm64_area + name="Area Object", + type=bpy.types.Object, + update=on_update_sm64_render_settings, + poll=poll_sm64_area, ) # OOT ootSceneObject: bpy.props.PointerProperty( - name="Scene Object", type=bpy.types.Object, update=on_update_oot_render_settings, poll=poll_oot_scene + name="Scene Object", + type=bpy.types.Object, + update=on_update_oot_render_settings, + poll=poll_oot_scene, ) ootSceneHeader: bpy.props.IntProperty( name="Header/Setup",