Skip to content

Commit bf66e58

Browse files
committed
feat: script sets most node attributes automatically now
1 parent 6f9601a commit bf66e58

File tree

1 file changed

+158
-58
lines changed

1 file changed

+158
-58
lines changed

node_to_python.py

Lines changed: 158 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,104 @@
3131
'NodeSocketTexture',
3232
'NodeSocketVirtual'}
3333

34+
node_settings = {
35+
#attribute
36+
"GeometryNodeAttributeStatistic" : ["data_type", "domain"],
37+
"GeometryNodeCaptureAttribute" : ["data_type", "domain"],
38+
"GeometryNodeAttributeDomainSize" : ["component"],
39+
"GeometryNodeStoreNamedAttribute" : ["data_type", "domain"],
40+
"GeometryNodeAttributeTransfer" : ["data_type", "mapping"],
41+
42+
#color
43+
"ShaderNodeMixRGB" : ["blend_type", "use_clamp"],
44+
45+
#curve
46+
"GeometryNodeCurveToPoints" : ["mode"],
47+
"GeometryNodeFillCurve" : ["mode"],
48+
"GeometryNodeFilletCurve" : ["mode"],
49+
"GeometryNodeResampleCurve" : ["mode"],
50+
"GeometryNodeSampleCurve" : ["mode"],
51+
"GeometryNodeTrimCurve" : ["mode"],
52+
"GeometryNodeCurveHandleTypeSelection" : ["mode", "handle_type"],
53+
"GeometryNodeSetCurveHandlePositions" : ["mode"],
54+
"GeometryNodeCurveSetHandles" : ["mode", "handle_type"],
55+
"GeometryNodeCurveSplineType" : ["spline_type"],
56+
57+
#curve primitives
58+
"GeometryNodeCurveArc" : ["mode"],
59+
"GeometryNodeCurvePrimitiveBezierSegment" : ["mode"],
60+
"GeometryNodeCurvePrimitiveCircle" : ["mode"],
61+
"GeometryNodeCurvePrimitiveLine" : ["mode"],
62+
"GeometryNodeCurvePrimitiveQuadrilateral" : ["mode"],
63+
64+
#geometry
65+
"GeometryNodeDeleteGeometry" : ["domain", "mode"],
66+
"GeometryNodeDuplicateElements" : ["domain"],
67+
"GeometryNodeProximity" : ["target_element"],
68+
"GeometryNodeMergeByDistance" : ["mode"],
69+
"GeometryNodeRaycast" : ["data_type", "mapping"],
70+
"GeometryNodeSeparateGeometry" : ["domain"],
71+
72+
#input
73+
"GeometryNodeCollectionInfo" : ["transform_space"],
74+
"GeometryNodeObjectInfo" : ["transform_space"],
75+
"GeometryNodeInputNamedAttribute" : ["data_type"],
76+
77+
#mesh
78+
"GeometryNodeExtrudeMesh" : ["mode"],
79+
"GeometryNodeMeshBoolean" : ["operation"],
80+
"GeometryNodeMeshToPoints" : ["mode"],
81+
"GeometryNodeSubdivisionSurface" : ["uv_smooth", "boundary_smooth"],
82+
"GeometryNodeTriangulate" : ["quad_method", "ngon_method"],
83+
"GeometryNodeScaleElements" : ["domain", "scale_mode"],
84+
85+
#mesh primitives
86+
"GeometryNodeMeshCone" : ["fill_type"],
87+
"GeometryNodeMeshCylinder" : ["fill_type"],
88+
"GeometryNodeMeshCircle" : ["fill_type"],
89+
"GeometryNodeMeshLine" : ["mode"],
90+
91+
#point
92+
"GeometryNodeDistributePointsOnFaces" : ["distribute_method"],
93+
"GeometryNodePointsToVolume" : ["resolution_mode"],
94+
95+
#text
96+
"GeometryNodeStringToCurves" : ["overflow", "align_x", "align_y",
97+
"pivot_mode"],
98+
99+
#texture
100+
"ShaderNodeTexBrick" : ["offset", "offset_frequency", "squash",
101+
"squash_frequency"],
102+
"ShaderNodeTexGradient" : ["gradient_type"],
103+
"GeometryNodeImageTexture" : ["interpolation", "extension"],
104+
"ShaderNodeTexMagic" : ["turbulence_depth"],
105+
"ShaderNodeTexNoise" : ["noise_dimensions"],
106+
"ShaderNodeTexVoronoi" : ["voronoi_dimensions", "feature", "distance"],
107+
"ShaderNodeTexWave" : ["wave_type", "bands_direction", "wave_profile"],
108+
"ShaderNodeTexWhiteNoise" : ["noise_dimensions"],
109+
110+
#utilities
111+
"GeometryNodeAccumulateField" : ["data_type", "domain"],
112+
"FunctionNodeAlignEulerToVector" : ["axis", "pivot_axis"],
113+
"FunctionNodeBooleanMath" : ["operation"],
114+
"ShaderNodeClamp" : ["clamp_type"],
115+
"FunctionNodeCompare" : ["data_type", "operation", "mode"],
116+
"GeometryNodeFieldAtIndex" : ["data_type", "domain"],
117+
"FunctionNodeFloatToInt" : ["rounding_mode"],
118+
"ShaderNodeMapRange" : ["data_type", "interpolation_type", "clamp"],
119+
"ShaderNodeMath" : ["operation", "use_clamp"],
120+
"FunctionNodeRandomValue" : ["data_type"],
121+
"FunctionNodeRotateEuler" : ["type", "space"],
122+
"GeometryNodeSwitch" : ["input_type"],
123+
124+
#vector
125+
"ShaderNodeVectorMath" : ["operation"],
126+
"ShaderNodeVectorRotate" : ["rotation_type", "invert"],
127+
128+
#volume
129+
"GeometryNodeVolumeToMesh" : ["resolution_mode"]
130+
}
131+
34132
class NodeToPython(bpy.types.Operator):
35133
bl_idname = "object.node_to_python"
36134
bl_label = "Node to Python"
@@ -82,10 +180,10 @@ def process_node_group(node_group, level):
82180
#initialize node group
83181
file.write(f"{outer}#initialize {ng_name} node group\n")
84182
file.write(f"{outer}def {ng_name}_node_group():\n")
85-
file.write(f"{inner}{ng_name}" +
86-
f"= bpy.data.node_groups.new(" +
87-
f"type = \"GeometryNodeTree\"," +
88-
f"name = \"{node_group.name}\")\n")
183+
file.write((f"{inner}{ng_name}"
184+
f"= bpy.data.node_groups.new("
185+
f"type = \"GeometryNodeTree\", "
186+
f"name = \"{node_group.name}\")\n"))
89187
file.write("\n")
90188

