2025-07-01

This commit is contained in:
2026-03-17 14:30:01 -06:00
parent f9a22056dd
commit 62b5978595
4579 changed files with 1257472 additions and 0 deletions
@@ -0,0 +1,99 @@
import bpy
from ..base_node import SN_ScriptingBaseNode
class PrintProperty(bpy.types.PropertyGroup):
text: bpy.props.StringProperty()
class SN_OT_ClearPrints(bpy.types.Operator):
bl_idname = "sn.clear_prints"
bl_label = "Clear Prints"
bl_description = "Clear this print nodes messages"
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
node_tree: bpy.props.StringProperty(options={"SKIP_SAVE", "HIDDEN"})
node: bpy.props.StringProperty(options={"SKIP_SAVE", "HIDDEN"})
def execute(self, context):
bpy.data.node_groups[self.node_tree].nodes[self.node].messages.clear()
return {"FINISHED"}
class SN_PrintNode(SN_ScriptingBaseNode, bpy.types.Node):
bl_idname = "SN_PrintNode"
bl_label = "Print"
bl_width_default = 200
node_color = "PROGRAM"
messages: bpy.props.CollectionProperty(type=PrintProperty)
print_on_node: bpy.props.BoolProperty(default=True,
name="Print On Node",
description="Show print results on this node",
update=SN_ScriptingBaseNode._evaluate)
limit_prints: bpy.props.IntProperty(default=0, min=0,
name="Amount of print messages that will be added to this node before a previous one is removed (0 is unlimited)",
update=SN_ScriptingBaseNode._evaluate)
def on_create(self, context):
self.add_execute_input()
self.add_string_input()
self.add_dynamic_string_input()
self.add_execute_output()
def evaluate(self, context):
self.messages.clear()
values = [inp.python_value for inp in self.inputs[1:-1]]
self.code_imperative = f"""
def sn_print(on_node, node, limit, *args):
print(*args)
if on_node:
try:
msg = node.messages.add()
msg.text = str(args)[1:-1]
if limit and len(node.messages) > limit:
node.messages.remove(0)
for area in bpy.context.screen.areas: area.tag_redraw()
except:
print("Can't add print outputs to the node when the print is run in an interface!")
"""
self.code = f"""
sn_print({self.print_on_node}, bpy.data.node_groups['{self.node_tree.name}'].nodes['{self.name}'], {self.limit_prints}, {", ".join(values)})
{self.indent(self.outputs[0].python_value, 5)}
"""
def evaluate_export(self, context):
self.messages.clear()
values = [inp.python_value for inp in self.inputs[1:-1]]
self.code = f"""
print({", ".join(values)})
{self.indent(self.outputs[0].python_value, 5)}
"""
def draw_node(self, context, layout):
layout.prop(self, "print_on_node", text="Print On Node")
if self.print_on_node:
layout.prop(self, "limit_prints", text="Limit")
if self.print_on_node:
if not self.messages:
box = layout.box()
box.label(text="Nothing printed!")
else:
row = layout.row()
row.label(text="Messages:")
op = row.operator("sn.clear_prints", text="", icon="TRASH", emboss=False)
op.node_tree = self.node_tree.name
op.node = self.name
col = layout.column(align=True)
col.scale_y = 0.9
for msg in self.messages:
box = col.box()
box.label(text=msg.text)
@@ -0,0 +1,23 @@
import bpy
from ..base_node import SN_ScriptingBaseNode
class SN_SleepNode(SN_ScriptingBaseNode, bpy.types.Node):
bl_idname = "SN_SleepNode"
bl_label = "Sleep"
bl_width_default = 200
node_color = "PROGRAM"
def on_create(self, context):
self.add_execute_input()
self.add_float_input("Seconds")
self.add_execute_output()
def evaluate(self, context):
self.code_import = "import time"
self.code = f"""
time.sleep({self.inputs[1].python_value})
{self.indent(self.outputs[0].python_value, 3)}
"""
@@ -0,0 +1,103 @@
import bpy
from random import uniform
from ..base_node import SN_ScriptingBaseNode
class SN_TimestampNode(SN_ScriptingBaseNode, bpy.types.Node):
bl_idname = "SN_TimestampNode"
bl_label = "Timestamp"
bl_width_default = 200
def update_connected_portals(self, context=None):
if self.timestamp_type == "START":
for ntree in bpy.data.node_groups:
if ntree.bl_idname == "ScriptingNodesTree":
for node in ntree.node_collection(self.bl_idname).nodes:
if node.timestamp_type == "END" and node.var_name == self.var_name:
node.var_name = self.var_name
timestamp_type: bpy.props.EnumProperty(name="Type",
description="Start the timestamp recording or print the total time since the start",
items=[("START", "Start", "Start", "MOD_TIME", 0),
("END", "End", "End", "TIME", 1)],
update=update_connected_portals)
timestamp: bpy.props.FloatProperty(name="Timestamp")
def update_var_name(self, context):
self.label = self.var_name
self._evaluate(context)
def get_var_name(self):
return self.get("var_name", "")
def set_var_name(self, value):
if self.timestamp_type == "INPUT":
for ntree in bpy.data.node_groups:
if ntree.bl_idname == "ScriptingNodesTree":
for node in ntree.node_collection(self.bl_idname).nodes:
if node.timestamp_type == "OUTPUT" and node.var_name == self.var_name:
node["var_name"] = value
self["var_name"] = value
var_name: bpy.props.StringProperty(name="Name",
description="The identifier that links this timestamp to other timestamps",
get=get_var_name, set=set_var_name,
update=update_var_name)
def update_custom_color(self, context):
# update own color
self.color = self.custom_color
# update connected color
if self.timestamp_type == "START":
for ntree in bpy.data.node_groups:
if ntree.bl_idname == "ScriptingNodesTree":
for node in ntree.node_collection(self.bl_idname).nodes:
if node.timestamp_type == "END" and node.var_name == self.var_name:
node.custom_color = self.custom_color
custom_color: bpy.props.FloatVectorProperty(name="Color",
size=3, min=0, max=1, subtype="COLOR",
description="The color of this node",
update=update_custom_color)
def on_create(self, context):
self.add_execute_input()
self.add_execute_output()
self.var_name = self.uuid
self.custom_color = (uniform(0,1), uniform(0,1), uniform(0,1))
def evaluate(self, context):
self.code_import = "import time"
if self.timestamp_type == "START":
self.code = f"""
t_{self.var_name} = time.time()
{self.indent(self.outputs[0].python_value, 3)}
"""
elif self.timestamp_type == "END":
self.code = f"""
if "t_{self.var_name}" in locals(): print(f"Time elapsed for {self.var_name}: {{(time.time() - t_{self.var_name}) * 1000}}ms")
try: bpy.data.node_groups['{self.node_tree.name}'].nodes['{self.name}'].timestamp = time.time() - t_{self.var_name}
except: print("Failed to set timestamp on node")
for a in bpy.context.screen.areas: a.tag_redraw()
{self.indent(self.outputs[0].python_value, 3)}
"""
def evaluate_export(self, context):
self.code = f"""
{self.indent(self.outputs[0].python_value, 3)}
"""
def draw_node(self, context, layout):
layout.prop(self, "timestamp_type", expand=True)
if self.timestamp_type == "START":
row = layout.row(align=True)
split = row.split(factor=0.6, align=True)
split.prop(self, "var_name", text="")
sub_split = split.split(factor=0.5, align=True)
sub_split.prop(self, "custom_color", text="")
sub_split.operator("sn.reset_portal", text="", icon="LOOP_BACK").node = self.name
else:
layout.label(text=f"Time elapsed: {self.timestamp*1000}ms")
@@ -0,0 +1,55 @@
import operator
import bpy
from ..base_node import SN_ScriptingBaseNode
class SN_OT_TriggerTriggerNode(bpy.types.Operator):
bl_idname = "sn.trigger_trigger_node"
bl_label = "Trigger Node"
bl_description = "Trigger this node"
bl_options = {"REGISTER", "INTERNAL"}
uid: bpy.props.StringProperty(options={"SKIP_SAVE", "HIDDEN"})
def execute(self, context):
if self.uid in context.scene.sn.function_store:
context.scene.sn.function_store[self.uid]()
else:
self.report({"WARNING"}, message="Couldn't find trigger function!")
return {"FINISHED"}
class SN_TriggerNode(SN_ScriptingBaseNode, bpy.types.Node):
bl_idname = "SN_TriggerNode"
bl_label = "Trigger"
is_trigger = True
bl_width_default = 200
def on_create(self, context):
self.add_execute_output()
def draw_node(self,context,layout):
row = layout.row()
row.scale_y = 1.2
op = row.operator("sn.trigger_trigger_node", text="Run Trigger", icon="PLAY")
op.uid = self.static_uid
@property
def handler_name(self):
return f"trigger_handler_{self.static_uid}"
def evaluate(self, context):
self.code = f"""
def {self.handler_name}():
{self.indent(self.outputs[0].python_value, 7) if self.outputs[0].python_value.strip() else 'pass'}
"""
self.code_register = f"bpy.context.scene.sn.function_store['{self.static_uid}'] = {self.handler_name}"
def evaluate_export(self, context):
self.code = ""
self.code_register = ""