2025-07-01
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_AddonInfoNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_AddonInfoNode"
|
||||
bl_label = "Addon Info"
|
||||
node_color = "DEFAULT"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_string_output("Name")
|
||||
self.add_string_output("Description")
|
||||
self.add_string_output("Author")
|
||||
self.add_string_output("Location")
|
||||
self.add_string_output("Warning")
|
||||
self.add_string_output("Doc URL")
|
||||
self.add_string_output("Tracker URL")
|
||||
self.add_string_output("Category")
|
||||
self.add_integer_vector_output("Version")
|
||||
self.add_integer_vector_output("Blender Version")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[0].python_value = f"bpy.context.scene.sn.addon_name"
|
||||
self.outputs[1].python_value = f"bpy.context.scene.sn.description"
|
||||
self.outputs[2].python_value = f"bpy.context.scene.sn.author"
|
||||
self.outputs[3].python_value = f"bpy.context.scene.sn.location"
|
||||
self.outputs[4].python_value = f"bpy.context.scene.sn.warning"
|
||||
self.outputs[5].python_value = f"bpy.context.scene.sn.doc_url"
|
||||
self.outputs[6].python_value = f"bpy.context.scene.sn.tracker_url"
|
||||
self.outputs[7].python_value = f"bpy.context.scene.sn.category"
|
||||
self.outputs[8].python_value = f"tuple(bpy.context.scene.sn.version)"
|
||||
self.outputs[9].python_value = f"tuple(bpy.context.scene.sn.blender)"
|
||||
|
||||
def evaluate_export(self, context):
|
||||
self.outputs[0].python_value = f"'{bpy.context.scene.sn.addon_name}'"
|
||||
self.outputs[1].python_value = f"'{bpy.context.scene.sn.description}'"
|
||||
self.outputs[2].python_value = f"'{bpy.context.scene.sn.author}'"
|
||||
self.outputs[3].python_value = f"'{bpy.context.scene.sn.location}'"
|
||||
self.outputs[4].python_value = f"'{bpy.context.scene.sn.warning}'"
|
||||
self.outputs[5].python_value = f"'{bpy.context.scene.sn.doc_url}'"
|
||||
self.outputs[6].python_value = f"'{bpy.context.scene.sn.tracker_url}'"
|
||||
self.outputs[7].python_value = f"'{bpy.context.scene.sn.category}'"
|
||||
self.outputs[8].python_value = str(tuple(bpy.context.scene.sn.version))
|
||||
self.outputs[9].python_value = str(tuple(bpy.context.scene.sn.blender))
|
||||
@@ -0,0 +1,30 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_2DViewZoomNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_2DViewZoomNode"
|
||||
bl_label = "2D View Zoom"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("Area")
|
||||
self.add_float_output("Zoom Level")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_imperative = """
|
||||
def get_zoom_level(area):
|
||||
ui_scale = bpy.context.preferences.system.ui_scale
|
||||
for region in area.regions:
|
||||
if region.type == "WINDOW":
|
||||
test_length = 1000
|
||||
x0, y0 = region.view2d.view_to_region(0, 0, clip=False)
|
||||
x1, y1 = region.view2d.view_to_region(test_length, test_length, clip=False)
|
||||
xl = x1 - x0
|
||||
yl = y1 - y0
|
||||
return (math.sqrt(xl**2 + yl**2) / test_length) * ui_scale
|
||||
return 1 * ui_scale
|
||||
"""
|
||||
self.code_import = "import math"
|
||||
self.outputs[0].python_value = f"get_zoom_level({self.inputs[0].python_value})"
|
||||
@@ -0,0 +1,73 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_AreaByTypeNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_AreaByTypeNode"
|
||||
bl_label = "Area By Type"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("Screen")
|
||||
self.add_property_output("First Area")
|
||||
self.add_property_output("Last Area")
|
||||
self.add_property_output("Biggest Area")
|
||||
self.add_list_output("All Areas")
|
||||
self.add_boolean_output("Area Exists")
|
||||
self.add_integer_output("Area Amount")
|
||||
|
||||
|
||||
def area_items(self,context):
|
||||
types = ["VIEW_3D", "IMAGE_EDITOR", "NODE_EDITOR", "SEQUENCE_EDITOR",
|
||||
"CLIP_EDITOR", "DOPESHEET_EDITOR", "GRAPH_EDITOR", "NLA_EDITOR",
|
||||
"TEXT_EDITOR", "CONSOLE", "INFO", "TOPBAR", "STATUSBAR", "OUTLINER",
|
||||
"PROPERTIES", "FILE_BROWSER", "PREFERENCES"]
|
||||
items = []
|
||||
for a_type in types:
|
||||
items.append((a_type,a_type.replace("_"," ").title(),a_type))
|
||||
return items
|
||||
|
||||
area_type: bpy.props.EnumProperty(name="Area Type",
|
||||
description="The type of area to find",
|
||||
items=area_items,
|
||||
update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_imperative = f"""
|
||||
def find_areas_of_type(screen, area_type):
|
||||
areas = []
|
||||
for area in screen.areas:
|
||||
if area.type == area_type:
|
||||
areas.append(area)
|
||||
return areas
|
||||
|
||||
def find_area_by_type(screen, area_type, index):
|
||||
areas = find_areas_of_type(screen, area_type)
|
||||
if areas:
|
||||
return areas[index]
|
||||
return None
|
||||
|
||||
def find_biggest_area_by_type(screen, area_type):
|
||||
areas = find_areas_of_type(screen, area_type)
|
||||
if not areas: return []
|
||||
max_area = (areas[0], areas[0].width * areas[0].height)
|
||||
for area in areas:
|
||||
if area.width * area.height > max_area[1]:
|
||||
max_area = (area, area.width * area.height)
|
||||
return max_area[0]
|
||||
"""
|
||||
|
||||
screen = "bpy.context.screen" if not "Screen" in self.inputs else self.inputs["Screen"].python_value
|
||||
self.outputs["First Area"].python_value = f"find_area_by_type({screen}, '{self.area_type}', 0)"
|
||||
self.outputs["Last Area"].python_value = f"find_area_by_type({screen}, '{self.area_type}', -1)"
|
||||
self.outputs["Biggest Area"].python_value = f"find_biggest_area_by_type({screen}, '{self.area_type}')"
|
||||
self.outputs["All Areas"].python_value = f"find_areas_of_type({screen}, '{self.area_type}')"
|
||||
self.outputs["Area Exists"].python_value = f"bool(find_areas_of_type({screen}, '{self.area_type}'))"
|
||||
self.outputs["Area Amount"].python_value = f"len(find_areas_of_type({screen}, '{self.area_type}'))"
|
||||
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "area_type", text="")
|
||||
@@ -0,0 +1,41 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_AreaLocationsNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_AreaLocationsNode"
|
||||
bl_label = "Area Locations"
|
||||
node_color = "DEFAULT"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("Area")
|
||||
self.add_integer_vector_output("Top Left").size = 2
|
||||
self.add_integer_vector_output("Top Center").size = 2
|
||||
self.add_integer_vector_output("Top Right").size = 2
|
||||
self.add_integer_vector_output("Bottom Left").size = 2
|
||||
self.add_integer_vector_output("Bottom Center").size = 2
|
||||
self.add_integer_vector_output("Bottom Right").size = 2
|
||||
self.add_integer_vector_output("Left Center").size = 2
|
||||
self.add_integer_vector_output("Right Center").size = 2
|
||||
self.add_integer_vector_output("Center").size = 2
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_imperative = """
|
||||
def region_by_type(area, region_type):
|
||||
for region in area.regions:
|
||||
if region.type == region_type:
|
||||
return region
|
||||
return area.regions[0]
|
||||
"""
|
||||
|
||||
self.outputs["Top Left"].python_value = f"(0, region_by_type({self.inputs['Area'].python_value}, 'WINDOW').height)"
|
||||
self.outputs["Top Center"].python_value = f"(region_by_type({self.inputs['Area'].python_value}, 'WINDOW').width/2, region_by_type({self.inputs['Area'].python_value}, 'WINDOW').height)"
|
||||
self.outputs["Top Right"].python_value = f"(region_by_type({self.inputs['Area'].python_value}, 'WINDOW').width, region_by_type({self.inputs['Area'].python_value}, 'WINDOW').height)"
|
||||
self.outputs["Bottom Left"].python_value = f"(0, 0)"
|
||||
self.outputs["Bottom Center"].python_value = f"(region_by_type({self.inputs['Area'].python_value}, 'WINDOW').width/2, 0)"
|
||||
self.outputs["Bottom Right"].python_value = f"(region_by_type({self.inputs['Area'].python_value}, 'WINDOW').width, 0)"
|
||||
self.outputs["Left Center"].python_value = f"(0, region_by_type({self.inputs['Area'].python_value}, 'WINDOW').height/2)"
|
||||
self.outputs["Right Center"].python_value = f"(region_by_type({self.inputs['Area'].python_value}, 'WINDOW').width, region_by_type({self.inputs['Area'].python_value}, 'WINDOW').height/2)"
|
||||
self.outputs["Center"].python_value = f"(region_by_type({self.inputs['Area'].python_value}, 'WINDOW').width/2, region_by_type({self.inputs['Area'].python_value}, 'WINDOW').height/2)"
|
||||
@@ -0,0 +1,38 @@
|
||||
import bpy
|
||||
import os
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_AssetNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_AssetNode"
|
||||
bl_label = "Asset"
|
||||
node_color = "STRING"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_string_output("Path").subtype = "FILE_PATH"
|
||||
|
||||
asset: bpy.props.StringProperty(name="Asset",
|
||||
description="Asset to get the path from",
|
||||
update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.asset and self.asset in context.scene.sn.assets:
|
||||
if context.scene.sn.assets[self.asset].path:
|
||||
self.outputs["Path"].python_value = f"r\'{context.scene.sn.assets[self.asset].path}\'"
|
||||
return
|
||||
self.outputs["Path"].python_value = "\'\'"
|
||||
|
||||
def evaluate_export(self, context):
|
||||
if self.asset and self.asset in context.scene.sn.assets:
|
||||
if context.scene.sn.assets[self.asset].path:
|
||||
self.code_import = "import os"
|
||||
name = os.path.basename(context.scene.sn.assets[self.asset].path)
|
||||
if not name: name = os.path.basename(os.path.dirname(context.scene.sn.assets[self.asset].path))
|
||||
self.outputs["Path"].python_value = f"os.path.join(os.path.dirname(__file__), 'assets', '{name}')"
|
||||
return
|
||||
self.outputs["Path"].python_value = "\'\'"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop_search(self, "asset", context.scene.sn, "assets")
|
||||
@@ -0,0 +1,274 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
from .blend_data_base import BlendDataBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_MaterialsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_MaterialsBlendDataNode"
|
||||
bl_label = "Materials"
|
||||
|
||||
data_type = "Material"
|
||||
data_type_plural = "Materials"
|
||||
|
||||
active_path = "bpy.context.object.active_material"
|
||||
data_path = "bpy.data.materials"
|
||||
|
||||
|
||||
|
||||
class SN_MetaballsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_MetaballsBlendDataNode"
|
||||
bl_label = "Metaballs"
|
||||
|
||||
data_type = "Metaball"
|
||||
data_type_plural = "Metaballs"
|
||||
|
||||
data_path = "bpy.data.metaballs"
|
||||
|
||||
|
||||
|
||||
class SN_CurvesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_CurvesBlendDataNode"
|
||||
bl_label = "Curves"
|
||||
|
||||
data_type = "Curve"
|
||||
data_type_plural = "Curves"
|
||||
|
||||
data_path = "bpy.data.curves"
|
||||
|
||||
|
||||
|
||||
class SN_ActionsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_ActionsBlendDataNode"
|
||||
bl_label = "Actions"
|
||||
|
||||
data_type = "Action"
|
||||
data_type_plural = "Actions"
|
||||
|
||||
data_path = "bpy.data.actions"
|
||||
|
||||
|
||||
|
||||
class SN_ArmaturesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_ArmaturesBlendDataNode"
|
||||
bl_label = "Armatures"
|
||||
|
||||
data_type = "Armature"
|
||||
data_type_plural = "Armatures"
|
||||
|
||||
data_path = "bpy.data.armatures"
|
||||
|
||||
|
||||
|
||||
class SN_ImagesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_ImagesBlendDataNode"
|
||||
bl_label = "Images"
|
||||
|
||||
data_type = "Image"
|
||||
data_type_plural = "Images"
|
||||
|
||||
data_path = "bpy.data.images"
|
||||
|
||||
|
||||
|
||||
class SN_LightsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_LightsBlendDataNode"
|
||||
bl_label = "Lights"
|
||||
|
||||
data_type = "Light"
|
||||
data_type_plural = "Lights"
|
||||
|
||||
data_path = "bpy.data.lights"
|
||||
|
||||
|
||||
|
||||
class SN_NodeGroupsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_NodeGroupsBlendDataNode"
|
||||
bl_label = "Node Groups"
|
||||
|
||||
data_type = "Node Group"
|
||||
data_type_plural = "Node Groups"
|
||||
|
||||
data_path = "bpy.data.node_groups"
|
||||
|
||||
|
||||
|
||||
class SN_TextsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_TextsBlendDataNode"
|
||||
bl_label = "Texts"
|
||||
|
||||
data_type = "Text"
|
||||
data_type_plural = "Texts"
|
||||
|
||||
data_path = "bpy.data.texts"
|
||||
|
||||
|
||||
|
||||
class SN_TexturesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_TexturesBlendDataNode"
|
||||
bl_label = "Textures"
|
||||
|
||||
data_type = "Texture"
|
||||
data_type_plural = "Textures"
|
||||
|
||||
data_path = "bpy.data.textures"
|
||||
|
||||
|
||||
|
||||
class SN_WorkspacesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_WorkspacesBlendDataNode"
|
||||
bl_label = "Workspaces"
|
||||
|
||||
data_type = "Workspace"
|
||||
data_type_plural = "Workspaces"
|
||||
|
||||
data_path = "bpy.data.workspaces"
|
||||
active_path = "bpy.context.workspace"
|
||||
|
||||
|
||||
|
||||
class SN_WorldsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_WorldsBlendDataNode"
|
||||
bl_label = "Worlds"
|
||||
|
||||
data_type = "World"
|
||||
data_type_plural = "Worlds"
|
||||
|
||||
data_path = "bpy.data.worlds"
|
||||
active_path = "bpy.context.scene.world"
|
||||
|
||||
|
||||
|
||||
class SN_BrushesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_BrushesBlendDataNode"
|
||||
bl_label = "Brushes"
|
||||
|
||||
data_type = "Brush"
|
||||
data_type_plural = "Brushes"
|
||||
|
||||
data_path = "bpy.data.brushes"
|
||||
|
||||
|
||||
|
||||
class SN_CamerasBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_CamerasBlendDataNode"
|
||||
bl_label = "Cameras"
|
||||
|
||||
data_type = "Camera"
|
||||
data_type_plural = "Cameras"
|
||||
|
||||
data_path = "bpy.data.cameras"
|
||||
active_path = "bpy.context.scene.camera.data"
|
||||
|
||||
|
||||
|
||||
class SN_FontsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_FontsBlendDataNode"
|
||||
bl_label = "Fonts"
|
||||
|
||||
data_type = "Font"
|
||||
data_type_plural = "Fonts"
|
||||
|
||||
data_path = "bpy.data.fonts"
|
||||
|
||||
|
||||
|
||||
class SN_GreasePencilsBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_GreasePencilsBlendDataNode"
|
||||
bl_label = "Grease Pencils"
|
||||
|
||||
data_type = "Grease Pencil"
|
||||
data_type_plural = "Grease Pencils"
|
||||
|
||||
data_path = "bpy.data.grease_pencils"
|
||||
|
||||
|
||||
|
||||
class SN_LatticesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_LatticesBlendDataNode"
|
||||
bl_label = "Lattices"
|
||||
|
||||
data_type = "Lattice"
|
||||
data_type_plural = "Lattices"
|
||||
|
||||
data_path = "bpy.data.lattices"
|
||||
|
||||
|
||||
|
||||
class SN_ScenesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_ScenesBlendDataNode"
|
||||
bl_label = "Scenes"
|
||||
|
||||
data_type = "Scene"
|
||||
data_type_plural = "Scenes"
|
||||
|
||||
data_path = "bpy.data.scenes"
|
||||
active_path = "bpy.context.scene"
|
||||
|
||||
|
||||
|
||||
class SN_ScreensBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_ScreensBlendDataNode"
|
||||
bl_label = "Screens"
|
||||
|
||||
data_type = "Screen"
|
||||
data_type_plural = "Screens"
|
||||
|
||||
data_path = "bpy.data.screens"
|
||||
active_path = "bpy.context.screen"
|
||||
|
||||
|
||||
|
||||
class SN_ShapeKeysBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_ShapeKeysBlendDataNode"
|
||||
bl_label = "Shape Keys"
|
||||
|
||||
data_type = "Shape Key"
|
||||
data_type_plural = "Shape Keys"
|
||||
|
||||
data_path = "bpy.data.shape_keys"
|
||||
|
||||
|
||||
|
||||
class SN_VolumesBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_VolumesBlendDataNode"
|
||||
bl_label = "Volumes"
|
||||
|
||||
data_type = "Volume"
|
||||
data_type_plural = "Volumes"
|
||||
|
||||
data_path = "bpy.data.volumes"
|
||||
|
||||
|
||||
|
||||
class SN_WindowManagersBlendDataNode(bpy.types.Node, BlendDataBaseNode, SN_ScriptingBaseNode):
|
||||
|
||||
bl_idname = "SN_WindowManagersBlendDataNode"
|
||||
bl_label = "Window Managers"
|
||||
|
||||
data_type = "Window Manager"
|
||||
data_type_plural = "Window Managers"
|
||||
|
||||
data_path = "bpy.data.window_managers"
|
||||
active_path = "bpy.context.window_manager"
|
||||
@@ -0,0 +1,22 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BlenderDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BlenderDataNode"
|
||||
bl_label = "Blender Data"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_output("Current Context")
|
||||
self.add_property_output("Blend Data")
|
||||
self.add_property_output("App")
|
||||
self.add_property_output("Path")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Current Context"].python_value = "bpy.context"
|
||||
self.outputs["Blend Data"].python_value = "bpy.data"
|
||||
self.outputs["App"].python_value = "bpy.app"
|
||||
self.outputs["Path"].python_value = "bpy.path"
|
||||
@@ -0,0 +1,39 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_CollectionsBlendDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_CollectionsBlendDataNode"
|
||||
bl_label = "Collections"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_collection_property_output("All Collections")
|
||||
self.add_collection_property_output("Scene Collections")
|
||||
self.add_property_output("Scene Collection")
|
||||
self.add_property_output("Active Collection")
|
||||
self.add_property_output("Indexed")
|
||||
self.add_integer_input("Index")
|
||||
|
||||
def update_index_type(self, context):
|
||||
inp = self.convert_socket(self.inputs[0], self.socket_names[self.index_type])
|
||||
inp.name = "Index" if self.index_type == "Integer" else "Name"
|
||||
self._evaluate(context)
|
||||
|
||||
index_type: bpy.props.EnumProperty(name="Index Type",
|
||||
description="The type of index to use",
|
||||
items=[("Integer", "Index", "Starts at 0. Negative indices go to the back of the list."),
|
||||
("String", "Name", "Refers to the name property of the element.")],
|
||||
update=update_index_type)
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["All Collections"].python_value = f"bpy.data.collections"
|
||||
self.outputs["Scene Collections"].python_value = f"bpy.context.scene.collection.children"
|
||||
self.outputs["Scene Collection"].python_value = f"bpy.context.scene.collection"
|
||||
self.outputs["Active Collection"].python_value = f"bpy.context.collection"
|
||||
self.outputs["Indexed"].python_value = f"bpy.data.collections[{self.inputs[0].python_value}]"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "index_type", expand=True)
|
||||
@@ -0,0 +1,37 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_MeshBlendDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_MeshBlendDataNode"
|
||||
bl_label = "Meshes"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_collection_property_output("All Meshes")
|
||||
self.add_list_output("Active Scene Meshes")
|
||||
self.add_property_output("Active Object Mesh")
|
||||
self.add_property_output("Indexed")
|
||||
self.add_integer_input("Index")
|
||||
|
||||
def update_index_type(self, context):
|
||||
inp = self.convert_socket(self.inputs[0], self.socket_names[self.index_type])
|
||||
inp.name = "Index" if self.index_type == "Integer" else "Name"
|
||||
self._evaluate(context)
|
||||
|
||||
index_type: bpy.props.EnumProperty(name="Index Type",
|
||||
description="The type of index to use",
|
||||
items=[("Integer", "Index", "Starts at 0. Negative indices go to the back of the list."),
|
||||
("String", "Name", "Refers to the name property of the element.")],
|
||||
update=update_index_type)
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["All Meshes"].python_value = f"bpy.data.meshes"
|
||||
self.outputs["Active Scene Meshes"].python_value = f"list(filter(lambda obj: obj, [obj.data if obj.type == 'MESH' else None for obj in bpy.context.scene.objects]))"
|
||||
self.outputs["Active Object Mesh"].python_value = f"(bpy.context.active_object.data if bpy.context.active_object.type == 'MESH' else None)"
|
||||
self.outputs["Indexed"].python_value = f"bpy.data.meshes[{self.inputs[0].python_value}]"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "index_type", expand=True)
|
||||
@@ -0,0 +1,39 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_ObjectBlendDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_ObjectBlendDataNode"
|
||||
bl_label = "Objects"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_collection_property_output("All Objects")
|
||||
self.add_collection_property_output("Active Scene Objects")
|
||||
self.add_collection_property_output("Selected Objects")
|
||||
self.add_property_output("Active Object")
|
||||
self.add_property_output("Indexed")
|
||||
self.add_integer_input("Index")
|
||||
|
||||
def update_index_type(self, context):
|
||||
inp = self.convert_socket(self.inputs[0], self.socket_names[self.index_type])
|
||||
inp.name = "Index" if self.index_type == "Integer" else "Name"
|
||||
self._evaluate(context)
|
||||
|
||||
index_type: bpy.props.EnumProperty(name="Index Type",
|
||||
description="The type of index to use",
|
||||
items=[("Integer", "Index", "Starts at 0. Negative indices go to the back of the list."),
|
||||
("String", "Name", "Refers to the name property of the element.")],
|
||||
update=update_index_type)
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["All Objects"].python_value = f"bpy.data.objects"
|
||||
self.outputs["Active Scene Objects"].python_value = f"bpy.context.scene.objects"
|
||||
self.outputs["Selected Objects"].python_value = f"bpy.context.view_layer.objects.selected"
|
||||
self.outputs["Active Object"].python_value = f"bpy.context.view_layer.objects.active"
|
||||
self.outputs["Indexed"].python_value = f"bpy.data.objects[{self.inputs[0].python_value}]"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "index_type", expand=True)
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import bpy
|
||||
|
||||
|
||||
|
||||
class BlendDataBaseNode():
|
||||
|
||||
data_type = ""
|
||||
data_type_plural = ""
|
||||
|
||||
active_path = ""
|
||||
data_path = ""
|
||||
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_collection_property_output(f"All {self.data_type_plural}")
|
||||
if self.active_path: self.add_property_output(f"Active {self.data_type}")
|
||||
self.add_property_output("Indexed")
|
||||
self.add_integer_input("Index")
|
||||
|
||||
def update_index_type(self, context):
|
||||
inp = self.convert_socket(self.inputs[0], self.socket_names[self.index_type])
|
||||
inp.name = "Index" if self.index_type == "Integer" else "Name"
|
||||
self._evaluate(context)
|
||||
|
||||
index_type: bpy.props.EnumProperty(name="Index Type",
|
||||
description="The type of index to use",
|
||||
items=[("Integer", "Index", "Starts at 0. Negative indices go to the back of the list."),
|
||||
("String", "Name", "Refers to the name property of the element.")],
|
||||
update=update_index_type)
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[f"All {self.data_type_plural}"].python_value = self.data_path
|
||||
if self.active_path: self.outputs[f"Active {self.data_type}"].python_value = self.active_path
|
||||
self.outputs["Indexed"].python_value = f"{self.data_path}[{self.inputs[0].python_value}]"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "index_type", expand=True)
|
||||
@@ -0,0 +1,21 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BooleanVectorNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BooleanVectorNode"
|
||||
bl_label = "Boolean Vector"
|
||||
node_color = "VECTOR"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_boolean_vector_input("Boolean").set_hide(True)
|
||||
self.add_boolean_vector_output("Boolean")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Boolean"].python_value = self.inputs["Boolean"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["Boolean"], "size")
|
||||
self.inputs["Boolean"].draw_socket(context, layout, self, "Value")
|
||||
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BooleanNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BooleanNode"
|
||||
bl_label = "Boolean"
|
||||
node_color = "BOOLEAN"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_boolean_input("Boolean").set_hide(True)
|
||||
self.add_boolean_output("Boolean")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Boolean"].python_value = self.inputs["Boolean"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["Boolean"], "default_value", text="Value")
|
||||
@@ -0,0 +1,35 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_ColorNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_ColorNode"
|
||||
bl_label = "Color"
|
||||
node_color = "VECTOR"
|
||||
|
||||
def update_size(self, context):
|
||||
if self.use_four:
|
||||
self.inputs[0].subtype = "COLOR_ALPHA"
|
||||
self.outputs[0].subtype = "COLOR_ALPHA"
|
||||
else:
|
||||
self.inputs[0].subtype = "COLOR"
|
||||
self.outputs[0].subtype = "COLOR"
|
||||
|
||||
use_four: bpy.props.BoolProperty(default=False,
|
||||
name="Use Alpha",
|
||||
update=update_size)
|
||||
|
||||
def on_create(self, context):
|
||||
socket = self.add_float_vector_input("Color")
|
||||
socket.set_hide(True)
|
||||
socket.subtype = "COLOR"
|
||||
self.add_float_vector_output("Color").subtype = "COLOR"
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Color"].python_value = self.inputs["Color"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "use_four")
|
||||
self.inputs["Color"].draw_socket(context, layout, self, "")
|
||||
@@ -0,0 +1,21 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_FloatVectorNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_FloatVectorNode"
|
||||
bl_label = "Float Vector"
|
||||
node_color = "VECTOR"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_float_vector_input("Float").set_hide(True)
|
||||
self.add_float_vector_output("Float")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Float"].python_value = self.inputs["Float"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["Float"], "size")
|
||||
self.inputs["Float"].draw_socket(context, layout, self, "Value")
|
||||
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_FloatNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_FloatNode"
|
||||
bl_label = "Float"
|
||||
node_color = "FLOAT"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_float_input("Float").set_hide(True)
|
||||
self.add_float_output("Float")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Float"].python_value = self.inputs["Float"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["Float"], "default_value", text="")
|
||||
@@ -0,0 +1,21 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_IntegerVectorNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_IntegerVectorNode"
|
||||
bl_label = "Integer Vector"
|
||||
node_color = "VECTOR"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_integer_vector_input("Integer").set_hide(True)
|
||||
self.add_integer_vector_output("Integer")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Integer"].python_value = self.inputs["Integer"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["Integer"], "size")
|
||||
self.inputs["Integer"].draw_socket(context, layout, self, "Value")
|
||||
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_IntegerNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_IntegerNode"
|
||||
bl_label = "Integer"
|
||||
node_color = "INTEGER"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_integer_input("Integer").set_hide(True)
|
||||
self.add_integer_output("Integer")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Integer"].python_value = self.inputs["Integer"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["Integer"], "default_value", text="")
|
||||
@@ -0,0 +1,19 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_ListNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_ListNode"
|
||||
bl_label = "List"
|
||||
node_color = "LIST"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_dynamic_data_input("Data")
|
||||
self.add_list_output("List")
|
||||
|
||||
def evaluate(self, context):
|
||||
items = [inp.python_value if inp.is_linked else None for inp in self.inputs[:-1]]
|
||||
items = filter(lambda item: item != None, items)
|
||||
self.outputs["List"].python_value = f"[{', '.join(items)}]"
|
||||
@@ -0,0 +1,12 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_NoneNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_NoneNode"
|
||||
bl_label = "None"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_data_output("None")
|
||||
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_StringNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_StringNode"
|
||||
bl_label = "String"
|
||||
node_color = "STRING"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_string_input("String").set_hide(True)
|
||||
self.add_string_output("String")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["String"].python_value = self.inputs["String"].python_value
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self.inputs["String"], "default_value", text="")
|
||||
@@ -0,0 +1,26 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BMeshDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BMeshDataNode"
|
||||
bl_label = "BMesh Object Data"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("BMesh")
|
||||
self.add_collection_property_output("Vertices")
|
||||
self.add_collection_property_output("Faces")
|
||||
self.add_collection_property_output("Edges")
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.inputs["BMesh"].is_linked:
|
||||
self.outputs["Vertices"].python_value = f"{self.inputs['BMesh'].python_value}.verts"
|
||||
self.outputs["Faces"].python_value = f"{self.inputs['BMesh'].python_value}.faces"
|
||||
self.outputs["Edges"].python_value = f"{self.inputs['BMesh'].python_value}.edges"
|
||||
else:
|
||||
self.outputs["Vertices"].reset_value()
|
||||
self.outputs["Faces"].reset_value()
|
||||
self.outputs["Edges"].reset_value()
|
||||
@@ -0,0 +1,58 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BMeshEdgeDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BMeshEdgeDataNode"
|
||||
bl_label = "BMesh Edge Data"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def update_with_transforms(self, context):
|
||||
self.inputs["Object"].set_hide(not self.with_transforms)
|
||||
self._evaluate(context)
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("BMesh Edge")
|
||||
self.add_property_input("Object").set_hide(True)
|
||||
self.add_collection_property_output("Vertices")
|
||||
self.add_collection_property_output("Faces")
|
||||
self.add_boolean_output("Selected")
|
||||
self.add_boolean_output("Hidden")
|
||||
self.add_boolean_output("Smooth")
|
||||
self.add_integer_output("Index")
|
||||
self.add_boolean_output("Is Manifold")
|
||||
self.add_boolean_output("Is Boundary")
|
||||
self.add_boolean_output("Is Contiguous")
|
||||
self.add_boolean_output("Is Convex")
|
||||
self.add_boolean_output("Is Wire")
|
||||
self.add_boolean_output("Is Seam")
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.inputs["BMesh Edge"].is_linked:
|
||||
self.outputs["Faces"].python_value = f"{self.inputs['BMesh Edge'].python_value}.link_faces"
|
||||
self.outputs["Vertices"].python_value = f"{self.inputs['BMesh Edge'].python_value}.verts"
|
||||
self.outputs["Is Boundary"].python_value = f"{self.inputs['BMesh Edge'].python_value}.is_boundary"
|
||||
self.outputs["Is Contiguous"].python_value = f"{self.inputs['BMesh Edge'].python_value}.is_contiguous"
|
||||
self.outputs["Is Convex"].python_value = f"{self.inputs['BMesh Edge'].python_value}.is_convex"
|
||||
self.outputs["Is Wire"].python_value = f"{self.inputs['BMesh Edge'].python_value}.is_wire"
|
||||
self.outputs["Is Seam"].python_value = f"{self.inputs['BMesh Edge'].python_value}.seam"
|
||||
self.outputs["Selected"].python_value = f"{self.inputs['BMesh Edge'].python_value}.select"
|
||||
self.outputs["Hidden"].python_value = f"{self.inputs['BMesh Edge'].python_value}.hide"
|
||||
self.outputs["Smooth"].python_value = f"{self.inputs['BMesh Edge'].python_value}.smooth"
|
||||
self.outputs["Index"].python_value = f"{self.inputs['BMesh Edge'].python_value}.index"
|
||||
self.outputs["Is Manifold"].python_value = f"{self.inputs['BMesh Edge'].python_value}.is_manifold"
|
||||
else:
|
||||
self.outputs["Index"].reset_value()
|
||||
self.outputs["Selected"].reset_value()
|
||||
self.outputs["Hidden"].reset_value()
|
||||
self.outputs["Smooth"].reset_value()
|
||||
self.outputs["Is Boundary"].reset_value()
|
||||
self.outputs["Is Contiguous"].reset_value()
|
||||
self.outputs["Is Convex"].reset_value()
|
||||
self.outputs["Is Wire"].reset_value()
|
||||
self.outputs["Is Seam"].reset_value()
|
||||
self.outputs["Vertices"].reset_value()
|
||||
self.outputs["Faces"].reset_value()
|
||||
self.outputs["Is Manifold"].reset_value()
|
||||
@@ -0,0 +1,48 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BMeshFaceDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BMeshFaceDataNode"
|
||||
bl_label = "BMesh Face Data"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("BMesh Face")
|
||||
self.add_property_input("Object").set_hide(True)
|
||||
self.add_collection_property_output("Edges")
|
||||
self.add_collection_property_output("Vertices")
|
||||
self.add_float_vector_output("Center")
|
||||
self.add_float_vector_output("Center Weighted")
|
||||
self.add_float_vector_output("Normal")
|
||||
self.add_boolean_output("Selected")
|
||||
self.add_boolean_output("Hidden")
|
||||
self.add_boolean_output("Smooth")
|
||||
self.add_integer_output("Index")
|
||||
self.add_integer_output("Material Index")
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.inputs["BMesh Face"].is_linked:
|
||||
self.outputs["Edges"].python_value = f"{self.inputs['BMesh Face'].python_value}.edges"
|
||||
self.outputs["Vertices"].python_value = f"{self.inputs['BMesh Face'].python_value}.verts"
|
||||
self.outputs["Center"].python_value = f"{self.inputs['BMesh Face'].python_value}.calc_center_median()"
|
||||
self.outputs["Center Weighted"].python_value = f"{self.inputs['BMesh Face'].python_value}.calc_center_median_weighted()"
|
||||
self.outputs["Normal"].python_value = f"{self.inputs['BMesh Face'].python_value}.normal"
|
||||
self.outputs["Selected"].python_value = f"{self.inputs['BMesh Face'].python_value}.select"
|
||||
self.outputs["Hidden"].python_value = f"{self.inputs['BMesh Face'].python_value}.hide"
|
||||
self.outputs["Smooth"].python_value = f"{self.inputs['BMesh Face'].python_value}.smooth"
|
||||
self.outputs["Index"].python_value = f"{self.inputs['BMesh Face'].python_value}.index"
|
||||
self.outputs["Material Index"].python_value = f"{self.inputs['BMesh Face'].python_value}.material_index"
|
||||
else:
|
||||
self.outputs["Normal"].reset_value()
|
||||
self.outputs["Index"].reset_value()
|
||||
self.outputs["Selected"].reset_value()
|
||||
self.outputs["Hidden"].reset_value()
|
||||
self.outputs["Center"].reset_value()
|
||||
self.outputs["Center Weighted"].reset_value()
|
||||
self.outputs["Smooth"].reset_value()
|
||||
self.outputs["Edges"].reset_value()
|
||||
self.outputs["Vertices"].reset_value()
|
||||
self.outputs["Material Index"].reset_value()
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_BMeshVertexDataNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_BMeshVertexDataNode"
|
||||
bl_label = "BMesh Vertex Data"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def update_with_transforms(self, context):
|
||||
self.inputs["Object"].set_hide(not self.with_transforms)
|
||||
self._evaluate(context)
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("BMesh Vertex")
|
||||
self.add_property_input("Object").set_hide(True)
|
||||
self.add_float_vector_output("Location")
|
||||
self.add_float_vector_output("Normal")
|
||||
self.add_boolean_output("Selected")
|
||||
self.add_boolean_output("Hidden")
|
||||
self.add_integer_output("Index")
|
||||
self.add_boolean_output("Is Boundary")
|
||||
self.add_boolean_output("Is Manifold")
|
||||
self.add_boolean_output("Is Wire")
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.inputs["BMesh Vertex"].is_linked:
|
||||
self.outputs["Location"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.co"
|
||||
self.outputs["Normal"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.normal"
|
||||
self.outputs["Selected"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.select"
|
||||
self.outputs["Hidden"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.hide"
|
||||
self.outputs["Index"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.index"
|
||||
self.outputs["Is Boundary"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.is_boundary"
|
||||
self.outputs["Is Manifold"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.is_manifold"
|
||||
self.outputs["Is Wire"].python_value = f"{self.inputs['BMesh Vertex'].python_value}.is_wire"
|
||||
else:
|
||||
self.outputs["Location"].reset_value()
|
||||
self.outputs["Normal"].reset_value()
|
||||
self.outputs["Index"].reset_value()
|
||||
self.outputs["Selected"].reset_value()
|
||||
self.outputs["Hidden"].reset_value()
|
||||
self.outputs["Index"].reset_value()
|
||||
self.outputs["Is Boundary"].reset_value()
|
||||
self.outputs["Is Manifold"].reset_value()
|
||||
self.outputs["Is Wire"].reset_value()
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_CreateLineLocationsNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_CreateLineLocationsNode"
|
||||
bl_label = "Create Line"
|
||||
node_color = "PROGRAM"
|
||||
|
||||
def update_use3d(self, context):
|
||||
for input in self.inputs:
|
||||
if input.bl_label == "Float Vector" and input.subtype == "NONE":
|
||||
input.size = 3 if self.use_3d else 2
|
||||
self._evaluate(context)
|
||||
|
||||
use_3d: bpy.props.BoolProperty(name="Use 3D",
|
||||
description="Whether to use 3D or 2D coordinates",
|
||||
default=False,
|
||||
update=update_use3d)
|
||||
|
||||
def on_create(self, context):
|
||||
inp = self.add_float_vector_input("Point 1")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 0
|
||||
inp.default_value[1] = 0
|
||||
inp.default_value[2] = 0
|
||||
|
||||
inp = self.add_float_vector_input("Point 2")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 0
|
||||
inp.default_value[1] = 1
|
||||
inp.default_value[2] = 1
|
||||
|
||||
self.add_list_output("Line")
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "use_3d", text="Use 3D")
|
||||
|
||||
def evaluate(self, context):
|
||||
p1 = self.inputs["Point 1"].python_value
|
||||
p2 = self.inputs["Point 2"].python_value
|
||||
|
||||
self.outputs[0].python_value = f"[{p1}, {p2}]"
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_CreateQuadLocationsNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_CreateQuadLocationsNode"
|
||||
bl_label = "Create Quad"
|
||||
node_color = "PROGRAM"
|
||||
|
||||
def update_use3d(self, context):
|
||||
for input in self.inputs:
|
||||
if input.bl_label == "Float Vector" and input.subtype == "NONE":
|
||||
input.size = 3 if self.use_3d else 2
|
||||
self._evaluate(context)
|
||||
|
||||
use_3d: bpy.props.BoolProperty(name="Use 3D",
|
||||
description="Whether to use 3D or 2D coordinates",
|
||||
default=False,
|
||||
update=update_use3d)
|
||||
|
||||
def on_create(self, context):
|
||||
inp = self.add_float_vector_input("Bottom Left")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 0
|
||||
inp.default_value[1] = 0
|
||||
inp.default_value[2] = 0
|
||||
|
||||
inp = self.add_float_vector_input("Bottom Right")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 1
|
||||
inp.default_value[1] = 0
|
||||
inp.default_value[2] = 0
|
||||
|
||||
inp = self.add_float_vector_input("Top Right")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 1
|
||||
inp.default_value[1] = 1
|
||||
inp.default_value[2] = 1
|
||||
|
||||
inp = self.add_float_vector_input("Top Left")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 0
|
||||
inp.default_value[1] = 1
|
||||
inp.default_value[2] = 1
|
||||
|
||||
self.add_list_output("Quad")
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "use_3d", text="Use 3D")
|
||||
|
||||
def evaluate(self, context):
|
||||
bl = self.inputs["Bottom Left"].python_value
|
||||
br = self.inputs["Bottom Right"].python_value
|
||||
tl = self.inputs["Top Left"].python_value
|
||||
tr = self.inputs["Top Right"].python_value
|
||||
|
||||
self.outputs[0].python_value = f"[{bl}, {br}, {tl}, {tr}]"
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_CreateTriangleLocationsNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_CreateTriangleLocationsNode"
|
||||
bl_label = "Create Triangle"
|
||||
node_color = "PROGRAM"
|
||||
|
||||
def update_use3d(self, context):
|
||||
for input in self.inputs:
|
||||
if input.bl_label == "Float Vector" and input.subtype == "NONE":
|
||||
input.size = 3 if self.use_3d else 2
|
||||
self._evaluate(context)
|
||||
|
||||
use_3d: bpy.props.BoolProperty(name="Use 3D",
|
||||
description="Whether to use 3D or 2D coordinates",
|
||||
default=False,
|
||||
update=update_use3d)
|
||||
|
||||
def on_create(self, context):
|
||||
inp = self.add_float_vector_input("Corner 1")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 0
|
||||
inp.default_value[1] = 0
|
||||
inp.default_value[2] = 0
|
||||
|
||||
inp = self.add_float_vector_input("Corner 2")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 1
|
||||
inp.default_value[1] = 0
|
||||
inp.default_value[2] = 0
|
||||
|
||||
inp = self.add_float_vector_input("Corner 3")
|
||||
inp.size = 2
|
||||
inp.default_value[0] = 1
|
||||
inp.default_value[1] = 1
|
||||
inp.default_value[2] = 1
|
||||
|
||||
self.add_list_output("Triangle")
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "use_3d", text="Use 3D")
|
||||
|
||||
def evaluate(self, context):
|
||||
c1 = self.inputs["Corner 1"].python_value
|
||||
c2 = self.inputs["Corner 2"].python_value
|
||||
c3 = self.inputs["Corner 3"].python_value
|
||||
|
||||
self.outputs[0].python_value = f"[{c1}, {c2}, {c3}]"
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_FacesToVertexLocationsNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_FacesToVertexLocationsNode"
|
||||
bl_label = "BMesh Faces To Vertex Locations"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_collection_property_input("Faces")
|
||||
self.add_list_output("All Locations")
|
||||
self.add_list_output("Quad Locations Only")
|
||||
self.add_list_output("Triangle Locations Only")
|
||||
self.add_list_output("Ngon Locations Only")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_imperative = f"""
|
||||
def get_bmesh_vertex_locations(faces):
|
||||
locations = []
|
||||
for face in faces:
|
||||
face_locations = []
|
||||
for vert in face.verts:
|
||||
face_locations.append(vert.co)
|
||||
locations.append(face_locations)
|
||||
return locations
|
||||
|
||||
def get_bmesh_vertex_locations_quads(faces):
|
||||
locations = get_bmesh_vertex_locations(faces)
|
||||
return [loc for loc in locations if len(loc) == 4]
|
||||
|
||||
def get_bmesh_vertex_locations_triangles(faces):
|
||||
locations = get_bmesh_vertex_locations(faces)
|
||||
return [loc for loc in locations if len(loc) == 3]
|
||||
|
||||
def get_bmesh_vertex_locations_ngons(faces):
|
||||
locations = get_bmesh_vertex_locations(faces)
|
||||
return [loc for loc in locations if len(loc) > 4]
|
||||
"""
|
||||
|
||||
self.outputs["All Locations"].python_value = f"get_bmesh_vertex_locations({self.inputs['Faces'].python_value})"
|
||||
self.outputs["Quad Locations Only"].python_value = f"get_bmesh_vertex_locations_quads({self.inputs['Faces'].python_value})"
|
||||
self.outputs["Triangle Locations Only"].python_value = f"get_bmesh_vertex_locations_triangles({self.inputs['Faces'].python_value})"
|
||||
self.outputs["Ngon Locations Only"].python_value = f"get_bmesh_vertex_locations_ngons({self.inputs['Faces'].python_value})"
|
||||
@@ -0,0 +1,43 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_ObjectToBMeshNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_ObjectToBMeshNode"
|
||||
bl_label = "Object To BMesh"
|
||||
node_color = "PROGRAM"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_execute_input()
|
||||
self.add_property_input("Object")
|
||||
self.add_boolean_input("Use Edit Mode").default_value = True
|
||||
self.add_boolean_input("Use Evaluated Mesh")
|
||||
self.add_boolean_input("Use Object Transforms").default_value = True
|
||||
self.add_execute_output()
|
||||
self.add_property_output("BMesh")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_import = f"import bmesh"
|
||||
|
||||
self.code = f"""
|
||||
bm_{self.static_uid} = bmesh.new()
|
||||
if {self.inputs["Object"].python_value}:
|
||||
if {self.inputs["Object"].python_value}.mode == 'EDIT' and {self.inputs["Use Edit Mode"].python_value}:
|
||||
bm_{self.static_uid} = bmesh.from_edit_mesh({self.inputs["Object"].python_value}.data)
|
||||
else:
|
||||
if {self.inputs["Use Evaluated Mesh"].python_value}:
|
||||
dg = bpy.context.evaluated_depsgraph_get()
|
||||
bm_{self.static_uid}.from_mesh({self.inputs["Object"].python_value}.evaluated_get(dg).to_mesh())
|
||||
else:
|
||||
bm_{self.static_uid}.from_mesh({self.inputs["Object"].python_value}.data)
|
||||
if {self.inputs["Use Object Transforms"].python_value}:
|
||||
bm_{self.static_uid}.transform({self.inputs["Object"].python_value}.matrix_world)
|
||||
bm_{self.static_uid}.verts.ensure_lookup_table()
|
||||
bm_{self.static_uid}.faces.ensure_lookup_table()
|
||||
bm_{self.static_uid}.edges.ensure_lookup_table()
|
||||
{self.indent(self.outputs[0].python_value, 3)}
|
||||
"""
|
||||
|
||||
self.outputs["BMesh"].python_value = f"bm_{self.static_uid}"
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_NgonToTriangleLocationsNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_NgonToTriangleLocationsNode"
|
||||
bl_label = "Ngon To Triangle Locations"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("Ngon")
|
||||
self.add_list_output("Triangle Locations")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_imperative = """
|
||||
def ngon_to_triangle_locations(ngon):
|
||||
bm = bmesh.new()
|
||||
for v in ngon.verts:
|
||||
bm.verts.new(v.co)
|
||||
bm.faces.new(bm.verts)
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||
bm.verts.ensure_lookup_table()
|
||||
new_faces = []
|
||||
for f in bm.faces:
|
||||
vert_locations = []
|
||||
for v in f.verts:
|
||||
vert_locations.append(tuple(v.co))
|
||||
new_faces.append(vert_locations)
|
||||
return new_faces
|
||||
"""
|
||||
|
||||
self.outputs["Triangle Locations"].python_value = f"ngon_to_triangle_locations({self.inputs['Ngon'].python_value})"
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import bpy
|
||||
from ...base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_TriangulateBmeshNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_TriangulateBmeshNode"
|
||||
bl_label = "Triangulate BMesh"
|
||||
node_color = "PROGRAM"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_execute_input()
|
||||
self.add_property_input("BMesh")
|
||||
self.add_execute_output()
|
||||
self.add_property_output("BMesh")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code = f"""
|
||||
bm_{self.static_uid} = {self.inputs["BMesh"].python_value}.copy()
|
||||
bmesh.ops.triangulate(bm_{self.static_uid}, faces=bm_{self.static_uid}.faces)
|
||||
bm_{self.static_uid}.verts.ensure_lookup_table()
|
||||
bm_{self.static_uid}.faces.ensure_lookup_table()
|
||||
bm_{self.static_uid}.edges.ensure_lookup_table()
|
||||
{self.indent(self.outputs[0].python_value, 3)}
|
||||
"""
|
||||
|
||||
self.outputs["BMesh"].python_value = f"bm_{self.static_uid}"
|
||||
@@ -0,0 +1,20 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_GetEditSelectNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_GetEditSelectNode"
|
||||
bl_label = "Get Edit Select Mode"
|
||||
node_color = "BOOLEAN"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_boolean_output("Vertex")
|
||||
self.add_boolean_output("Edge")
|
||||
self.add_boolean_output("Face")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[0].python_value = f"bpy.context.tool_settings.mesh_select_mode[0]"
|
||||
self.outputs[1].python_value = f"bpy.context.tool_settings.mesh_select_mode[1]"
|
||||
self.outputs[2].python_value = f"bpy.context.tool_settings.mesh_select_mode[2]"
|
||||
@@ -0,0 +1,111 @@
|
||||
import bpy
|
||||
import os
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_IconNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_IconNode"
|
||||
bl_label = "Icon"
|
||||
node_color = "ICON"
|
||||
bl_width_default = 200
|
||||
|
||||
def on_create(self, context):
|
||||
inp = self.add_string_input("Image Path")
|
||||
inp.subtype = "FILE_PATH"
|
||||
inp.set_hide(True)
|
||||
self.add_icon_output("Icon")
|
||||
|
||||
|
||||
def update_icon_source(self, context):
|
||||
if self.icon_source == "PATH":
|
||||
self.inputs[0].set_hide(False)
|
||||
else:
|
||||
self.inputs[0].set_hide(True)
|
||||
self._evaluate(context)
|
||||
|
||||
icon_source: bpy.props.EnumProperty(name="Icon Source",
|
||||
description="The source of the icons",
|
||||
items=[("BLENDER","Blender","Blender",0),
|
||||
("CUSTOM","Image","Image",1),
|
||||
("PATH","Path","Path",2)],
|
||||
update=update_icon_source)
|
||||
|
||||
|
||||
icon: bpy.props.IntProperty(name="Value", description="Value of this socket", update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
def update_icon_file(self, context):
|
||||
if self.icon_file:
|
||||
self.icon_file.use_fake_user = True
|
||||
self.icon_file.preview_ensure()
|
||||
self._evaluate(context)
|
||||
|
||||
icon_file: bpy.props.PointerProperty(type=bpy.types.Image,
|
||||
name="Image File",
|
||||
description="The image you want to use as an icon",
|
||||
update=update_icon_file)
|
||||
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.icon_source == "BLENDER":
|
||||
self.outputs["Icon"].python_value = f"{self.icon}"
|
||||
elif self.icon_source == "CUSTOM":
|
||||
if self.icon_file:
|
||||
self.outputs["Icon"].python_value = f"bpy.data.images['{self.icon_file.name}'].preview.icon_id"
|
||||
else:
|
||||
self.outputs["Icon"].python_value = "0"
|
||||
elif self.icon_source == "PATH":
|
||||
self.code_import = "import os"
|
||||
self.code_imperative = f"""
|
||||
def load_preview_icon(path):
|
||||
global _icons
|
||||
if not path in _icons:
|
||||
if os.path.exists(path):
|
||||
_icons.load(path, path, "IMAGE")
|
||||
else:
|
||||
return 0
|
||||
return _icons[path].icon_id
|
||||
"""
|
||||
self.outputs["Icon"].python_value = f"load_preview_icon({self.inputs[0].python_value})"
|
||||
|
||||
|
||||
def evaluate_export(self, context):
|
||||
if self.icon_source == "BLENDER":
|
||||
self.outputs["Icon"].python_value = f"{self.icon}"
|
||||
elif self.icon_source == "CUSTOM":
|
||||
if self.icon_file:
|
||||
self.outputs["Icon"].python_value = f"_icons['{self.icon_file.name}'].icon_id"
|
||||
self.code_import = "import os"
|
||||
self.code_register = f"""
|
||||
if not '{self.icon_file.name}' in _icons: _icons.load('{self.icon_file.name}', os.path.join(os.path.dirname(__file__), 'icons', '{self.icon_file.name}'), "IMAGE")
|
||||
"""
|
||||
else:
|
||||
self.outputs["Icon"].python_value = "0"
|
||||
elif self.icon_source == "PATH":
|
||||
self.code_import = "import os"
|
||||
self.code_imperative = f"""
|
||||
def load_preview_icon(path):
|
||||
global _icons
|
||||
if not path in _icons:
|
||||
if os.path.exists(path):
|
||||
_icons.load(path, path, "IMAGE")
|
||||
else:
|
||||
return 0
|
||||
return _icons[path].icon_id
|
||||
"""
|
||||
self.outputs["Icon"].python_value = f"load_preview_icon({self.inputs[0].python_value})"
|
||||
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
row = layout.row()
|
||||
row.scale_y = 1.2
|
||||
row.prop(self,"icon_source",expand=True)
|
||||
|
||||
if self.icon_source == "BLENDER":
|
||||
op = layout.operator("sn.select_icon", text="Choose Icon", icon_value=self.icon)
|
||||
op.icon_data_path = f"bpy.data.node_groups['{self.node_tree.name}'].nodes['{self.name}']"
|
||||
elif self.icon_source == "CUSTOM":
|
||||
layout.template_ID(self, "icon_file", new="image.new", open="image.open", live_icon=True)
|
||||
if self.icon_file and not self.icon_file.filepath:
|
||||
layout.label(text="Image not saved!", icon="ERROR")
|
||||
@@ -0,0 +1,19 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_IsExportNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_IsExportNode"
|
||||
bl_label = "Is Export"
|
||||
node_color = "BOOLEAN"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_boolean_output("Is Export")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[0].python_value = f"False"
|
||||
|
||||
def evaluate_export(self, context):
|
||||
self.outputs[0].python_value = f"True"
|
||||
@@ -0,0 +1,34 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_InModeNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_InModeNode"
|
||||
bl_label = "In Mode"
|
||||
node_color = "BOOLEAN"
|
||||
|
||||
def get_modes(self,context):
|
||||
items = []
|
||||
modes = ["EDIT_MESH", "EDIT_CURVE", "EDIT_SURFACE", "EDIT_TEXT", "EDIT_ARMATURE",
|
||||
"EDIT_METABALL", "EDIT_LATTICE", "POSE", "SCULPT", "PAINT_WEIGHT", "PAINT_VERTEX",
|
||||
"PAINT_TEXTURE", "PARTICLE", "OBJECT", "PAINT_GPENCIL", "EDIT_GPENCIL",
|
||||
"SCULPT_GPENCIL" "WEIGHT_GPENCIL", "VERTEX_GPENCIL", "SCULPT_CURVES"]
|
||||
for mode in modes:
|
||||
items.append((mode,mode.replace("_"," ").title(),mode))
|
||||
return items
|
||||
|
||||
modes: bpy.props.EnumProperty(items=get_modes,
|
||||
update=SN_ScriptingBaseNode._evaluate,
|
||||
name="Mode",
|
||||
description="The mode which the active mode is compared to")
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "modes", text="")
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_boolean_output("In Mode")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[0].python_value = f"'{self.modes}'==bpy.context.mode"
|
||||
@@ -0,0 +1,46 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
from ..Input.Node_Idname import NodeType
|
||||
|
||||
class SN_NodeIsIdname(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_NodeIsIdname"
|
||||
bl_label = "Node Is Idname"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
|
||||
nodes: bpy.props.CollectionProperty(type=NodeType)
|
||||
|
||||
node: bpy.props.StringProperty(name="Node",
|
||||
description="The node to get the type for",
|
||||
update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("Node")
|
||||
self.add_boolean_output("Is ID Name")
|
||||
|
||||
# load internal nodes
|
||||
for name in dir(bpy.types):
|
||||
cls = getattr(bpy.types, name)
|
||||
if hasattr(cls, "bl_rna") and cls.bl_rna.base and "Node" in cls.bl_rna.base.bl_rna.name:
|
||||
item = self.nodes.add()
|
||||
item.name = f"{cls.bl_rna.name} ({cls.bl_rna.base.bl_rna.name})"
|
||||
item.identifier = cls.bl_rna.identifier
|
||||
|
||||
# load python nodes
|
||||
for cls in bpy.types.Node.__subclasses__():
|
||||
item = self.nodes.add()
|
||||
item.name = f"{cls.bl_rna.name} (Python)"
|
||||
item.identifier = cls.bl_rna.identifier
|
||||
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.node:
|
||||
self.outputs[0].python_value = f"{self.inputs[0].python_value}.bl_rna.identifier == '{self.nodes[self.node].identifier}'"
|
||||
else:
|
||||
self.outputs[0].python_value = "False"
|
||||
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop_search(self, "node", self, "nodes", text="")
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
class SN_IsObjectType(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_IsObjectType"
|
||||
bl_label = "Is Object Type"
|
||||
node_color = "PROPERTY"
|
||||
|
||||
|
||||
types: bpy.props.EnumProperty(items=[('MESH', 'Mesh', ''), ('CURVE', 'Curve', ''), ('SURFACE', 'Surface', ''), ('META', 'Metaball', ''), ('FONT', 'Text', ''), ('HAIR', 'Hair', ''), ('POINTCLOUD', 'Point Cloud', ''), ('VOLUME', 'Volume', ''), ('GPENCIL', 'Grease Pencil', ''), ('ARMATURE', 'Armature', ''), ('LATTICE', 'Lattice', ''), ('EMPTY', 'Empty', ''), ('LIGHT', 'Light', ''), ('LIGHT_PROBE', 'Light Probe', ''), ('CAMERA', 'Camera', ''), ('SPEAKER', 'Speaker', '')],
|
||||
update=SN_ScriptingBaseNode._evaluate,
|
||||
name="Type",
|
||||
description="The type which the object is compared to")
|
||||
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "types", text="")
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_input("Object")
|
||||
self.add_boolean_output("Is Type")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[0].python_value = f"{self.inputs[0].python_value}.type == '{self.types}'"
|
||||
@@ -0,0 +1,33 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_NamedIconNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_NamedIconNode"
|
||||
bl_label = "Named Icon"
|
||||
node_color = "ICON"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_string_output("Icon")
|
||||
|
||||
|
||||
def update_selected(self, context):
|
||||
for icon in bpy.types.UILayout.bl_rna.functions["prop"].parameters["icon"].enum_items:
|
||||
if icon.value == self.icon:
|
||||
self.icon_name = icon.name
|
||||
return
|
||||
self.icon_name = "NONE"
|
||||
self["icon"] = 0
|
||||
|
||||
icon: bpy.props.IntProperty(name="Value", description="Value of this socket", update=update_selected)
|
||||
icon_name: bpy.props.StringProperty(default="NONE" ,update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs[0].python_value = f"'{self.icon_name}'"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
op = layout.operator("sn.select_icon", text="Choose Icon", icon_value=self.icon)
|
||||
op.icon_data_path = f"bpy.data.node_groups['{self.node_tree.name}'].nodes['{self.name}']"
|
||||
@@ -0,0 +1,53 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class NodeType(bpy.types.PropertyGroup):
|
||||
|
||||
name: bpy.props.StringProperty()
|
||||
|
||||
identifier: bpy.props.StringProperty()
|
||||
|
||||
|
||||
|
||||
class SN_NodeIdnameNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_NodeIdnameNode"
|
||||
bl_label = "Node Idname"
|
||||
node_color = "STRING"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_string_output("Idname")
|
||||
|
||||
# load internal nodes
|
||||
for name in dir(bpy.types):
|
||||
cls = getattr(bpy.types, name)
|
||||
if hasattr(cls, "bl_rna") and cls.bl_rna.base and "Node" in cls.bl_rna.base.bl_rna.name:
|
||||
item = self.nodes.add()
|
||||
item.name = f"{cls.bl_rna.name} ({cls.bl_rna.base.bl_rna.name})"
|
||||
item.identifier = cls.bl_rna.identifier
|
||||
|
||||
# load python nodes
|
||||
for cls in bpy.types.Node.__subclasses__():
|
||||
item = self.nodes.add()
|
||||
item.name = f"{cls.bl_rna.name} (Python)"
|
||||
item.identifier = cls.bl_rna.identifier
|
||||
|
||||
|
||||
nodes: bpy.props.CollectionProperty(type=NodeType)
|
||||
|
||||
node: bpy.props.StringProperty(name="Node",
|
||||
description="The node to get the type for",
|
||||
update=SN_ScriptingBaseNode._evaluate)
|
||||
|
||||
|
||||
def evaluate(self, context):
|
||||
if self.node:
|
||||
self.outputs["Idname"].python_value = f"'{self.nodes[self.node].identifier}'"
|
||||
else:
|
||||
self.outputs["Idname"].python_value = "''"
|
||||
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop_search(self, "node", self, "nodes", text="")
|
||||
@@ -0,0 +1,33 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_RandomColorNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_RandomColorNode"
|
||||
bl_label = "Random Color"
|
||||
node_color = "VECTOR"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_boolean_input("Use Alpha")
|
||||
inp = self.add_float_input("Fixed Alpha")
|
||||
inp.can_be_disabled = True
|
||||
inp.disabled = True
|
||||
inp.default_value = 1.0
|
||||
inp = self.add_integer_input("Seed")
|
||||
inp.can_be_disabled = True
|
||||
inp.disabled = True
|
||||
self.add_float_vector_output("Random Color").subtype = "COLOR"
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_import = "import random"
|
||||
self.code_imperative = """
|
||||
def random_color(use_alpha, fixed_alpha, seed):
|
||||
random.seed(seed)
|
||||
if use_alpha:
|
||||
return (random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1), fixed_alpha if fixed_alpha != None else random.uniform(0, 1))
|
||||
else:
|
||||
return (random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1))
|
||||
"""
|
||||
self.outputs[0].python_value = f"random_color({self.inputs['Use Alpha'].python_value}, {'None' if self.inputs['Fixed Alpha'].disabled else self.inputs['Fixed Alpha'].python_value}, {'None' if self.inputs['Seed'].disabled else self.inputs['Seed'].python_value})"
|
||||
@@ -0,0 +1,47 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_RandomNumberNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_RandomNumberNode"
|
||||
bl_label = "Random Number"
|
||||
node_color = "FLOAT"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_float_input("Minimum")
|
||||
self.add_float_input("Maximum")
|
||||
inp = self.add_integer_input("Seed")
|
||||
inp.can_be_disabled = True
|
||||
inp.disabled = True
|
||||
self.add_integer_output("Random Number")
|
||||
|
||||
def update_num_type(self, context):
|
||||
self.convert_socket(self.outputs[0], self.socket_names[self.number_type])
|
||||
self._evaluate(context)
|
||||
|
||||
number_type: bpy.props.EnumProperty(name="Type",
|
||||
description="Type of number",
|
||||
items=[("Integer", "Integer", "Integer"),
|
||||
("Float", "Float", "Float")],
|
||||
update=update_num_type)
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_import = "import random"
|
||||
self.code_imperative = """
|
||||
def random_integer(min, max, seed):
|
||||
random.seed(seed)
|
||||
return random.randint(int(min), int(max))
|
||||
|
||||
def random_float(min, max, seed):
|
||||
random.seed(seed)
|
||||
return random.uniform(min, max)
|
||||
"""
|
||||
if self.number_type == "Integer":
|
||||
self.outputs[0].python_value = f"random_integer({self.inputs['Minimum'].python_value}, {self.inputs['Maximum'].python_value}, {'None' if self.inputs['Seed'].disabled else self.inputs['Seed'].python_value})"
|
||||
else:
|
||||
self.outputs[0].python_value = f"random_float({self.inputs['Minimum'].python_value}, {self.inputs['Maximum'].python_value}, {'None' if self.inputs['Seed'].disabled else self.inputs['Seed'].python_value})"
|
||||
|
||||
def draw_node(self, context, layout):
|
||||
layout.prop(self, "number_type")
|
||||
@@ -0,0 +1,46 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_SceneContextNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_SceneContextNode"
|
||||
bl_label = "Scene Context"
|
||||
node_color = "DEFAULT"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_property_output("Active Scene")
|
||||
self.add_property_output("Active Area")
|
||||
self.add_property_output("Active View Layer")
|
||||
self.add_property_output("Active Camera")
|
||||
self.add_property_output("Active Bone")
|
||||
self.add_property_output("Active Pose Bone")
|
||||
self.add_property_output("Preferences")
|
||||
self.add_property_output("Window Manager")
|
||||
self.add_property_output("Window")
|
||||
self.add_property_output("Screen")
|
||||
self.add_string_output("Engine")
|
||||
self.add_string_output("Mode")
|
||||
self.add_string_output("Blend Filepath")
|
||||
self.add_boolean_output("File Is Saved")
|
||||
self.add_boolean_output("File Has Changes")
|
||||
self.add_integer_output("Current Frame")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.outputs["Active Scene"].python_value = f"bpy.context.scene"
|
||||
self.outputs["Active Area"].python_value = f"bpy.context.area"
|
||||
self.outputs["Active View Layer"].python_value = f"bpy.context.view_layer"
|
||||
self.outputs["Active Camera"].python_value = f"bpy.context.scene.camera"
|
||||
self.outputs["Active Bone"].python_value = f"bpy.context.active_bone"
|
||||
self.outputs["Active Pose Bone"].python_value = f"bpy.context.active_pose_bone"
|
||||
self.outputs["Preferences"].python_value = f"bpy.context.preferences"
|
||||
self.outputs["Window Manager"].python_value = f"bpy.context.window_manager"
|
||||
self.outputs["Window"].python_value = f"bpy.context.window"
|
||||
self.outputs["Screen"].python_value = f"bpy.context.screen"
|
||||
self.outputs["Engine"].python_value = f"bpy.context.engine"
|
||||
self.outputs["Mode"].python_value = f"bpy.context.mode"
|
||||
self.outputs["Blend Filepath"].python_value = f"bpy.data.filepath"
|
||||
self.outputs["File Is Saved"].python_value = f"bpy.data.is_saved"
|
||||
self.outputs["File Has Changes"].python_value = f"bpy.data.is_dirty"
|
||||
self.outputs["Current Frame"].python_value = f"bpy.context.scene.frame_current"
|
||||
@@ -0,0 +1,33 @@
|
||||
import bpy
|
||||
from ..base_node import SN_ScriptingBaseNode
|
||||
|
||||
|
||||
|
||||
class SN_TimeNode(SN_ScriptingBaseNode, bpy.types.Node):
|
||||
|
||||
bl_idname = "SN_TimeNode"
|
||||
bl_label = "Time and Date"
|
||||
node_color = "INTEGER"
|
||||
|
||||
def on_create(self, context):
|
||||
self.add_string_output("Time")
|
||||
self.add_integer_output("Hours")
|
||||
self.add_integer_output("Minutes")
|
||||
self.add_integer_output("Seconds")
|
||||
self.add_integer_output("Milliseconds")
|
||||
self.add_string_output("Date")
|
||||
self.add_integer_output("Year")
|
||||
self.add_integer_output("Month")
|
||||
self.add_integer_output("Day")
|
||||
|
||||
def evaluate(self, context):
|
||||
self.code_import = f"from datetime import datetime"
|
||||
self.outputs[0].python_value = "str(datetime.now().time()).split(\".\")[0]"
|
||||
self.outputs[1].python_value = "datetime.now().time().hour"
|
||||
self.outputs[2].python_value = "datetime.now().time().minute"
|
||||
self.outputs[3].python_value = "datetime.now().time().second"
|
||||
self.outputs[4].python_value = "datetime.now().time().microsecond//1000"
|
||||
self.outputs[5].python_value = "str(datetime.now().date())"
|
||||
self.outputs[6].python_value = "datetime.now().date().year"
|
||||
self.outputs[7].python_value = "datetime.now().date().month"
|
||||
self.outputs[8].python_value = "datetime.now().date().day"
|
||||
Reference in New Issue
Block a user