91189
#initialize nodes
@@ -98,8 +196,9 @@ def process_node_group(node_group, level):
98196
for input in node.outputs:
99197
if input.bl_idname != "NodeSocketVirtual":
100198
file.write(f"{inner}#input {input.name}\n")
101-
file.write(f"{inner}{ng_name}.inputs.new" +
102-
f"(\"{input.bl_idname}\", \"{input.name}\")\n")
199+
file.write((f"{inner}{ng_name}.inputs.new"
200+
f"(\"{input.bl_idname}\", "
201+
f"\"{input.name}\")\n"))
103202
if input.bl_idname in default_sockets:
104203
socket = node_group.inputs[input.name]
105204
if input.bl_idname == 'NodeSocketColor':
@@ -113,76 +212,79 @@ def process_node_group(node_group, level):
113212
dv = socket.default_value
114213

115214
#default value
116-
file.write(f"{inner}{ng_name}" +
117-
f".inputs[\"{input.name}\"]" +
118-
f".default_value = {dv}\n")
215+
file.write((f"{inner}{ng_name}"
216+
f".inputs[\"{input.name}\"]"
217+
f".default_value = {dv}\n"))
119218
if input.bl_idname in value_sockets:
120219
#min value
121-
file.write(f"{inner}{ng_name}" +
122-
f".inputs[\"{input.name}\"]" +
123-
f".min_value = " +
124-
f"{socket.min_value}\n")
220+
file.write((f"{inner}{ng_name}"
221+
f".inputs[\"{input.name}\"]"
222+
f".min_value = "
223+
f"{socket.min_value}\n"))
125224
#max value
126-
file.write(f"{inner}{ng_name}" +
127-
f".inputs[\"{input.name}\"]" +
128-
f".max_value = " +
129-
f"{socket.max_value}\n")
225+
file.write((f"{inner}{ng_name}"
226+
f".inputs[\"{input.name}\"]"
227+
f".max_value = "
228+
f"{socket.max_value}\n"))
130229
file.write("\n")
131230
file.write("\n")
132231
elif node.bl_idname == 'NodeGroupOutput':
133232
file.write(f"{inner}#{ng_name} outputs\n")
134233
for output in node.inputs:
135234
if output.bl_idname != 'NodeSocketVirtual':
136-
file.write(f"{inner}{ng_name}.outputs" +
137-
f".new(\"{output.bl_idname}\", " +
138-
f"\"{output.name}\")\n")
235+
file.write((f"{inner}{ng_name}.outputs"
236+
f".new(\"{output.bl_idname}\", "
237+
f"\"{output.name}\")\n"))
139238
file.write("\n")
140239

141240
#create node
142241
node_name = node.name.lower()
143242
node_name = node_name.replace(' ', '_').replace('.', '_')
144243
file.write(f"{inner}#node {node.name}\n")
145-
file.write(f"{inner}{node_name} " +
146-
f"= {ng_name}.nodes.new(\"{node.bl_idname}\")\n")
147-
file.write(f"{inner}{node_name}.location " +
148-
f"= ({node.location.x}, {node.location.y})\n")
149-
file.write(f"{inner}{node_name}.width, {node_name}.height " +
150-
f"= {node.width}, {node.height}\n")
244+
file.write((f"{inner}{node_name} "
245+
f"= {ng_name}.nodes.new(\"{node.bl_idname}\")\n"))
246+
file.write((f"{inner}{node_name}.location "
247+
f"= ({node.location.x}, {node.location.y})\n"))
248+
file.write((f"{inner}{node_name}.width, {node_name}.height "
249+
f"= {node.width}, {node.height}\n"))
151250
if node.label:
152251
file.write(f"{inner}{node_name}.label = \"{node.label}\"\n")
153252

