From 6e079d9f5cf65d45666f315152e1cf355b31d5d2 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:16:15 -0500 Subject: [PATCH 01/21] typo: fixed spelling of SelectMaterialMenu.bl_idname --- materials.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/materials.py b/materials.py index 466bee5..101cdd6 100644 --- a/materials.py +++ b/materials.py @@ -272,7 +272,7 @@ def draw(self, context): self.layout.prop(self, "mode") class SelectMaterialMenu(bpy.types.Menu): - bl_idname = "NODE_MT_npt_mat_selection" + bl_idname = "NODE_MT_ntp_mat_selection" bl_label = "Select Material" @classmethod @@ -312,4 +312,4 @@ def draw(self, context): row.alignment = 'EXPAND' row.operator_context = 'INVOKE_DEFAULT' - row.menu("NODE_MT_npt_mat_selection", text="Materials") \ No newline at end of file + row.menu("NODE_MT_ntp_mat_selection", text="Materials") \ No newline at end of file From 80d7a138c74d7517d8d694abddef567c8b423cc9 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:47:59 -0500 Subject: [PATCH 02/21] refactor: class renaming --- __init__.py | 24 +++++++++++++++--------- geo_nodes.py | 16 ++++++++-------- materials.py | 14 +++++++------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/__init__.py b/__init__.py index 201080e..fc987da 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ "name": "Node to Python", "description": "Convert Blender node groups to a Python add-on!", "author": "Brendan Parmer", - "version": (2, 2, 0), + "version": (3, 0, 0), "blender": (3, 0, 0), "location": "Node", "category": "Node", @@ -10,12 +10,14 @@ if "bpy" in locals(): import importlib - importlib.reload(materials) + importlib.reload(compositor) importlib.reload(geo_nodes) + importlib.reload(materials) importlib.reload(options) else: - from . import materials + from . import compositor from . import geo_nodes + from . import materials from . import options import bpy @@ -37,12 +39,16 @@ def draw(self, context): classes = [NodeToPythonMenu, options.NTPOptions, - geo_nodes.GeoNodesToPython, - geo_nodes.SelectGeoNodesMenu, - geo_nodes.GeoNodesToPythonPanel, - materials.MaterialToPython, - materials.SelectMaterialMenu, - materials.MaterialToPythonPanel, + compositor.NTPCompositorOperator, + compositor.NTPCompositorScenesMenu, + compositor.NTPCompositorGroupsMenu, + compositor.NTPCompositingPanel, + geo_nodes.NTPGeoNodesOperator, + geo_nodes.NTPGeoNodesMenu, + geo_nodes.NTPGeoNodesPanel, + materials.NTPMaterialOperator, + materials.NTPMaterialMenu, + materials.NTPMaterialPanel, options.NTPOptionsPanel ] diff --git a/geo_nodes.py b/geo_nodes.py index c70f219..f5f6de1 100644 --- a/geo_nodes.py +++ b/geo_nodes.py @@ -164,8 +164,8 @@ image_nodes = {'GeometryNodeInputImage'} -class GeoNodesToPython(bpy.types.Operator): - bl_idname = "node.geo_nodes_to_python" +class NTPGeoNodesOperator(bpy.types.Operator): + bl_idname = "node.ntp_geo_nodes" bl_label = "Geo Nodes to Python" bl_options = {'REGISTER', 'UNDO'} @@ -386,8 +386,8 @@ def invoke(self, context, event): def draw(self, context): self.layout.prop(self, "mode") -class SelectGeoNodesMenu(bpy.types.Menu): - bl_idname = "NODE_MT_ntp_geo_nodes_selection" +class NTPGeoNodesMenu(bpy.types.Menu): + bl_idname = "NODE_MT_ntp_geo_nodes" bl_label = "Select Geo Nodes" @classmethod @@ -402,12 +402,12 @@ def draw(self, context): if node.type == 'GEOMETRY'] for geo_ng in geo_node_groups: - op = layout.operator(GeoNodesToPython.bl_idname, text=geo_ng.name) + op = layout.operator(NTPGeoNodesOperator.bl_idname, text=geo_ng.name) op.geo_nodes_group_name = geo_ng.name -class GeoNodesToPythonPanel(bpy.types.Panel): +class NTPGeoNodesPanel(bpy.types.Panel): bl_label = "Geometry Nodes to Python" - bl_idname = "NODE_PT_geo_nodes_to_python" + bl_idname = "NODE_PT_geo_nodes" bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' bl_context = '' @@ -434,4 +434,4 @@ def draw(self, context): row.alignment = 'EXPAND' row.operator_context = 'INVOKE_DEFAULT' - row.menu("NODE_MT_ntp_geo_nodes_selection", text="Geometry Nodes") \ No newline at end of file + row.menu("NODE_MT_ntp_geo_nodes", text="Geometry Nodes") \ No newline at end of file diff --git a/materials.py b/materials.py index 101cdd6..66c0d00 100644 --- a/materials.py +++ b/materials.py @@ -77,8 +77,8 @@ image_nodes = {'ShaderNodeTexEnvironment', 'ShaderNodeTexImage'} -class MaterialToPython(bpy.types.Operator): - bl_idname = "node.material_to_python" +class NTPMaterialOperator(bpy.types.Operator): + bl_idname = "node.ntp_material" bl_label = "Material to Python" bl_options = {'REGISTER', 'UNDO'} @@ -271,8 +271,8 @@ def invoke(self, context, event): def draw(self, context): self.layout.prop(self, "mode") -class SelectMaterialMenu(bpy.types.Menu): - bl_idname = "NODE_MT_ntp_mat_selection" +class NTPMaterialMenu(bpy.types.Menu): + bl_idname = "NODE_MT_ntp_material" bl_label = "Select Material" @classmethod @@ -283,10 +283,10 @@ def draw(self, context): layout = self.layout.column_flow(columns=1) layout.operator_context = 'INVOKE_DEFAULT' for mat in bpy.data.materials: - op = layout.operator(MaterialToPython.bl_idname, text=mat.name) + op = layout.operator(NTPMaterialOperator.bl_idname, text=mat.name) op.material_name = mat.name -class MaterialToPythonPanel(bpy.types.Panel): +class NTPMaterialPanel(bpy.types.Panel): bl_label = "Material to Python" bl_idname = "NODE_PT_mat_to_python" bl_space_type = 'NODE_EDITOR' @@ -312,4 +312,4 @@ def draw(self, context): row.alignment = 'EXPAND' row.operator_context = 'INVOKE_DEFAULT' - row.menu("NODE_MT_ntp_mat_selection", text="Materials") \ No newline at end of file + row.menu("NODE_MT_ntp_material", text="Materials") \ No newline at end of file From 04099612f1718bd85d15fabd32ce1155f27ab9d2 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Fri, 4 Aug 2023 23:55:18 -0500 Subject: [PATCH 03/21] feat: added UI for compositor nodes --- compositor.py | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 compositor.py diff --git a/compositor.py b/compositor.py new file mode 100644 index 0000000..f8dbbf4 --- /dev/null +++ b/compositor.py @@ -0,0 +1,289 @@ +import bpy +import os + +from .utils import * +from io import StringIO + +node_settings = { +} + +curve_nodes = {'ShaderNodeFloatCurve', + 'ShaderNodeVectorCurve', + 'ShaderNodeRGBCurve'} + +image_nodes = {'ShaderNodeTexEnvironment', + 'ShaderNodeTexImage'} + +class NTPCompositorOperator(bpy.types.Operator): + bl_idname = "node.compositor_to_python" + bl_label = "Compositor to Python" + bl_options = {'REGISTER', 'UNDO'} + + mode : bpy.props.EnumProperty( + name = "Mode", + items = [ + ('SCRIPT', "Script", "Copy just the node group to the Blender clipboard"), + ('ADDON', "Addon", "Create a full addon") + ] + ) + + compositor_name: bpy.props.StringProperty(name="Node Group") + is_scene : bpy.props.BoolProperty(name="Is Scene", description="Blender stores compositing node trees differently for scenes and in groups") + + def execute(self, context): + """ + #find node group to replicate + nt = bpy.data.materials[self.material_name].node_tree + if nt is None: + self.report({'ERROR'},("NodeToPython: This doesn't seem to be a " + "valid material. Is Use Nodes selected?")) + return {'CANCELLED'} + + #set up names to use in generated addon + mat_var = clean_string(self.material_name) + + if self.mode == 'ADDON': + dir = bpy.path.abspath(context.scene.ntp_options.dir_path) + if not dir or dir == "": + self.report({'ERROR'}, + ("NodeToPython: Save your blender file before using " + "NodeToPython!")) + return {'CANCELLED'} + + zip_dir = os.path.join(dir, mat_var) + addon_dir = os.path.join(zip_dir, mat_var) + if not os.path.exists(addon_dir): + os.makedirs(addon_dir) + file = open(f"{addon_dir}/__init__.py", "w") + + create_header(file, self.material_name) + class_name = clean_string(self.material_name, lower=False) + init_operator(file, class_name, mat_var, self.material_name) + + file.write("\tdef execute(self, context):\n") + else: + file = StringIO("") + + def create_material(indent: str): + file.write((f"{indent}mat = bpy.data.materials.new(" + f"name = {str_to_py_str(self.material_name)})\n")) + file.write(f"{indent}mat.use_nodes = True\n") + + if self.mode == 'ADDON': + create_material("\t\t") + elif self.mode == 'SCRIPT': + create_material("") + + #set to keep track of already created node trees + node_trees = set() + + #dictionary to keep track of node->variable name pairs + node_vars = {} + + #keeps track of all used variables + used_vars = {} + + def is_outermost_node_group(level: int) -> bool: + if self.mode == 'ADDON' and level == 2: + return True + elif self.mode == 'SCRIPT' and level == 0: + return True + return False + + def process_mat_node_group(node_tree, level, node_vars, used_vars): + if is_outermost_node_group(level): + nt_var = create_var(self.material_name, used_vars) + nt_name = self.material_name + else: + nt_var = create_var(node_tree.name, used_vars) + nt_name = node_tree.name + + outer, inner = make_indents(level) + + #initialize node group + file.write(f"{outer}#initialize {nt_var} node group\n") + file.write(f"{outer}def {nt_var}_node_group():\n") + + if is_outermost_node_group(level): #outermost node group + file.write(f"{inner}{nt_var} = mat.node_tree\n") + file.write(f"{inner}#start with a clean node tree\n") + file.write(f"{inner}for node in {nt_var}.nodes:\n") + file.write(f"{inner}\t{nt_var}.nodes.remove(node)\n") + else: + file.write((f"{inner}{nt_var}" + f"= bpy.data.node_groups.new(" + f"type = \'ShaderNodeTree\', " + f"name = {str_to_py_str(nt_name)})\n")) + file.write("\n") + + inputs_set = False + outputs_set = False + + #initialize nodes + file.write(f"{inner}#initialize {nt_var} nodes\n") + + #dictionary to keep track of node->variable name pairs + node_vars = {} + + for node in node_tree.nodes: + if node.bl_idname == 'ShaderNodeGroup': + node_nt = node.node_tree + if node_nt is not None and node_nt not in node_trees: + process_mat_node_group(node_nt, level + 1, node_vars, + used_vars) + node_trees.add(node_nt) + + node_var = create_node(node, file, inner, nt_var, node_vars, + used_vars) + + set_settings_defaults(node, node_settings, file, inner, node_var) + hide_sockets(node, file, inner, node_var) + + if node.bl_idname == 'ShaderNodeGroup': + if node.node_tree is not None: + file.write((f"{inner}{node_var}.node_tree = " + f"bpy.data.node_groups" + f"[\"{node.node_tree.name}\"]\n")) + elif node.bl_idname == 'NodeGroupInput' and not inputs_set: + group_io_settings(node, file, inner, "input", nt_var, node_tree) + inputs_set = True + + elif node.bl_idname == 'NodeGroupOutput' and not outputs_set: + group_io_settings(node, file, inner, "output", nt_var, node_tree) + outputs_set = True + + elif node.bl_idname in image_nodes and self.mode == 'ADDON': + img = node.image + if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: + save_image(img, addon_dir) + load_image(img, file, inner, f"{node_var}.image") + image_user_settings(node, file, inner, node_var) + + elif node.bl_idname == 'ShaderNodeValToRGB': + color_ramp_settings(node, file, inner, node_var) + + elif node.bl_idname in curve_nodes: + curve_node_settings(node, file, inner, node_var) + + if self.mode == 'ADDON': + set_input_defaults(node, file, inner, node_var, addon_dir) + else: + set_input_defaults(node, file, inner, node_var) + set_output_defaults(node, file, inner, node_var) + + set_parents(node_tree, file, inner, node_vars) + set_locations(node_tree, file, inner, node_vars) + set_dimensions(node_tree, file, inner, node_vars) + + init_links(node_tree, file, inner, nt_var, node_vars) + + file.write(f"\n{outer}{nt_var}_node_group()\n\n") + + if self.mode == 'ADDON': + level = 2 + else: + level = 0 + process_mat_node_group(nt, level, node_vars, used_vars) + + if self.mode == 'ADDON': + file.write("\t\treturn {'FINISHED'}\n\n") + + create_menu_func(file, class_name) + create_register_func(file, class_name) + create_unregister_func(file, class_name) + create_main_func(file) + else: + context.window_manager.clipboard = file.getvalue() + + file.close() + + if self.mode == 'ADDON': + zip_addon(zip_dir) + """ + if self.mode == 'SCRIPT': + location = "clipboard" + else: + location = dir + self.report({'INFO'}, f"NodeToPython: Saved compositor nodes to {location}") + return {'FINISHED'} + + def invoke(self, context, event): + return context.window_manager.invoke_props_dialog(self) + def draw(self, context): + self.layout.prop(self, "mode") + +class NTPCompositorScenesMenu(bpy.types.Menu): + bl_idname = "NODE_MT_ntp_comp_scenes" + bl_label = "Select " + + @classmethod + def poll(cls, context): + return True + + def draw(self, context): + layout = self.layout.column_flow(columns=1) + layout.operator_context = 'INVOKE_DEFAULT' + for scene in bpy.data.scenes: + if scene.node_tree: + op = layout.operator(NTPCompositorOperator.bl_idname, text=scene.name) + op.compositor_name = scene.name + op.is_scene = True + print(scene.node_tree.name) + +class NTPCompositorGroupsMenu(bpy.types.Menu): + bl_idname = "NODE_MT_ntp_comp_groups" + bl_label = "Select " + + @classmethod + def poll(cls, context): + return True + + def draw(self, context): + layout = self.layout.column_flow(columns=1) + layout.operator_context = 'INVOKE_DEFAULT' + for node_group in bpy.data.node_groups: + if isinstance(node_group, bpy.types.CompositorNodeTree): + op = layout.operator(NTPCompositorOperator.bl_idname, text=node_group.name) + op.compositor_name = node_group.name + op.is_scene = False + +class NTPCompositingPanel(bpy.types.Panel): + bl_label = "Compositor to Python" + bl_idname = "NODE_PT_ntp_compositor" + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + bl_context = '' + bl_category = "NodeToPython" + + @classmethod + def poll(cls, context): + return True + + def draw_header(self, context): + layout = self.layout + + def draw(self, context): + layout = self.layout + scenes_row = layout.row() + + # Disables menu when there are no materials + scenes = [scene for scene in bpy.data.scenes + if scene.node_tree is not None] + scenes_exist = len(scenes) > 0 + scenes_row.enabled = scenes_exist + + scenes_row.alignment = 'EXPAND' + scenes_row.operator_context = 'INVOKE_DEFAULT' + scenes_row.menu("NODE_MT_ntp_comp_scenes", + text="Scene Compositor Nodes") + + groups_row = layout.row() + groups = [ng for ng in bpy.data.node_groups + if isinstance(ng, bpy.types.CompositorNodeTree)] + groups_exist = len(groups) > 0 + groups_row.enabled = groups_exist + + groups_row.alignment = 'EXPAND' + groups_row.operator_context = 'INVOKE_DEFAULT' + groups_row.menu("NODE_MT_ntp_comp_groups", + text="Group Compositor Nodes") From cffd9aed8140613d9f2cb8d39f48e65628a97071 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sun, 6 Aug 2023 16:06:57 -0500 Subject: [PATCH 04/21] feat: add most compositor node settings --- compositor.py | 194 ++++++++++++++++++++++++++++++++++++++++++-------- materials.py | 6 +- 2 files changed, 166 insertions(+), 34 deletions(-) diff --git a/compositor.py b/compositor.py index f8dbbf4..ef978b2 100644 --- a/compositor.py +++ b/compositor.py @@ -5,14 +5,140 @@ from io import StringIO node_settings = { + #Input + 'CompositorNodeBokehImage' : ["flaps", "angle", "rounding", "catadioptric", + "shift"], + 'CompositorNodeImage' : [], #TODO: handle image selection + 'CompositorNodeMask' : ["use_feather", "size_source", "size_x", "size_y", + "use_motion_blur", + "motion_blur_samples", "motion_blur_shutter"], #TODO: handle mask selection + 'CompositorNodeMovieClip' : [], #TODO: handle movie clip selection + 'CompositorNodeRLayers' : ["name", "layer"], + 'CompositorNodeRGB' : [], #should be handled by outputs + 'CompositorNodeSceneTime' : [], #should be good + 'CompositorNodeTexture' : [], #TODO: handle texture selection + 'CompositorNodeTime' : ["frame_start", "frame_end"], + 'CompositorNodeTrackPos' : [], #TODO: handle movie selection + 'CompositorNodeValue' : [], #should be handled by outputs (why is this a separate class??) + + #Output + 'CompositorNodeComposite' : ["use_alpha"], + 'CompositorNodeOutputFile' : ["base_path"], #TODO: doesn't seem portable + 'CompositorNodeLevels' : ["channel"], + 'CompositorNodeSplitViewer' : ["axis", "factor"], + 'CompositorNodeViewer' : ["use_alpha"], + + #Color + 'CompositorNodeAlphaOver' : ["use_premultiply", "premul"], + 'CompositorNodeBrightContrast' : ["use_premultiply"], + 'CompositorNodeColorBalance' : ["correction_method", "lift", "gamma", + "gain", "offset", "power", "slope", + "offset_basis"], + 'CompositorNodeColorCorrection' : ["red", "green", "blue", + "master_saturation", "master_contrast", + "master_gamma", "master_gain", + "master_lift", + "highlights_saturation", "highlights_contrast", + "highlights_gamma", "highlights_gain", + "highlights_lift", + "midtones_saturation", "midtones_contrast", + "midtones_gamma", "midtones_gain", + "midtones_lift", + "shadows_saturation", "shadows_contrast", + "shadows_gamma", "shadows_gain", + "shadows_lift", + "midtones_start", "midtones_end"], + 'CompositorNodeExposure' : [], + 'CompositorNodeGamma' : [], + 'CompositorNodeHueCorrect' : [], + 'CompositorNodeHueSat' : [], + 'CompositorNodeInvert' : ["invert_rgb", "invert_alpha"], + 'CompositorNodeMixRGB' : ["blend_type", "use_alpha", "use_clamp"], #TODO: has an update() method, may need to figure out why... + 'CompositorNodePosterize' : [], + 'CompositorNodeCurveRGB' : [], + 'CompositorNodeTonemap' : ["tonemap_type", "intensity", "contrast", "adaptation", "correction", "key", "offset", "gamma"], + 'CompositorNodeZcombine' : ["use_alpha", "use_antialias_z"], + + #Converter + 'CompositorNodePremulKey' : ["mapping"], + 'CompositorNodeValToRGB' : [], #TODO: check to see if this'll work out of the box + 'CompositorNodeConvertColorSpace' : ["from_color_space", "to_color_space"], + 'CompositorNodeCombineColor' : ["mode", "ycc_mode"], #why isn't this standardized across blender? + 'CompositorNodeCombineXYZ' : [], + 'CompositorNodeIDMask' : ["index", "use_antialiasing"], + 'CompositorNodeMath' : ["operation", "use_clamp"], + 'CompositorNodeRGBToBW' : [], + 'CompositorNodeSeparateColor' : ["mode", "ycc_mode"], + 'CompositorNodeSeparateXYZ' : [], + 'CompositorNodeSetAlpha' : ["mode"], + 'CompositorNodeSwitchView' : [], + + #Filter + 'CompositorNodeAntiAliasing' : ["threshold", "contrast_limit", "corner_rounding"], + 'CompositorNodeBilateralblur' : ["iterations", "sigma_color", "sigma_space"], + 'CompositorNodeBlur' : ["filter_type", "use_variable_size", "use_gamma_correction", "use_relative", "aspect_correction", "factor", "factor_x", "factor_y", "use_extended_bounds"], + 'CompositorNodeBokehBlur' : ["use_variable_size", "blur_max", "use_extended_bounds"], + 'CompositorNodeDefocus' : ["bokeh", "angle", "use_gamma_correction", "f_stop", "blur_max", "threshold", "use_preview", "use_zbuffer", "z_scale"], + 'CompositorNodeDespeckle' : ["threshold", "threshold_neighbor"], + 'CompositorNodeDilateErode' : ["mode", "distance", "edge", "falloff"], + 'CompositorNodeDBlur' : ["iterations", "center_x", "center_y", "distance", "angle", "spin", "zoom"], + 'CompositorNodeFilter' : ["filter_type"], + 'CompositorNodeGlare' : ["glare_type", "quality", "iterations", "color_modulation", "mix", "threshold", "streaks", "angle_offset", "fade", "size", "use_rotate_45"], + 'CompositorNodeInpaint' : ["distance"], + 'CompositorNodePixelate' : [], + 'CompositorNodeSunBeams' : ["source", "ray_length"], #TODO: check that source doesn't freak out + 'CompositorNodeVecBlur' : ["samples", "factor", "speed_min", "speed_max", "use_curved"], + + #Vector + 'CompositorNodeMapRange' : ["use_clamp"], + 'CompositorNodeMapValue' : ["offset", "size", "use_min", "min", "use_max", "max"], #why are all these vectors?? TODO: check to make sure it doesn't flip + 'CompositorNodeNormal' : [], #should be handled with io system + 'CompositorNodeNormalize' : [], + 'CompositorNodeCurveVec' : [], + + #Matte + 'CompositorNodeBoxMask' : ["x", "y", "width", "height", "rotation", "mask_type"], + 'CompositorNodeChannelMatte' : ["color_space", "matte_channel", "limit_method", "limit_channel", "limit_max", "limit_min"], + 'CompositorNodeChromaMatte' : ["tolerance", "threshold", "gain"], + 'CompositorNodeColorMatte' : ["color_hue", "color_saturation", "color_value"], + 'CompositorNodeColorSpill' : ["channel", "limit_method", "ratio", "use_unspill", "unspill_red", "unspill_green", "unspill_blue"], + 'CompositorNodeCryptomatteV2' : ["source"], #TODO: will need a lot of special handling + 'CompositorNodeCryptomatte' : [], #TODO: will likely need same handling as above + 'CompositorNodeDiffMatte' : ["tolerance", "falloff"], + 'CompositorNodeDistanceMatte' : ["tolerance", "falloff", "channel"], + 'CompositorNodeDoubleEdgeMask' : ["inner_mode", "edge_mode"], + 'CompositorNodeEllipseMask' : ["x", "y", "width", "height", "rotation", "mask_type"], + 'CompositorNodeKeying' : ["blur_pre", "screen_balance", "despill_factor", "despill_balance", "edge_kernel_radius", "edge_kernel_tolerance", "clip_black", "clip_white", "dilate_distance", "feather_falloff", "feather_distance", "blur_post"], + 'CompositorNodeKeyingScreen' : [], #TODO: movie stuff + 'CompositorNodeLumaMatte' : ["limit_max", "limit_min"], + + #Distort + 'CompositorNodeCornerPin' : [], + 'CompositorNodeCrop' : ["use_crop_size", "relative", "min_x", "max_x", "min_y", "max_y", "rel_min_x", "rel_max_x", "rel_min_y", "rel_max_y"], + 'CompositorNodeDisplace' : [], + 'CompositorNodeFlip' : ["axis"], + 'CompositorNodeLensdist' : ["use_projector", "use_jitter", "use_fit"], + 'CompositorNodeMapUV' : ["alpha"], + 'CompositorNodeMovieDistortion' : [], #TODO: movie stuff + 'CompositorNodePlaneTrackDeform' : ["use_motion_blur", "motion_blur_samples", "motion_blur_shutter"], #TODO: movie stuff + 'CompositorNodeRotate' : ["filter_type"], + 'CompositorNodeScale' : ["space", "frame_method", "offset_x", "offset_y"], + 'CompositorNodeStablize' : [], #TODO: movie stuff + 'CompositorNodeTransform' : ["filter_type"], + 'CompositorNodeTranslate' : ["use_relative", "wrapping"], + + #Layout + 'CompositorNodeSwitch' : ["check"] } -curve_nodes = {'ShaderNodeFloatCurve', - 'ShaderNodeVectorCurve', - 'ShaderNodeRGBCurve'} +curve_nodes = { + 'CompositorNodeTime', #TODO: check this works + 'CompositorNodeHueCorrect', #TODO: probbably will need custom work + 'CompositorNodeCurveRGB', #may just work out of the box + 'CompositorNodeCurveVec', #may just work out of the box +} -image_nodes = {'ShaderNodeTexEnvironment', - 'ShaderNodeTexImage'} +image_nodes = {'CompositorNodeImage',} class NTPCompositorOperator(bpy.types.Operator): bl_idname = "node.compositor_to_python" @@ -33,14 +159,19 @@ class NTPCompositorOperator(bpy.types.Operator): def execute(self, context): """ #find node group to replicate - nt = bpy.data.materials[self.material_name].node_tree + if self.is_scene: + nt = bpy.data.scenes[self.compositor_name].node_tree + else: + nt = bpy.data.node_groups[self.compositor_name] if nt is None: + #shouldn't happen self.report({'ERROR'},("NodeToPython: This doesn't seem to be a " - "valid material. Is Use Nodes selected?")) + "valid compositor node tree. Is Use Nodes " + "selected?")) return {'CANCELLED'} #set up names to use in generated addon - mat_var = clean_string(self.material_name) + comp_var = clean_string(self.compositor_name) if self.mode == 'ADDON': dir = bpy.path.abspath(context.scene.ntp_options.dir_path) @@ -50,29 +181,30 @@ def execute(self, context): "NodeToPython!")) return {'CANCELLED'} - zip_dir = os.path.join(dir, mat_var) - addon_dir = os.path.join(zip_dir, mat_var) + zip_dir = os.path.join(dir, comp_var) + addon_dir = os.path.join(zip_dir, comp_var) if not os.path.exists(addon_dir): os.makedirs(addon_dir) file = open(f"{addon_dir}/__init__.py", "w") - create_header(file, self.material_name) - class_name = clean_string(self.material_name, lower=False) - init_operator(file, class_name, mat_var, self.material_name) + create_header(file, self.compositor_name) + class_name = clean_string(self.compositor_name, lower=False) + init_operator(file, class_name, comp_var, self.compositor_name) file.write("\tdef execute(self, context):\n") else: file = StringIO("") - def create_material(indent: str): - file.write((f"{indent}mat = bpy.data.materials.new(" - f"name = {str_to_py_str(self.material_name)})\n")) - file.write(f"{indent}mat.use_nodes = True\n") - - if self.mode == 'ADDON': - create_material("\t\t") - elif self.mode == 'SCRIPT': - create_material("") + if self.is_scene: + def create_scene(indent: str): + file.write((f"{indent}scene = bpy.data.scenes.new(" #TODO: see if using scene as name effects nodes named scene + f"name = {str_to_py_str(self.compositor_name)})\n")) + file.write(f"{indent}scene.use_nodes = True\n") + + if self.mode == 'ADDON': + create_scene("\t\t") + elif self.mode == 'SCRIPT': + create_scene("") #set to keep track of already created node trees node_trees = set() @@ -90,10 +222,10 @@ def is_outermost_node_group(level: int) -> bool: return True return False - def process_mat_node_group(node_tree, level, node_vars, used_vars): + def process_comp_node_group(node_tree, level, node_vars, used_vars): if is_outermost_node_group(level): - nt_var = create_var(self.material_name, used_vars) - nt_name = self.material_name + nt_var = create_var(self.compositor_name, used_vars) + nt_name = self.compositor_name else: nt_var = create_var(node_tree.name, used_vars) nt_name = node_tree.name @@ -105,14 +237,14 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): file.write(f"{outer}def {nt_var}_node_group():\n") if is_outermost_node_group(level): #outermost node group - file.write(f"{inner}{nt_var} = mat.node_tree\n") + file.write(f"{inner}{nt_var} = scene.node_tree\n") file.write(f"{inner}#start with a clean node tree\n") file.write(f"{inner}for node in {nt_var}.nodes:\n") file.write(f"{inner}\t{nt_var}.nodes.remove(node)\n") else: file.write((f"{inner}{nt_var}" f"= bpy.data.node_groups.new(" - f"type = \'ShaderNodeTree\', " + f"type = \'CompositorNodeTree\', " f"name = {str_to_py_str(nt_name)})\n")) file.write("\n") @@ -126,10 +258,10 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): node_vars = {} for node in node_tree.nodes: - if node.bl_idname == 'ShaderNodeGroup': + if node.bl_idname == 'CompositorNodeGroup': node_nt = node.node_tree if node_nt is not None and node_nt not in node_trees: - process_mat_node_group(node_nt, level + 1, node_vars, + process_comp_node_group(node_nt, level + 1, node_vars, used_vars) node_trees.add(node_nt) @@ -139,7 +271,7 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): set_settings_defaults(node, node_settings, file, inner, node_var) hide_sockets(node, file, inner, node_var) - if node.bl_idname == 'ShaderNodeGroup': + if node.bl_idname == 'CompositorNodeGroup': if node.node_tree is not None: file.write((f"{inner}{node_var}.node_tree = " f"bpy.data.node_groups" @@ -183,7 +315,7 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): level = 2 else: level = 0 - process_mat_node_group(nt, level, node_vars, used_vars) + process_comp_node_group(nt, level, node_vars, used_vars) if self.mode == 'ADDON': file.write("\t\treturn {'FINISHED'}\n\n") diff --git a/materials.py b/materials.py index 66c0d00..e65210f 100644 --- a/materials.py +++ b/materials.py @@ -125,7 +125,7 @@ def execute(self, context): file = StringIO("") def create_material(indent: str): - file.write((f"{indent}mat = bpy.data.materials.new(" + file.write((f"{indent}mat = bpy.data.materials.new(" #TODO: see if using mat effects nodes named mat f"name = {str_to_py_str(self.material_name)})\n")) file.write(f"{indent}mat.use_nodes = True\n") @@ -282,7 +282,7 @@ def poll(cls, context): def draw(self, context): layout = self.layout.column_flow(columns=1) layout.operator_context = 'INVOKE_DEFAULT' - for mat in bpy.data.materials: + for mat in bpy.data.materials: #TODO: filter by node tree exists op = layout.operator(NTPMaterialOperator.bl_idname, text=mat.name) op.material_name = mat.name @@ -306,7 +306,7 @@ def draw(self, context): row = layout.row() # Disables menu when there are no materials - materials = bpy.data.materials + materials = bpy.data.materials #TODO: filter by node tree exist materials_exist = len(materials) > 0 row.enabled = materials_exist From 486872db3ab11783e62f5b448079c2d2cd8cbdad Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sun, 13 Aug 2023 16:48:38 -0500 Subject: [PATCH 05/21] feat: scene copying and setup --- compositor.py | 37 ++++++++++++++++++++++++++++--------- materials.py | 2 +- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/compositor.py b/compositor.py index ef978b2..0b21453 100644 --- a/compositor.py +++ b/compositor.py @@ -4,6 +4,14 @@ from .utils import * from io import StringIO +SCENE_VAR = "scene" +BASE_NAME_VAR = "base_name" +END_NAME_VAR = "end_name" + +ntp_vars = {SCENE_VAR, BASE_NAME_VAR, END_NAME_VAR} +#TODO: do something similar for geo nodes and materials, should be useful for +# possible conflicts between ntp_vars and node vars + node_settings = { #Input 'CompositorNodeBokehImage' : ["flaps", "angle", "rounding", "catadioptric", @@ -157,7 +165,6 @@ class NTPCompositorOperator(bpy.types.Operator): is_scene : bpy.props.BoolProperty(name="Is Scene", description="Blender stores compositing node trees differently for scenes and in groups") def execute(self, context): - """ #find node group to replicate if self.is_scene: nt = bpy.data.scenes[self.compositor_name].node_tree @@ -194,12 +201,24 @@ def execute(self, context): file.write("\tdef execute(self, context):\n") else: file = StringIO("") - if self.is_scene: def create_scene(indent: str): - file.write((f"{indent}scene = bpy.data.scenes.new(" #TODO: see if using scene as name effects nodes named scene - f"name = {str_to_py_str(self.compositor_name)})\n")) - file.write(f"{indent}scene.use_nodes = True\n") + file.write(f"{indent}{SCENE_VAR} = bpy.context.window.scene.copy()\n\n") #TODO: see if using scene as name effects nodes named scene + + #TODO: wrap in more general unique name util function + file.write(f"{indent}# Generate unique scene name\n") + file.write(f"{indent}{BASE_NAME_VAR} = {str_to_py_str(self.compositor_name)}\n") + file.write(f"{indent}{END_NAME_VAR} = {BASE_NAME_VAR}\n") + file.write(f"{indent}if bpy.data.scenes.get({END_NAME_VAR}) != None:\n") + file.write(f"{indent}\ti = 1\n") + file.write(f"{indent}\t{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"\n") + file.write(f"{indent}\twhile bpy.data.scenes.get({END_NAME_VAR}) != None:\n") + file.write(f"{indent}\t\t{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"\n") + file.write(f"{indent}\t\ti += 1\n\n") + + file.write(f"{indent}{SCENE_VAR}.name = {END_NAME_VAR}\n") + file.write(f"{indent}{SCENE_VAR}.use_fake_user = True\n") + file.write(f"{indent}bpy.context.window.scene = {SCENE_VAR}\n") if self.mode == 'ADDON': create_scene("\t\t") @@ -237,7 +256,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): file.write(f"{outer}def {nt_var}_node_group():\n") if is_outermost_node_group(level): #outermost node group - file.write(f"{inner}{nt_var} = scene.node_tree\n") + file.write(f"{inner}{nt_var} = {SCENE_VAR}.node_tree\n") file.write(f"{inner}#start with a clean node tree\n") file.write(f"{inner}for node in {nt_var}.nodes:\n") file.write(f"{inner}\t{nt_var}.nodes.remove(node)\n") @@ -247,7 +266,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): f"type = \'CompositorNodeTree\', " f"name = {str_to_py_str(nt_name)})\n")) file.write("\n") - + """ inputs_set = False outputs_set = False @@ -310,7 +329,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): init_links(node_tree, file, inner, nt_var, node_vars) file.write(f"\n{outer}{nt_var}_node_group()\n\n") - + """ if self.mode == 'ADDON': level = 2 else: @@ -331,7 +350,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): if self.mode == 'ADDON': zip_addon(zip_dir) - """ + if self.mode == 'SCRIPT': location = "clipboard" else: diff --git a/materials.py b/materials.py index e65210f..c0ee1ba 100644 --- a/materials.py +++ b/materials.py @@ -131,7 +131,7 @@ def create_material(indent: str): if self.mode == 'ADDON': create_material("\t\t") - elif self.mode == 'SCRIPT': + elif self.mode == 'SCRIPT': #TODO: should add option for just creating the node group create_material("") #set to keep track of already created node trees From 3e9ebc9161683288b7cac49d2706dd0629dbdeab Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Thu, 24 Aug 2023 23:47:15 -0500 Subject: [PATCH 06/21] refactor: add types to node settings --- compositor.py | 478 +++++++++++++++++++++++++++++++++++--------------- geo_nodes.py | 409 ++++++++++++++++++++++++++++++------------ materials.py | 248 +++++++++++++++++++------- utils.py | 32 ++-- 4 files changed, 835 insertions(+), 332 deletions(-) diff --git a/compositor.py b/compositor.py index 0b21453..7df8437 100644 --- a/compositor.py +++ b/compositor.py @@ -12,142 +12,336 @@ #TODO: do something similar for geo nodes and materials, should be useful for # possible conflicts between ntp_vars and node vars -node_settings = { - #Input - 'CompositorNodeBokehImage' : ["flaps", "angle", "rounding", "catadioptric", - "shift"], - 'CompositorNodeImage' : [], #TODO: handle image selection - 'CompositorNodeMask' : ["use_feather", "size_source", "size_x", "size_y", - "use_motion_blur", - "motion_blur_samples", "motion_blur_shutter"], #TODO: handle mask selection - 'CompositorNodeMovieClip' : [], #TODO: handle movie clip selection - 'CompositorNodeRLayers' : ["name", "layer"], - 'CompositorNodeRGB' : [], #should be handled by outputs - 'CompositorNodeSceneTime' : [], #should be good - 'CompositorNodeTexture' : [], #TODO: handle texture selection - 'CompositorNodeTime' : ["frame_start", "frame_end"], - 'CompositorNodeTrackPos' : [], #TODO: handle movie selection - 'CompositorNodeValue' : [], #should be handled by outputs (why is this a separate class??) - - #Output - 'CompositorNodeComposite' : ["use_alpha"], - 'CompositorNodeOutputFile' : ["base_path"], #TODO: doesn't seem portable - 'CompositorNodeLevels' : ["channel"], - 'CompositorNodeSplitViewer' : ["axis", "factor"], - 'CompositorNodeViewer' : ["use_alpha"], - - #Color - 'CompositorNodeAlphaOver' : ["use_premultiply", "premul"], - 'CompositorNodeBrightContrast' : ["use_premultiply"], - 'CompositorNodeColorBalance' : ["correction_method", "lift", "gamma", - "gain", "offset", "power", "slope", - "offset_basis"], - 'CompositorNodeColorCorrection' : ["red", "green", "blue", - "master_saturation", "master_contrast", - "master_gamma", "master_gain", - "master_lift", - "highlights_saturation", "highlights_contrast", - "highlights_gamma", "highlights_gain", - "highlights_lift", - "midtones_saturation", "midtones_contrast", - "midtones_gamma", "midtones_gain", - "midtones_lift", - "shadows_saturation", "shadows_contrast", - "shadows_gamma", "shadows_gain", - "shadows_lift", - "midtones_start", "midtones_end"], - 'CompositorNodeExposure' : [], - 'CompositorNodeGamma' : [], - 'CompositorNodeHueCorrect' : [], - 'CompositorNodeHueSat' : [], - 'CompositorNodeInvert' : ["invert_rgb", "invert_alpha"], - 'CompositorNodeMixRGB' : ["blend_type", "use_alpha", "use_clamp"], #TODO: has an update() method, may need to figure out why... - 'CompositorNodePosterize' : [], - 'CompositorNodeCurveRGB' : [], - 'CompositorNodeTonemap' : ["tonemap_type", "intensity", "contrast", "adaptation", "correction", "key", "offset", "gamma"], - 'CompositorNodeZcombine' : ["use_alpha", "use_antialias_z"], - - #Converter - 'CompositorNodePremulKey' : ["mapping"], - 'CompositorNodeValToRGB' : [], #TODO: check to see if this'll work out of the box - 'CompositorNodeConvertColorSpace' : ["from_color_space", "to_color_space"], - 'CompositorNodeCombineColor' : ["mode", "ycc_mode"], #why isn't this standardized across blender? - 'CompositorNodeCombineXYZ' : [], - 'CompositorNodeIDMask' : ["index", "use_antialiasing"], - 'CompositorNodeMath' : ["operation", "use_clamp"], - 'CompositorNodeRGBToBW' : [], - 'CompositorNodeSeparateColor' : ["mode", "ycc_mode"], - 'CompositorNodeSeparateXYZ' : [], - 'CompositorNodeSetAlpha' : ["mode"], - 'CompositorNodeSwitchView' : [], - - #Filter - 'CompositorNodeAntiAliasing' : ["threshold", "contrast_limit", "corner_rounding"], - 'CompositorNodeBilateralblur' : ["iterations", "sigma_color", "sigma_space"], - 'CompositorNodeBlur' : ["filter_type", "use_variable_size", "use_gamma_correction", "use_relative", "aspect_correction", "factor", "factor_x", "factor_y", "use_extended_bounds"], - 'CompositorNodeBokehBlur' : ["use_variable_size", "blur_max", "use_extended_bounds"], - 'CompositorNodeDefocus' : ["bokeh", "angle", "use_gamma_correction", "f_stop", "blur_max", "threshold", "use_preview", "use_zbuffer", "z_scale"], - 'CompositorNodeDespeckle' : ["threshold", "threshold_neighbor"], - 'CompositorNodeDilateErode' : ["mode", "distance", "edge", "falloff"], - 'CompositorNodeDBlur' : ["iterations", "center_x", "center_y", "distance", "angle", "spin", "zoom"], - 'CompositorNodeFilter' : ["filter_type"], - 'CompositorNodeGlare' : ["glare_type", "quality", "iterations", "color_modulation", "mix", "threshold", "streaks", "angle_offset", "fade", "size", "use_rotate_45"], - 'CompositorNodeInpaint' : ["distance"], - 'CompositorNodePixelate' : [], - 'CompositorNodeSunBeams' : ["source", "ray_length"], #TODO: check that source doesn't freak out - 'CompositorNodeVecBlur' : ["samples", "factor", "speed_min", "speed_max", "use_curved"], - - #Vector - 'CompositorNodeMapRange' : ["use_clamp"], - 'CompositorNodeMapValue' : ["offset", "size", "use_min", "min", "use_max", "max"], #why are all these vectors?? TODO: check to make sure it doesn't flip - 'CompositorNodeNormal' : [], #should be handled with io system +compositor_node_settings : dict[str, list[(str, str)]] = { + # INPUT + 'CompositorNodeBokehImage' : [("angle", "float"), + ("catadioptric", "float"), + ("flaps", "int"), + ("rounding", "float"), + ("shift", "float")], + 'CompositorNodeImage' : [("frame_duration", "int"), + ("frame_offset", "int"), + ("frame_start", "int"), + ("image", "Image"), #TODO: handle image selection + ("layer", "enum"), + ("use_auto_refresh", "bool"), + ("use_cyclic", "bool"), + ("use_straight_alpha_output", "bool"), + ("view", "enum")], + 'CompositorNodeMask' : [("mask", "Mask"), #TODO + ("motion_blur_samples", "int"), + ("motion_blur_shutter", "float"), + ("size_source", "enum"), + ("size_x", "int"), + ("size_y", "int"), + ("use_feather", "bool"), + ("use_motion_blur", "bool")], + 'CompositorNodeMovieClip' : [("clip", "MovieClip")], #TODO: handle movie clip selection + 'CompositorNodeRLayers' : [("layer", "enum"), + ("scene", "Scene")], #TODO + 'CompositorNodeRGB' : [], + 'CompositorNodeSceneTime' : [], + 'CompositorNodeTexture' : [("node_output", "int"), #TODO: ?? + ("texture", "Texture")], #TODO: handle texture selection + 'CompositorNodeTime' : [("curve", "CurveMapping"), + ("frame_end", "int"), + ("frame_start", "int")], + 'CompositorNodeTrackPos' : [("clip", "MovieClip"), #TODO: this is probably wrong + ("frame_relative", "int") + ("position", "enum"), + ("track_name", "str"), + ("tracking_object", "str")], + 'CompositorNodeValue' : [], #should be handled by outputs (why is this a separate class??) + + + # OUTPUT + 'CompositorNodeComposite' : [("use_alpha", "bool")], + 'CompositorNodeOutputFile' : [("active_input_index", "int"), #TODO: probably not right at all + ("base_path", "str"), + ("file_slots", "CompositorNodeOutputFileFileSlots"), + ("format", "ImageFormatSettings"), + ("layer_slots", "CompositorNodeOutputFileLayerSlots")], + 'CompositorNodeLevels' : [("channel", "enum")], + 'CompositorNodeSplitViewer' : [("axis", "enum"), + ("factor", "int")], + 'CompositorNodeViewer' : [("center_x", "float"), + ("center_y", "float"), + ("tile_order", "enum"), + ("use_alpha", "bool")], + + + # COLOR + 'CompositorNodeAlphaOver' : [("premul", "float"), + ("use_premultiply", "bool")], + 'CompositorNodeBrightContrast' : [("use_premultiply", "bool")], + 'CompositorNodeColorBalance' : [("correction_method", "enum"), + ("gain", "Vec3"), + ("gamma", "Vec3"), + ("lift", "Vec3"), + ("offset", "Vec3"), + ("offset_basis", "float"), + ("power", "Vec3"), + ("slope", "Vec3")], + 'CompositorNodeColorCorrection' : [("blue", "bool"), + ("green", "bool"), + ("highlights_contrast", "float"), + ("highlights_gain", "float"), + CurveMapp ("midtones_lift", "float"), + ("midtones_saturation", "float"), + ("midtones_start", "float"), + ("red", "bool"), + ("shadows_contrast", "float"), + ("shadows_gain", "float"), + ("shadows_gamma", "float"), + ("shadows_lift", "float"), + ("shadows_saturation", "float")], + 'CompositorNodeExposure' : [], + 'CompositorNodeGamma' : [], + 'CompositorNodeHueCorrect' : [("mapping", "CurveMapping")], + 'CompositorNodeHueSat' : [], + 'CompositorNodeInvert' : [("invert_alpha", "bool"), + ("invert_rgb", "bool")], + 'CompositorNodeMixRGB' : [("blend_type", "enum"), + ("use_alpha", "bool"), + ("use_clamp", "bool")], #TODO: has an update() method, may need to figure out why... + 'CompositorNodePosterize' : [], + 'CompositorNodeCurveRGB' : [("mapping", "CurveMapping")], + 'CompositorNodeTonemap' : [("adaptation", "float"), + ("contrast", "float"), + ("correction", "float"), + ("gamma", "float"), + ("intensity", "float"), + ("key", "float"), + ("offset", "float"), + ("tonemap_type", "enum")], + 'CompositorNodeZcombine' : [("use_alpha", "bool"), + ("use_antialias_z", "bool")], + + + # CONVERTER + 'CompositorNodePremulKey' : [("mapping", "enum")], + 'CompositorNodeValToRGB' : [("color_ramp", "ColorRamp")], #TODO: check to see if this'll work out of the box + 'CompositorNodeConvertColorSpace' : [("from_color_space", "enum"), + ("to_color_space", "enum")], + 'CompositorNodeCombineColor' : [("mode", "enum"), + ("ycc_mode", "enum")], #why isn't this standardized across blender? + 'CompositorNodeCombineXYZ' : [], + 'CompositorNodeIDMask' : [("index", "int"), + ("use_antialiasing", "bool")], + 'CompositorNodeMath' : [("operation", "enum"), + ("use_clamp", "bool")], + 'CompositorNodeRGBToBW' : [], + 'CompositorNodeSeparateColor' : [("mode", "enum"), + ("ycc_mode", "enum")], + 'CompositorNodeSeparateXYZ' : [], + 'CompositorNodeSetAlpha' : [("mode", "enum")], + 'CompositorNodeSwitchView' : [], + + + # FILTER + 'CompositorNodeAntiAliasing' : [("contrast_limit", "float"), + ("corner_rounding", "float"), + ("threshold", "float")], + 'CompositorNodeBilateralblur' : [("iterations", "int"), + ("sigma_color", "float"), + ("sigma_space", "float")], + 'CompositorNodeBlur' : [("aspect_correction", "enum"), + ("factor", "float"), + ("factor_x", "float"), + ("factor_y", "float"), + ("filter_type", "enum"), + ("size_x", "int"), + ("size_y", "int"), + ("use_bokeh", "bool"), + ("use_extended_bounds", "bool"), + ("use_gamma_correction", "bool"), + ("use_relative", "bool"), + ("use_variable_size", "bool")], + 'CompositorNodeBokehBlur' : [("blur_max", "float"), + ("use_extended_bounds", "bool"), + ("use_variable_size", "bool")], + 'CompositorNodeDefocus' : [("angle", "float"), + ("blur_max", "float"), + ("bokeh", "enum"), + ("f_stop", "float"), + ("scene", "Scene"), #TODO + ("threshold", "float"), + ("use_gamma_correction", "bool"), + ("use_preview", "bool"), + ("use_zbuffer", "bool"), + ("z_scale", "float")], + 'CompositorNodeDespeckle' : [("threshold", "float"), + ("threshold_neighbor", "float")], + 'CompositorNodeDilateErode' : [("distance", "int"), + ("edge", "float"), + ("falloff", "enum"), + ("mode", "enum")], + 'CompositorNodeDBlur' : [("angle", "float"), + ("center_x", "float"), + ("center_y", "float"), + ("distance", "float"), + ("iterations", "int"), + ("spin", "float"), + ("zoom", "float")], + 'CompositorNodeFilter' : [("filter_type", "enum")], + 'CompositorNodeGlare' : [("angle_offset", "float"), + ("color_modulation", "float"), + ("fade", "float"), + ("glare_type", "enum"), + ("iterations", "int"), + ("mix", "float"), + ("quality", "enum"), + ("size", "int"), + ("streaks", "int"), + ("threshold", "float"), + ("use_rotate_45", "bool")], + 'CompositorNodeInpaint' : [("distance", "int")], + 'CompositorNodePixelate' : [], + 'CompositorNodeSunBeams' : [("ray_length", "float"), + ("source", "Vec2")], + 'CompositorNodeVecBlur' : [("factor", "float"), + ("samples", "int"), + ("speed_max", "int"), + ("speed_min", "int"), + ("use_curved", "bool")], + + + # VECTOR + 'CompositorNodeMapRange' : [("use_clamp", "bool")], + 'CompositorNodeMapValue' : [("max", "Vec1"), + ("min", "Vec1"), + ("offset", "Vec1"), + ("size", "Vec1"), + ("use_max", "bool"), + ("use_min", "bool")], #why are all these vectors?? TODO: check to make sure it doesn't flip + 'CompositorNodeNormal' : [], 'CompositorNodeNormalize' : [], - 'CompositorNodeCurveVec' : [], - - #Matte - 'CompositorNodeBoxMask' : ["x", "y", "width", "height", "rotation", "mask_type"], - 'CompositorNodeChannelMatte' : ["color_space", "matte_channel", "limit_method", "limit_channel", "limit_max", "limit_min"], - 'CompositorNodeChromaMatte' : ["tolerance", "threshold", "gain"], - 'CompositorNodeColorMatte' : ["color_hue", "color_saturation", "color_value"], - 'CompositorNodeColorSpill' : ["channel", "limit_method", "ratio", "use_unspill", "unspill_red", "unspill_green", "unspill_blue"], - 'CompositorNodeCryptomatteV2' : ["source"], #TODO: will need a lot of special handling - 'CompositorNodeCryptomatte' : [], #TODO: will likely need same handling as above - 'CompositorNodeDiffMatte' : ["tolerance", "falloff"], - 'CompositorNodeDistanceMatte' : ["tolerance", "falloff", "channel"], - 'CompositorNodeDoubleEdgeMask' : ["inner_mode", "edge_mode"], - 'CompositorNodeEllipseMask' : ["x", "y", "width", "height", "rotation", "mask_type"], - 'CompositorNodeKeying' : ["blur_pre", "screen_balance", "despill_factor", "despill_balance", "edge_kernel_radius", "edge_kernel_tolerance", "clip_black", "clip_white", "dilate_distance", "feather_falloff", "feather_distance", "blur_post"], - 'CompositorNodeKeyingScreen' : [], #TODO: movie stuff - 'CompositorNodeLumaMatte' : ["limit_max", "limit_min"], - - #Distort - 'CompositorNodeCornerPin' : [], - 'CompositorNodeCrop' : ["use_crop_size", "relative", "min_x", "max_x", "min_y", "max_y", "rel_min_x", "rel_max_x", "rel_min_y", "rel_max_y"], - 'CompositorNodeDisplace' : [], - 'CompositorNodeFlip' : ["axis"], - 'CompositorNodeLensdist' : ["use_projector", "use_jitter", "use_fit"], - 'CompositorNodeMapUV' : ["alpha"], - 'CompositorNodeMovieDistortion' : [], #TODO: movie stuff - 'CompositorNodePlaneTrackDeform' : ["use_motion_blur", "motion_blur_samples", "motion_blur_shutter"], #TODO: movie stuff - 'CompositorNodeRotate' : ["filter_type"], - 'CompositorNodeScale' : ["space", "frame_method", "offset_x", "offset_y"], - 'CompositorNodeStablize' : [], #TODO: movie stuff - 'CompositorNodeTransform' : ["filter_type"], - 'CompositorNodeTranslate' : ["use_relative", "wrapping"], - - #Layout + 'CompositorNodeCurveVec' : [("mapping", "CurveMapping")], + + + # MATTE + 'CompositorNodeBoxMask' : [("height", "float"), + ("mask_type", "enum"), + ("rotation", "float"), + ("width", "float"), + ("x", "float"), + ("y", "float")], + 'CompositorNodeChannelMatte' : [("color_space", "enum"), + ("limit_channel", "enum"), + ("limit_max", "float"), + ("limit_method", "enum"), + ("limit_min", "float"), + ("matte_channel", "enum")], + 'CompositorNodeChromaMatte' : [("gain", "float"), + ("lift", "float"), + ("shadow_adjust", "float"), + ("threshold", "float"), + ("tolerance", "float")], + 'CompositorNodeColorMatte' : [("color_hue", "float"), + ("color_saturation", "float"), + ("color_value", "float")], + 'CompositorNodeColorSpill' : [("channel", "enum"), + ("limit_channel", "enum"), + ("limit_method", "enum"), + ("ratio", "float"), + ("unspill_blue", "float"), + ("unspill_green", "float"), + ("unspill_red", "float"), + ("use_unspill", "bool")], + 'CompositorNodeCryptomatteV2' : [("add", "Vec3"), #TODO: will need a lot of special handling + ("entries", "CryptomatteEntry"), #TODO: (readonly?) + ("frame_duration", "int"), + ("frame_offset", "int"), + ("frame_start", "int"), + ("has_layers", "bool"), #TODO: readonly? + ("has_views", "bool"), #TODO: readonly? + ("image", "Image"), + ("layer", "enum"), + ("layer_name", "enum"), + ("matte_id", "str"), + ("remove", "Vec3"), + ("scene", "Scene"), + ("source", "enum"), + ("use_auto_refresh", "bool"), + ("use_cyclic", "bool"), + ("view", "enum")], + 'CompositorNodeCryptomatte' : [("add", "Vec3"), #TODO: will need a lot of special handling + ("matte_id", "str"), + ("remove", "Vec3")], + 'CompositorNodeDiffMatte' : [("falloff", "float"), + ("tolerance", "float")], + 'CompositorNodeDistanceMatte' : [("channel", "enum"), + ("falloff", "float"), + ("tolerance", "float")], + 'CompositorNodeDoubleEdgeMask' : [("edge_mode", "enum"), + ("inner_mode", "enum")], + 'CompositorNodeEllipseMask' : [("height", "float"), + ("mask_type", "enum"), + ("rotation", "float"), + ("width", "float"), + ("x", "float"), + ("y", "float")], + 'CompositorNodeKeying' : [("blur_post", "int"), + ("blur_pre", "int"), + ("clip_black", "float"), + ("clip_white", "float"), + ("despill_balance", "float"), + ("despill_factor", "float"), + ("dilate_distance", "int"), + ("edge_kernel_radius", "int"), + ("edge_kernel_tolerance", "float"), + ("feather_distance", "int"), + ("feather_falloff", "enum"), + ("screen_balance", "float")], + 'CompositorNodeKeyingScreen' : [("clip", "MovieClip"), + ("tracing_object", "str")], #TODO: movie stuff + 'CompositorNodeLumaMatte' : [("limit_max", "float"), + ("limit_min", "float")], + + + # DISTORT + 'CompositorNodeCornerPin' : [], + 'CompositorNodeCrop' : [("max_x", "int"), + ("max_y", "int"), + ("min_x", "int"), + ("min_y", "int"), + ("rel_max_x", "float"), + ("rel_max_y", "float"), + ("rel_min_x", "float"), + ("rel_min_y", "float"), + ("relative", "bool"), + ("use_crop_size", "bool")], + 'CompositorNodeDisplace' : [], + 'CompositorNodeFlip' : [("axis", "enum")], + 'CompositorNodeLensdist' : [("use_fit", "bool"), + ("use_jitter", "bool"), + ("use_projector", "bool")], + 'CompositorNodeMapUV' : [("alpha", "int")], + 'CompositorNodeMovieDistortion' : [("clip", "MovieClip"), + ("distortion_type", "enum")], #TODO: movie stuff + 'CompositorNodePlaneTrackDeform' : [("clip", "MovieClip"), + ("motion_blur_samples", "int"), + ("motion_blur_shutter", "float"), + ("plane_track_name", "str"), + ("tracking_object", "str"), + ("use_motion_blur", "bool")], #TODO: movie stuff + 'CompositorNodeRotate' : [("filter_type", "enum")], + 'CompositorNodeScale' : [("frame_method", "enum"), + ("offset_x", "float"), + ("offset_y", "float"), + ("space", "enum")], + 'CompositorNodeStablize' : [("clip", "MovieClip"), + ("filter_type", "enum"), + ("invert", "bool")], #TODO: movie stuff + 'CompositorNodeTransform' : [("filter_type", "enum")], + 'CompositorNodeTranslate' : [("use_relative", "bool"), + ("wrap_axis", "enum")], + + + # LAYOUT 'CompositorNodeSwitch' : ["check"] } -curve_nodes = { - 'CompositorNodeTime', #TODO: check this works - 'CompositorNodeHueCorrect', #TODO: probbably will need custom work - 'CompositorNodeCurveRGB', #may just work out of the box - 'CompositorNodeCurveVec', #may just work out of the box -} - -image_nodes = {'CompositorNodeImage',} - class NTPCompositorOperator(bpy.types.Operator): bl_idname = "node.compositor_to_python" bl_label = "Compositor to Python" @@ -266,7 +460,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): f"type = \'CompositorNodeTree\', " f"name = {str_to_py_str(nt_name)})\n")) file.write("\n") - """ + inputs_set = False outputs_set = False @@ -303,14 +497,14 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): group_io_settings(node, file, inner, "output", nt_var, node_tree) outputs_set = True - elif node.bl_idname in image_nodes and self.mode == 'ADDON': - img = node.image - if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: - save_image(img, addon_dir) - load_image(img, file, inner, f"{node_var}.image") - image_user_settings(node, file, inner, node_var) - - elif node.bl_idname == 'ShaderNodeValToRGB': + # elif node.bl_idname in image_nodes and self.mode == 'ADDON': + # img = node.image + # if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: + # save_image(img, addon_dir) + # load_image(img, file, inner, f"{node_var}.image") + # image_user_settings(node, file, inner, node_var) + + elif node.bl_idname == 'CompositorNodeValToRGB': color_ramp_settings(node, file, inner, node_var) elif node.bl_idname in curve_nodes: @@ -329,7 +523,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): init_links(node_tree, file, inner, nt_var, node_vars) file.write(f"\n{outer}{nt_var}_node_group()\n\n") - """ + if self.mode == 'ADDON': level = 2 else: diff --git a/geo_nodes.py b/geo_nodes.py index f5f6de1..b83d058 100644 --- a/geo_nodes.py +++ b/geo_nodes.py @@ -4,166 +4,341 @@ from .utils import * from io import StringIO -geo_node_settings = { - # Attribute nodes - "GeometryNodeAttributeStatistic" : ["data_type", "domain"], - "GeometryNodeAttributeDomainSize" : ["component"], +geo_node_settings : dict[str, list[(str, str)]] = { + # ATTRIBUTE + 'GeometryNodeAttributeStatistic' : [("data_type", "enum"), + ("domain", "enum")], + 'GeometryNodeAttributeDomainSize' : [("component", "enum")], + 'GeometryNodeBlurAttribute' : [("data_type", "enum")], + 'GeometryNodeCaptureAttribute' : [("data_type", "enum"), + ("domain", "enum")], + 'GeometryNodeRemoveAttribute' : [], + 'GeometryNodeStoreNamedAttribute' : [("data_type", "enum"), + ("domain", "enum")], + 'GeometryNodeAttributeTransfer' : [("data_type", "enum"), + ("domain", "enum"), + ("mapping", "enum")], + + # INPUT + # Input > Constant + 'FunctionNodeInputBool' : [("boolean", "bool")], + 'FunctionNodeInputColor' : [("color", "Vec4")], + 'GeometryNodeInputImage' : [("image", "Image")], + 'FunctionNodeInputInt' : [("integer", "int")], + 'GeometryNodeInputMaterial' : [("material", "Material")], + 'FunctionNodeInputString' : [("string", "str")], + 'ShaderNodeValue' : [], + 'FunctionNodeInputVector' : [("vector", "Vec3")], + + #Input > Group + 'NodeGroupInput' : [], + + # Input > Scene + 'GeometryNodeCollectionInfo' : [("transform_space", "enum")], + 'GeometryNodeImageInfo' : [], + 'GeometryNodeIsViewport' : [], + 'GeometryNodeObjectInfo' : [("transform_space", "enum")], + 'GeometryNodeSelfObject' : [], + 'GeometryNodeInputSceneTime' : [], - "GeometryNodeBlurAttribute" : ["data_type"], - "GeometryNodeCaptureAttribute" : ["data_type", "domain"], - "GeometryNodeStoreNamedAttribute" : ["data_type", "domain"], - "GeometryNodeAttributeTransfer" : ["data_type", "mapping"], - # Input Nodes - # Input > Constant - "FunctionNodeInputBool" : ["boolean"], - "FunctionNodeInputColor" : ["color"], - "FunctionNodeInputInt" : ["integer"], - "GeometryNodeInputMaterial" : ["material"], - "FunctionNodeInputString" : ["string"], - "FunctionNodeInputVector" : ["vector"], + # OUTPUT + 'GeometryNodeViewer' : [("data_type", "enum"), + ("domain", "enum")], - # Input > Scene - "GeometryNodeCollectionInfo" : ["transform_space"], - "GeometryNodeObjectInfo" : ["transform_space"], - # Output Nodes - "GeometryNodeViewer" : ["domain"], + # GEOMETRY + 'GeometryNodeJoinGeometry' : [], + 'GeometryNodeGeometryToInstance' : [], - # Geometry Nodes # Geometry > Read - "GeometryNodeInputNamedAttribute" : ["data_type"], + 'GeometryNodeInputID' : [], + 'GeometryNodeInputIndex' : [], + 'GeometryNodeInputNamedAttribute' : [("data_type", "enum")], + 'GeometryNodeInputNormal' : [], + 'GeometryNodeInputPosition' : [], + 'GeometryNodeInputRadius' : [], # Geometry > Sample - "GeometryNodeProximity" : ["target_element"], - "GeometryNodeRaycast" : ["data_type", "mapping"], - "GeometryNodeSampleIndex" : ["data_type", "domain", "clamp"], - "GeometryNodeSampleNearest" : ["domain"], + 'GeometryNodeProximity' : [("target_element", "enum")], + 'GeometryNodeIndexOfNearest' : [], + 'GeometryNodeRaycast' : [("data_type", "enum"), + ("mapping", "enum")], + 'GeometryNodeSampleIndex' : [("clamp", "bool"), + ("data_type", "enum"), + ("domain", "enum")], + 'GeometryNodeSampleNearest' : [("domain", "enum")], + + # Geometry > Write + 'GeometryNodeSetID' : [], + 'GeometryNodeSetPosition' : [], # Geometry > Operations - "GeometryNodeDeleteGeometry" : ["domain", "mode"], - "GeometryNodeDuplicateElements" : ["domain"], - "GeometryNodeMergeByDistance" : ["mode"], - "GeometryNodeSeparateGeometry" : ["domain"], - - - # Curve + 'GeometryNodeBoundBox' : [], + 'GeometryNodeConvexHull' : [], + 'GeometryNodeDeleteGeometry' : [("domain", "enum"), + ("mode", "enum")], + 'GeometryNodeDuplicateElements' : [("domain", "enum")], + 'GeometryNodeMergeByDistance' : [("mode", "enum")], + 'GeometryNodeTransform' : [], + 'GeometryNodeSeparateComponents' : [], + 'GeometryNodeSeparateGeometry' : [("domain", "enum")], + + + # CURVE # Curve > Read - "GeometryNodeCurveHandleTypeSelection" : ["mode", "handle_type"], + 'GeometryNodeInputCurveHandlePositions' : [], + 'GeometryNodeCurveLength' : [], + 'GeometryNodeInputTangent' : [], + 'GeometryNodeInputCurveTilt' : [], + 'GeometryNodeCurveEndpointSelection' : [], + 'GeometryNodeCurveHandleTypeSelection' : [("handle_type", "enum"), + ("mode", "enum")], + 'GeometryNodeInputSplineCyclic' : [], + 'GeometryNodeSplineLength' : [], + 'GeometryNodeSplineParameter' : [], + 'GeometryNodeInputSplineResolution' : [], # Curve > Sample - "GeometryNodeSampleCurve" : ["data_type", "mode", "use_all_curves"], + 'GeometryNodeSampleCurve' : [("data_type", "enum"), + ("mode", "enum"), + ("use_all_curves", "bool")], # Curve > Write - "GeometryNodeSetCurveNormal" : ["mode"], - "GeometryNodeSetCurveHandlePositions" : ["mode"], - "GeometryNodeCurveSetHandles" : ["mode", "handle_type"], - "GeometryNodeCurveSplineType" : ["spline_type"], + 'GeometryNodeSetCurveNormal' : [("mode", "enum")], + 'GeometryNodeSetCurveRadius' : [], + 'GeometryNodeSetCurveTilt' : [], + 'GeometryNodeSetCurveHandlePositions' : [("mode", "enum")], + 'GeometryNodeCurveSetHandles' : [("handle_type", "enum"), + ("mode", "enum")], + 'GeometryNodeSetSplineCyclic' : [], + 'GeometryNodeSetSplineResolution' : [], + 'GeometryNodeCurveSplineType' : [("spline_type", "enum")], # Curve > Operations - "GeometryNodeCurveToPoints" : ["mode"], - "GeometryNodeFillCurve" : ["mode"], - "GeometryNodeFilletCurve" : ["mode"], - "GeometryNodeResampleCurve" : ["mode"], - "GeometryNodeTrimCurve" : ["mode"], + 'GeometryNodeCurveToMesh' : [], + 'GeometryNodeCurveToPoints' : [("mode", "enum")], + 'GeometryNodeDeformCurvesOnSurface' : [], + 'GeometryNodeFillCurve' : [("mode", "enum")], + 'GeometryNodeFilletCurve' : [("mode", "enum")], + 'GeometryNodeInterpolateCurves' : [], + 'GeometryNodeResampleCurve' : [("mode", "enum")], + 'GeometryNodeReverseCurve' : [], + 'GeometryNodeSubdivideCurve' : [], + 'GeometryNodeTrimCurve' : [("mode", "enum")], # Curve > Primitives - "GeometryNodeCurveArc" : ["mode"], - "GeometryNodeCurvePrimitiveBezierSegment" : ["mode"], - "GeometryNodeCurvePrimitiveCircle" : ["mode"], - "GeometryNodeCurvePrimitiveLine" : ["mode"], - "GeometryNodeCurvePrimitiveQuadrilateral" : ["mode"], - + 'GeometryNodeCurveArc' : [("mode", "enum")], + 'GeometryNodeCurvePrimitiveBezierSegment' : [("mode", "enum")], + 'GeometryNodeCurvePrimitiveCircle' : [("mode", "enum")], + 'GeometryNodeCurvePrimitiveLine' : [("mode", "enum")], + 'GeometryNodeCurveSpiral' : [], + 'GeometryNodeCurveQuadraticBezier' : [], + 'GeometryNodeCurvePrimitiveQuadrilateral' : [("mode", "enum")], + 'GeometryNodeCurveStar' : [], + + # Curve > Topology + 'GeometryNodeOffsetPointInCurve' : [], + 'GeometryNodeCurveOfPoint' : [], + 'GeometryNodePointsOfCurve' : [], + + + # INSTANCES + 'GeometryNodeInstanceOnPoints' : [], + 'GeometryNodeInstancesToPoints' : [], + 'GeometryNodeRealizeInstances' : [("legacy_behavior", "bool")], + 'GeometryNodeRotateInstances' : [], + 'GeometryNodeScaleInstances' : [], + 'GeometryNodeTranslateInstances' : [], + 'GeometryNodeInputInstanceRotation' : [], + 'GeometryNodeInputInstanceScale' : [], + + + # MESH + # Mesh > Read + 'GeometryNodeInputMeshEdgeAngle' : [], + 'GeometryNodeInputMeshEdgeNeighbors' : [], + 'GeometryNodeInputMeshEdgeVertices' : [], + 'GeometryNodeEdgesToFaceGroups' : [], + 'GeometryNodeInputMeshFaceArea' : [], + 'GeometryNodeInputMeshFaceNeighbors' : [], + 'GeometryNodeMeshFaceSetBoundaries' : [], + 'GeometryNodeInputMeshFaceIsPlanar' : [], + 'GeometryNodeInputShadeSmooth' : [], + 'GeometryNodeInputMeshIsland' : [], + 'GeometryNodeInputShortestEdgePaths' : [], + 'GeometryNodeInputMeshVertexNeighbors' : [], - # Mesh Nodes # Mesh > Sample - "GeometryNodeSampleNearestSurface" : ["data_type"], - "GeometryNodeSampleUVSurface" : ["data_type"], + 'GeometryNodeSampleNearestSurface' : [("data_type", "enum")], + 'GeometryNodeSampleUVSurface' : [("data_type", "enum")], + + # Mesh > Write + 'GeometryNodeSetShadeSmooth' : [], # Mesh > Operations - "GeometryNodeExtrudeMesh" : ["mode"], - "GeometryNodeMeshBoolean" : ["operation"], - "GeometryNodeMeshToPoints" : ["mode"], - "GeometryNodeMeshToVolume" : ["resolution_mode"], - "GeometryNodeScaleElements" : ["domain", "scale_mode"], - "GeometryNodeSubdivisionSurface" : ["uv_smooth", "boundary_smooth"], - "GeometryNodeTriangulate" : ["quad_method", "ngon_method"], + 'GeometryNodeDualMesh' : [], + 'GeometryNodeEdgePathsToCurves' : [], + 'GeometryNodeEdgePathsToSelection' : [], + 'GeometryNodeExtrudeMesh' : [("mode", "enum")], + 'GeometryNodeFlipFaces' : [], + 'GeometryNodeMeshBoolean' : [("operation", "enum")], + 'GeometryNodeMeshToCurve' : [], + 'GeometryNodeMeshToPoints' : [("mode", "enum")], + 'GeometryNodeMeshToVolume' : [("resolution_mode", "enum")], + 'GeometryNodeScaleElements' : [("domain", "enum"), + ("scale_mode", "enum")], + 'GeometryNodeSplitEdges' : [], + 'GeometryNodeSubdivideMesh' : [], + 'GeometryNodeSubdivisionSurface' : [("boundary_smooth", "enum"), + ("uv_smooth", "enum")], + 'GeometryNodeTriangulate' : [("ngon_method", "enum"), + ("quad_method", "enum")], # Mesh > Primitives - "GeometryNodeMeshCone" : ["fill_type"], - "GeometryNodeMeshCylinder" : ["fill_type"], - "GeometryNodeMeshCircle" : ["fill_type"], - "GeometryNodeMeshLine" : ["mode"], + 'GeometryNodeMeshCone' : [("fill_type", "enum")], + 'GeometryNodeMeshCube' : [], + 'GeometryNodeMeshCylinder' : [("fill_type", "enum")], + 'GeometryNodeMeshGrid' : [], + 'GeometryNodeMeshIcoSphere' : [], + 'GeometryNodeMeshCircle' : [("fill_type", "enum")], + 'GeometryNodeMeshLine' : [("count_mode", "enum"), + ("mode", "enum")], + 'GeometryNodeMeshUVSphere' : [], + + # Mesh > Topology + 'GeometryNodeCornersOfFace' : [], + 'GeometryNodeCornersOfVertex' : [], + 'GeometryNodeEdgesOfCorner' : [], + 'GeometryNodeEdgesOfVertex' : [], + 'GeometryNodeFaceOfCorner' : [], + 'GeometryNodeOffsetCornerInFace' : [], + 'GeometryNodeVertexOfCorner' : [], # Mesh > UV - "GeometryNodeUVUnwrap" : ["method"], - + 'GeometryNodeUVPackIslands' : [], + 'GeometryNodeUVUnwrap' : [("method", "enum")], - # Point Nodes - "GeometryNodeDistributePointsInVolume" : ["mode"], - "GeometryNodeDistributePointsOnFaces" : ["distribute_method"], - "GeometryNodePointsToVolume" : ["resolution_mode"], - # Volume Nodes - "GeometryNodeVolumeToMesh" : ["resolution_mode"], + # POINT + 'GeometryNodeDistributePointsInVolume' : [("mode", "enum")], + 'GeometryNodeDistributePointsOnFaces' : [("distribute_method", "enum"), + ("use_legacy_normal", "bool")], + 'GeometryNodePoints' : [], + 'GeometryNodePointsToVertices' : [], + 'GeometryNodePointsToVolume' : [("resolution_mode", "enum")], + 'GeometryNodeSetPointRadius' : [], - # Texture Nodes - "ShaderNodeTexBrick" : ["offset", "offset_frequency", "squash", - "squash_frequency"], - "ShaderNodeTexGradient" : ["gradient_type"], - "GeometryNodeImageTexture" : ["interpolation", "extension"], - "ShaderNodeTexMagic" : ["turbulence_depth"], - "ShaderNodeTexNoise" : ["noise_dimensions"], - "ShaderNodeTexVoronoi" : ["voronoi_dimensions", "feature", "distance"], - "ShaderNodeTexWave" : ["wave_type", "bands_direction", "wave_profile"], - "ShaderNodeTexWhiteNoise" : ["noise_dimensions"], + # VOLUME + 'GeometryNodeVolumeCube' : [], + 'GeometryNodeVolumeToMesh' : [("resolution_mode", "enum")], - - # Utilities + + # SIMULATION + 'GeometryNodeSimulationInput' : [], + 'GeometryNodeSimulationOutput' : [], + + + # MATERIAL + 'GeometryNodeReplaceMaterial' : [], + 'GeometryNodeInputMaterialIndex' : [], + 'GeometryNodeMaterialSelection' : [], + 'GeometryNodeSetMaterial' : [], + 'GeometryNodeSetMaterialIndex' : [], + + + # TEXTURE + 'ShaderNodeTexBrick' : [("offset", "float"), + ("offset_frequency", "int"), + ("squash", "float"), + ("squash_frequency", "int")], + 'ShaderNodeTexChecker' : [], + 'ShaderNodeTexGradient' : [("gradient_type", "enum")], + 'GeometryNodeImageTexture' : [("extension", "enum"), + ("interpolation", "enum")], + 'ShaderNodeTexMagic' : [("turbulence_depth", "int")], + 'ShaderNodeTexMusgrave' : [("musgrave_dimensions", "enum"), + ("musgrave_type", "enum")], + 'ShaderNodeTexNoise' : [("noise_dimensions", "enum")], + 'ShaderNodeTexVoronoi' : [("distance", "enum"), + ("feature", "enum"), + ("voronoi_dimensions", "enum")], + 'ShaderNodeTexWave' : [("bands_direction", "enum"), + ("rings_direction", "enum"), + ("wave_profile", "enum"), + ("wave_type", "enum")], + 'ShaderNodeTexWhiteNoise' : [("noise_dimensions", "enum")], + + + # UTILITIES + 'ShaderNodeMix' : [("blend_type", "enum"), + ("clamp_factor", "bool"), + ("clamp_result", "bool"), + ("data_type", "enum"), + ("factor_mode", "enum")], + 'FunctionNodeRandomValue' : [("data_type", "enum")], + 'GeometryNodeSwitch' : [("input_type", "enum")], + # Utilities > Color - "FunctionNodeCombineColor" : ["mode"], - "ShaderNodeMixRGB" : ["blend_type", "use_clamp"], #legacy - "FunctionNodeSeparateColor" : ["mode"], + 'ShaderNodeValToRGB' : [("color_ramp", "ColorRamp")], + 'ShaderNodeRGBCurve' : [("mapping", "CurveMapping")], + 'FunctionNodeCombineColor' : [("mode", "enum")], + 'ShaderNodeMixRGB' : [("blend_type", "enum"), + ("use_alpha", "bool"), + ("use_clamp", "bool")], #legacy + 'FunctionNodeSeparateColor' : [("mode", "enum")], # Utilities > Text - "GeometryNodeStringToCurves" : ["overflow", "align_x", "align_y", - "pivot_mode"], + 'GeometryNodeStringJoin' : [], + 'FunctionNodeReplaceString' : [], + 'FunctionNodeSliceString' : [], + 'FunctionNodeStringLength' : [], + 'GeometryNodeStringToCurves' : [("align_x", "enum"), + ("align_y", "enum"), + ("font", "Font"), #TODO: font + ("overflow", "enum"), + ("pivot_mode", "enum")], + 'FunctionNodeValueToString' : [], + 'FunctionNodeInputSpecialCharacters' : [], # Utilities > Vector - "ShaderNodeVectorMath" : ["operation"], - "ShaderNodeVectorRotate" : ["rotation_type", "invert"], + 'ShaderNodeVectorCurve' : [("mapping", "CurveMapping")], + 'ShaderNodeVectorMath' : [("operation", "enum")], + 'ShaderNodeVectorRotate' : [("invert", "bool"), + ("rotation_type", "enum")], + 'ShaderNodeCombineXYZ' : [], + 'ShaderNodeSeparateXYZ' : [], # Utilities > Field - "GeometryNodeAccumulateField" : ["data_type", "domain"], - "GeometryNodeFieldAtIndex" : ["data_type", "domain"], - "GeometryNodeFieldOnDomain" : ["data_type", "domain" ], + 'GeometryNodeAccumulateField' : [("data_type", "enum"), + ("domain", "enum")], + 'GeometryNodeFieldAtIndex' : [("data_type", "enum"), + ("domain", "enum")], + 'GeometryNodeFieldOnDomain' : [("data_type", "enum"), + ("domain", "enum")], # Utilities > Math - "FunctionNodeBooleanMath" : ["operation"], - "ShaderNodeClamp" : ["clamp_type"], - "FunctionNodeCompare" : ["data_type", "operation", "mode"], - "FunctionNodeFloatToInt" : ["rounding_mode"], - "ShaderNodeMapRange" : ["data_type", "interpolation_type", "clamp"], - "ShaderNodeMath" : ["operation", "use_clamp"], - - # Utilities > Rotate - "FunctionNodeAlignEulerToVector" : ["axis", "pivot_axis"], - "FunctionNodeRotateEuler" : ["type", "space"], - - # Utilities > General - "ShaderNodeMix" : ["data_type", "blend_type", "clamp_result", - "clamp_factor", "factor_mode"], - "FunctionNodeRandomValue" : ["data_type"], - "GeometryNodeSwitch" : ["input_type"] + 'FunctionNodeBooleanMath' : [("operation", "enum")], + 'ShaderNodeClamp' : [("clamp_type", "enum")], + 'FunctionNodeCompare' : [("data_type", "enum"), + ("mode", "enum"), + ("operation", "enum")], + 'ShaderNodeFloatCurve' : [("mapping", "CurveMapping")], + 'FunctionNodeFloatToInt' : [("rounding_mode", "enum")], + 'ShaderNodeMapRange' : [("clamp", "bool"), + ("data_type", "enum"), + ("interpolation_type", "enum")], + 'ShaderNodeMath' : [("operation", "enum"), + ("use_clamp", "bool")], + + # Utilities > Rotation + 'FunctionNodeAlignEulerToVector' : [("axis", "enum"), + ("pivot_axis", "enum")], + 'FunctionNodeRotateEuler' : [("space", "enum"), + ("type", "enum")] } -curve_nodes = {'ShaderNodeFloatCurve', - 'ShaderNodeVectorCurve', - 'ShaderNodeRGBCurve'} - -image_nodes = {'GeometryNodeInputImage'} - class NTPGeoNodesOperator(bpy.types.Operator): bl_idname = "node.ntp_geo_nodes" bl_label = "Geo Nodes to Python" diff --git a/materials.py b/materials.py index c0ee1ba..a0d37f0 100644 --- a/materials.py +++ b/materials.py @@ -4,70 +4,192 @@ from .utils import * from io import StringIO -node_settings = { - #input - "ShaderNodeAmbientOcclusion" : ["samples", "inside", "only_local"], - "ShaderNodeAttribute" : ["attribute_type", "attribute_name"], - "ShaderNodeBevel" : ["samples"], - "ShaderNodeVertexColor" : ["layer_name"], - "ShaderNodeTangent" : ["direction_type", "axis"], - "ShaderNodeTexCoord" : ["object", "from_instancer"], - "ShaderNodeUVMap" : ["from_instancer", "uv_map"], - "ShaderNodeWireframe" : ["use_pixel_size"], - - #output - "ShaderNodeOutputAOV" : ["name"], - "ShaderNodeOutputMaterial" : ["target"], - - #shader - "ShaderNodeBsdfGlass" : ["distribution"], - "ShaderNodeBsdfGlossy" : ["distribution"], - "ShaderNodeBsdfPrincipled" : ["distribution", "subsurface_method"], - "ShaderNodeBsdfRefraction" : ["distribution"], - "ShaderNodeSubsurfaceScattering" : ["falloff"], - - #texture - "ShaderNodeTexBrick" : ["offset", "offset_frequency", "squash", "squash_frequency"], - "ShaderNodeTexEnvironment" : ["interpolation", "projection", "image_user.frame_duration", "image_user.frame_start", "image_user.frame_offset", "image_user.use_cyclic", "image_user.use_auto_refresh"], - "ShaderNodeTexGradient" : ["gradient_type"], - "ShaderNodeTexIES" : ["mode"], - "ShaderNodeTexImage" : ["interpolation", "projection", "projection_blend", - "extension"], - "ShaderNodeTexMagic" : ["turbulence_depth"], - "ShaderNodeTexMusgrave" : ["musgrave_dimensions", "musgrave_type"], - "ShaderNodeTexNoise" : ["noise_dimensions"], - "ShaderNodeTexPointDensity" : ["point_source", "object", "space", "radius", - "interpolation", "resolution", - "vertex_color_source"], - "ShaderNodeTexSky" : ["sky_type", "sun_direction", "turbidity", - "ground_albedo", "sun_disc", "sun_size", - "sun_intensity", "sun_elevation", - "sun_rotation", "altitude", "air_density", - "dust_density", "ozone_density"], - "ShaderNodeTexVoronoi" : ["voronoi_dimensions", "feature", "distance"], - "ShaderNodeTexWave" : ["wave_type", "rings_direction", "wave_profile"], - "ShaderNodeTexWhiteNoise" : ["noise_dimensions"], - - #color - "ShaderNodeMix" : ["data_type", "clamp_factor", "factor_mode", "blend_type", - "clamp_result"], - - #vector - "ShaderNodeBump" : ["invert"], - "ShaderNodeDisplacement" : ["space"], - "ShaderNodeMapping" : ["vector_type"], - "ShaderNodeNormalMap" : ["space", "uv_map"], - "ShaderNodeVectorDisplacement" : ["space"], - "ShaderNodeVectorRotate" : ["rotation_type", "invert"], - "ShaderNodeVectorTransform" : ["vector_type", "convert_from", "convert_to"], +shader_node_settings : dict[str, list[(str, str)]] = { + # INPUT + 'ShaderNodeAmbientOcclusion' : [("inside", "bool"), + ("only_local", "bool"), + ("samples", "int")], + 'ShaderNodeAttribute' : [("attribute_name", "str"), + ("attribute_type", "enum")], + 'ShaderNodeBevel' : [("samples", "int")], + 'ShaderNodeCameraData' : [], + 'ShaderNodeVertexColor' : [("layer_name", "str")], + 'ShaderNodeHairInfo' : [], + 'ShaderNodeFresnel' : [], + 'ShaderNodeNewGeometry' : [], + 'ShaderNodeLayerWeight' : [], + 'ShaderNodeLightPath' : [], + 'ShaderNodeObjectInfo' : [], + 'ShaderNodeParticleInfo' : [], + 'ShaderNodePointInfo' : [], + 'ShaderNodeRGB' : [], + 'ShaderNodeTangent' : [("axis", "enum"), + ("direction_type", "enum"), + ("uv_map", "str")], #TODO: makes sense? maybe make special type + 'ShaderNodeTexCoord' : [("from_instancer", "bool"), + ("object", "Object")], + 'ShaderNodeUVAlongStroke' : [("use_tips", "bool")], + 'ShaderNodeUVMap' : [("from_instancer", "bool"), + ("uv_map", "str")], #TODO: see ShaderNodeTangent + 'ShaderNodeValue' : [], + 'ShaderNodeVolumeInfo' : [], + 'ShaderNodeWireframe' : [("use_pixel_size", "bool")], + + + # OUTPUT + 'ShaderNodeOutputAOV' : [("name", "str")], + 'ShaderNodeOutputLight' : [("is_active_output", "bool"), + ("target", "enum")], + 'ShaderNodeOutputLineStyle' : [("blend_type", "enum"), + ("is_active_output", "bool"), + ("target", "enum"), + ("use_alpha", "bool"), + ("use_clamp", "bool")], + 'ShaderNodeOutputMaterial' : [("is_active_output", "bool"), + ("target", "enum")], + 'ShaderNodeOutputWorld' : [("is_active_output", "bool"), + ("target", "enum")], + + + # SHADER + 'ShaderNodeAddShader' : [], + 'ShaderNodeBsdfAnisotropic' : [("distribution", "enum")], + 'ShaderNodeBackground' : [], + 'ShaderNodeBsdfDiffuse' : [], + 'ShaderNodeEmission' : [], + 'ShaderNodeBsdfGlass' : [("distribution", "enum")], + 'ShaderNodeBsdfGlossy' : [("distribution", "enum")], + 'ShaderNodeBsdfHair' : [("component", "enum")], + 'ShaderNodeHoldout' : [], + 'ShaderNodeMixShader' : [], + 'ShaderNodeBsdfPrincipled' : [("distribution", "enum"), + ("subsurface_method", "enum")], + 'ShaderNodeBsdfHairPrincipled' : [("parametrization", "enum")], + 'ShaderNodeVolumePrincipled' : [], + 'ShaderNodeBsdfRefraction' : [("distribution", "enum")], + 'ShaderNodeEeveeSpecular' : [], + 'ShaderNodeSubsurfaceScattering' : [("falloff", "enum")], + 'ShaderNodeBsdfToon' : [("component", "enum")], + 'ShaderNodeBsdfTranslucent' : [], + 'ShaderNodeBsdfTransparent' : [], + 'ShaderNodeBsdfVelvet' : [], + 'ShaderNodeVolumeAbsorption' : [], + 'ShaderNodeVolumeScatter' : [], + + + # TEXTURE + 'ShaderNodeTexBrick' : [("offset", "float"), + ("offset_frequency", "int"), + ("squash", "float"), + ("squash_frequency", "int")], + 'ShaderNodeTexChecker' : [], + 'ShaderNodeTexEnvironment' : [("image", "Image"), + ("image_user", "ImageUser"), + ("interpolation", "enum"), + ("projection", "enum")], + 'ShaderNodeTexGradient' : [("gradient_type", "enum")], + 'ShaderNodeTexIES' : [("filepath", "str"), #TODO + ("ies", "Text"), + ("mode", "enum")], + 'ShaderNodeTexImage' : [("extension", "enum"), + ("image", "Image"), + ("image_user", "ImageUser"), + ("interpolation", "enum"), + ("projection", "enum"), + ("projection_blend", "float")], + 'ShaderNodeTexMagic' : [("turbulence_depth", "int")], + 'ShaderNodeTexMusgrave' : [("musgrave_dimensions", "enum"), + ("musgrave_type", "enum")], + 'ShaderNodeTexNoise' : [("noise_dimensions", "enum")], + 'ShaderNodeTexPointDensity' : [("interpolation", "enum"), + ("object", "Object"), + ("particle_color_source", "enum"), + ("particle_system", "ParticleSystem"), + ("point_source", "enum"), + ("radius", "float"), + ("resolution", "int"), + ("space", "enum"), + ("vertex_attribute_name", "str"), #TODO + ("vertex_color_source", "enum")], + 'ShaderNodeTexSky' : [("air_density", "float"), + ("altitude", "float"), + ("dust_density", "float"), + ("ground_albedo", "float"), + ("ozone_density", "float"), + ("sky_type", "enum"), + ("sun_direction", "Vec3"), + ("sun_disc", "bool"), + ("sun_elevation", "float"), + ("sun_intensity", "float"), + ("sun_rotation", "float"), + ("sun_size", "float") + ("turbidity", "float")], + 'ShaderNodeTexVoronoi' : [("distance", "enum"), + ("feature", "enum"), + ("voronoi_dimensions", "enum")], + 'ShaderNodeTexWave' : [("bands_direction", "enum"), + ("rings_direction", "enum"), + ("wave_profile", "enum"), + ("wave_type", "enum")], + 'ShaderNodeTexWhiteNoise' : [("noise_dimensions", "enum")], + + + # COLOR + 'ShaderNodeBrightContrast' : [], + 'ShaderNodeGamma' : [], + 'ShaderNodeHueSaturation' : [], + 'ShaderNodeInvert' : [], + 'ShaderNodeLightFalloff' : [], + 'ShaderNodeMix' : [("blend_type", "enum"), + ("clamp_factor", "bool"), + ("clamp_result", "bool"), + ("data_type", "enum"), + ("factor_mode", "enum")], + 'ShaderNodeRGBCurve' : [("mapping", "CurveMapping")], + + + # VECTOR + 'ShaderNodeBump' : [("invert", "bool")], + 'ShaderNodeDisplacement' : [("space", "enum")], + 'ShaderNodeMapping' : [("vector_type", "enum")], + 'ShaderNodeNormalMap' : [("space", "enum"), + ("uv_map", "str")], #TODO + 'ShaderNodeVectorCurve' : [("mapping", "CurveMapping")], + 'ShaderNodeVectorDisplacement' : [("space", "enum")], + 'ShaderNodeVectorRotate' : [("invert", "bool"), + ("rotation_type", "enum")], + 'ShaderNodeVectorTransform' : [("convert_from", "enum"), + ("convert_to", "enum"), + ("vector_type", "enum")], - #converter - "ShaderNodeClamp" : ["clamp_type"], - "ShaderNodeCombineColor" : ["mode"], - "ShaderNodeMapRange" : ["data_type", "interpolation_type", "clamp"], - "ShaderNodeMath" : ["operation", "use_clamp"], - "ShaderNodeSeparateColor" : ["mode"], - "ShaderNodeVectorMath" : ["operation"] + + # CONVERTER + 'ShaderNodeBlackbody' : [], + 'ShaderNodeClamp' : [("clamp_type", "enum")], + 'ShaderNodeValToRGB' : [("color_ramp", "ColorRamp")], + 'ShaderNodeCombineColor' : [("mode", "enum")], + 'ShaderNodeCombineXYZ' : [], + 'ShaderNodeFloatCurve' : [("mapping", "CurveMapping")], + 'ShaderNodeMapRange' : [("clamp", "bool"), + ("data_type", "enum"), + ("interpolation_type", "enum")], + 'ShaderNodeMath' : [("operation", "enum"), + ("use_clamp", "bool")], + 'ShaderNodeRGBToBW' : [], + 'ShaderNodeSeparateColor' : [("mode", "enum")], + 'ShaderNodeSeparateXYZ' : [], + 'ShaderNodeShaderToRGB' : [], + 'ShaderNodeVectorMath' : [("operation", "enum")], + 'ShaderNodeWavelength' : [], + + + # SCRIPT + 'ShaderNodeScript' : [("bytecode", "str"), #TODO: test all that + ("bytecode_hash", "str"), + ("filepath", "str"), + ("mode", "enum"), + ("script", "text"), + ("use_auto_update", "bool")] } curve_nodes = {'ShaderNodeFloatCurve', diff --git a/utils.py b/utils.py index d66632b..6d09ec0 100644 --- a/utils.py +++ b/utils.py @@ -94,6 +94,13 @@ def img_to_py_str(img) -> str: format = img.file_format.lower() return f"{name}.{format}" +type_to_py_str : dict[str, function] = { + "enum" : enum_to_py_str, + "str" : str_to_py_str, + "vec3" : vec3_to_py_str, + "vec4" : vec4_to_py_str +} + def create_header(file: TextIO, name: str): """ Sets up the bl_info and imports the Blender API @@ -384,40 +391,45 @@ def curve_node_settings(node, file: TextIO, inner: str, node_var: str): node_var (str): variable name for the add-on's curve node """ + if node.bl_idname == 'CompositorNodeTime': + mapping = node.curve #TODO: ask for consistency here? + else: + mapping = node.mapping + #mapping settings file.write(f"{inner}#mapping settings\n") mapping_var = f"{inner}{node_var}.mapping" #extend - extend = enum_to_py_str(node.mapping.extend) + extend = enum_to_py_str(mapping.extend) file.write(f"{mapping_var}.extend = {extend}\n") #tone - tone = enum_to_py_str(node.mapping.tone) + tone = enum_to_py_str(mapping.tone) file.write(f"{mapping_var}.tone = {tone}\n") #black level - b_lvl_str = vec3_to_py_str(node.mapping.black_level) + b_lvl_str = vec3_to_py_str(mapping.black_level) file.write((f"{mapping_var}.black_level = {b_lvl_str}\n")) #white level - w_lvl_str = vec3_to_py_str(node.mapping.white_level) + w_lvl_str = vec3_to_py_str(mapping.white_level) file.write((f"{mapping_var}.white_level = {w_lvl_str}\n")) #minima and maxima - min_x = node.mapping.clip_min_x + min_x = mapping.clip_min_x file.write(f"{mapping_var}.clip_min_x = {min_x}\n") - min_y = node.mapping.clip_min_y + min_y = mapping.clip_min_y file.write(f"{mapping_var}.clip_min_y = {min_y}\n") - max_x = node.mapping.clip_max_x + max_x = mapping.clip_max_x file.write(f"{mapping_var}.clip_max_x = {max_x}\n") - max_y = node.mapping.clip_max_y + max_y = mapping.clip_max_y file.write(f"{mapping_var}.clip_max_y = {max_y}\n") #use_clip - use_clip = node.mapping.use_clip + use_clip = mapping.use_clip file.write(f"{mapping_var}.use_clip = {use_clip}\n") #create curves - for i, curve in enumerate(node.mapping.curves): + for i, curve in enumerate(mapping.curves): file.write(f"{inner}#curve {i}\n") curve_i = f"{node_var}_curve_{i}" file.write((f"{inner}{curve_i} = {node_var}.mapping.curves[{i}]\n")) From 7805bcf4618cbacecc3471fe864a809484603bf1 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 26 Aug 2023 16:14:54 -0500 Subject: [PATCH 07/21] refactor: switch dictionary format to use type enums instead of strings --- compositor.py | 664 ++++++++++++++++++++++++++++---------------------- geo_nodes.py | 450 +++++++++++++++++++++++----------- materials.py | 339 ++++++++++++++++---------- utils.py | 93 +++++-- 4 files changed, 966 insertions(+), 580 deletions(-) diff --git a/compositor.py b/compositor.py index 7df8437..9384b1b 100644 --- a/compositor.py +++ b/compositor.py @@ -12,334 +12,415 @@ #TODO: do something similar for geo nodes and materials, should be useful for # possible conflicts between ntp_vars and node vars -compositor_node_settings : dict[str, list[(str, str)]] = { +compositor_node_settings : dict[str, list[(str, ST)]] = { # INPUT - 'CompositorNodeBokehImage' : [("angle", "float"), - ("catadioptric", "float"), - ("flaps", "int"), - ("rounding", "float"), - ("shift", "float")], - 'CompositorNodeImage' : [("frame_duration", "int"), - ("frame_offset", "int"), - ("frame_start", "int"), - ("image", "Image"), #TODO: handle image selection - ("layer", "enum"), - ("use_auto_refresh", "bool"), - ("use_cyclic", "bool"), - ("use_straight_alpha_output", "bool"), - ("view", "enum")], - 'CompositorNodeMask' : [("mask", "Mask"), #TODO - ("motion_blur_samples", "int"), - ("motion_blur_shutter", "float"), - ("size_source", "enum"), - ("size_x", "int"), - ("size_y", "int"), - ("use_feather", "bool"), - ("use_motion_blur", "bool")], - 'CompositorNodeMovieClip' : [("clip", "MovieClip")], #TODO: handle movie clip selection - 'CompositorNodeRLayers' : [("layer", "enum"), - ("scene", "Scene")], #TODO + 'CompositorNodeBokehImage' : [("angle", ST.FLOAT), + ("catadioptric", ST.FLOAT), + ("flaps", ST.INT), + ("rounding", ST.FLOAT), + ("shift", ST.FLOAT)], + + 'CompositorNodeImage' : [("frame_duration", ST.INT), + ("frame_offset", ST.INT), + ("frame_start", ST.INT), + ("image", ST.IMAGE), + ("layer", ST.ENUM), + ("use_auto_refresh", ST.BOOL), + ("use_cyclic", ST.BOOL), + ("use_straight_alpha_output", ST.BOOL), + ("view", ST.ENUM)], + + 'CompositorNodeMask' : [("mask", ST.MASK), + ("motion_blur_samples", ST.INT), + ("motion_blur_shutter", ST.FLOAT), + ("size_source", ST.ENUM), + ("size_x", ST.INT), + ("size_y", ST.INT), + ("use_feather", ST.BOOL), + ("use_motion_blur", ST.BOOL)], + + 'CompositorNodeMovieClip' : [("clip", ST.MOVIE_CLIP)], + + 'CompositorNodeRLayers' : [("layer", ST.ENUM), + ("scene", ST.SCENE)], + 'CompositorNodeRGB' : [], + 'CompositorNodeSceneTime' : [], - 'CompositorNodeTexture' : [("node_output", "int"), #TODO: ?? - ("texture", "Texture")], #TODO: handle texture selection - 'CompositorNodeTime' : [("curve", "CurveMapping"), - ("frame_end", "int"), - ("frame_start", "int")], - 'CompositorNodeTrackPos' : [("clip", "MovieClip"), #TODO: this is probably wrong - ("frame_relative", "int") - ("position", "enum"), - ("track_name", "str"), - ("tracking_object", "str")], - 'CompositorNodeValue' : [], #should be handled by outputs (why is this a separate class??) + + 'CompositorNodeTexture' : [("node_output", ST.INT), #TODO: ?? + ("texture", ST.TEXTURE)], + + 'CompositorNodeTime' : [("curve", ST.CURVE_MAPPING), + ("frame_end", ST.INT), + ("frame_start", ST.INT)], + + 'CompositorNodeTrackPos' : [("clip", ST.MOVIE_CLIP), + ("frame_relative", ST.INT) + ("position", ST.ENUM), + ("track_name", ST.STRING), #TODO: probably not right + ("tracking_object", ST.STRING)], + + 'CompositorNodeValue' : [], #TODO: double check that outputs set here # OUTPUT - 'CompositorNodeComposite' : [("use_alpha", "bool")], - 'CompositorNodeOutputFile' : [("active_input_index", "int"), #TODO: probably not right at all - ("base_path", "str"), - ("file_slots", "CompositorNodeOutputFileFileSlots"), - ("format", "ImageFormatSettings"), - ("layer_slots", "CompositorNodeOutputFileLayerSlots")], - 'CompositorNodeLevels' : [("channel", "enum")], - 'CompositorNodeSplitViewer' : [("axis", "enum"), - ("factor", "int")], - 'CompositorNodeViewer' : [("center_x", "float"), - ("center_y", "float"), - ("tile_order", "enum"), - ("use_alpha", "bool")], + 'CompositorNodeComposite' : [("use_alpha", ST.BOOL)], + + 'CompositorNodeOutputFile' : [("active_input_index", ST.INT), #TODO: probably not right at all + + ("base_path", ST.STRING), + ("file_slots", ST.FILE_SLOTS), + ("format", ST.IMAGE_FORMAT_SETTINGS), + ("layer_slots", ST.LAYER_SLOTS)], + + 'CompositorNodeLevels' : [("channel", ST.ENUM)], + + 'CompositorNodeSplitViewer' : [("axis", ST.ENUM), + ("factor", ST.INT)], + + 'CompositorNodeViewer' : [("center_x", ST.FLOAT), + ("center_y", ST.FLOAT), + ("tile_order", ST.ENUM), + ("use_alpha", ST.BOOL)], # COLOR - 'CompositorNodeAlphaOver' : [("premul", "float"), - ("use_premultiply", "bool")], - 'CompositorNodeBrightContrast' : [("use_premultiply", "bool")], - 'CompositorNodeColorBalance' : [("correction_method", "enum"), - ("gain", "Vec3"), - ("gamma", "Vec3"), - ("lift", "Vec3"), - ("offset", "Vec3"), - ("offset_basis", "float"), - ("power", "Vec3"), - ("slope", "Vec3")], - 'CompositorNodeColorCorrection' : [("blue", "bool"), - ("green", "bool"), - ("highlights_contrast", "float"), - ("highlights_gain", "float"), - CurveMapp ("midtones_lift", "float"), - ("midtones_saturation", "float"), - ("midtones_start", "float"), - ("red", "bool"), - ("shadows_contrast", "float"), - ("shadows_gain", "float"), - ("shadows_gamma", "float"), - ("shadows_lift", "float"), - ("shadows_saturation", "float")], + 'CompositorNodeAlphaOver' : [("premul", ST.FLOAT), + ("use_premultiply", ST.BOOL)], + + 'CompositorNodeBrightContrast' : [("use_premultiply", ST.BOOL)], + + 'CompositorNodeColorBalance' : [("correction_method", ST.ENUM), + ("gain", ST.VEC3), + ("gamma", ST.VEC3), + ("lift", ST.VEC3), + ("offset", ST.VEC3), + ("offset_basis", ST.FLOAT), + ("power", ST.VEC3), + ("slope", ST.VEC3)], + + 'CompositorNodeColorCorrection' : [("blue", ST.BOOL), + ("green", ST.BOOL), + ("highlights_contrast", ST.FLOAT), + ("highlights_gain", ST.FLOAT), + ("midtones_lift", ST.FLOAT), + ("midtones_saturation", ST.FLOAT), + ("midtones_start", ST.FLOAT), + ("red", ST.BOOL), + ("shadows_contrast", ST.FLOAT), + ("shadows_gain", ST.FLOAT), + ("shadows_gamma", ST.FLOAT), + ("shadows_lift", ST.FLOAT), + ("shadows_saturation", ST.FLOAT)], + 'CompositorNodeExposure' : [], + 'CompositorNodeGamma' : [], - 'CompositorNodeHueCorrect' : [("mapping", "CurveMapping")], + + 'CompositorNodeHueCorrect' : [("mapping", ST.CURVE_MAPPING)], + 'CompositorNodeHueSat' : [], - 'CompositorNodeInvert' : [("invert_alpha", "bool"), - ("invert_rgb", "bool")], - 'CompositorNodeMixRGB' : [("blend_type", "enum"), - ("use_alpha", "bool"), - ("use_clamp", "bool")], #TODO: has an update() method, may need to figure out why... + + 'CompositorNodeInvert' : [("invert_alpha", ST.BOOL), + ("invert_rgb", ST.BOOL)], + + 'CompositorNodeMixRGB' : [("blend_type", ST.ENUM), + ("use_alpha", ST.BOOL), + ("use_clamp", ST.BOOL)], #TODO: what is update() method for? + 'CompositorNodePosterize' : [], - 'CompositorNodeCurveRGB' : [("mapping", "CurveMapping")], - 'CompositorNodeTonemap' : [("adaptation", "float"), - ("contrast", "float"), - ("correction", "float"), - ("gamma", "float"), - ("intensity", "float"), - ("key", "float"), - ("offset", "float"), - ("tonemap_type", "enum")], - 'CompositorNodeZcombine' : [("use_alpha", "bool"), - ("use_antialias_z", "bool")], + + 'CompositorNodeCurveRGB' : [("mapping", ST.CURVE_MAPPING)], + + 'CompositorNodeTonemap' : [("adaptation", ST.FLOAT), + ("contrast", ST.FLOAT), + ("correction", ST.FLOAT), + ("gamma", ST.FLOAT), + ("intensity", ST.FLOAT), + ("key", ST.FLOAT), + ("offset", ST.FLOAT), + ("tonemap_type", ST.ENUM)], + + 'CompositorNodeZcombine' : [("use_alpha", ST.BOOL), + ("use_antialias_z", ST.BOOL)], # CONVERTER - 'CompositorNodePremulKey' : [("mapping", "enum")], - 'CompositorNodeValToRGB' : [("color_ramp", "ColorRamp")], #TODO: check to see if this'll work out of the box - 'CompositorNodeConvertColorSpace' : [("from_color_space", "enum"), - ("to_color_space", "enum")], - 'CompositorNodeCombineColor' : [("mode", "enum"), - ("ycc_mode", "enum")], #why isn't this standardized across blender? + 'CompositorNodePremulKey' : [("mapping", ST.ENUM)], + + 'CompositorNodeValToRGB' : [("color_ramp", ST.COLOR_RAMP)], + + 'CompositorNodeConvertColorSpace' : [("from_color_space", ST.ENUM), + ("to_color_space", ST.ENUM)], + + 'CompositorNodeCombineColor' : [("mode", ST.ENUM), + ("ycc_mode", ST.ENUM)], + 'CompositorNodeCombineXYZ' : [], - 'CompositorNodeIDMask' : [("index", "int"), - ("use_antialiasing", "bool")], - 'CompositorNodeMath' : [("operation", "enum"), - ("use_clamp", "bool")], + + 'CompositorNodeIDMask' : [("index", ST.INT), + ("use_antialiasing", ST.BOOL)], + + 'CompositorNodeMath' : [("operation", ST.ENUM), + ("use_clamp", ST.BOOL)], + 'CompositorNodeRGBToBW' : [], - 'CompositorNodeSeparateColor' : [("mode", "enum"), - ("ycc_mode", "enum")], + + 'CompositorNodeSeparateColor' : [("mode", ST.ENUM), + ("ycc_mode", ST.ENUM)], + 'CompositorNodeSeparateXYZ' : [], - 'CompositorNodeSetAlpha' : [("mode", "enum")], + + 'CompositorNodeSetAlpha' : [("mode", ST.ENUM)], + 'CompositorNodeSwitchView' : [], # FILTER - 'CompositorNodeAntiAliasing' : [("contrast_limit", "float"), - ("corner_rounding", "float"), - ("threshold", "float")], - 'CompositorNodeBilateralblur' : [("iterations", "int"), - ("sigma_color", "float"), - ("sigma_space", "float")], - 'CompositorNodeBlur' : [("aspect_correction", "enum"), - ("factor", "float"), - ("factor_x", "float"), - ("factor_y", "float"), - ("filter_type", "enum"), - ("size_x", "int"), - ("size_y", "int"), - ("use_bokeh", "bool"), - ("use_extended_bounds", "bool"), - ("use_gamma_correction", "bool"), - ("use_relative", "bool"), - ("use_variable_size", "bool")], - 'CompositorNodeBokehBlur' : [("blur_max", "float"), - ("use_extended_bounds", "bool"), - ("use_variable_size", "bool")], - 'CompositorNodeDefocus' : [("angle", "float"), - ("blur_max", "float"), - ("bokeh", "enum"), - ("f_stop", "float"), - ("scene", "Scene"), #TODO - ("threshold", "float"), - ("use_gamma_correction", "bool"), - ("use_preview", "bool"), - ("use_zbuffer", "bool"), - ("z_scale", "float")], - 'CompositorNodeDespeckle' : [("threshold", "float"), - ("threshold_neighbor", "float")], - 'CompositorNodeDilateErode' : [("distance", "int"), - ("edge", "float"), - ("falloff", "enum"), - ("mode", "enum")], - 'CompositorNodeDBlur' : [("angle", "float"), - ("center_x", "float"), - ("center_y", "float"), - ("distance", "float"), - ("iterations", "int"), - ("spin", "float"), - ("zoom", "float")], - 'CompositorNodeFilter' : [("filter_type", "enum")], - 'CompositorNodeGlare' : [("angle_offset", "float"), - ("color_modulation", "float"), - ("fade", "float"), - ("glare_type", "enum"), - ("iterations", "int"), - ("mix", "float"), - ("quality", "enum"), - ("size", "int"), - ("streaks", "int"), - ("threshold", "float"), - ("use_rotate_45", "bool")], - 'CompositorNodeInpaint' : [("distance", "int")], + 'CompositorNodeAntiAliasing' : [("contrast_limit", ST.FLOAT), + ("corner_rounding", ST.FLOAT), + ("threshold", ST.FLOAT)], + + 'CompositorNodeBilateralblur' : [("iterations", ST.INT), + ("sigma_color", ST.FLOAT), + ("sigma_space", ST.FLOAT)], + + 'CompositorNodeBlur' : [("aspect_correction", ST.ENUM), + ("factor", ST.FLOAT), + ("factor_x", ST.FLOAT), + ("factor_y", ST.FLOAT), + ("filter_type", ST.ENUM), + ("size_x", ST.INT), + ("size_y", ST.INT), + ("use_bokeh", ST.BOOL), + ("use_extended_bounds", ST.BOOL), + ("use_gamma_correction", ST.BOOL), + ("use_relative", ST.BOOL), + ("use_variable_size", ST.BOOL)], + + 'CompositorNodeBokehBlur' : [("blur_max", ST.FLOAT), + ("use_extended_bounds", ST.BOOL), + ("use_variable_size", ST.BOOL)], + + 'CompositorNodeDefocus' : [("angle", ST.FLOAT), + ("blur_max", ST.FLOAT), + ("bokeh", ST.ENUM), + ("f_stop", ST.FLOAT), + ("scene", ST.SCENE), + ("threshold", ST.FLOAT), + ("use_gamma_correction", ST.BOOL), + ("use_preview", ST.BOOL), + ("use_zbuffer", ST.BOOL), + ("z_scale", ST.FLOAT)], + + 'CompositorNodeDespeckle' : [("threshold", ST.FLOAT), + ("threshold_neighbor", ST.FLOAT)], + + 'CompositorNodeDilateErode' : [("distance", ST.INT), + ("edge", ST.FLOAT), + ("falloff", ST.ENUM), + ("mode", ST.ENUM)], + + 'CompositorNodeDBlur' : [("angle", ST.FLOAT), + ("center_x", ST.FLOAT), + ("center_y", ST.FLOAT), + ("distance", ST.FLOAT), + ("iterations", ST.INT), + ("spin", ST.FLOAT), + ("zoom", ST.FLOAT)], + + 'CompositorNodeFilter' : [("filter_type", ST.ENUM)], + + 'CompositorNodeGlare' : [("angle_offset", ST.FLOAT), + ("color_modulation", ST.FLOAT), + ("fade", ST.FLOAT), + ("glare_type", ST.ENUM), + ("iterations", ST.INT), + ("mix", ST.FLOAT), + ("quality", ST.ENUM), + ("size", ST.INT), + ("streaks", ST.INT), + ("threshold", ST.FLOAT), + ("use_rotate_45", ST.BOOL)], + + 'CompositorNodeInpaint' : [("distance", ST.INT)], + 'CompositorNodePixelate' : [], - 'CompositorNodeSunBeams' : [("ray_length", "float"), - ("source", "Vec2")], - 'CompositorNodeVecBlur' : [("factor", "float"), - ("samples", "int"), - ("speed_max", "int"), - ("speed_min", "int"), - ("use_curved", "bool")], + + 'CompositorNodeSunBeams' : [("ray_length", ST.FLOAT), + ("source", ST.VEC2)], + + 'CompositorNodeVecBlur' : [("factor", ST.FLOAT), + ("samples", ST.INT), + ("speed_max", ST.INT), + ("speed_min", ST.INT), + ("use_curved", ST.BOOL)], # VECTOR - 'CompositorNodeMapRange' : [("use_clamp", "bool")], - 'CompositorNodeMapValue' : [("max", "Vec1"), - ("min", "Vec1"), - ("offset", "Vec1"), - ("size", "Vec1"), - ("use_max", "bool"), - ("use_min", "bool")], #why are all these vectors?? TODO: check to make sure it doesn't flip + 'CompositorNodeMapRange' : [("use_clamp", ST.BOOL)], + + 'CompositorNodeMapValue' : [("max", ST.VEC1), + ("min", ST.VEC1), + ("offset", ST.VEC1), + ("size", ST.VEC1), + ("use_max", ST.BOOL), + ("use_min", ST.BOOL)], #why are all these vectors?? TODO: check to make sure it doesn't flip + 'CompositorNodeNormal' : [], + 'CompositorNodeNormalize' : [], - 'CompositorNodeCurveVec' : [("mapping", "CurveMapping")], + + 'CompositorNodeCurveVec' : [("mapping", ST.CURVE_MAPPING)], # MATTE - 'CompositorNodeBoxMask' : [("height", "float"), - ("mask_type", "enum"), - ("rotation", "float"), - ("width", "float"), - ("x", "float"), - ("y", "float")], - 'CompositorNodeChannelMatte' : [("color_space", "enum"), - ("limit_channel", "enum"), - ("limit_max", "float"), - ("limit_method", "enum"), - ("limit_min", "float"), - ("matte_channel", "enum")], - 'CompositorNodeChromaMatte' : [("gain", "float"), - ("lift", "float"), - ("shadow_adjust", "float"), - ("threshold", "float"), - ("tolerance", "float")], - 'CompositorNodeColorMatte' : [("color_hue", "float"), - ("color_saturation", "float"), - ("color_value", "float")], - 'CompositorNodeColorSpill' : [("channel", "enum"), - ("limit_channel", "enum"), - ("limit_method", "enum"), - ("ratio", "float"), - ("unspill_blue", "float"), - ("unspill_green", "float"), - ("unspill_red", "float"), - ("use_unspill", "bool")], - 'CompositorNodeCryptomatteV2' : [("add", "Vec3"), #TODO: will need a lot of special handling - ("entries", "CryptomatteEntry"), #TODO: (readonly?) - ("frame_duration", "int"), - ("frame_offset", "int"), - ("frame_start", "int"), - ("has_layers", "bool"), #TODO: readonly? - ("has_views", "bool"), #TODO: readonly? - ("image", "Image"), - ("layer", "enum"), - ("layer_name", "enum"), - ("matte_id", "str"), - ("remove", "Vec3"), - ("scene", "Scene"), - ("source", "enum"), - ("use_auto_refresh", "bool"), - ("use_cyclic", "bool"), - ("view", "enum")], - 'CompositorNodeCryptomatte' : [("add", "Vec3"), #TODO: will need a lot of special handling - ("matte_id", "str"), - ("remove", "Vec3")], - 'CompositorNodeDiffMatte' : [("falloff", "float"), - ("tolerance", "float")], - 'CompositorNodeDistanceMatte' : [("channel", "enum"), - ("falloff", "float"), - ("tolerance", "float")], - 'CompositorNodeDoubleEdgeMask' : [("edge_mode", "enum"), - ("inner_mode", "enum")], - 'CompositorNodeEllipseMask' : [("height", "float"), - ("mask_type", "enum"), - ("rotation", "float"), - ("width", "float"), - ("x", "float"), - ("y", "float")], - 'CompositorNodeKeying' : [("blur_post", "int"), - ("blur_pre", "int"), - ("clip_black", "float"), - ("clip_white", "float"), - ("despill_balance", "float"), - ("despill_factor", "float"), - ("dilate_distance", "int"), - ("edge_kernel_radius", "int"), - ("edge_kernel_tolerance", "float"), - ("feather_distance", "int"), - ("feather_falloff", "enum"), - ("screen_balance", "float")], - 'CompositorNodeKeyingScreen' : [("clip", "MovieClip"), - ("tracing_object", "str")], #TODO: movie stuff - 'CompositorNodeLumaMatte' : [("limit_max", "float"), - ("limit_min", "float")], + 'CompositorNodeBoxMask' : [("height", ST.FLOAT), + ("mask_type", ST.ENUM), + ("rotation", ST.FLOAT), + ("width", ST.FLOAT), + ("x", ST.FLOAT), + ("y", ST.FLOAT)], + + 'CompositorNodeChannelMatte' : [("color_space", ST.ENUM), + ("limit_channel", ST.ENUM), + ("limit_max", ST.FLOAT), + ("limit_method", ST.ENUM), + ("limit_min", ST.FLOAT), + ("matte_channel", ST.ENUM)], + + 'CompositorNodeChromaMatte' : [("gain", ST.FLOAT), + ("lift", ST.FLOAT), + ("shadow_adjust", ST.FLOAT), + ("threshold", ST.FLOAT), + ("tolerance", ST.FLOAT)], + + 'CompositorNodeColorMatte' : [("color_hue", ST.FLOAT), + ("color_saturation", ST.FLOAT), + ("color_value", ST.FLOAT)], + + 'CompositorNodeColorSpill' : [("channel", ST.ENUM), + ("limit_channel", ST.ENUM), + ("limit_method", ST.ENUM), + ("ratio", ST.FLOAT), + ("unspill_blue", ST.FLOAT), + ("unspill_green", ST.FLOAT), + ("unspill_red", ST.FLOAT), + ("use_unspill", ST.BOOL)], + + 'CompositorNodeCryptomatteV2' : [("add", ST.VEC3), + ("entries", ST.CRYPTOMATTE_ENTRIES), + ("frame_duration", ST.INT), + ("frame_offset", ST.INT), + ("frame_start", ST.INT), + ("has_layers", ST.BOOL), #TODO: readonly? + ("has_views", ST.BOOL), #TODO: readonly? + ("image", ST.IMAGE), + ("layer", ST.ENUM), + ("layer_name", ST.ENUM), + ("matte_id", ST.STRING), + ("remove", ST.VEC3), + ("scene", ST.SCENE), + ("source", ST.ENUM), + ("use_auto_refresh", ST.BOOL), + ("use_cyclic", ST.BOOL), + ("view", ST.ENUM)], + + 'CompositorNodeCryptomatte' : [("add", ST.VEC3), #TODO: may need special handling + ("matte_id", ST.STRING), + ("remove", ST.VEC3)], + + 'CompositorNodeDiffMatte' : [("falloff", ST.FLOAT), + ("tolerance", ST.FLOAT)], + + 'CompositorNodeDistanceMatte' : [("channel", ST.ENUM), + ("falloff", ST.FLOAT), + ("tolerance", ST.FLOAT)], + + 'CompositorNodeDoubleEdgeMask' : [("edge_mode", ST.ENUM), + ("inner_mode", ST.ENUM)], + + 'CompositorNodeEllipseMask' : [("height", ST.FLOAT), + ("mask_type", ST.ENUM), + ("rotation", ST.FLOAT), + ("width", ST.FLOAT), + ("x", ST.FLOAT), + ("y", ST.FLOAT)], + + 'CompositorNodeKeying' : [("blur_post", ST.INT), + ("blur_pre", ST.INT), + ("clip_black", ST.FLOAT), + ("clip_white", ST.FLOAT), + ("despill_balance", ST.FLOAT), + ("despill_factor", ST.FLOAT), + ("dilate_distance", ST.INT), + ("edge_kernel_radius", ST.INT), + ("edge_kernel_tolerance", ST.FLOAT), + ("feather_distance", ST.INT), + ("feather_falloff", ST.ENUM), + ("screen_balance", ST.FLOAT)], + + 'CompositorNodeKeyingScreen' : [("clip", ST.MOVIE_CLIP), + ("tracing_object", ST.STRING)], + + 'CompositorNodeLumaMatte' : [("limit_max", ST.FLOAT), + ("limit_min", ST.FLOAT)], # DISTORT 'CompositorNodeCornerPin' : [], - 'CompositorNodeCrop' : [("max_x", "int"), - ("max_y", "int"), - ("min_x", "int"), - ("min_y", "int"), - ("rel_max_x", "float"), - ("rel_max_y", "float"), - ("rel_min_x", "float"), - ("rel_min_y", "float"), - ("relative", "bool"), - ("use_crop_size", "bool")], + + 'CompositorNodeCrop' : [("max_x", ST.INT), + ("max_y", ST.INT), + ("min_x", ST.INT), + ("min_y", ST.INT), + ("rel_max_x", ST.FLOAT), + ("rel_max_y", ST.FLOAT), + ("rel_min_x", ST.FLOAT), + ("rel_min_y", ST.FLOAT), + ("relative", ST.BOOL), + ("use_crop_size", ST.BOOL)], + 'CompositorNodeDisplace' : [], - 'CompositorNodeFlip' : [("axis", "enum")], - 'CompositorNodeLensdist' : [("use_fit", "bool"), - ("use_jitter", "bool"), - ("use_projector", "bool")], - 'CompositorNodeMapUV' : [("alpha", "int")], - 'CompositorNodeMovieDistortion' : [("clip", "MovieClip"), - ("distortion_type", "enum")], #TODO: movie stuff - 'CompositorNodePlaneTrackDeform' : [("clip", "MovieClip"), - ("motion_blur_samples", "int"), - ("motion_blur_shutter", "float"), - ("plane_track_name", "str"), - ("tracking_object", "str"), - ("use_motion_blur", "bool")], #TODO: movie stuff - 'CompositorNodeRotate' : [("filter_type", "enum")], - 'CompositorNodeScale' : [("frame_method", "enum"), - ("offset_x", "float"), - ("offset_y", "float"), - ("space", "enum")], - 'CompositorNodeStablize' : [("clip", "MovieClip"), - ("filter_type", "enum"), - ("invert", "bool")], #TODO: movie stuff - 'CompositorNodeTransform' : [("filter_type", "enum")], - 'CompositorNodeTranslate' : [("use_relative", "bool"), - ("wrap_axis", "enum")], + + 'CompositorNodeFlip' : [("axis", ST.ENUM)], + + 'CompositorNodeLensdist' : [("use_fit", ST.BOOL), + ("use_jitter", ST.BOOL), + ("use_projector", ST.BOOL)], + + 'CompositorNodeMapUV' : [("alpha", ST.INT)], + + 'CompositorNodeMovieDistortion' : [("clip", ST.MOVIE_CLIP), + ("distortion_type", ST.ENUM)], + + 'CompositorNodePlaneTrackDeform' : [("clip", ST.MOVIE_CLIP), + ("motion_blur_samples", ST.INT), + ("motion_blur_shutter", ST.FLOAT), + ("plane_track_name", ST.STRING), + ("tracking_object", ST.STRING), + ("use_motion_blur", ST.BOOL)], + + 'CompositorNodeRotate' : [("filter_type", ST.ENUM)], + + 'CompositorNodeScale' : [("frame_method", ST.ENUM), + ("offset_x", ST.FLOAT), + ("offset_y", ST.FLOAT), + ("space", ST.ENUM)], + + 'CompositorNodeStablize' : [("clip", ST.MOVIE_CLIP), + ("filter_type", ST.ENUM), + ("invert", ST.BOOL)], + + 'CompositorNodeTransform' : [("filter_type", ST.ENUM)], + + 'CompositorNodeTranslate' : [("use_relative", ST.BOOL), + ("wrap_axis", ST.ENUM)], # LAYOUT - 'CompositorNodeSwitch' : ["check"] + 'CompositorNodeSwitch' : [("check", ST.BOOL)] } class NTPCompositorOperator(bpy.types.Operator): @@ -434,8 +515,9 @@ def is_outermost_node_group(level: int) -> bool: elif self.mode == 'SCRIPT' and level == 0: return True return False - + """ def process_comp_node_group(node_tree, level, node_vars, used_vars): + if is_outermost_node_group(level): nt_var = create_var(self.compositor_name, used_vars) nt_name = self.compositor_name @@ -481,7 +563,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): node_var = create_node(node, file, inner, nt_var, node_vars, used_vars) - set_settings_defaults(node, node_settings, file, inner, node_var) + set_settings_defaults(node, compositor_node_settings, file, inner, node_var) hide_sockets(node, file, inner, node_var) if node.bl_idname == 'CompositorNodeGroup': @@ -544,7 +626,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): if self.mode == 'ADDON': zip_addon(zip_dir) - + """ if self.mode == 'SCRIPT': location = "clipboard" else: diff --git a/geo_nodes.py b/geo_nodes.py index b83d058..ee8c03a 100644 --- a/geo_nodes.py +++ b/geo_nodes.py @@ -4,339 +4,501 @@ from .utils import * from io import StringIO -geo_node_settings : dict[str, list[(str, str)]] = { +geo_node_settings : dict[str, list[(str, ST)]] = { # ATTRIBUTE - 'GeometryNodeAttributeStatistic' : [("data_type", "enum"), - ("domain", "enum")], - 'GeometryNodeAttributeDomainSize' : [("component", "enum")], - 'GeometryNodeBlurAttribute' : [("data_type", "enum")], - 'GeometryNodeCaptureAttribute' : [("data_type", "enum"), - ("domain", "enum")], + 'GeometryNodeAttributeStatistic' : [("data_type", ST.ENUM), + ("domain", ST.ENUM)], + + 'GeometryNodeAttributeDomainSize' : [("component", ST.ENUM)], + + 'GeometryNodeBlurAttribute' : [("data_type", ST.ENUM)], + + 'GeometryNodeCaptureAttribute' : [("data_type", ST.ENUM), + ("domain", ST.ENUM)], + 'GeometryNodeRemoveAttribute' : [], - 'GeometryNodeStoreNamedAttribute' : [("data_type", "enum"), - ("domain", "enum")], - 'GeometryNodeAttributeTransfer' : [("data_type", "enum"), - ("domain", "enum"), - ("mapping", "enum")], + + 'GeometryNodeStoreNamedAttribute' : [("data_type", ST.ENUM), + ("domain", ST.ENUM)], + + 'GeometryNodeAttributeTransfer' : [("data_type", ST.ENUM), + ("domain", ST.ENUM), + ("mapping", ST.ENUM)], # INPUT # Input > Constant - 'FunctionNodeInputBool' : [("boolean", "bool")], - 'FunctionNodeInputColor' : [("color", "Vec4")], - 'GeometryNodeInputImage' : [("image", "Image")], - 'FunctionNodeInputInt' : [("integer", "int")], - 'GeometryNodeInputMaterial' : [("material", "Material")], - 'FunctionNodeInputString' : [("string", "str")], + 'FunctionNodeInputBool' : [("boolean", ST.BOOL)], + + 'FunctionNodeInputColor' : [("color", ST.VEC4)], + + 'GeometryNodeInputImage' : [("image", ST.IMAGE)], + + 'FunctionNodeInputInt' : [("integer", ST.INT)], + + 'GeometryNodeInputMaterial' : [("material", ST.MATERIAL)], + + 'FunctionNodeInputString' : [("string", ST.STRING)], + 'ShaderNodeValue' : [], - 'FunctionNodeInputVector' : [("vector", "Vec3")], + + 'FunctionNodeInputVector' : [("vector", ST.VEC3)], #Input > Group 'NodeGroupInput' : [], # Input > Scene - 'GeometryNodeCollectionInfo' : [("transform_space", "enum")], + 'GeometryNodeCollectionInfo' : [("transform_space", ST.ENUM)], + 'GeometryNodeImageInfo' : [], + 'GeometryNodeIsViewport' : [], - 'GeometryNodeObjectInfo' : [("transform_space", "enum")], + + 'GeometryNodeObjectInfo' : [("transform_space", ST.ENUM)], + 'GeometryNodeSelfObject' : [], + 'GeometryNodeInputSceneTime' : [], # OUTPUT - 'GeometryNodeViewer' : [("data_type", "enum"), - ("domain", "enum")], + 'GeometryNodeViewer' : [("data_type", ST.ENUM), + + ("domain", ST.ENUM)], # GEOMETRY 'GeometryNodeJoinGeometry' : [], + 'GeometryNodeGeometryToInstance' : [], # Geometry > Read 'GeometryNodeInputID' : [], + 'GeometryNodeInputIndex' : [], - 'GeometryNodeInputNamedAttribute' : [("data_type", "enum")], + + 'GeometryNodeInputNamedAttribute' : [("data_type", ST.ENUM)], + 'GeometryNodeInputNormal' : [], + 'GeometryNodeInputPosition' : [], + 'GeometryNodeInputRadius' : [], # Geometry > Sample - 'GeometryNodeProximity' : [("target_element", "enum")], + 'GeometryNodeProximity' : [("target_element", ST.ENUM)], + 'GeometryNodeIndexOfNearest' : [], - 'GeometryNodeRaycast' : [("data_type", "enum"), - ("mapping", "enum")], - 'GeometryNodeSampleIndex' : [("clamp", "bool"), - ("data_type", "enum"), - ("domain", "enum")], - 'GeometryNodeSampleNearest' : [("domain", "enum")], + + 'GeometryNodeRaycast' : [("data_type", ST.ENUM), + ("mapping", ST.ENUM)], + + 'GeometryNodeSampleIndex' : [("clamp", ST.BOOL), + ("data_type", ST.ENUM), + ("domain", ST.ENUM)], + + 'GeometryNodeSampleNearest' : [("domain", ST.ENUM)], # Geometry > Write 'GeometryNodeSetID' : [], + 'GeometryNodeSetPosition' : [], # Geometry > Operations 'GeometryNodeBoundBox' : [], + 'GeometryNodeConvexHull' : [], - 'GeometryNodeDeleteGeometry' : [("domain", "enum"), - ("mode", "enum")], - 'GeometryNodeDuplicateElements' : [("domain", "enum")], - 'GeometryNodeMergeByDistance' : [("mode", "enum")], + + 'GeometryNodeDeleteGeometry' : [("domain", ST.ENUM), + ("mode", ST.ENUM)], + + 'GeometryNodeDuplicateElements' : [("domain", ST.ENUM)], + + 'GeometryNodeMergeByDistance' : [("mode", ST.ENUM)], + 'GeometryNodeTransform' : [], + 'GeometryNodeSeparateComponents' : [], - 'GeometryNodeSeparateGeometry' : [("domain", "enum")], + + 'GeometryNodeSeparateGeometry' : [("domain", ST.ENUM)], # CURVE # Curve > Read 'GeometryNodeInputCurveHandlePositions' : [], + 'GeometryNodeCurveLength' : [], + 'GeometryNodeInputTangent' : [], + 'GeometryNodeInputCurveTilt' : [], + 'GeometryNodeCurveEndpointSelection' : [], - 'GeometryNodeCurveHandleTypeSelection' : [("handle_type", "enum"), - ("mode", "enum")], + + 'GeometryNodeCurveHandleTypeSelection' : [("handle_type", ST.ENUM), + ("mode", ST.ENUM)], + 'GeometryNodeInputSplineCyclic' : [], + 'GeometryNodeSplineLength' : [], + 'GeometryNodeSplineParameter' : [], + 'GeometryNodeInputSplineResolution' : [], # Curve > Sample - 'GeometryNodeSampleCurve' : [("data_type", "enum"), - ("mode", "enum"), - ("use_all_curves", "bool")], + 'GeometryNodeSampleCurve' : [("data_type", ST.ENUM), + ("mode", ST.ENUM), + ("use_all_curves", ST.BOOL)], # Curve > Write - 'GeometryNodeSetCurveNormal' : [("mode", "enum")], + 'GeometryNodeSetCurveNormal' : [("mode", ST.ENUM)], + 'GeometryNodeSetCurveRadius' : [], + 'GeometryNodeSetCurveTilt' : [], - 'GeometryNodeSetCurveHandlePositions' : [("mode", "enum")], - 'GeometryNodeCurveSetHandles' : [("handle_type", "enum"), - ("mode", "enum")], + + 'GeometryNodeSetCurveHandlePositions' : [("mode", ST.ENUM)], + + 'GeometryNodeCurveSetHandles' : [("handle_type", ST.ENUM), + ("mode", ST.ENUM)], + 'GeometryNodeSetSplineCyclic' : [], + 'GeometryNodeSetSplineResolution' : [], - 'GeometryNodeCurveSplineType' : [("spline_type", "enum")], + + 'GeometryNodeCurveSplineType' : [("spline_type", ST.ENUM)], # Curve > Operations 'GeometryNodeCurveToMesh' : [], - 'GeometryNodeCurveToPoints' : [("mode", "enum")], + + 'GeometryNodeCurveToPoints' : [("mode", ST.ENUM)], + 'GeometryNodeDeformCurvesOnSurface' : [], - 'GeometryNodeFillCurve' : [("mode", "enum")], - 'GeometryNodeFilletCurve' : [("mode", "enum")], + + 'GeometryNodeFillCurve' : [("mode", ST.ENUM)], + + 'GeometryNodeFilletCurve' : [("mode", ST.ENUM)], + 'GeometryNodeInterpolateCurves' : [], - 'GeometryNodeResampleCurve' : [("mode", "enum")], + + 'GeometryNodeResampleCurve' : [("mode", ST.ENUM)], + 'GeometryNodeReverseCurve' : [], + 'GeometryNodeSubdivideCurve' : [], - 'GeometryNodeTrimCurve' : [("mode", "enum")], + + 'GeometryNodeTrimCurve' : [("mode", ST.ENUM)], # Curve > Primitives - 'GeometryNodeCurveArc' : [("mode", "enum")], - 'GeometryNodeCurvePrimitiveBezierSegment' : [("mode", "enum")], - 'GeometryNodeCurvePrimitiveCircle' : [("mode", "enum")], - 'GeometryNodeCurvePrimitiveLine' : [("mode", "enum")], + 'GeometryNodeCurveArc' : [("mode", ST.ENUM)], + + 'GeometryNodeCurvePrimitiveBezierSegment' : [("mode", ST.ENUM)], + + 'GeometryNodeCurvePrimitiveCircle' : [("mode", ST.ENUM)], + + 'GeometryNodeCurvePrimitiveLine' : [("mode", ST.ENUM)], + 'GeometryNodeCurveSpiral' : [], + 'GeometryNodeCurveQuadraticBezier' : [], - 'GeometryNodeCurvePrimitiveQuadrilateral' : [("mode", "enum")], + + 'GeometryNodeCurvePrimitiveQuadrilateral' : [("mode", ST.ENUM)], + 'GeometryNodeCurveStar' : [], # Curve > Topology 'GeometryNodeOffsetPointInCurve' : [], + 'GeometryNodeCurveOfPoint' : [], + 'GeometryNodePointsOfCurve' : [], # INSTANCES 'GeometryNodeInstanceOnPoints' : [], + 'GeometryNodeInstancesToPoints' : [], - 'GeometryNodeRealizeInstances' : [("legacy_behavior", "bool")], + + 'GeometryNodeRealizeInstances' : [("legacy_behavior", ST.BOOL)], + 'GeometryNodeRotateInstances' : [], + 'GeometryNodeScaleInstances' : [], + 'GeometryNodeTranslateInstances' : [], + 'GeometryNodeInputInstanceRotation' : [], + 'GeometryNodeInputInstanceScale' : [], # MESH # Mesh > Read 'GeometryNodeInputMeshEdgeAngle' : [], + 'GeometryNodeInputMeshEdgeNeighbors' : [], + 'GeometryNodeInputMeshEdgeVertices' : [], + 'GeometryNodeEdgesToFaceGroups' : [], + 'GeometryNodeInputMeshFaceArea' : [], + 'GeometryNodeInputMeshFaceNeighbors' : [], + 'GeometryNodeMeshFaceSetBoundaries' : [], + 'GeometryNodeInputMeshFaceIsPlanar' : [], + 'GeometryNodeInputShadeSmooth' : [], + 'GeometryNodeInputMeshIsland' : [], + 'GeometryNodeInputShortestEdgePaths' : [], + 'GeometryNodeInputMeshVertexNeighbors' : [], # Mesh > Sample - 'GeometryNodeSampleNearestSurface' : [("data_type", "enum")], - 'GeometryNodeSampleUVSurface' : [("data_type", "enum")], + 'GeometryNodeSampleNearestSurface' : [("data_type", ST.ENUM)], + + 'GeometryNodeSampleUVSurface' : [("data_type", ST.ENUM)], # Mesh > Write 'GeometryNodeSetShadeSmooth' : [], # Mesh > Operations 'GeometryNodeDualMesh' : [], + 'GeometryNodeEdgePathsToCurves' : [], + 'GeometryNodeEdgePathsToSelection' : [], - 'GeometryNodeExtrudeMesh' : [("mode", "enum")], + + 'GeometryNodeExtrudeMesh' : [("mode", ST.ENUM)], + 'GeometryNodeFlipFaces' : [], - 'GeometryNodeMeshBoolean' : [("operation", "enum")], + + 'GeometryNodeMeshBoolean' : [("operation", ST.ENUM)], + 'GeometryNodeMeshToCurve' : [], - 'GeometryNodeMeshToPoints' : [("mode", "enum")], - 'GeometryNodeMeshToVolume' : [("resolution_mode", "enum")], - 'GeometryNodeScaleElements' : [("domain", "enum"), - ("scale_mode", "enum")], + + 'GeometryNodeMeshToPoints' : [("mode", ST.ENUM)], + + 'GeometryNodeMeshToVolume' : [("resolution_mode", ST.ENUM)], + + 'GeometryNodeScaleElements' : [("domain", ST.ENUM), + ("scale_mode", ST.ENUM)], + 'GeometryNodeSplitEdges' : [], + 'GeometryNodeSubdivideMesh' : [], - 'GeometryNodeSubdivisionSurface' : [("boundary_smooth", "enum"), - ("uv_smooth", "enum")], - 'GeometryNodeTriangulate' : [("ngon_method", "enum"), - ("quad_method", "enum")], + + 'GeometryNodeSubdivisionSurface' : [("boundary_smooth", ST.ENUM), + ("uv_smooth", ST.ENUM)], + + 'GeometryNodeTriangulate' : [("ngon_method", ST.ENUM), + ("quad_method", ST.ENUM)], # Mesh > Primitives - 'GeometryNodeMeshCone' : [("fill_type", "enum")], + 'GeometryNodeMeshCone' : [("fill_type", ST.ENUM)], + 'GeometryNodeMeshCube' : [], - 'GeometryNodeMeshCylinder' : [("fill_type", "enum")], + + 'GeometryNodeMeshCylinder' : [("fill_type", ST.ENUM)], + 'GeometryNodeMeshGrid' : [], + 'GeometryNodeMeshIcoSphere' : [], - 'GeometryNodeMeshCircle' : [("fill_type", "enum")], - 'GeometryNodeMeshLine' : [("count_mode", "enum"), - ("mode", "enum")], + + 'GeometryNodeMeshCircle' : [("fill_type", ST.ENUM)], + + 'GeometryNodeMeshLine' : [("count_mode", ST.ENUM), + ("mode", ST.ENUM)], + 'GeometryNodeMeshUVSphere' : [], # Mesh > Topology 'GeometryNodeCornersOfFace' : [], + 'GeometryNodeCornersOfVertex' : [], + 'GeometryNodeEdgesOfCorner' : [], + 'GeometryNodeEdgesOfVertex' : [], + 'GeometryNodeFaceOfCorner' : [], + 'GeometryNodeOffsetCornerInFace' : [], + 'GeometryNodeVertexOfCorner' : [], # Mesh > UV 'GeometryNodeUVPackIslands' : [], - 'GeometryNodeUVUnwrap' : [("method", "enum")], + + 'GeometryNodeUVUnwrap' : [("method", ST.ENUM)], # POINT - 'GeometryNodeDistributePointsInVolume' : [("mode", "enum")], - 'GeometryNodeDistributePointsOnFaces' : [("distribute_method", "enum"), - ("use_legacy_normal", "bool")], + 'GeometryNodeDistributePointsInVolume' : [("mode", ST.ENUM)], + + 'GeometryNodeDistributePointsOnFaces' : [("distribute_method", ST.ENUM), + ("use_legacy_normal", ST.BOOL)], + 'GeometryNodePoints' : [], + 'GeometryNodePointsToVertices' : [], - 'GeometryNodePointsToVolume' : [("resolution_mode", "enum")], + + 'GeometryNodePointsToVolume' : [("resolution_mode", ST.ENUM)], + 'GeometryNodeSetPointRadius' : [], # VOLUME 'GeometryNodeVolumeCube' : [], - 'GeometryNodeVolumeToMesh' : [("resolution_mode", "enum")], + + 'GeometryNodeVolumeToMesh' : [("resolution_mode", ST.ENUM)], # SIMULATION 'GeometryNodeSimulationInput' : [], + 'GeometryNodeSimulationOutput' : [], # MATERIAL 'GeometryNodeReplaceMaterial' : [], + 'GeometryNodeInputMaterialIndex' : [], + 'GeometryNodeMaterialSelection' : [], + 'GeometryNodeSetMaterial' : [], + 'GeometryNodeSetMaterialIndex' : [], # TEXTURE - 'ShaderNodeTexBrick' : [("offset", "float"), - ("offset_frequency", "int"), - ("squash", "float"), - ("squash_frequency", "int")], + 'ShaderNodeTexBrick' : [("offset", ST.FLOAT), + ("offset_frequency", ST.INT), + ("squash", ST.FLOAT), + ("squash_frequency", ST.INT)], + 'ShaderNodeTexChecker' : [], - 'ShaderNodeTexGradient' : [("gradient_type", "enum")], - 'GeometryNodeImageTexture' : [("extension", "enum"), - ("interpolation", "enum")], - 'ShaderNodeTexMagic' : [("turbulence_depth", "int")], - 'ShaderNodeTexMusgrave' : [("musgrave_dimensions", "enum"), - ("musgrave_type", "enum")], - 'ShaderNodeTexNoise' : [("noise_dimensions", "enum")], - 'ShaderNodeTexVoronoi' : [("distance", "enum"), - ("feature", "enum"), - ("voronoi_dimensions", "enum")], - 'ShaderNodeTexWave' : [("bands_direction", "enum"), - ("rings_direction", "enum"), - ("wave_profile", "enum"), - ("wave_type", "enum")], - 'ShaderNodeTexWhiteNoise' : [("noise_dimensions", "enum")], + + 'ShaderNodeTexGradient' : [("gradient_type", ST.ENUM)], + + 'GeometryNodeImageTexture' : [("extension", ST.ENUM), + ("interpolation", ST.ENUM)], + + 'ShaderNodeTexMagic' : [("turbulence_depth", ST.INT)], + + 'ShaderNodeTexMusgrave' : [("musgrave_dimensions", ST.ENUM), + ("musgrave_type", ST.ENUM)], + + 'ShaderNodeTexNoise' : [("noise_dimensions", ST.ENUM)], + + 'ShaderNodeTexVoronoi' : [("distance", ST.ENUM), + ("feature", ST.ENUM), + ("voronoi_dimensions", ST.ENUM)], + + 'ShaderNodeTexWave' : [("bands_direction", ST.ENUM), + ("rings_direction", ST.ENUM), + ("wave_profile", ST.ENUM), + ("wave_type", ST.ENUM)], + + 'ShaderNodeTexWhiteNoise' : [("noise_dimensions", ST.ENUM)], # UTILITIES - 'ShaderNodeMix' : [("blend_type", "enum"), - ("clamp_factor", "bool"), - ("clamp_result", "bool"), - ("data_type", "enum"), - ("factor_mode", "enum")], - 'FunctionNodeRandomValue' : [("data_type", "enum")], - 'GeometryNodeSwitch' : [("input_type", "enum")], + 'ShaderNodeMix' : [("blend_type", ST.ENUM), + ("clamp_factor", ST.BOOL), + ("clamp_result", ST.BOOL), + ("data_type", ST.ENUM), + ("factor_mode", ST.ENUM)], + + 'FunctionNodeRandomValue' : [("data_type", ST.ENUM)], + + 'GeometryNodeSwitch' : [("input_type", ST.ENUM)], # Utilities > Color - 'ShaderNodeValToRGB' : [("color_ramp", "ColorRamp")], - 'ShaderNodeRGBCurve' : [("mapping", "CurveMapping")], - 'FunctionNodeCombineColor' : [("mode", "enum")], - 'ShaderNodeMixRGB' : [("blend_type", "enum"), - ("use_alpha", "bool"), - ("use_clamp", "bool")], #legacy - 'FunctionNodeSeparateColor' : [("mode", "enum")], + 'ShaderNodeValToRGB' : [("color_ramp", ST.COLOR_RAMP)], + + 'ShaderNodeRGBCurve' : [("mapping", ST.CURVE_MAPPING)], + + 'FunctionNodeCombineColor' : [("mode", ST.ENUM)], + + 'ShaderNodeMixRGB' : [("blend_type", ST.ENUM), + ("use_alpha", ST.BOOL), + ("use_clamp", ST.BOOL)], #legacy + + 'FunctionNodeSeparateColor' : [("mode", ST.ENUM)], # Utilities > Text 'GeometryNodeStringJoin' : [], + 'FunctionNodeReplaceString' : [], + 'FunctionNodeSliceString' : [], + 'FunctionNodeStringLength' : [], - 'GeometryNodeStringToCurves' : [("align_x", "enum"), - ("align_y", "enum"), - ("font", "Font"), #TODO: font - ("overflow", "enum"), - ("pivot_mode", "enum")], + + 'GeometryNodeStringToCurves' : [("align_x", ST.ENUM), + ("align_y", ST.ENUM), + ("font", ST.FONT), + ("overflow", ST.ENUM), + ("pivot_mode", ST.ENUM)], + 'FunctionNodeValueToString' : [], + 'FunctionNodeInputSpecialCharacters' : [], # Utilities > Vector - 'ShaderNodeVectorCurve' : [("mapping", "CurveMapping")], - 'ShaderNodeVectorMath' : [("operation", "enum")], - 'ShaderNodeVectorRotate' : [("invert", "bool"), - ("rotation_type", "enum")], + 'ShaderNodeVectorCurve' : [("mapping", ST.CURVE_MAPPING)], + + 'ShaderNodeVectorMath' : [("operation", ST.ENUM)], + + 'ShaderNodeVectorRotate' : [("invert", ST.BOOL), + ("rotation_type", ST.ENUM)], + 'ShaderNodeCombineXYZ' : [], + 'ShaderNodeSeparateXYZ' : [], # Utilities > Field - 'GeometryNodeAccumulateField' : [("data_type", "enum"), - ("domain", "enum")], - 'GeometryNodeFieldAtIndex' : [("data_type", "enum"), - ("domain", "enum")], - 'GeometryNodeFieldOnDomain' : [("data_type", "enum"), - ("domain", "enum")], + 'GeometryNodeAccumulateField' : [("data_type", ST.ENUM), + ("domain", ST.ENUM)], + + 'GeometryNodeFieldAtIndex' : [("data_type", ST.ENUM), + ("domain", ST.ENUM)], + + 'GeometryNodeFieldOnDomain' : [("data_type", ST.ENUM), + ("domain", ST.ENUM)], # Utilities > Math - 'FunctionNodeBooleanMath' : [("operation", "enum")], - 'ShaderNodeClamp' : [("clamp_type", "enum")], - 'FunctionNodeCompare' : [("data_type", "enum"), - ("mode", "enum"), - ("operation", "enum")], - 'ShaderNodeFloatCurve' : [("mapping", "CurveMapping")], - 'FunctionNodeFloatToInt' : [("rounding_mode", "enum")], - 'ShaderNodeMapRange' : [("clamp", "bool"), - ("data_type", "enum"), - ("interpolation_type", "enum")], - 'ShaderNodeMath' : [("operation", "enum"), - ("use_clamp", "bool")], + 'FunctionNodeBooleanMath' : [("operation", ST.ENUM)], + + 'ShaderNodeClamp' : [("clamp_type", ST.ENUM)], + + 'FunctionNodeCompare' : [("data_type", ST.ENUM), + ("mode", ST.ENUM), + ("operation", ST.ENUM)], + + 'ShaderNodeFloatCurve' : [("mapping", ST.CURVE_MAPPING)], + + 'FunctionNodeFloatToInt' : [("rounding_mode", ST.ENUM)], + + 'ShaderNodeMapRange' : [("clamp", ST.BOOL), + ("data_type", ST.ENUM), + ("interpolation_type", ST.ENUM)], + + 'ShaderNodeMath' : [("operation", ST.ENUM), + ("use_clamp", ST.BOOL)], # Utilities > Rotation - 'FunctionNodeAlignEulerToVector' : [("axis", "enum"), - ("pivot_axis", "enum")], - 'FunctionNodeRotateEuler' : [("space", "enum"), - ("type", "enum")] + 'FunctionNodeAlignEulerToVector' : [("axis", ST.ENUM), + ("pivot_axis", ST.ENUM)], + + 'FunctionNodeRotateEuler' : [("space", ST.ENUM), + ("type", ST.ENUM)] } class NTPGeoNodesOperator(bpy.types.Operator): diff --git a/materials.py b/materials.py index a0d37f0..5bbdf2c 100644 --- a/materials.py +++ b/materials.py @@ -4,192 +4,277 @@ from .utils import * from io import StringIO -shader_node_settings : dict[str, list[(str, str)]] = { +#TODO: move to a json, different ones for each blender version? +shader_node_settings : dict[str, list[(str, ST)]] = { # INPUT - 'ShaderNodeAmbientOcclusion' : [("inside", "bool"), - ("only_local", "bool"), - ("samples", "int")], - 'ShaderNodeAttribute' : [("attribute_name", "str"), - ("attribute_type", "enum")], - 'ShaderNodeBevel' : [("samples", "int")], + 'ShaderNodeAmbientOcclusion' : [("inside", ST.BOOL), + ("only_local", ST.BOOL), + ("samples", ST.INT)], + + 'ShaderNodeAttribute' : [("attribute_name", ST.STRING), #TODO: separate attribute type? + ("attribute_type", ST.ENUM)], + + 'ShaderNodeBevel' : [("samples", ST.INT)], + 'ShaderNodeCameraData' : [], - 'ShaderNodeVertexColor' : [("layer_name", "str")], + + 'ShaderNodeVertexColor' : [("layer_name", ST.STRING)], #TODO: separate color attribute type? + 'ShaderNodeHairInfo' : [], + 'ShaderNodeFresnel' : [], + 'ShaderNodeNewGeometry' : [], + 'ShaderNodeLayerWeight' : [], + 'ShaderNodeLightPath' : [], + 'ShaderNodeObjectInfo' : [], + 'ShaderNodeParticleInfo' : [], + 'ShaderNodePointInfo' : [], + 'ShaderNodeRGB' : [], - 'ShaderNodeTangent' : [("axis", "enum"), - ("direction_type", "enum"), - ("uv_map", "str")], #TODO: makes sense? maybe make special type - 'ShaderNodeTexCoord' : [("from_instancer", "bool"), - ("object", "Object")], - 'ShaderNodeUVAlongStroke' : [("use_tips", "bool")], - 'ShaderNodeUVMap' : [("from_instancer", "bool"), - ("uv_map", "str")], #TODO: see ShaderNodeTangent + + 'ShaderNodeTangent' : [("axis", ST.ENUM), + ("direction_type", ST.ENUM), + ("uv_map", ST.STRING)], #TODO: special UV Map type? + + 'ShaderNodeTexCoord' : [("from_instancer", ST.BOOL), + ("object", ST.OBJECT)], + + 'ShaderNodeUVAlongStroke' : [("use_tips", ST.BOOL)], + + 'ShaderNodeUVMap' : [("from_instancer", ST.BOOL), + ("uv_map", ST.STRING)], #TODO: see ShaderNodeTangent + 'ShaderNodeValue' : [], + 'ShaderNodeVolumeInfo' : [], - 'ShaderNodeWireframe' : [("use_pixel_size", "bool")], + + 'ShaderNodeWireframe' : [("use_pixel_size", ST.BOOL)], # OUTPUT - 'ShaderNodeOutputAOV' : [("name", "str")], - 'ShaderNodeOutputLight' : [("is_active_output", "bool"), - ("target", "enum")], - 'ShaderNodeOutputLineStyle' : [("blend_type", "enum"), - ("is_active_output", "bool"), - ("target", "enum"), - ("use_alpha", "bool"), - ("use_clamp", "bool")], - 'ShaderNodeOutputMaterial' : [("is_active_output", "bool"), - ("target", "enum")], - 'ShaderNodeOutputWorld' : [("is_active_output", "bool"), - ("target", "enum")], + 'ShaderNodeOutputAOV' : [("name", ST.STRING)], + + 'ShaderNodeOutputLight' : [("is_active_output", ST.BOOL), + ("target", ST.ENUM)], + + 'ShaderNodeOutputLineStyle' : [("blend_type", ST.ENUM), + ("is_active_output", ST.BOOL), + ("target", ST.ENUM), + ("use_alpha", ST.BOOL), + ("use_clamp", ST.BOOL)], + + 'ShaderNodeOutputMaterial' : [("is_active_output", ST.BOOL), + ("target", ST.ENUM)], + + 'ShaderNodeOutputWorld' : [("is_active_output", ST.BOOL), + ("target", ST.ENUM)], # SHADER 'ShaderNodeAddShader' : [], - 'ShaderNodeBsdfAnisotropic' : [("distribution", "enum")], + + 'ShaderNodeBsdfAnisotropic' : [("distribution", ST.ENUM)], + 'ShaderNodeBackground' : [], + 'ShaderNodeBsdfDiffuse' : [], + 'ShaderNodeEmission' : [], - 'ShaderNodeBsdfGlass' : [("distribution", "enum")], - 'ShaderNodeBsdfGlossy' : [("distribution", "enum")], - 'ShaderNodeBsdfHair' : [("component", "enum")], + + 'ShaderNodeBsdfGlass' : [("distribution", ST.ENUM)], + + 'ShaderNodeBsdfGlossy' : [("distribution", ST.ENUM)], + + 'ShaderNodeBsdfHair' : [("component", ST.ENUM)], + 'ShaderNodeHoldout' : [], + 'ShaderNodeMixShader' : [], - 'ShaderNodeBsdfPrincipled' : [("distribution", "enum"), - ("subsurface_method", "enum")], - 'ShaderNodeBsdfHairPrincipled' : [("parametrization", "enum")], + + 'ShaderNodeBsdfPrincipled' : [("distribution", ST.ENUM), + ("subsurface_method", ST.ENUM)], + + 'ShaderNodeBsdfHairPrincipled' : [("parametrization", ST.ENUM)], + 'ShaderNodeVolumePrincipled' : [], - 'ShaderNodeBsdfRefraction' : [("distribution", "enum")], + + 'ShaderNodeBsdfRefraction' : [("distribution", ST.ENUM)], + 'ShaderNodeEeveeSpecular' : [], - 'ShaderNodeSubsurfaceScattering' : [("falloff", "enum")], - 'ShaderNodeBsdfToon' : [("component", "enum")], + + 'ShaderNodeSubsurfaceScattering' : [("falloff", ST.ENUM)], + + 'ShaderNodeBsdfToon' : [("component", ST.ENUM)], + 'ShaderNodeBsdfTranslucent' : [], + 'ShaderNodeBsdfTransparent' : [], + 'ShaderNodeBsdfVelvet' : [], + 'ShaderNodeVolumeAbsorption' : [], + 'ShaderNodeVolumeScatter' : [], # TEXTURE - 'ShaderNodeTexBrick' : [("offset", "float"), - ("offset_frequency", "int"), - ("squash", "float"), - ("squash_frequency", "int")], + 'ShaderNodeTexBrick' : [("offset", ST.FLOAT), + ("offset_frequency", ST.INT), + ("squash", ST.FLOAT), + ("squash_frequency", ST.INT)], + 'ShaderNodeTexChecker' : [], - 'ShaderNodeTexEnvironment' : [("image", "Image"), - ("image_user", "ImageUser"), - ("interpolation", "enum"), - ("projection", "enum")], - 'ShaderNodeTexGradient' : [("gradient_type", "enum")], - 'ShaderNodeTexIES' : [("filepath", "str"), #TODO - ("ies", "Text"), - ("mode", "enum")], - 'ShaderNodeTexImage' : [("extension", "enum"), - ("image", "Image"), - ("image_user", "ImageUser"), - ("interpolation", "enum"), - ("projection", "enum"), - ("projection_blend", "float")], - 'ShaderNodeTexMagic' : [("turbulence_depth", "int")], - 'ShaderNodeTexMusgrave' : [("musgrave_dimensions", "enum"), - ("musgrave_type", "enum")], - 'ShaderNodeTexNoise' : [("noise_dimensions", "enum")], - 'ShaderNodeTexPointDensity' : [("interpolation", "enum"), - ("object", "Object"), - ("particle_color_source", "enum"), - ("particle_system", "ParticleSystem"), - ("point_source", "enum"), - ("radius", "float"), - ("resolution", "int"), - ("space", "enum"), - ("vertex_attribute_name", "str"), #TODO - ("vertex_color_source", "enum")], - 'ShaderNodeTexSky' : [("air_density", "float"), - ("altitude", "float"), - ("dust_density", "float"), - ("ground_albedo", "float"), - ("ozone_density", "float"), - ("sky_type", "enum"), - ("sun_direction", "Vec3"), - ("sun_disc", "bool"), - ("sun_elevation", "float"), - ("sun_intensity", "float"), - ("sun_rotation", "float"), - ("sun_size", "float") - ("turbidity", "float")], - 'ShaderNodeTexVoronoi' : [("distance", "enum"), - ("feature", "enum"), - ("voronoi_dimensions", "enum")], - 'ShaderNodeTexWave' : [("bands_direction", "enum"), - ("rings_direction", "enum"), - ("wave_profile", "enum"), - ("wave_type", "enum")], - 'ShaderNodeTexWhiteNoise' : [("noise_dimensions", "enum")], + + 'ShaderNodeTexEnvironment' : [("image", ST.IMAGE), + ("image_user", ST.IMAGE_USER), + ("interpolation", ST.ENUM), + ("projection", ST.ENUM)], + + 'ShaderNodeTexGradient' : [("gradient_type", ST.ENUM)], + + 'ShaderNodeTexIES' : [("filepath", ST.STRING), #TODO + ("ies", ST.TEXT), + ("mode", ST.ENUM)], + + 'ShaderNodeTexImage' : [("extension", ST.ENUM), + ("image", ST.IMAGE), + ("image_user", ST.IMAGE_USER), + ("interpolation", ST.ENUM), + ("projection", ST.ENUM), + ("projection_blend", ST.FLOAT)], + + 'ShaderNodeTexMagic' : [("turbulence_depth", ST.INT)], + + 'ShaderNodeTexMusgrave' : [("musgrave_dimensions", ST.ENUM), + ("musgrave_type", ST.ENUM)], + + 'ShaderNodeTexNoise' : [("noise_dimensions", ST.ENUM)], + + 'ShaderNodeTexPointDensity' : [("interpolation", ST.ENUM), + ("object", ST.OBJECT), + ("particle_color_source", ST.ENUM), + ("particle_system", ST.PARTICLE_SYSTEM), + ("point_source", ST.ENUM), + ("radius", ST.FLOAT), + ("resolution", ST.INT), + ("space", ST.ENUM), + ("vertex_attribute_name", ST.STRING), #TODO + ("vertex_color_source", ST.ENUM)], + + 'ShaderNodeTexSky' : [("air_density", ST.FLOAT), + ("altitude", ST.FLOAT), + ("dust_density", ST.FLOAT), + ("ground_albedo", ST.FLOAT), + ("ozone_density", ST.FLOAT), + ("sky_type", ST.ENUM), + ("sun_direction", ST.VEC3), + ("sun_disc", ST.BOOL), + ("sun_elevation", ST.FLOAT), + ("sun_intensity", ST.FLOAT), + ("sun_rotation", ST.FLOAT), + ("sun_size", ST.FLOAT) + ("turbidity", ST.FLOAT)], + + 'ShaderNodeTexVoronoi' : [("distance", ST.ENUM), + ("feature", ST.ENUM), + ("voronoi_dimensions", ST.ENUM)], + + 'ShaderNodeTexWave' : [("bands_direction", ST.ENUM), + ("rings_direction", ST.ENUM), + ("wave_profile", ST.ENUM), + ("wave_type", ST.ENUM)], + + 'ShaderNodeTexWhiteNoise' : [("noise_dimensions", ST.ENUM)], # COLOR 'ShaderNodeBrightContrast' : [], + 'ShaderNodeGamma' : [], + 'ShaderNodeHueSaturation' : [], + 'ShaderNodeInvert' : [], + 'ShaderNodeLightFalloff' : [], - 'ShaderNodeMix' : [("blend_type", "enum"), - ("clamp_factor", "bool"), - ("clamp_result", "bool"), - ("data_type", "enum"), - ("factor_mode", "enum")], - 'ShaderNodeRGBCurve' : [("mapping", "CurveMapping")], + + 'ShaderNodeMix' : [("blend_type", ST.ENUM), + ("clamp_factor", ST.BOOL), + ("clamp_result", ST.BOOL), + ("data_type", ST.ENUM), + ("factor_mode", ST.ENUM)], + + 'ShaderNodeRGBCurve' : [("mapping", ST.CURVE_MAPPING)], # VECTOR - 'ShaderNodeBump' : [("invert", "bool")], - 'ShaderNodeDisplacement' : [("space", "enum")], - 'ShaderNodeMapping' : [("vector_type", "enum")], - 'ShaderNodeNormalMap' : [("space", "enum"), - ("uv_map", "str")], #TODO - 'ShaderNodeVectorCurve' : [("mapping", "CurveMapping")], - 'ShaderNodeVectorDisplacement' : [("space", "enum")], - 'ShaderNodeVectorRotate' : [("invert", "bool"), - ("rotation_type", "enum")], - 'ShaderNodeVectorTransform' : [("convert_from", "enum"), - ("convert_to", "enum"), - ("vector_type", "enum")], + 'ShaderNodeBump' : [("invert", ST.BOOL)], + + 'ShaderNodeDisplacement' : [("space", ST.ENUM)], + + 'ShaderNodeMapping' : [("vector_type", ST.ENUM)], + + 'ShaderNodeNormalMap' : [("space", ST.ENUM), + ("uv_map", ST.STRING)], #TODO + + 'ShaderNodeVectorCurve' : [("mapping", ST.CURVE_MAPPING)], + + 'ShaderNodeVectorDisplacement' : [("space", ST.ENUM)], + + 'ShaderNodeVectorRotate' : [("invert", ST.BOOL), + ("rotation_type", ST.ENUM)], + + 'ShaderNodeVectorTransform' : [("convert_from", ST.ENUM), + ("convert_to", ST.ENUM), + ("vector_type", ST.ENUM)], # CONVERTER 'ShaderNodeBlackbody' : [], - 'ShaderNodeClamp' : [("clamp_type", "enum")], - 'ShaderNodeValToRGB' : [("color_ramp", "ColorRamp")], - 'ShaderNodeCombineColor' : [("mode", "enum")], + + 'ShaderNodeClamp' : [("clamp_type", ST.ENUM)], + + 'ShaderNodeValToRGB' : [("color_ramp", ST.COLOR_RAMP)], + + 'ShaderNodeCombineColor' : [("mode", ST.ENUM)], + 'ShaderNodeCombineXYZ' : [], - 'ShaderNodeFloatCurve' : [("mapping", "CurveMapping")], - 'ShaderNodeMapRange' : [("clamp", "bool"), - ("data_type", "enum"), - ("interpolation_type", "enum")], - 'ShaderNodeMath' : [("operation", "enum"), - ("use_clamp", "bool")], + + 'ShaderNodeFloatCurve' : [("mapping", ST.CURVE_MAPPING)], + + 'ShaderNodeMapRange' : [("clamp", ST.BOOL), + ("data_type", ST.ENUM), + ("interpolation_type", ST.ENUM)], + + 'ShaderNodeMath' : [("operation", ST.ENUM), + ("use_clamp", ST.BOOL)], + 'ShaderNodeRGBToBW' : [], - 'ShaderNodeSeparateColor' : [("mode", "enum")], + + 'ShaderNodeSeparateColor' : [("mode", ST.ENUM)], + 'ShaderNodeSeparateXYZ' : [], + 'ShaderNodeShaderToRGB' : [], - 'ShaderNodeVectorMath' : [("operation", "enum")], + + 'ShaderNodeVectorMath' : [("operation", ST.ENUM)], + 'ShaderNodeWavelength' : [], # SCRIPT - 'ShaderNodeScript' : [("bytecode", "str"), #TODO: test all that - ("bytecode_hash", "str"), - ("filepath", "str"), - ("mode", "enum"), - ("script", "text"), - ("use_auto_update", "bool")] + 'ShaderNodeScript' : [("bytecode", ST.STRING), #TODO: test all that + ("bytecode_hash", ST.STRING), + ("filepath", ST.STRING), + ("mode", ST.ENUM), + ("script", ST.TEXT), + ("use_auto_update", ST.BOOL)] } curve_nodes = {'ShaderNodeFloatCurve', diff --git a/utils.py b/utils.py index 6d09ec0..372daae 100644 --- a/utils.py +++ b/utils.py @@ -1,6 +1,7 @@ import bpy import mathutils +from enum import Enum, auto import os import re import shutil @@ -13,6 +14,37 @@ 'NodeSocketShader', 'NodeSocketVirtual'} +class ST(Enum): + """ + Socket and Settings Types + """ + ENUM = auto() + STRING = auto() + BOOL = auto() + INT = auto() + FLOAT = auto() + VEC1 = auto() + VEC2 = auto() + VEC3 = auto() + VEC4 = auto() + MATERIAL = auto() #Could use a look + OBJECT = auto() #Could take a looking at + IMAGE = auto() #needs refactor + IMAGE_USER = auto() #unimplemented + MOVIE_CLIP = auto() #unimplmented + COLOR_RAMP = auto() #needs refactor + CURVE_MAPPING = auto() #needs refactor + TEXTURE = auto() #unimplemented + TEXT = auto() #unimplemented + SCENE = auto() #unimplemented + PARTICLE_SYSTEM = auto() #unimplemented + FONT = auto() #unimplemented + MASK = auto() #unimplemented + CRYPTOMATTE_ENTRIES = auto() #unimplemented + IMAGE_FORMAT_SETTINGS = auto() + FILE_SLOTS = auto() + LAYER_SLOTS = auto() #unimplemented + #node tree input sockets that have default properties default_sockets = {'VALUE', 'INT', 'BOOLEAN', 'VECTOR', 'RGBA'} @@ -56,29 +88,53 @@ def str_to_py_str(string: str) -> str: """ return f"\"{string}\"" -def vec3_to_py_str(vec) -> str: +def vec1_to_py_str(vec1) -> str: + """ + Converts a 1D vector to a string usable by the add-on + + Parameters: + vec1: a 1d vector + + Returns: + (str): string representation of the vector + """ + return f"({vec1[0]})" + +def vec2_to_py_str(vec2) -> str: + """ + Converts a 2D vector to a string usable by the add-on + + Parameters: + vec2: a 2D vector + + Returns: + (str): string representation of the vector + """ + return f"({vec2[0]}, {vec2[1]})" + +def vec3_to_py_str(vec3) -> str: """ Converts a 3D vector to a string usable by the add-on Parameters: - vec (mathutils.Vector): a 3d vector + vec3: a 3d vector Returns: - (str): string version + (str): string representation of the vector """ - return f"({vec[0]}, {vec[1]}, {vec[2]})" + return f"({vec3[0]}, {vec3[1]}, {vec3[2]})" -def vec4_to_py_str(vec) -> str: +def vec4_to_py_str(vec4) -> str: """ Converts a 4D vector to a string usable by the add-on Parameters: - vec (mathutils.Vector): a 4d vector + vec4: a 4d vector Returns: (str): string version """ - return f"({vec[0]}, {vec[1]}, {vec[2]}, {vec[3]})" + return f"({vec4[0]}, {vec4[1]}, {vec4[2]}, {vec4[3]})" def img_to_py_str(img) -> str: """ @@ -94,13 +150,6 @@ def img_to_py_str(img) -> str: format = img.file_format.lower() return f"{name}.{format}" -type_to_py_str : dict[str, function] = { - "enum" : enum_to_py_str, - "str" : str_to_py_str, - "vec3" : vec3_to_py_str, - "vec4" : vec4_to_py_str -} - def create_header(file: TextIO, name: str): """ Sets up the bl_info and imports the Blender API @@ -220,7 +269,7 @@ def create_node(node, file: TextIO, inner: str, node_tree_var: str, return node_var -def set_settings_defaults(node, settings: dict, file: TextIO, inner: str, +def set_settings_defaults(node, settings: dict[str, list[(str, str)]], file: TextIO, inner: str, node_var: str): """ Sets the defaults for any settings a node may have @@ -233,9 +282,16 @@ def set_settings_defaults(node, settings: dict, file: TextIO, inner: str, node_var (str): name of the variable we're using for the node in our add-on """ if node.bl_idname in settings: - for setting in settings[node.bl_idname]: + for (setting, type) in settings[node.bl_idname]: attr = getattr(node, setting, None) - if attr: + if not attr: + continue + if type == "enum": + file.write(f"{inner}{node_var}.{setting} = {enum_to_py_str(attr)}\n") + elif type == "str": + file.write(f"{inner}{node_var}.{setting} = {str_to_py_str(attr)}\n") + elif type == "int": + file.write(f"{inner}{node_var}.{setting} = {attr}\n") if type(attr) == str: attr = enum_to_py_str(attr) if type(attr) == mathutils.Vector: @@ -552,7 +608,7 @@ def set_output_defaults(node, file: TextIO, inner: str, node_var: str): 'ShaderNodeNormal'} if node.bl_idname in output_default_nodes: - dv = node.outputs[0].default_value + dv = node.outputs[0].default_value #TODO: see if this is still the case if node.bl_idname == 'ShaderNodeRGB': dv = vec4_to_py_str(list(dv)) if node.bl_idname == 'ShaderNodeNormal': @@ -641,6 +697,7 @@ def init_links(node_tree, file: TextIO, inner: str, node_tree_var: str, gnashing of teeth. This is a quick fix that doesn't run quick """ + #TODO: try using index() method for i, item in enumerate(link.from_node.outputs.items()): if item[1] == input_socket: input_idx = i From f1d85b66d3a6d7c4c8527e10f691e6589a2853a8 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 26 Aug 2023 17:37:23 -0500 Subject: [PATCH 08/21] refactor: changed set_settings_default() to use new types --- utils.py | 53 ++++++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/utils.py b/utils.py index 372daae..8d1d945 100644 --- a/utils.py +++ b/utils.py @@ -269,7 +269,7 @@ def create_node(node, file: TextIO, inner: str, node_tree_var: str, return node_var -def set_settings_defaults(node, settings: dict[str, list[(str, str)]], file: TextIO, inner: str, +def set_settings_defaults(node, settings: dict[str, list[(str, ST)]], file: TextIO, inner: str, node_var: str): """ Sets the defaults for any settings a node may have @@ -286,32 +286,31 @@ def set_settings_defaults(node, settings: dict[str, list[(str, str)]], file: Tex attr = getattr(node, setting, None) if not attr: continue - if type == "enum": - file.write(f"{inner}{node_var}.{setting} = {enum_to_py_str(attr)}\n") - elif type == "str": - file.write(f"{inner}{node_var}.{setting} = {str_to_py_str(attr)}\n") - elif type == "int": - file.write(f"{inner}{node_var}.{setting} = {attr}\n") - if type(attr) == str: - attr = enum_to_py_str(attr) - if type(attr) == mathutils.Vector: - attr = vec3_to_py_str(attr) - if type(attr) == bpy.types.bpy_prop_array: - attr = vec4_to_py_str(list(attr)) - if type(attr) == bpy.types.Material: - name = str_to_py_str(attr.name) - file.write((f"{inner}if {name} in bpy.data.materials:\n")) - file.write((f"{inner}\t{node_var}.{setting} = " - f"bpy.data.materials[{name}]\n")) - continue - if type(attr) == bpy.types.Object: - name = str_to_py_str(attr.name) - file.write((f"{inner}if {name} in bpy.data.objects:\n")) - file.write((f"{inner}\t{node_var}.{setting} = " - f"bpy.data.objects[{name}]\n")) - continue - file.write((f"{inner}{node_var}.{setting} " - f"= {attr}\n")) + setting_str = f"{inner}{node_var}.{setting}" + if type == ST.ENUM: + file.write(f"{setting_str} = {enum_to_py_str(attr)}\n") + elif type == ST.STRING: + file.write(f"{setting_str} = {str_to_py_str(attr)}\n") + elif type == ST.BOOL or type == ST.INT or type == ST.FLOAT: + file.write(f"{setting_str} = {attr}\n") + elif type == ST.VEC1: + file.write(f"{setting_str} = {vec1_to_py_str(attr)}\n") + elif type == ST.VEC2: + file.write(f"{setting_str} = {vec2_to_py_str(attr)}\n") + elif type == ST.VEC3: + file.write(f"{setting_str} = {vec3_to_py_str(attr)}\n") + elif type == ST.VEC4: + file.write(f"{setting_str} = {vec4_to_py_str(attr)}\n") + elif type == ST.MATERIAL: + name = str_to_py_str(attr.name) + file.write((f"{inner}if {name} in bpy.data.materials:\n")) + file.write((f"{inner}\t{node_var}.{setting} = " + f"bpy.data.materials[{name}]\n")) + elif type == ST.OBJECT: + name = str_to_py_str(attr.name) + file.write((f"{inner}if {name} in bpy.data.objects:\n")) + file.write((f"{inner}\t{node_var}.{setting} = " + f"bpy.data.objects[{name}]\n")) def hide_sockets(node, file: TextIO, inner: str, node_var: str): """ From c0eb85dcf70c01e3544679222022d67d5d6009ae Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 26 Aug 2023 17:58:57 -0500 Subject: [PATCH 09/21] style: better type hints for util functions --- utils.py | 150 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 35 deletions(-) diff --git a/utils.py b/utils.py index 8d1d945..5103827 100644 --- a/utils.py +++ b/utils.py @@ -16,7 +16,7 @@ class ST(Enum): """ - Socket and Settings Types + Settings Types """ ENUM = auto() STRING = auto() @@ -136,7 +136,7 @@ def vec4_to_py_str(vec4) -> str: """ return f"({vec4[0]}, {vec4[1]}, {vec4[2]}, {vec4[3]})" -def img_to_py_str(img) -> str: +def img_to_py_str(img : bpy.types.Image) -> str: """ Converts a Blender image into its string @@ -150,7 +150,7 @@ def img_to_py_str(img) -> str: format = img.file_format.lower() return f"{name}.{format}" -def create_header(file: TextIO, name: str): +def create_header(file: TextIO, name: str) -> None: """ Sets up the bl_info and imports the Blender API @@ -172,7 +172,7 @@ def create_header(file: TextIO, name: str): file.write("import os\n") file.write("\n") -def init_operator(file: TextIO, name: str, idname: str, label: str): +def init_operator(file: TextIO, name: str, idname: str, label: str) -> None: """ Initializes the add-on's operator @@ -229,8 +229,13 @@ def make_indents(level: int) -> Tuple[str, str]: inner = "\t"*(level + 1) return outer, inner -def create_node(node, file: TextIO, inner: str, node_tree_var: str, - node_vars: dict, used_vars: set) -> str: +def create_node(node: bpy.types.Node, + file: TextIO, + inner: str, + node_tree_var: str, + node_vars: dict[bpy.types.Node, str], + used_vars: set #necessary? + ) -> str: """ Initializes a new node with location, dimension, and label info @@ -239,8 +244,8 @@ def create_node(node, file: TextIO, inner: str, node_tree_var: str, file (TextIO): file containing the generated add-on inner (str): indentation level for this logic node_tree_var (str): variable name for the node tree - node_vars (dict): dictionary containing (bpy.types.Node, str) - pairs, with a Node and its corresponding variable name + node_vars (dict): dictionary containing Node to corresponding variable name + pairs used_vars (set): set of used variable names Returns: @@ -269,8 +274,12 @@ def create_node(node, file: TextIO, inner: str, node_tree_var: str, return node_var -def set_settings_defaults(node, settings: dict[str, list[(str, ST)]], file: TextIO, inner: str, - node_var: str): +def set_settings_defaults(node: bpy.types.Node, + settings: dict[str, list[(str, ST)]], + file: TextIO, + inner: str, + node_var: str + ) -> None: """ Sets the defaults for any settings a node may have @@ -312,7 +321,11 @@ def set_settings_defaults(node, settings: dict[str, list[(str, ST)]], file: Text file.write((f"{inner}\t{node_var}.{setting} = " f"bpy.data.objects[{name}]\n")) -def hide_sockets(node, file: TextIO, inner: str, node_var: str): +def hide_sockets(node: bpy.types.Node, + file: TextIO, + inner: str, + node_var: str + ) -> None: """ Hide hidden sockets @@ -329,7 +342,25 @@ def hide_sockets(node, file: TextIO, inner: str, node_var: str): if socket.hide is True: file.write(f"{inner}{node_var}.outputs[{i}].hide = True\n") -def group_io_settings(node, file: TextIO, inner: str, io: str, node_tree_var: str, node_tree): +def group_io_settings(node: bpy.types.Node, + file: TextIO, + inner: str, + io: str, #TODO: convert to enum + node_tree_var: str, + node_tree: bpy.types.NodeTree + ) -> None: + """ + Set the settings for group input and output sockets + + Parameters: + node (bpy.types.Node) : group input/output node + file (TextIO): file we're generating the add-on into + inner (str): indentation string + io (str): whether we're generating the input or output settings + node_tree_var (str): variable name of the generated node tree + node_tree (bpy.types.NodeTree): node tree that we're generating input + and output settings for + """ if io == "input": ios = node.outputs ntio = node_tree.inputs @@ -393,7 +424,11 @@ def group_io_settings(node, file: TextIO, inner: str, io: str, node_tree_var: st file.write("\n") file.write("\n") -def color_ramp_settings(node, file: TextIO, inner: str, node_var: str): +def color_ramp_settings(node: bpy.types.Node, + file: TextIO, + inner: str, + node_var: str + ) -> None: """ Replicate a color ramp node @@ -435,7 +470,11 @@ def color_ramp_settings(node, file: TextIO, inner: str, node_var: str): color_str = vec4_to_py_str(element.color) file.write((f"{inner}{element_var}.color = {color_str}\n\n")) -def curve_node_settings(node, file: TextIO, inner: str, node_var: str): +def curve_node_settings(node: bpy.types.Node, + file: TextIO, + inner: str, + node_var: str + ) -> None: """ Sets defaults for Float, Vector, and Color curves @@ -506,8 +545,12 @@ def curve_node_settings(node, file: TextIO, inner: str, node_var: str): file.write(f"{inner}#update curve after changes\n") file.write(f"{mapping_var}.update()\n") -def set_input_defaults(node, file: TextIO, inner: str, node_var: str, - addon_dir: str = ""): +def set_input_defaults(node: bpy.types.Node, + file: TextIO, + inner: str, + node_var: str, + addon_dir: str = "" + ) -> None: """ Sets defaults for input sockets @@ -574,7 +617,12 @@ def set_input_defaults(node, file: TextIO, inner: str, node_var: str, f" = {default_val}\n")) file.write("\n") -def in_file_inputs(input, file: TextIO, inner: str, socket_var: str, type: str): +def in_file_inputs(input: bpy.types.NodeSocket, + file: TextIO, + inner: str, + socket_var: str, + type: str + ) -> None: """ Sets inputs for a node input if one already exists in the blend file @@ -592,7 +640,11 @@ def in_file_inputs(input, file: TextIO, inner: str, socket_var: str, type: str): file.write((f"{inner}\t{socket_var}.default_value = " f"bpy.data.{type}[{name}]\n")) -def set_output_defaults(node, file: TextIO, inner: str, node_var: str): +def set_output_defaults(node: bpy.types.Node, + file: TextIO, + inner: str, + node_var: str + ) -> None: """ Some output sockets need default values set. It's rather annoying @@ -614,7 +666,11 @@ def set_output_defaults(node, file: TextIO, inner: str, node_var: str): dv = vec3_to_py_str(dv) file.write((f"{inner}{node_var}.outputs[0].default_value = {dv}\n")) -def set_parents(node_tree, file: TextIO, inner: str, node_vars: dict): +def set_parents(node_tree: bpy.types.NodeTree, + file: TextIO, + inner: str, + node_vars: dict[bpy.types.Node, str] + ) -> None: """ Sets parents for all nodes, mostly used to put nodes in frames @@ -622,7 +678,8 @@ def set_parents(node_tree, file: TextIO, inner: str, node_vars: dict): node_tree (bpy.types.NodeTree): node tree we're obtaining nodes from file (TextIO): file for the generated add-on inner (str): indentation string - node_vars (dict): dictionary for (node, variable) name pairs + node_vars (dict[bpy.types.Node, str]): dictionary for node->variable name + pairs """ parent_comment = False for node in node_tree.nodes: @@ -635,7 +692,11 @@ def set_parents(node_tree, file: TextIO, inner: str, node_vars: dict): file.write(f"{inner}{node_var}.parent = {parent_var}\n") file.write("\n") -def set_locations(node_tree, file: TextIO, inner: str, node_vars: dict): +def set_locations(node_tree: bpy.types.NodeTree, + file: TextIO, + inner: str, + node_vars: dict[bpy.types.Node, str] + ) -> None: """ Set locations for all nodes @@ -643,7 +704,8 @@ def set_locations(node_tree, file: TextIO, inner: str, node_vars: dict): node_tree (bpy.types.NodeTree): node tree we're obtaining nodes from file (TextIO): file for the generated add-on inner (str): indentation string - node_vars (dict): dictionary for (node, variable) name pairs + node_vars (dict[bpy.types.Node, str]): dictionary for (node, variable) name + pairs """ file.write(f"{inner}#Set locations\n") @@ -653,7 +715,11 @@ def set_locations(node_tree, file: TextIO, inner: str, node_vars: dict): f"= ({node.location.x}, {node.location.y})\n")) file.write("\n") -def set_dimensions(node_tree, file: TextIO, inner: str, node_vars: dict): +def set_dimensions(node_tree: bpy.types.NodeTree, + file: TextIO, + inner: str, + node_vars: dict[bpy.types.Node, str] + ) -> None: """ Set dimensions for all nodes @@ -661,7 +727,8 @@ def set_dimensions(node_tree, file: TextIO, inner: str, node_vars: dict): node_tree (bpy.types.NodeTree): node tree we're obtaining nodes from file (TextIO): file for the generated add-on inner (str): indentation string - node_vars (dict): dictionary for (node, variable) name pairs + node_vars (dict[bpy.types.Node, str]): dictionary for (node, variable) name + pairs """ file.write(f"{inner}#Set dimensions\n") @@ -671,8 +738,12 @@ def set_dimensions(node_tree, file: TextIO, inner: str, node_vars: dict): f"= {node.width}, {node.height}\n")) file.write("\n") -def init_links(node_tree, file: TextIO, inner: str, node_tree_var: str, - node_vars: dict): +def init_links(node_tree: bpy.types.NodeTree, + file: TextIO, + inner: str, + node_tree_var: str, + node_vars: dict[bpy.types.Node, str] + ) -> None: """ Create all the links between nodes @@ -681,7 +752,8 @@ def init_links(node_tree, file: TextIO, inner: str, node_tree_var: str, file (TextIO): file we're generating the add-on into inner (str): indentation node_tree_var (str): variable name we're using for the copied node tree - node_vars (dict): dictionary containing node to variable name pairs + node_vars (dict[bpy.types.Node, str]): dictionary containing node to + variable name pairs """ if node_tree.links: @@ -716,7 +788,7 @@ def init_links(node_tree, file: TextIO, inner: str, node_tree_var: str, f".outputs[{input_idx}], " f"{out_node_var}.inputs[{output_idx}])\n")) -def create_menu_func(file: TextIO, name: str): +def create_menu_func(file: TextIO, name: str) -> None: """ Creates the menu function @@ -729,7 +801,7 @@ def create_menu_func(file: TextIO, name: str): file.write(f"\tself.layout.operator({name}.bl_idname)\n") file.write("\n") -def create_register_func(file: TextIO, name: str): +def create_register_func(file: TextIO, name: str) -> None: """ Creates the register function @@ -742,7 +814,7 @@ def create_register_func(file: TextIO, name: str): file.write("\tbpy.types.VIEW3D_MT_object.append(menu_func)\n") file.write("\n") -def create_unregister_func(file: TextIO, name: str): +def create_unregister_func(file: TextIO, name: str) -> None: """ Creates the unregister function @@ -755,7 +827,7 @@ def create_unregister_func(file: TextIO, name: str): file.write("\tbpy.types.VIEW3D_MT_object.remove(menu_func)\n") file.write("\n") -def create_main_func(file: TextIO): +def create_main_func(file: TextIO) -> None: """ Creates the main function @@ -765,7 +837,7 @@ def create_main_func(file: TextIO): file.write("if __name__ == \"__main__\":\n") file.write("\tregister()") -def save_image(img, addon_dir: str): +def save_image(img: bpy.types.Image, addon_dir: str) -> None: """ Saves an image to an image directory of the add-on @@ -788,7 +860,11 @@ def save_image(img, addon_dir: str): if not os.path.exists(img_path): img.save_render(img_path) -def load_image(img, file: TextIO, inner: str, img_var: str): +def load_image(img: bpy.types.Image, + file: TextIO, + inner: str, + img_var: str + ) -> None: """ Loads an image from the add-on into a blend file and assigns it @@ -828,7 +904,11 @@ def load_image(img, file: TextIO, inner: str, img_var: str): alpha_mode = enum_to_py_str(img.alpha_mode) file.write(f"{inner}{img_var}.alpha_mode = {alpha_mode}\n") -def image_user_settings(node, file: TextIO, inner: str, node_var: str): +def image_user_settings(node: bpy.types.Node, + file: TextIO, + inner: str, + node_var: str + ) -> None: """ Replicate the image user of an image node @@ -852,7 +932,7 @@ def image_user_settings(node, file: TextIO, inner: str, node_var: str): file.write((f"{inner}{img_usr_var}.{img_usr_attr} = " f"{getattr(img_usr, img_usr_attr)}\n")) -def zip_addon(zip_dir: str): +def zip_addon(zip_dir: str) -> None: """ Zips up the addon and removes the directory From 38d3dfae81dba008d611f2d3c82c4423a9655ced Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sun, 27 Aug 2023 15:20:50 -0500 Subject: [PATCH 10/21] refactor: curve mappings and color ramps now handled with other node settings --- compositor.py | 12 +++--------- geo_nodes.py | 31 ++++++++++++------------------- materials.py | 14 ++------------ utils.py | 51 ++++++++++++++++++++++++++++++++++----------------- 4 files changed, 51 insertions(+), 57 deletions(-) diff --git a/compositor.py b/compositor.py index 9384b1b..beac3cc 100644 --- a/compositor.py +++ b/compositor.py @@ -56,7 +56,7 @@ ("frame_start", ST.INT)], 'CompositorNodeTrackPos' : [("clip", ST.MOVIE_CLIP), - ("frame_relative", ST.INT) + ("frame_relative", ST.INT), ("position", ST.ENUM), ("track_name", ST.STRING), #TODO: probably not right ("tracking_object", ST.STRING)], @@ -515,7 +515,7 @@ def is_outermost_node_group(level: int) -> bool: elif self.mode == 'SCRIPT' and level == 0: return True return False - """ + def process_comp_node_group(node_tree, level, node_vars, used_vars): if is_outermost_node_group(level): @@ -585,12 +585,6 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): # save_image(img, addon_dir) # load_image(img, file, inner, f"{node_var}.image") # image_user_settings(node, file, inner, node_var) - - elif node.bl_idname == 'CompositorNodeValToRGB': - color_ramp_settings(node, file, inner, node_var) - - elif node.bl_idname in curve_nodes: - curve_node_settings(node, file, inner, node_var) if self.mode == 'ADDON': set_input_defaults(node, file, inner, node_var, addon_dir) @@ -626,7 +620,7 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): if self.mode == 'ADDON': zip_addon(zip_dir) - """ + if self.mode == 'SCRIPT': location = "clipboard" else: diff --git a/geo_nodes.py b/geo_nodes.py index ee8c03a..50a4c58 100644 --- a/geo_nodes.py +++ b/geo_nodes.py @@ -135,7 +135,7 @@ 'GeometryNodeCurveEndpointSelection' : [], 'GeometryNodeCurveHandleTypeSelection' : [("handle_type", ST.ENUM), - ("mode", ST.ENUM)], + ("mode", ST.ENUM_SET)], 'GeometryNodeInputSplineCyclic' : [], @@ -160,7 +160,7 @@ 'GeometryNodeSetCurveHandlePositions' : [("mode", ST.ENUM)], 'GeometryNodeCurveSetHandles' : [("handle_type", ST.ENUM), - ("mode", ST.ENUM)], + ("mode", ST.ENUM_SET)], 'GeometryNodeSetSplineCyclic' : [], @@ -549,13 +549,13 @@ def execute(self, context): file = StringIO("") #set to keep track of already created node trees - node_trees = set() + node_trees: set[bpy.types.NodeTree] = set() #dictionary to keep track of node->variable name pairs - node_vars = {} + node_vars: dict[bpy.types.Node, str] = {} #dictionary to keep track of variables->usage count pairs - used_vars = {} + used_vars: dict[str, int] = {} def process_geo_nodes_group(node_tree, level, node_vars, used_vars): nt_var = create_var(node_tree.name, used_vars) @@ -608,19 +608,6 @@ def process_geo_nodes_group(node_tree, level, node_vars, used_vars): file.write((f"{inner}{node_var}.node_tree = " f"bpy.data.node_groups" f"[{str_to_py_str(node.node_tree.name)}]\n")) - - elif node.bl_idname == 'ShaderNodeValToRGB': - color_ramp_settings(node, file, inner, node_var) - - elif node.bl_idname in curve_nodes: - curve_node_settings(node, file, inner, node_var) - - elif node.bl_idname in image_nodes and self.mode == 'ADDON': - img = node.image - if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: - save_image(img, addon_dir) - load_image(img, file, inner, f"{node_var}.image") - elif node.bl_idname == 'GeometryNodeSimulationInput': sim_inputs.append(node) @@ -639,7 +626,13 @@ def process_geo_nodes_group(node_tree, level, node_vars, used_vars): attr_domain = enum_to_py_str(si.attribute_domain) file.write((f"{inner}{si_var}.attribute_domain = " f"{attr_domain}\n")) - + """ + elif node.bl_idname in image_nodes and self.mode == 'ADDON': + img = node.image + if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: + save_image(img, addon_dir) + load_image(img, file, inner, f"{node_var}.image") + """ if node.bl_idname != 'GeometryNodeSimulationInput': if self.mode == 'ADDON': set_input_defaults(node, file, inner, node_var, addon_dir) diff --git a/materials.py b/materials.py index 5bbdf2c..2cfab43 100644 --- a/materials.py +++ b/materials.py @@ -178,7 +178,7 @@ ("sun_elevation", ST.FLOAT), ("sun_intensity", ST.FLOAT), ("sun_rotation", ST.FLOAT), - ("sun_size", ST.FLOAT) + ("sun_size", ST.FLOAT), ("turbidity", ST.FLOAT)], 'ShaderNodeTexVoronoi' : [("distance", ST.ENUM), @@ -277,10 +277,6 @@ ("use_auto_update", ST.BOOL)] } -curve_nodes = {'ShaderNodeFloatCurve', - 'ShaderNodeVectorCurve', - 'ShaderNodeRGBCurve'} - image_nodes = {'ShaderNodeTexEnvironment', 'ShaderNodeTexImage'} @@ -403,7 +399,7 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): node_var = create_node(node, file, inner, nt_var, node_vars, used_vars) - set_settings_defaults(node, node_settings, file, inner, node_var) + set_settings_defaults(node, shader_node_settings, file, inner, node_var) hide_sockets(node, file, inner, node_var) if node.bl_idname == 'ShaderNodeGroup': @@ -426,12 +422,6 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): load_image(img, file, inner, f"{node_var}.image") image_user_settings(node, file, inner, node_var) - elif node.bl_idname == 'ShaderNodeValToRGB': - color_ramp_settings(node, file, inner, node_var) - - elif node.bl_idname in curve_nodes: - curve_node_settings(node, file, inner, node_var) - if self.mode == 'ADDON': set_input_defaults(node, file, inner, node_var, addon_dir) else: diff --git a/utils.py b/utils.py index 5103827..15fe3e8 100644 --- a/utils.py +++ b/utils.py @@ -19,6 +19,7 @@ class ST(Enum): Settings Types """ ENUM = auto() + ENUM_SET = auto() STRING = auto() BOOL = auto() INT = auto() @@ -298,6 +299,8 @@ def set_settings_defaults(node: bpy.types.Node, setting_str = f"{inner}{node_var}.{setting}" if type == ST.ENUM: file.write(f"{setting_str} = {enum_to_py_str(attr)}\n") + elif type == ST.ENUM_SET: + file.write(f"{setting_str} = {attr}\n") elif type == ST.STRING: file.write(f"{setting_str} = {str_to_py_str(attr)}\n") elif type == ST.BOOL or type == ST.INT or type == ST.FLOAT: @@ -320,6 +323,10 @@ def set_settings_defaults(node: bpy.types.Node, file.write((f"{inner}if {name} in bpy.data.objects:\n")) file.write((f"{inner}\t{node_var}.{setting} = " f"bpy.data.objects[{name}]\n")) + elif type == ST.COLOR_RAMP: + color_ramp_settings(node, file, inner, node_var, setting) + elif type == ST.CURVE_MAPPING: + curve_mapping_settings(node, file, inner, node_var, setting) def hide_sockets(node: bpy.types.Node, file: TextIO, @@ -427,7 +434,8 @@ def group_io_settings(node: bpy.types.Node, def color_ramp_settings(node: bpy.types.Node, file: TextIO, inner: str, - node_var: str + node_var: str, + color_ramp_name: str ) -> None: """ Replicate a color ramp node @@ -437,43 +445,51 @@ def color_ramp_settings(node: bpy.types.Node, file (TextIO): file we're generating the add-on into inner (str): indentation node_var (str): name of the variable we're using for the color ramp + color_ramp_name (str): name of the color ramp to be copied """ - color_ramp = node.color_ramp + color_ramp: bpy.types.ColorRamp = getattr(node, color_ramp_name) + if not color_ramp: + raise ValueError(f"No color ramp named \"{color_ramp_name}\" found") + #settings + ramp_str = f"{inner}{node_var}.{color_ramp_name}" + color_mode = enum_to_py_str(color_ramp.color_mode) - file.write(f"{inner}{node_var}.color_ramp.color_mode = {color_mode}\n") + file.write(f"{ramp_str}.color_mode = {color_mode}\n") hue_interpolation = enum_to_py_str(color_ramp.hue_interpolation) - file.write((f"{inner}{node_var}.color_ramp.hue_interpolation = " + file.write((f"{ramp_str}.hue_interpolation = " f"{hue_interpolation}\n")) interpolation = enum_to_py_str(color_ramp.interpolation) - file.write((f"{inner}{node_var}.color_ramp.interpolation " + file.write((f"{ramp_str}.interpolation " f"= {interpolation}\n")) file.write("\n") #key points - file.write((f"{inner}{node_var}.color_ramp.elements.remove" - f"({node_var}.color_ramp.elements[0])\n")) + file.write(f"{inner}#initialize color ramp elements\n") + file.write((f"{ramp_str}.elements.remove" + f"({ramp_str}.elements[0])\n")) for i, element in enumerate(color_ramp.elements): element_var = f"{node_var}_cre_{i}" if i == 0: file.write(f"{inner}{element_var} = " - f"{node_var}.color_ramp.elements[{i}]\n") + f"{ramp_str}.elements[{i}]\n") file.write(f"{inner}{element_var}.position = {element.position}\n") else: file.write((f"{inner}{element_var} = " - f"{node_var}.color_ramp.elements" + f"{ramp_str}.elements" f".new({element.position})\n")) file.write((f"{inner}{element_var}.alpha = " f"{element.alpha}\n")) color_str = vec4_to_py_str(element.color) file.write((f"{inner}{element_var}.color = {color_str}\n\n")) -def curve_node_settings(node: bpy.types.Node, +def curve_mapping_settings(node: bpy.types.Node, file: TextIO, inner: str, - node_var: str + node_var: str, + curve_mapping_name: str ) -> None: """ Sets defaults for Float, Vector, and Color curves @@ -483,16 +499,16 @@ def curve_node_settings(node: bpy.types.Node, file (TextIO): file we're generating the add-on into inner (str): indentation node_var (str): variable name for the add-on's curve node + curve_mapping_name (str): name of the curve mapping to be set """ - if node.bl_idname == 'CompositorNodeTime': - mapping = node.curve #TODO: ask for consistency here? - else: - mapping = node.mapping + mapping = getattr(node, curve_mapping_name) + if not mapping: + raise ValueError(f"Curve mapping \"{curve_mapping_name}\" not found in node \"{node.bl_idname}\"") #mapping settings file.write(f"{inner}#mapping settings\n") - mapping_var = f"{inner}{node_var}.mapping" + mapping_var = f"{inner}{node_var}.{curve_mapping_name}" #extend extend = enum_to_py_str(mapping.extend) @@ -526,7 +542,8 @@ def curve_node_settings(node: bpy.types.Node, for i, curve in enumerate(mapping.curves): file.write(f"{inner}#curve {i}\n") curve_i = f"{node_var}_curve_{i}" - file.write((f"{inner}{curve_i} = {node_var}.mapping.curves[{i}]\n")) + file.write((f"{inner}{curve_i} = " + f"{node_var}.{curve_mapping_name}.curves[{i}]\n")) for j, point in enumerate(curve.points): point_j = f"{inner}{curve_i}_point_{j}" From cf38a9cc42d03626d3c8151b50d4657c89332c47 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sun, 27 Aug 2023 15:40:08 -0500 Subject: [PATCH 11/21] style: better type hinting --- geo_nodes.py | 21 +++++++++++++++------ materials.py | 26 ++++++++++++++++++-------- utils.py | 14 +++++++------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/geo_nodes.py b/geo_nodes.py index 50a4c58..9d403fb 100644 --- a/geo_nodes.py +++ b/geo_nodes.py @@ -542,7 +542,7 @@ def execute(self, context): create_header(file, nt.name) class_name = clean_string(nt.name.replace(" ", "").replace('.', ""), - lower = False) + lower = False) #TODO: should probably be standardized name to class name util method init_operator(file, class_name, nt_var, nt.name) file.write("\tdef execute(self, context):\n") else: @@ -557,7 +557,18 @@ def execute(self, context): #dictionary to keep track of variables->usage count pairs used_vars: dict[str, int] = {} - def process_geo_nodes_group(node_tree, level, node_vars, used_vars): + def process_geo_nodes_group(node_tree: bpy.types.NodeTree, + level: int, + ) -> None: + """ + Generates a Python function to recreate a node tree + + Parameters: + node_tree (bpy.types.NodeTree): node tree to be recreated + level (int): number of tabs to use for each line, used with + node groups within node groups and script/add-on differences + """ + nt_var = create_var(node_tree.name, used_vars) outer, inner = make_indents(level) @@ -583,14 +594,12 @@ def process_geo_nodes_group(node_tree, level, node_vars, used_vars): if node.bl_idname == 'GeometryNodeGroup': node_nt = node.node_tree if node_nt is not None and node_nt not in node_trees: - process_geo_nodes_group(node_nt, level + 1, node_vars, - used_vars) + process_geo_nodes_group(node_nt, level + 1) node_trees.add(node_nt) elif node.bl_idname == 'NodeGroupInput' and not inputs_set: group_io_settings(node, file, inner, "input", nt_var, node_tree) inputs_set = True - elif node.bl_idname == 'NodeGroupOutput' and not outputs_set: group_io_settings(node, file, inner, "output", nt_var, node_tree) @@ -673,7 +682,7 @@ def process_geo_nodes_group(node_tree, level, node_vars, used_vars): level = 2 else: level = 0 - process_geo_nodes_group(nt, level, node_vars, used_vars) + process_geo_nodes_group(nt, level) def apply_modifier(): #get object diff --git a/materials.py b/materials.py index 2cfab43..de3ad30 100644 --- a/materials.py +++ b/materials.py @@ -338,13 +338,13 @@ def create_material(indent: str): create_material("") #set to keep track of already created node trees - node_trees = set() + node_trees: set[bpy.types.NodeTree] = set() #dictionary to keep track of node->variable name pairs - node_vars = {} + node_vars: dict[bpy.types.Node, str] = {} - #keeps track of all used variables - used_vars = {} + #keeps track of all used base vareiable names and usage counts + used_vars: dict[str, int] = {} def is_outermost_node_group(level: int) -> bool: if self.mode == 'ADDON' and level == 2: @@ -353,7 +353,18 @@ def is_outermost_node_group(level: int) -> bool: return True return False - def process_mat_node_group(node_tree, level, node_vars, used_vars): + def process_mat_node_group(node_tree: bpy.types.NodeTree, + level: int + ) -> None: + """ + Generates a Python function to recreate a node tree + + Parameters: + node_tree (bpy.types.NodeTree): node tree to be recreated + level (int): number of tabs to use for each line, used with + node groups within node groups and script/add-on differences + """ + if is_outermost_node_group(level): nt_var = create_var(self.material_name, used_vars) nt_name = self.material_name @@ -392,8 +403,7 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): if node.bl_idname == 'ShaderNodeGroup': node_nt = node.node_tree if node_nt is not None and node_nt not in node_trees: - process_mat_node_group(node_nt, level + 1, node_vars, - used_vars) + process_mat_node_group(node_nt, level + 1) node_trees.add(node_nt) node_var = create_node(node, file, inner, nt_var, node_vars, @@ -440,7 +450,7 @@ def process_mat_node_group(node_tree, level, node_vars, used_vars): level = 2 else: level = 0 - process_mat_node_group(nt, level, node_vars, used_vars) + process_mat_node_group(nt, level) if self.mode == 'ADDON': file.write("\t\treturn {'FINISHED'}\n\n") diff --git a/utils.py b/utils.py index 15fe3e8..fc07b45 100644 --- a/utils.py +++ b/utils.py @@ -7,7 +7,7 @@ import shutil from typing import TextIO, Tuple -image_dir_name = "imgs" +IMAGE_DIR_NAME = "imgs" #node input sockets that are messy to set default values for dont_set_defaults = {'NodeSocketGeometry', @@ -189,13 +189,13 @@ def init_operator(file: TextIO, name: str, idname: str, label: str) -> None: file.write("\tbl_options = {\'REGISTER\', \'UNDO\'}\n") file.write("\n") -def create_var(name: str, used_vars: dict) -> str: +def create_var(name: str, used_vars: dict[str, int]) -> str: """ Creates a unique variable name for a node tree Parameters: name (str): basic string we'd like to create the variable name out of - used_vars (dict): dictionary containing variable names and usage counts + used_vars (dict[str, int]): dictionary containing variable names and usage counts Returns: clean_name (str): variable name for the node tree @@ -235,7 +235,7 @@ def create_node(node: bpy.types.Node, inner: str, node_tree_var: str, node_vars: dict[bpy.types.Node, str], - used_vars: set #necessary? + used_vars: dict[str, int] ) -> str: """ Initializes a new node with location, dimension, and label info @@ -247,7 +247,7 @@ def create_node(node: bpy.types.Node, node_tree_var (str): variable name for the node tree node_vars (dict): dictionary containing Node to corresponding variable name pairs - used_vars (set): set of used variable names + used_vars dict[str, int]: dictionary of base variable names to usage counts Returns: node_var (str): variable name for the node @@ -867,7 +867,7 @@ def save_image(img: bpy.types.Image, addon_dir: str) -> None: return #create image dir if one doesn't exist - img_dir = os.path.join(addon_dir, image_dir_name) + img_dir = os.path.join(addon_dir, IMAGE_DIR_NAME) if not os.path.exists(img_dir): os.mkdir(img_dir) @@ -901,7 +901,7 @@ def load_image(img: bpy.types.Image, file.write((f"{inner}base_dir = " f"os.path.dirname(os.path.abspath(__file__))\n")) file.write((f"{inner}image_path = " - f"os.path.join(base_dir, \"{image_dir_name}\", " + f"os.path.join(base_dir, \"{IMAGE_DIR_NAME}\", " f"\"{img_str}\")\n")) file.write((f"{inner}{img_var} = " f"bpy.data.images.load(image_path, check_existing = True)\n")) From c4e43d41f3763a807c880e53e29fa9101fa8da78 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sun, 27 Aug 2023 16:01:25 -0500 Subject: [PATCH 12/21] feat: basic compositor node tree regeneration --- compositor.py | 5 +++-- materials.py | 10 ++++++---- utils.py | 3 ++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compositor.py b/compositor.py index beac3cc..5932e64 100644 --- a/compositor.py +++ b/compositor.py @@ -44,7 +44,8 @@ 'CompositorNodeRLayers' : [("layer", ST.ENUM), ("scene", ST.SCENE)], - 'CompositorNodeRGB' : [], + 'CompositorNodeRGB' : [], #TODO: add output to output handler + #Maybe just make setting handler handle defaults? 'CompositorNodeSceneTime' : [], @@ -268,7 +269,7 @@ ("use_max", ST.BOOL), ("use_min", ST.BOOL)], #why are all these vectors?? TODO: check to make sure it doesn't flip - 'CompositorNodeNormal' : [], + 'CompositorNodeNormal' : [], #TODO: output :( 'CompositorNodeNormalize' : [], diff --git a/materials.py b/materials.py index de3ad30..1060cab 100644 --- a/materials.py +++ b/materials.py @@ -4,6 +4,8 @@ from .utils import * from io import StringIO +MAT_VAR = "mat" + #TODO: move to a json, different ones for each blender version? shader_node_settings : dict[str, list[(str, ST)]] = { # INPUT @@ -328,9 +330,9 @@ def execute(self, context): file = StringIO("") def create_material(indent: str): - file.write((f"{indent}mat = bpy.data.materials.new(" #TODO: see if using mat effects nodes named mat + file.write((f"{indent}{MAT_VAR} = bpy.data.materials.new(" f"name = {str_to_py_str(self.material_name)})\n")) - file.write(f"{indent}mat.use_nodes = True\n") + file.write(f"{indent}{MAT_VAR}.use_nodes = True\n") if self.mode == 'ADDON': create_material("\t\t") @@ -364,7 +366,7 @@ def process_mat_node_group(node_tree: bpy.types.NodeTree, level (int): number of tabs to use for each line, used with node groups within node groups and script/add-on differences """ - + if is_outermost_node_group(level): nt_var = create_var(self.material_name, used_vars) nt_name = self.material_name @@ -379,7 +381,7 @@ def process_mat_node_group(node_tree: bpy.types.NodeTree, file.write(f"{outer}def {nt_var}_node_group():\n") if is_outermost_node_group(level): #outermost node group - file.write(f"{inner}{nt_var} = mat.node_tree\n") + file.write(f"{inner}{nt_var} = {MAT_VAR}.node_tree\n") file.write(f"{inner}#start with a clean node tree\n") file.write(f"{inner}for node in {nt_var}.nodes:\n") file.write(f"{inner}\t{nt_var}.nodes.remove(node)\n") diff --git a/utils.py b/utils.py index fc07b45..6668bb2 100644 --- a/utils.py +++ b/utils.py @@ -99,7 +99,7 @@ def vec1_to_py_str(vec1) -> str: Returns: (str): string representation of the vector """ - return f"({vec1[0]})" + return f"[{vec1[0]}]" def vec2_to_py_str(vec2) -> str: """ @@ -295,6 +295,7 @@ def set_settings_defaults(node: bpy.types.Node, for (setting, type) in settings[node.bl_idname]: attr = getattr(node, setting, None) if not attr: + print(f"\"{node_var}.{setting}\" not found") continue setting_str = f"{inner}{node_var}.{setting}" if type == ST.ENUM: From ba3e4cba55d34562b95a12c9336590002f7d8fac Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sun, 27 Aug 2023 16:46:44 -0500 Subject: [PATCH 13/21] feat: compositor nodes output socket default value settings --- compositor.py | 9 ++++----- utils.py | 11 +++++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/compositor.py b/compositor.py index 5932e64..84580b9 100644 --- a/compositor.py +++ b/compositor.py @@ -44,8 +44,7 @@ 'CompositorNodeRLayers' : [("layer", ST.ENUM), ("scene", ST.SCENE)], - 'CompositorNodeRGB' : [], #TODO: add output to output handler - #Maybe just make setting handler handle defaults? + 'CompositorNodeRGB' : [], 'CompositorNodeSceneTime' : [], @@ -62,7 +61,7 @@ ("track_name", ST.STRING), #TODO: probably not right ("tracking_object", ST.STRING)], - 'CompositorNodeValue' : [], #TODO: double check that outputs set here + 'CompositorNodeValue' : [], # OUTPUT @@ -267,9 +266,9 @@ ("offset", ST.VEC1), ("size", ST.VEC1), ("use_max", ST.BOOL), - ("use_min", ST.BOOL)], #why are all these vectors?? TODO: check to make sure it doesn't flip + ("use_min", ST.BOOL)], - 'CompositorNodeNormal' : [], #TODO: output :( + 'CompositorNodeNormal' : [], 'CompositorNodeNormalize' : [], diff --git a/utils.py b/utils.py index 6668bb2..aebcd34 100644 --- a/utils.py +++ b/utils.py @@ -674,13 +674,16 @@ def set_output_defaults(node: bpy.types.Node, """ output_default_nodes = {'ShaderNodeValue', 'ShaderNodeRGB', - 'ShaderNodeNormal'} + 'ShaderNodeNormal', + 'CompositorNodeValue', + 'CompositorNodeRGB', + 'CompositorNodeNormal'} if node.bl_idname in output_default_nodes: - dv = node.outputs[0].default_value #TODO: see if this is still the case - if node.bl_idname == 'ShaderNodeRGB': + dv = node.outputs[0].default_value + if node.bl_idname in {'ShaderNodeRGB', 'CompositorNodeRGB'}: dv = vec4_to_py_str(list(dv)) - if node.bl_idname == 'ShaderNodeNormal': + if node.bl_idname in {'ShaderNodeNormal', 'CompositorNodeNormal'}: dv = vec3_to_py_str(dv) file.write((f"{inner}{node_var}.outputs[0].default_value = {dv}\n")) From 0f4949708d76ea28231719988b5e0d9a6e0c1ded Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:51:51 -0500 Subject: [PATCH 14/21] fix: initialize scene after creating a name to prevent conflicts --- compositor.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compositor.py b/compositor.py index 84580b9..b6487cf 100644 --- a/compositor.py +++ b/compositor.py @@ -476,10 +476,9 @@ def execute(self, context): file.write("\tdef execute(self, context):\n") else: file = StringIO("") + if self.is_scene: def create_scene(indent: str): - file.write(f"{indent}{SCENE_VAR} = bpy.context.window.scene.copy()\n\n") #TODO: see if using scene as name effects nodes named scene - #TODO: wrap in more general unique name util function file.write(f"{indent}# Generate unique scene name\n") file.write(f"{indent}{BASE_NAME_VAR} = {str_to_py_str(self.compositor_name)}\n") @@ -491,6 +490,7 @@ def create_scene(indent: str): file.write(f"{indent}\t\t{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"\n") file.write(f"{indent}\t\ti += 1\n\n") + file.write(f"{indent}{SCENE_VAR} = bpy.context.window.scene.copy()\n\n") file.write(f"{indent}{SCENE_VAR}.name = {END_NAME_VAR}\n") file.write(f"{indent}{SCENE_VAR}.use_fake_user = True\n") file.write(f"{indent}bpy.context.window.scene = {SCENE_VAR}\n") @@ -516,8 +516,7 @@ def is_outermost_node_group(level: int) -> bool: return True return False - def process_comp_node_group(node_tree, level, node_vars, used_vars): - + def process_comp_node_group(node_tree, level, node_vars, used_vars): if is_outermost_node_group(level): nt_var = create_var(self.compositor_name, used_vars) nt_name = self.compositor_name From 12dcdbce533c70dade80b07680e03b1a6c36609d Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 2 Sep 2023 17:54:42 -0500 Subject: [PATCH 15/21] style: consistency of compositor names --- __init__.py | 2 +- compositor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__init__.py b/__init__.py index fc987da..a4bf342 100644 --- a/__init__.py +++ b/__init__.py @@ -42,7 +42,7 @@ def draw(self, context): compositor.NTPCompositorOperator, compositor.NTPCompositorScenesMenu, compositor.NTPCompositorGroupsMenu, - compositor.NTPCompositingPanel, + compositor.NTPCompositorPanel, geo_nodes.NTPGeoNodesOperator, geo_nodes.NTPGeoNodesMenu, geo_nodes.NTPGeoNodesPanel, diff --git a/compositor.py b/compositor.py index b6487cf..ef24c0e 100644 --- a/compositor.py +++ b/compositor.py @@ -667,7 +667,7 @@ def draw(self, context): op.compositor_name = node_group.name op.is_scene = False -class NTPCompositingPanel(bpy.types.Panel): +class NTPCompositorPanel(bpy.types.Panel): bl_label = "Compositor to Python" bl_idname = "NODE_PT_ntp_compositor" bl_space_type = 'NODE_EDITOR' From 6825040ce2117d2dd4eee03125190f1517f66130 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 2 Sep 2023 19:03:29 -0500 Subject: [PATCH 16/21] refactor: images and image users now use settings function --- compositor.py | 12 +-- geo_nodes.py | 12 +-- materials.py | 11 +-- utils.py | 232 ++++++++++++++++++++++++++------------------------ 4 files changed, 131 insertions(+), 136 deletions(-) diff --git a/compositor.py b/compositor.py index ef24c0e..a28d01e 100644 --- a/compositor.py +++ b/compositor.py @@ -455,6 +455,7 @@ def execute(self, context): #set up names to use in generated addon comp_var = clean_string(self.compositor_name) + addon_dir = None if self.mode == 'ADDON': dir = bpy.path.abspath(context.scene.ntp_options.dir_path) if not dir or dir == "": @@ -562,7 +563,8 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): node_var = create_node(node, file, inner, nt_var, node_vars, used_vars) - set_settings_defaults(node, compositor_node_settings, file, inner, node_var) + set_settings_defaults(node, compositor_node_settings, file, + addon_dir, inner, node_var) hide_sockets(node, file, inner, node_var) if node.bl_idname == 'CompositorNodeGroup': @@ -577,14 +579,6 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): elif node.bl_idname == 'NodeGroupOutput' and not outputs_set: group_io_settings(node, file, inner, "output", nt_var, node_tree) outputs_set = True - - # elif node.bl_idname in image_nodes and self.mode == 'ADDON': - # img = node.image - # if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: - # save_image(img, addon_dir) - # load_image(img, file, inner, f"{node_var}.image") - # image_user_settings(node, file, inner, node_var) - if self.mode == 'ADDON': set_input_defaults(node, file, inner, node_var, addon_dir) else: diff --git a/geo_nodes.py b/geo_nodes.py index 9d403fb..c5502d6 100644 --- a/geo_nodes.py +++ b/geo_nodes.py @@ -523,6 +523,7 @@ def execute(self, context): #set up names to use in generated addon nt_var = clean_string(nt.name) + addon_dir = None if self.mode == 'ADDON': #find base directory to save new addon dir = bpy.path.abspath(context.scene.ntp_options.dir_path) @@ -608,8 +609,8 @@ def process_geo_nodes_group(node_tree: bpy.types.NodeTree, #create node node_var = create_node(node, file, inner, nt_var, node_vars, used_vars) - set_settings_defaults(node, geo_node_settings, file, inner, - node_var) + set_settings_defaults(node, geo_node_settings, file, addon_dir, + inner, node_var) hide_sockets(node, file, inner, node_var) if node.bl_idname == 'GeometryNodeGroup': @@ -635,13 +636,6 @@ def process_geo_nodes_group(node_tree: bpy.types.NodeTree, attr_domain = enum_to_py_str(si.attribute_domain) file.write((f"{inner}{si_var}.attribute_domain = " f"{attr_domain}\n")) - """ - elif node.bl_idname in image_nodes and self.mode == 'ADDON': - img = node.image - if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: - save_image(img, addon_dir) - load_image(img, file, inner, f"{node_var}.image") - """ if node.bl_idname != 'GeometryNodeSimulationInput': if self.mode == 'ADDON': set_input_defaults(node, file, inner, node_var, addon_dir) diff --git a/materials.py b/materials.py index 1060cab..1566ce4 100644 --- a/materials.py +++ b/materials.py @@ -307,6 +307,7 @@ def execute(self, context): #set up names to use in generated addon mat_var = clean_string(self.material_name) + addon_dir = None if self.mode == 'ADDON': dir = bpy.path.abspath(context.scene.ntp_options.dir_path) if not dir or dir == "": @@ -411,7 +412,8 @@ def process_mat_node_group(node_tree: bpy.types.NodeTree, node_var = create_node(node, file, inner, nt_var, node_vars, used_vars) - set_settings_defaults(node, shader_node_settings, file, inner, node_var) + set_settings_defaults(node, shader_node_settings, file, + addon_dir, inner, node_var) hide_sockets(node, file, inner, node_var) if node.bl_idname == 'ShaderNodeGroup': @@ -427,13 +429,6 @@ def process_mat_node_group(node_tree: bpy.types.NodeTree, group_io_settings(node, file, inner, "output", nt_var, node_tree) outputs_set = True - elif node.bl_idname in image_nodes and self.mode == 'ADDON': - img = node.image - if img is not None and img.source in {'FILE', 'GENERATED', 'TILED'}: - save_image(img, addon_dir) - load_image(img, file, inner, f"{node_var}.image") - image_user_settings(node, file, inner, node_var) - if self.mode == 'ADDON': set_input_defaults(node, file, inner, node_var, addon_dir) else: diff --git a/utils.py b/utils.py index aebcd34..2360636 100644 --- a/utils.py +++ b/utils.py @@ -18,6 +18,7 @@ class ST(Enum): """ Settings Types """ + # Primitives ENUM = auto() ENUM_SET = auto() STRING = auto() @@ -28,13 +29,20 @@ class ST(Enum): VEC2 = auto() VEC3 = auto() VEC4 = auto() - MATERIAL = auto() #Could use a look - OBJECT = auto() #Could take a looking at + + # Special settings + COLOR_RAMP = auto() + CURVE_MAPPING = auto() + + # Asset Library + MATERIAL = auto() # Handle with asset library + OBJECT = auto() # Handle with asset library + + # Image IMAGE = auto() #needs refactor - IMAGE_USER = auto() #unimplemented + IMAGE_USER = auto() #needs refactor MOVIE_CLIP = auto() #unimplmented - COLOR_RAMP = auto() #needs refactor - CURVE_MAPPING = auto() #needs refactor + TEXTURE = auto() #unimplemented TEXT = auto() #unimplemented SCENE = auto() #unimplemented @@ -277,7 +285,8 @@ def create_node(node: bpy.types.Node, def set_settings_defaults(node: bpy.types.Node, settings: dict[str, list[(str, ST)]], - file: TextIO, + file: TextIO, + addon_dir: str, inner: str, node_var: str ) -> None: @@ -288,16 +297,17 @@ def set_settings_defaults(node: bpy.types.Node, node (bpy.types.Node): the node object we're copying settings from settings (dict): a predefined dictionary of all settings every node has file (TextIO): file we're generating the add-on into + addon_dir (str): directory that the addon is saved into inner (str): indentation node_var (str): name of the variable we're using for the node in our add-on """ if node.bl_idname in settings: - for (setting, type) in settings[node.bl_idname]: - attr = getattr(node, setting, None) + for (attr_name, type) in settings[node.bl_idname]: + attr = getattr(node, attr_name, None) if not attr: - print(f"\"{node_var}.{setting}\" not found") + print(f"\"{node_var}.{attr_name}\" not found") continue - setting_str = f"{inner}{node_var}.{setting}" + setting_str = f"{inner}{node_var}.{attr_name}" if type == ST.ENUM: file.write(f"{setting_str} = {enum_to_py_str(attr)}\n") elif type == ST.ENUM_SET: @@ -317,17 +327,24 @@ def set_settings_defaults(node: bpy.types.Node, elif type == ST.MATERIAL: name = str_to_py_str(attr.name) file.write((f"{inner}if {name} in bpy.data.materials:\n")) - file.write((f"{inner}\t{node_var}.{setting} = " + file.write((f"{inner}\t{node_var}.{attr_name} = " f"bpy.data.materials[{name}]\n")) elif type == ST.OBJECT: name = str_to_py_str(attr.name) file.write((f"{inner}if {name} in bpy.data.objects:\n")) - file.write((f"{inner}\t{node_var}.{setting} = " + file.write((f"{inner}\t{node_var}.{attr_name} = " f"bpy.data.objects[{name}]\n")) elif type == ST.COLOR_RAMP: - color_ramp_settings(node, file, inner, node_var, setting) + color_ramp_settings(node, file, inner, node_var, attr_name) elif type == ST.CURVE_MAPPING: - curve_mapping_settings(node, file, inner, node_var, setting) + curve_mapping_settings(node, file, inner, node_var, attr_name) + elif type == ST.IMAGE: + if addon_dir is not None and attr is not None: + if attr.source in {'FILE', 'GENERATED', 'TILED'}: + save_image(attr, addon_dir) + load_image(attr, file, inner, f"{node_var}.{attr_name}") + elif type == ST.IMAGE_USER: + image_user_settings(attr, file, inner, f"{node_var}.{attr_name}") def hide_sockets(node: bpy.types.Node, file: TextIO, @@ -563,6 +580,96 @@ def curve_mapping_settings(node: bpy.types.Node, file.write(f"{inner}#update curve after changes\n") file.write(f"{mapping_var}.update()\n") +def save_image(img: bpy.types.Image, addon_dir: str) -> None: + """ + Saves an image to an image directory of the add-on + + Parameters: + img (bpy.types.Image): image to be saved + addon_dir (str): directory of the addon + """ + + if img is None: + return + + #create image dir if one doesn't exist + img_dir = os.path.join(addon_dir, IMAGE_DIR_NAME) + if not os.path.exists(img_dir): + os.mkdir(img_dir) + + #save the image + img_str = img_to_py_str(img) + img_path = f"{img_dir}/{img_str}" + if not os.path.exists(img_path): + img.save_render(img_path) + +def load_image(img: bpy.types.Image, + file: TextIO, + inner: str, + img_var: str + ) -> None: + """ + Loads an image from the add-on into a blend file and assigns it + + Parameters: + img (bpy.types.Image): Blender image from the original node group + file (TextIO): file for the generated add-on + inner (str): indentation string + img_var (str): variable name to be used for the image + """ + + if img is None: + return + + img_str = img_to_py_str(img) + + #TODO: convert to special variables + file.write(f"{inner}#load image {img_str}\n") + file.write((f"{inner}base_dir = " + f"os.path.dirname(os.path.abspath(__file__))\n")) + file.write((f"{inner}image_path = " + f"os.path.join(base_dir, \"{IMAGE_DIR_NAME}\", " + f"\"{img_str}\")\n")) + file.write((f"{inner}{img_var} = " + f"bpy.data.images.load(image_path, check_existing = True)\n")) + + #copy image settings + file.write(f"{inner}#set image settings\n") + + #source + source = enum_to_py_str(img.source) + file.write(f"{inner}{img_var}.source = {source}\n") + + #color space settings + color_space = enum_to_py_str(img.colorspace_settings.name) + file.write(f"{inner}{img_var}.colorspace_settings.name = {color_space}\n") + + #alpha mode + alpha_mode = enum_to_py_str(img.alpha_mode) + file.write(f"{inner}{img_var}.alpha_mode = {alpha_mode}\n") + +def image_user_settings(img_user: bpy.types.ImageUser, + file: TextIO, + inner: str, + img_user_var: str + ) -> None: + """ + Replicate the image user of an image node + + Parameters + img_usr (bpy.types.ImageUser): image user to be copied + file (TextIO): file we're generating the add-on into + inner (str): indentation + img_usr_var (str): variable name for the generated image user + """ + + img_usr_attrs = ["frame_current", "frame_duration", "frame_offset", + "frame_start", "tile", "use_auto_refresh", "use_cyclic"] + + for img_usr_attr in img_usr_attrs: + file.write((f"{inner}{img_user_var}.{img_usr_attr} = " + f"{getattr(img_user, img_usr_attr)}\n")) + def set_input_defaults(node: bpy.types.Node, file: TextIO, inner: str, @@ -857,102 +964,7 @@ def create_main_func(file: TextIO) -> None: """ file.write("if __name__ == \"__main__\":\n") file.write("\tregister()") - -def save_image(img: bpy.types.Image, addon_dir: str) -> None: - """ - Saves an image to an image directory of the add-on - - Parameters: - img (bpy.types.Image): image to be saved - addon_dir (str): directory of the addon - """ - - if img is None: - return - - #create image dir if one doesn't exist - img_dir = os.path.join(addon_dir, IMAGE_DIR_NAME) - if not os.path.exists(img_dir): - os.mkdir(img_dir) - - #save the image - img_str = img_to_py_str(img) - img_path = f"{img_dir}/{img_str}" - if not os.path.exists(img_path): - img.save_render(img_path) - -def load_image(img: bpy.types.Image, - file: TextIO, - inner: str, - img_var: str - ) -> None: - """ - Loads an image from the add-on into a blend file and assigns it - - Parameters: - img (bpy.types.Image): Blender image from the original node group - file (TextIO): file for the generated add-on - inner (str): indentation string - img_var (str): variable name to be used for the image - """ - - if img is None: - return - - img_str = img_to_py_str(img) - - file.write(f"{inner}#load image {img_str}\n") - file.write((f"{inner}base_dir = " - f"os.path.dirname(os.path.abspath(__file__))\n")) - file.write((f"{inner}image_path = " - f"os.path.join(base_dir, \"{IMAGE_DIR_NAME}\", " - f"\"{img_str}\")\n")) - file.write((f"{inner}{img_var} = " - f"bpy.data.images.load(image_path, check_existing = True)\n")) - - #copy image settings - file.write(f"{inner}#set image settings\n") - - #source - source = enum_to_py_str(img.source) - file.write(f"{inner}{img_var}.source = {source}\n") - - #color space settings - color_space = enum_to_py_str(img.colorspace_settings.name) - file.write(f"{inner}{img_var}.colorspace_settings.name = {color_space}\n") - - #alpha mode - alpha_mode = enum_to_py_str(img.alpha_mode) - file.write(f"{inner}{img_var}.alpha_mode = {alpha_mode}\n") - -def image_user_settings(node: bpy.types.Node, - file: TextIO, - inner: str, - node_var: str - ) -> None: - """ - Replicate the image user of an image node - - Parameters - node (bpy.types.Node): node object we're copying settings from - file (TextIO): file we're generating the add-on into - inner (str): indentation - node_var (str): name of the variable we're using for the color ramp - """ - - if not hasattr(node, "image_user"): - raise ValueError("Node must have attribute \"image_user\"") - - img_usr = node.image_user - img_usr_var = f"{node_var}.image_user" - - img_usr_attrs = ["frame_current", "frame_duration", "frame_offset", - "frame_start", "tile", "use_auto_refresh", "use_cyclic"] - - for img_usr_attr in img_usr_attrs: - file.write((f"{inner}{img_usr_var}.{img_usr_attr} = " - f"{getattr(img_usr, img_usr_attr)}\n")) - + def zip_addon(zip_dir: str) -> None: """ Zips up the addon and removes the directory From f12189d809bc1154a17a53a3942521a24dbce1d6 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 2 Sep 2023 19:15:45 -0500 Subject: [PATCH 17/21] typo: material used_vars comment --- materials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/materials.py b/materials.py index 1566ce4..75be899 100644 --- a/materials.py +++ b/materials.py @@ -346,7 +346,7 @@ def create_material(indent: str): #dictionary to keep track of node->variable name pairs node_vars: dict[bpy.types.Node, str] = {} - #keeps track of all used base vareiable names and usage counts + #keeps track of all used base variable names and usage counts used_vars: dict[str, int] = {} def is_outermost_node_group(level: int) -> bool: From 0921c25d580e27321c48ad79b210c72a592937b5 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 9 Sep 2023 18:31:58 -0500 Subject: [PATCH 18/21] fix: hue correction node removes excess points --- utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/utils.py b/utils.py index 2360636..ce6658d 100644 --- a/utils.py +++ b/utils.py @@ -562,6 +562,14 @@ def curve_mapping_settings(node: bpy.types.Node, curve_i = f"{node_var}_curve_{i}" file.write((f"{inner}{curve_i} = " f"{node_var}.{curve_mapping_name}.curves[{i}]\n")) + + # Remove default points when CurveMap is initialized with more than + # two points (just CompositorNodeHueCorrect) + if (node.bl_idname == 'CompositorNodeHueCorrect'): + file.write((f"{inner}for i in " + f"range(len({curve_i}.points.values()) - 1, 1, -1):\n")) + file.write(f"{inner}\t{curve_i}.points.remove({curve_i}.points[i])\n") + for j, point in enumerate(curve.points): point_j = f"{inner}{curve_i}_point_{j}" From e5da19622cfbf0182febff037af5f9e287547c97 Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 9 Sep 2023 19:06:33 -0500 Subject: [PATCH 19/21] fix: 0 settings now initialized --- compositor.py | 4 ++-- utils.py | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compositor.py b/compositor.py index a28d01e..385c4bd 100644 --- a/compositor.py +++ b/compositor.py @@ -314,8 +314,8 @@ ("frame_duration", ST.INT), ("frame_offset", ST.INT), ("frame_start", ST.INT), - ("has_layers", ST.BOOL), #TODO: readonly? - ("has_views", ST.BOOL), #TODO: readonly? + #("has_layers", ST.BOOL), #TODO: readonly? + #("has_views", ST.BOOL), #TODO: readonly? ("image", ST.IMAGE), ("layer", ST.ENUM), ("layer_name", ST.ENUM), diff --git a/utils.py b/utils.py index ce6658d..71a1334 100644 --- a/utils.py +++ b/utils.py @@ -304,12 +304,13 @@ def set_settings_defaults(node: bpy.types.Node, if node.bl_idname in settings: for (attr_name, type) in settings[node.bl_idname]: attr = getattr(node, attr_name, None) - if not attr: + if attr is None: print(f"\"{node_var}.{attr_name}\" not found") continue setting_str = f"{inner}{node_var}.{attr_name}" if type == ST.ENUM: - file.write(f"{setting_str} = {enum_to_py_str(attr)}\n") + if attr != '': + file.write(f"{setting_str} = {enum_to_py_str(attr)}\n") elif type == ST.ENUM_SET: file.write(f"{setting_str} = {attr}\n") elif type == ST.STRING: From d90b6d80a86ccdb7bcd93f4cc319586089fc266a Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 9 Sep 2023 20:27:32 -0500 Subject: [PATCH 20/21] fix: color balance now only initializes needed members --- compositor.py | 37 ++++++++++++++++++++++++++----------- utils.py | 15 +++++++++++++++ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/compositor.py b/compositor.py index 385c4bd..1bc537f 100644 --- a/compositor.py +++ b/compositor.py @@ -92,19 +92,19 @@ 'CompositorNodeBrightContrast' : [("use_premultiply", ST.BOOL)], 'CompositorNodeColorBalance' : [("correction_method", ST.ENUM), - ("gain", ST.VEC3), - ("gamma", ST.VEC3), - ("lift", ST.VEC3), - ("offset", ST.VEC3), + ("gain", ST.COLOR), + ("gamma", ST.COLOR), + ("lift", ST.COLOR), + ("offset", ST.COLOR), ("offset_basis", ST.FLOAT), - ("power", ST.VEC3), - ("slope", ST.VEC3)], + ("power", ST.COLOR), + ("slope", ST.COLOR)], 'CompositorNodeColorCorrection' : [("blue", ST.BOOL), ("green", ST.BOOL), ("highlights_contrast", ST.FLOAT), ("highlights_gain", ST.FLOAT), - ("midtones_lift", ST.FLOAT), + ("midtones_lift", ST.FLOAT), ("midtones_saturation", ST.FLOAT), ("midtones_start", ST.FLOAT), ("red", ST.BOOL), @@ -309,7 +309,7 @@ ("unspill_red", ST.FLOAT), ("use_unspill", ST.BOOL)], - 'CompositorNodeCryptomatteV2' : [("add", ST.VEC3), + 'CompositorNodeCryptomatteV2' : [("add", ST.COLOR), ("entries", ST.CRYPTOMATTE_ENTRIES), ("frame_duration", ST.INT), ("frame_offset", ST.INT), @@ -320,16 +320,16 @@ ("layer", ST.ENUM), ("layer_name", ST.ENUM), ("matte_id", ST.STRING), - ("remove", ST.VEC3), + ("remove", ST.COLOR), ("scene", ST.SCENE), ("source", ST.ENUM), ("use_auto_refresh", ST.BOOL), ("use_cyclic", ST.BOOL), ("view", ST.ENUM)], - 'CompositorNodeCryptomatte' : [("add", ST.VEC3), #TODO: may need special handling + 'CompositorNodeCryptomatte' : [("add", ST.COLOR), #TODO: may need special handling ("matte_id", ST.STRING), - ("remove", ST.VEC3)], + ("remove", ST.COLOR)], 'CompositorNodeDiffMatte' : [("falloff", ST.FLOAT), ("tolerance", ST.FLOAT)], @@ -563,6 +563,21 @@ def process_comp_node_group(node_tree, level, node_vars, used_vars): node_var = create_node(node, file, inner, nt_var, node_vars, used_vars) + if node.bl_idname == 'CompositorNodeColorBalance': + if node.correction_method == 'LIFT_GAMMA_GAIN': + lst = [("correction_method", ST.ENUM), + ("gain", ST.COLOR), + ("gamma", ST.COLOR), + ("lift", ST.COLOR)] + else: + lst = [("correction_method", ST.ENUM), + ("offset", ST.COLOR), + ("offset_basis", ST.FLOAT), + ("power", ST.COLOR), + ("slope", ST.COLOR)] + + compositor_node_settings['CompositorNodeColorBalance'] = lst + set_settings_defaults(node, compositor_node_settings, file, addon_dir, inner, node_var) hide_sockets(node, file, inner, node_var) diff --git a/utils.py b/utils.py index 71a1334..a2b0cf6 100644 --- a/utils.py +++ b/utils.py @@ -29,6 +29,7 @@ class ST(Enum): VEC2 = auto() VEC3 = auto() VEC4 = auto() + COLOR = auto() # Special settings COLOR_RAMP = auto() @@ -145,6 +146,18 @@ def vec4_to_py_str(vec4) -> str: """ return f"({vec4[0]}, {vec4[1]}, {vec4[2]}, {vec4[3]})" +def color_to_py_str(color: mathutils.Color) -> str: + """ + Converts a mathutils.Color into a string + + Parameters: + color (mathutils.Color): a Blender color + + Returns: + (str): string version + """ + return f"mathutils.Color(({color.r}, {color.g}, {color.b}))" + def img_to_py_str(img : bpy.types.Image) -> str: """ Converts a Blender image into its string @@ -325,6 +338,8 @@ def set_settings_defaults(node: bpy.types.Node, file.write(f"{setting_str} = {vec3_to_py_str(attr)}\n") elif type == ST.VEC4: file.write(f"{setting_str} = {vec4_to_py_str(attr)}\n") + elif type == ST.COLOR: + file.write(f"{setting_str} = {color_to_py_str(attr)}\n") elif type == ST.MATERIAL: name = str_to_py_str(attr.name) file.write((f"{inner}if {name} in bpy.data.materials:\n")) From 6e18e33ae8d7cc92a364bad7cc35247995fd7f9e Mon Sep 17 00:00:00 2001 From: BrendanParmer <51296046+BrendanParmer@users.noreply.github.com> Date: Sat, 9 Sep 2023 20:39:46 -0500 Subject: [PATCH 21/21] fix: add missing color correction attributes --- compositor.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/compositor.py b/compositor.py index 1bc537f..6ff9e03 100644 --- a/compositor.py +++ b/compositor.py @@ -100,19 +100,36 @@ ("power", ST.COLOR), ("slope", ST.COLOR)], - 'CompositorNodeColorCorrection' : [("blue", ST.BOOL), - ("green", ST.BOOL), - ("highlights_contrast", ST.FLOAT), - ("highlights_gain", ST.FLOAT), - ("midtones_lift", ST.FLOAT), + 'CompositorNodeColorCorrection' : [("red", ST.BOOL), + ("green", ST.BOOL), + ("blue", ST.BOOL), + #master + ("master_saturation", ST.FLOAT), + ("master_contrast", ST.FLOAT), + ("master_gamma", ST.FLOAT), + ("master_gain", ST.FLOAT), + ("master_lift", ST.FLOAT), + #highlights + ("highlights_saturation", ST.FLOAT), + ("highlights_contrast", ST.FLOAT), + ("highlights_gamma", ST.FLOAT), + ("highlights_gain", ST.FLOAT), + ("highlights_lift", ST.FLOAT), + #midtones ("midtones_saturation", ST.FLOAT), - ("midtones_start", ST.FLOAT), - ("red", ST.BOOL), - ("shadows_contrast", ST.FLOAT), - ("shadows_gain", ST.FLOAT), - ("shadows_gamma", ST.FLOAT), - ("shadows_lift", ST.FLOAT), - ("shadows_saturation", ST.FLOAT)], + ("midtones_contrast", ST.FLOAT), + ("midtones_gamma", ST.FLOAT), + ("midtones_gain", ST.FLOAT), + ("midtones_lift", ST.FLOAT), + #shadows + ("shadows_saturation", ST.FLOAT), + ("shadows_contrast", ST.FLOAT), + ("shadows_gamma", ST.FLOAT), + ("shadows_gain", ST.FLOAT), + ("shadows_lift", ST.FLOAT), + #midtones location + ("midtones_start", ST.FLOAT), + ("midtones_end", ST.FLOAT)], 'CompositorNodeExposure' : [],