Hey everyone!
I don’t have cool artwork for nodevember, but I just released the Copy/Paste Nodes add-on on the extensions platform. The main goal is, to allow node trees to be shared in text form, without rebuilding from screenshots. I am looking for feedback, and testers! Let me know if you find this useful or if you have a node tree where it doesn’t work. It should work in all blender node trees (geometry, shaders, compositing, etc).
As an example, here is a geometry nodes version of the Solidify modifier. You can copy this text block, and paste in a geometry nodes tree with CTRL-ALT-V when the extension is installed.
# Copy/Paste Nodes JSON export (https://extensions.blender.org/add-ons/copy-paste-nodes/)
{
"version": 1,
"type": "GEOMETRY",
"nodes": [
{
"bl_idname": "GeometryNodeGroup",
"node_tree": "Solidify",
"props": {"_loc": [-60.0, 80.0], "name": "Solidify"},
"inputs": {"0": {"name": "Mesh", "links": []}}
}
],
"node_trees": {
".High Quality Normal": {
"nodes": [
{
"bl_idname": "NodeGroupOutput",
"props": {"_loc": [1100.0, 100.0], "name": "Group Output"},
"inputs": {"0": {"name": "Normal", "_val": [0.0, 0.0, 0.0], "links": [["Vector Math.003", 0]]}}
},
{
"bl_idname": "GeometryNodeInputNormal",
"props": {"_loc": [-220.0, 20.0], "name": "Normal.005"}
},
{
"bl_idname": "GeometryNodeFieldOnDomain",
"props": {
"data_type": "FLOAT_VECTOR",
"domain": "EDGE",
"_loc": [740.0, 100.0],
"name": "Evaluate on Domain"
},
"inputs": {"0": {"name": "Value", "_val": [0.0, 0.0, 0.0], "links": [["Switch", 0]]}}
},
{
"bl_idname": "GeometryNodeFieldOnDomain",
"props": {
"data_type": "FLOAT_VECTOR",
"domain": "FACE",
"_loc": [-40.0, 20.0],
"name": "Evaluate on Domain.001"
},
"inputs": {"0": {"name": "Value", "_val": [0.0, 0.0, 0.0], "links": [["Normal.005", 1]]}}
},
{
"bl_idname": "GeometryNodeInputMeshEdgeAngle",
"props": {"_loc": [-40.0, -140.0], "name": "Edge Angle"}
},
{
"bl_idname": "ShaderNodeVectorMath",
"props": {"_loc": [340.0, -60.0], "name": "Vector Math", "operation": "SCALE"},
"inputs": {
"0": {"name": "Vector", "links": [["Vector Math.001", 0]]},
"3": {"name": "Scale", "links": [["Math", 0]]}
}
},
{
"bl_idname": "GeometryNodeInputMeshEdgeNeighbors",
"props": {"_loc": [140.0, 140.0], "name": "Edge Neighbors"}
},
{
"bl_idname": "FunctionNodeCompare",
"props": {"data_type": "INT", "_loc": [340.0, 200.0], "name": "Compare.001", "operation": "EQUAL"},
"inputs": {"2": {"name": "A", "links": [["Edge Neighbors", 0]]}, "3": {"name": "B", "_val": 2}}
},
{
"bl_idname": "GeometryNodeSwitch",
"props": {"input_type": "VECTOR", "_loc": [560.0, 100.0], "name": "Switch"},
"inputs": {
"0": {"name": "Switch", "links": [["Compare.001", 0]]},
"1": {"name": "False", "_val": [0.0, 0.0, 0.0], "links": [["Vector Math.001", 0]]},
"2": {"name": "True", "_val": [0.0, 0.0, 0.0], "links": [["Vector Math", 0]]}
}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"_loc": [140.0, -140.0], "name": "Math", "operation": "MULTIPLY"},
"inputs": {
"0": {"name": "Value", "links": [["Edge Angle", 0]]},
"1": {"name": "Value", "_val": 2.0, "links": [["Math.002", 0]]}
}
},
{
"bl_idname": "ShaderNodeVectorMath",
"props": {"_loc": [920.0, 100.0], "name": "Vector Math.003", "operation": "NORMALIZE"},
"inputs": {"0": {"name": "Vector", "links": [["Evaluate on Domain", 0]]}}
},
{
"bl_idname": "ShaderNodeVectorMath",
"props": {"_loc": [140.0, 20.0], "name": "Vector Math.001", "operation": "NORMALIZE"},
"inputs": {"0": {"name": "Vector", "links": [["Evaluate on Domain.001", 0]]}}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"_loc": [-40.0, -240.0], "name": "Math.002", "operation": "DIVIDE"},
"inputs": {"0": {"name": "Value", "_val": 2.0}, "1": {"name": "Value", "_val": 3.1415927}}
}
],
"interface": {
"items_tree": [
{
"_val": [0.0, 0.0, 0.0],
"dimensions": 3,
"in_out": "OUTPUT",
"name": "Normal",
"socket_type": "NodeSocketVector"
}
]
},
"props": {}
},
"Solidify": {
"nodes": [
{
"bl_idname": "NodeGroupOutput",
"props": {"_loc": [780.0, 60.0], "name": "Group Output"},
"inputs": {
"0": {"name": "Mesh", "links": [["Merge by Distance.001", 0]]},
"1": {"name": "Inner", "_val": false, "links": [["Capture Attribute.001", 1]]},
"2": {"name": "Outer", "_val": false, "links": [["Extrude Mesh", 1]]},
"3": {"name": "Rim", "_val": false, "links": [["Extrude Mesh", 2]]}
}
},
{
"bl_idname": "NodeGroupInput",
"props": {"_loc": [-1780.0, 160.0], "name": "Group Input"},
"outputs": {"1": {"_val": 1.0}, "2": {"_val": -1.0}}
},
{
"bl_idname": "GeometryNodeExtrudeMesh",
"props": {"_loc": [-300.0, 20.0], "name": "Extrude Mesh"},
"inputs": {
"0": {"name": "Mesh", "links": [["Capture Attribute", 0]]},
"3": {"name": "Offset Scale", "_val": 0.0},
"4": {"name": "Individual", "_val": false}
}
},
{
"bl_idname": "FunctionNodeBooleanMath",
"props": {"hide": true, "_loc": [20.0, 40.0], "name": "Boolean Math", "operation": "NOT"},
"inputs": {"0": {"name": "Boolean", "links": [["Extrude Mesh", 1]]}}
},
{
"bl_idname": "GeometryNodeJoinGeometry",
"props": {"_loc": [20.0, 120.0], "name": "Join Geometry"},
"inputs": {"0": {"name": "Geometry", "links": [["Capture Attribute.001", 0], ["Extrude Mesh", 0]]}}
},
{
"bl_idname": "GeometryNodeFlipFaces",
"props": {"_loc": [-340.0, 220.0], "name": "Flip Faces"},
"inputs": {"0": {"name": "Mesh", "links": [["Capture Attribute", 0]]}}
},
{
"bl_idname": "GeometryNodeCaptureAttribute",
"props": {
"capture_items": [
{"data_type": "FLOAT", "name": "Thickness"},
{"data_type": "BOOLEAN", "name": "Boundary"},
{"data_type": "BOOLEAN", "name": "Even Thickness"},
{"data_type": "FLOAT", "name": "Even Scale"},
{"data_type": "FLOAT", "name": "Offset"}
],
"_loc": [-523.5819, 116.82643],
"name": "Capture Attribute"
},
"inputs": {
"0": {"name": "Geometry", "links": [["Capture Attribute.002", 0]]},
"1": {"name": "Thickness", "_val": 0.0, "links": [["Group Input", 1]]},
"2": {"name": "Boundary", "_val": false, "links": [["Evaluate on Domain", 0]]},
"3": {"name": "Even Thickness", "_val": false, "links": [["Group Input", 3]]},
"4": {"name": "Even Scale", "_val": 0.0, "links": [["Evaluate on Domain.001", 0]]},
"5": {"name": "Offset", "_val": 0.0, "links": [["Group Input", 2]]}
}
},
{
"bl_idname": "GeometryNodeSetPosition",
"props": {"_loc": [420.0, 120.0], "name": "Set Position"},
"inputs": {
"0": {"name": "Geometry", "links": [["Join Geometry", 0]]},
"3": {"name": "Offset", "links": [["Vector Math.002", 0]]}
}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"hide": true, "_loc": [-100.0, -100.0], "name": "Math"},
"inputs": {
"0": {"name": "Value", "links": [["Extrude Mesh", 1]]},
"1": {"name": "Value", "_val": 1.0, "links": [["Math.001", 0]]}
}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"_loc": [-300.0, -300.0], "name": "Math.001", "operation": "MULTIPLY_ADD"},
"inputs": {
"0": {"name": "Value", "_val": -1.0, "links": [["Capture Attribute", 5]]},
"2": {"name": "Value", "_val": -0.5}
}
},
{
"bl_idname": "ShaderNodeVectorMath",
"props": {"hide": true, "_loc": [240.0, 40.0], "name": "Vector Math.002", "operation": "SCALE"},
"inputs": {
"0": {"name": "Vector", "links": [["Capture Attribute.002", 1]]},
"3": {"name": "Scale", "links": [["Math.005", 0]]}
}
},
{
"bl_idname": "GeometryNodeCaptureAttribute",
"props": {
"capture_items": [{"data_type": "BOOLEAN", "name": "Inner"}],
"domain": "FACE",
"_loc": [-160.0, 220.0],
"name": "Capture Attribute.001"
},
"inputs": {
"0": {"name": "Geometry", "links": [["Flip Faces", 0]]},
"1": {"name": "Inner", "_val": true}
}
},
{
"bl_idname": "ShaderNodeVectorMath",
"props": {
"hide": true,
"_loc": [-1240.0, -80.0],
"name": "Vector Math.004",
"operation": "DOT_PRODUCT"
},
"inputs": {
"0": {"name": "Vector", "links": [["Capture Attribute.002", 1]]},
"1": {"name": "Vector", "links": [["Normal.003", 1]]}
}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"_loc": [-1060.0, -260.0], "name": "Math.004", "operation": "DIVIDE"},
"inputs": {
"0": {"name": "Value", "_val": 1.0},
"1": {"name": "Value", "links": [["Vector Math.004", 1]]}
}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"hide": true, "_loc": [-100.0, -260.0], "name": "Math.002", "operation": "MULTIPLY"},
"inputs": {
"0": {"name": "Value", "links": [["Capture Attribute", 1]]},
"1": {"name": "Value", "links": [["Capture Attribute", 4]]}
}
},
{
"bl_idname": "GeometryNodeSwitch",
"props": {"input_type": "FLOAT", "_loc": [60.0, -140.0], "name": "Switch"},
"inputs": {
"0": {"name": "Switch", "links": [["Capture Attribute", 3]]},
"1": {"name": "False", "_val": 0.0, "links": [["Capture Attribute", 1]]},
"2": {"name": "True", "_val": 0.0, "links": [["Math.002", 0]]}
}
},
{
"bl_idname": "GeometryNodeFieldOnDomain",
"props": {"domain": "CORNER", "_loc": [-720.0, -80.0], "name": "Evaluate on Domain.001"},
"inputs": {"0": {"name": "Value", "links": [["Switch.002", 0]]}}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"hide": true, "_loc": [-880.0, -260.0], "name": "Math.003", "operation": "ABSOLUTE"},
"inputs": {"0": {"name": "Value", "links": [["Math.004", 0]]}}
},
{
"bl_idname": "FunctionNodeBooleanMath",
"props": {"hide": true, "_loc": [20.0, 0.0], "name": "Boolean Math.001"},
"inputs": {
"0": {"name": "Boolean", "links": [["Boolean Math", 0]]},
"1": {"name": "Boolean", "links": [["Capture Attribute", 2]]}
}
},
{
"bl_idname": "GeometryNodeCaptureAttribute",
"props": {
"capture_items": [{"data_type": "FLOAT_VECTOR", "name": "Normal"}],
"_loc": [-1420.0, 120.0],
"name": "Capture Attribute.002"
},
"inputs": {
"0": {"name": "Geometry", "links": [["Group Input", 0]]},
"1": {"name": "Normal", "_val": [0.0, 0.0, 0.0], "links": [["Switch.001", 0]]}
}
},
{
"bl_idname": "GeometryNodeInputNormal",
"props": {"_loc": [-1780.0, -120.0], "name": "Normal.003"}
},
{
"bl_idname": "GeometryNodeGroup",
"node_tree": ".High Quality Normal",
"props": {"_loc": [-1780.0, -20.0], "name": "Group.001"}
},
{
"bl_idname": "GeometryNodeSwitch",
"props": {"input_type": "VECTOR", "_loc": [-1600.0, 40.0], "name": "Switch.001"},
"inputs": {
"0": {"name": "Switch", "links": [["Group Input", 4]]},
"1": {"name": "False", "_val": [0.0, 0.0, 0.0], "links": [["Normal.003", 1]]},
"2": {"name": "True", "_val": [0.0, 0.0, 0.0], "links": [["Group.001", 0]]}
}
},
{
"bl_idname": "ShaderNodeMath",
"props": {"hide": true, "_loc": [60.0, -100.0], "name": "Math.005", "operation": "MULTIPLY"},
"inputs": {
"0": {"name": "Value", "links": [["Math", 0]]},
"1": {"name": "Value", "links": [["Switch", 0]]}
}
},
{
"bl_idname": "FunctionNodeCompare",
"props": {"_loc": [-1060.0, -80.0], "name": "Compare", "operation": "EQUAL"},
"inputs": {
"0": {"name": "A", "links": [["Vector Math.004", 1]]},
"12": {"name": "Epsilon", "_val": 0.0001}
}
},
{
"bl_idname": "GeometryNodeSwitch",
"props": {"input_type": "FLOAT", "_loc": [-880.0, -80.0], "name": "Switch.002"},
"inputs": {
"0": {"name": "Switch", "links": [["Compare", 0]]},
"1": {"name": "False", "_val": 0.0, "links": [["Math.003", 0]]},
"2": {"name": "True", "_val": 1.0}
}
},
{
"bl_idname": "GeometryNodeMergeByDistance",
"props": {"_loc": [600.0, 120.0], "name": "Merge by Distance.001"},
"inputs": {
"0": {"name": "Geometry", "links": [["Set Position", 0]]},
"1": {"name": "Selection", "links": [["Boolean Math.001", 0]]},
"2": {"name": "Distance", "_val": 1e-05}
}
},
{
"bl_idname": "GeometryNodeInputMeshEdgeNeighbors",
"props": {"_loc": [-1040.0, 220.0], "name": "Edge Neighbors"}
},
{
"bl_idname": "FunctionNodeCompare",
"props": {"data_type": "INT", "_loc": [-880.0, 220.0], "name": "Compare.001", "operation": "EQUAL"},
"inputs": {"2": {"name": "A", "links": [["Edge Neighbors", 0]]}, "3": {"name": "B", "_val": 1}}
},
{
"bl_idname": "GeometryNodeFieldOnDomain",
"props": {
"data_type": "BOOLEAN",
"domain": "EDGE",
"_loc": [-720.0, 220.0],
"name": "Evaluate on Domain"
},
"inputs": {"0": {"name": "Value", "links": [["Compare.001", 0]]}}
}
],
"interface": {
"items_tree": [
{"in_out": "OUTPUT", "name": "Mesh", "socket_type": "NodeSocketGeometry"},
{"in_out": "OUTPUT", "name": "Inner", "socket_type": "NodeSocketBool"},
{"in_out": "OUTPUT", "name": "Outer", "socket_type": "NodeSocketBool"},
{"in_out": "OUTPUT", "name": "Rim", "socket_type": "NodeSocketBool"},
{"in_out": "INPUT", "name": "Mesh", "socket_type": "NodeSocketGeometry"},
{
"_val": 0.01,
"in_out": "INPUT",
"max_value": 10000.0,
"min_value": -10000.0,
"name": "Thickness"
},
{
"_val": -1.0,
"in_out": "INPUT",
"max_value": 1.0,
"min_value": -1.0,
"name": "Offset",
"subtype": "FACTOR"
},
{"in_out": "INPUT", "name": "Even Thickness", "socket_type": "NodeSocketBool"},
{"in_out": "INPUT", "name": "High Quality Normals", "socket_type": "NodeSocketBool"}
]
},
"props": {"color_tag": "GEOMETRY", "is_modifier": true}
}
}
}