154253
#special nodes
155-
"""
156-
NTS: these aren't special
157-
pretty much every node has something similar
158-
you're gonna need to make some sort of dictionary with all
159-
the different quirky features for each node.
160-
"""
161-
if node.bl_idname == 'GeometryNodeGroup':
162-
file.write(f"{inner}{node_name}.node_tree = " +
163-
f"bpy.data.node_groups" +
164-
f"[\"{node.node_tree.name}\"]\n")
254+
print(node.bl_idname)
255+
if node.bl_idname in node_settings:
256+
for setting in node_settings[node.bl_idname]:
257+
print(type(getattr(node, setting)))
258+
attr = getattr(node, setting)
259+
if type(attr) == str:
260+
attr = f"\'{attr}\'"
261+
file.write((f"{inner}{node_name}.{setting} = "
262+
f"{attr}\n"))
263+
elif node.bl_idname == 'GeometryNodeGroup':
264+
file.write((f"{inner}{node_name}.node_tree = "
265+
f"bpy.data.node_groups"
266+
f"[\"{node.node_tree.name}\"]\n"))
165267
elif node.bl_idname == 'ShaderNodeValToRGB':
166268
color_ramp = node.color_ramp
167269
file.write("\n")
168-
file.write(f"{inner}{node_name}.color_ramp.color_mode = " +
169-
f"\'{color_ramp.color_mode}\'\n")
170-
file.write(f"{inner}{node_name}.color_ramp" +
171-
f".hue_interpolation = " +
172-
f"\'{color_ramp.hue_interpolation}\'\n")
173-
file.write(f"{inner}{node_name}.color_ramp.interpolation" +
174-
f" = '{color_ramp.interpolation}'\n")
270+
file.write((f"{inner}{node_name}.color_ramp.color_mode = "
271+
f"\'{color_ramp.color_mode}\'\n"))
272+
file.write((f"{inner}{node_name}.color_ramp"
273+
f".hue_interpolation = "
274+
f"\'{color_ramp.hue_interpolation}\'\n"))
275+
file.write((f"{inner}{node_name}.color_ramp.interpolation "
276+
f"= '{color_ramp.interpolation}'\n"))
175277
file.write("\n")
176278
for i, element in enumerate(color_ramp.elements):
177-
file.write(f"{inner}{node_name}_cre_{i} = " +
178-
f"{node_name}.color_ramp.elements" +
179-
f".new({element.position})\n")
180-
file.write(f"{inner}{node_name}_cre_{i}.alpha = " +
181-
f"{element.alpha}\n")
279+
file.write((f"{inner}{node_name}_cre_{i} = "
280+
f"{node_name}.color_ramp.elements"
281+
f".new({element.position})\n"))
282+
file.write((f"{inner}{node_name}_cre_{i}.alpha = "
283+
f"{element.alpha}\n"))
182284
col = element.color
183285
r, g, b, a = col[0], col[1], col[2], col[3]
184-
file.write(f"{inner}{node_name}_cre_{i}.color = " +
185-
f"({r}, {g}, {b}, {a})\n\n")
286+
file.write((f"{inner}{node_name}_cre_{i}.color = "
287+
f"({r}, {g}, {b}, {a})\n\n"))
186288

187289
for input in node.inputs:
188290
if input.bl_idname not in dont_set_defaults:
@@ -197,13 +299,11 @@ def process_node_group(node_group, level):
197299
else:
198300
dv = input.default_value
199301
if dv is not None:
200-
file.write(f"{inner}{node_name}" +
201-
f".inputs[\"{input.name}\"]" +
202-
f".default_value = {dv}\n")
203-
302+
file.write((f"{inner}{node_name}"
303+
f".inputs[\"{input.name}\"]"
304+
f".default_value = {dv}\n"))
204305
file.write("\n")
205306

206-
207307
#initialize links
208308
if node_group.links:
209309
file.write(f"{inner}#initialize {ng_name} links\n")
@@ -214,9 +314,9 @@ def process_node_group(node_group, level):
214314
output_node = link.to_node.name.lower().replace(' ', '_')
215315
output_socket = link.to_socket.name
216316

217-
file.write(f"{inner}{ng_name}.links.new({input_node}" +
218-
f".outputs[\"{input_socket}\"], " +
219-
f"{output_node}.inputs[\"{output_socket}\"])\n")
317+
file.write((f"{inner}{ng_name}.links.new({input_node}"
318+
f".outputs[\"{input_socket}\"], "
319+
f"{output_node}.inputs[\"{output_socket}\"])\n"))
220320

221321
#create node group
222322
file.write("\n")

0 commit comments

Comments
 (0)