<buttonid="sidebar-toggle"class="icon-button"type="button"title="Toggle Table of Contents"aria-label="Toggle Table of Contents"aria-controls="sidebar">
<inputtype="search"id="searchbar"name="searchbar"placeholder="Search this book ..."aria-controls="searchresults-outer"aria-describedby="searchresults-header">
<p>The fastest way to get up and running is with Blender's built-in <em>Text Editor</em>.
You can edit and execute your scripts right inside of Blender:</p>
<ol>
<li>Open a <em>Text Editor</em> space.</li>
</ol>
<p><imgsrc="setup/../images/text_editor_space.png"alt="A screenshot of the available spaces, with the Text Editor space highlighted"/></p>
<olstart="2">
<li>Create a new text data-block with the <em>New</em> button.</li>
</ol>
<p><imgsrc="setup/../images/text_editor_new.png"alt="A screenshot of the Text Editor space with the new button"/></p>
<olstart="3">
<li>Start writing a Geometry Script. As an example, you can paste in the script below. More detailed instructions on writing scripts are in later chapters.</li>
<li>Click the run button to execute the script. This will create a Geometry Nodes tree named <em>Repeat Grid</em>.</li>
</ol>
<p><imgsrc="setup/../images/text_editor_run_script.png"alt="A screenshot of the Text Editor space with the Run Script button"/></p>
<olstart="5">
<li>Create a <em>Geometry Nodes</em> modifier on any object in your scene and select the <em>Repeat Grid</em> tree.</li>
</ol>
<p><imgsrc="setup/../images/geometry_nodes_modifier.png"alt="A screenshot of the Blender window with a 3x3 grid of cubes on the left and a Geometry Nodes modifier with the Repeat Grid tree selected on the right"/></p>
<p>This guide will show how to setup <ahref="https://code.visualstudio.com/">Visual Studio Code</a> to edit Geometry Scripts. However, the same concepts apply to other IDEs.</p>
<p>This guide assumes you have already installed Visual Studio Code and setup the <ahref="https://marketplace.visualstudio.com/items?itemName=ms-python.python">Python extension</a>. If not, please setup those tools before continuing.</p>
<p>When the Geometry Script add-on starts, it generates a Python typeshed file that can be used to provide code completion.
All we have to do is add the right path to the Python extension's configuration:</p>
<ol>
<li>Open Blender preferences and expand the <em>Geometry Script</em> preferences</li>
<li>Copy the <em>Typeshed Path</em></li>
</ol>
<p><imgsrc="setup/../images/addon_preferences.png"alt="A screenshot of the Geometry Script preferences"/></p>
<olstart="3">
<li>In VS Code, open the Settings UI (<code>Shift+CTRL+P</code> or <code>Shift+CMD+P</code>><code>Preferences > Open Settings (UI)</code>)</li>
<li>Find the setting <code>Python > Analysis: Extra Paths</code></li>
<li>Click <em>Add Item</em>, then paste in the path copied from Blender and click <em>OK</em></li>
</ol>
<p><imgsrc="setup/../images/vscode_extra_paths.png"alt="A screenshot of the Python > Analysis: Extra Paths setting with the path pasted in"/></p>
<olstart="6">
<li>Create a new Python file, such as <code>Repeat Grid.py</code> and start writing a script. As you type, you should get helpful suggestions for every available node.</li>
</ol>
<p><imgsrc="setup/../images/vscode_code_completion.png"alt="A screenshot of a script with the documentation for instance_on_points appearing as the user types."/></p>
<h2id="linking-with-blender"><aclass="header"href="#linking-with-blender">Linking with Blender</a></h2>
<p>Writing a script is great, but we want to see it run in Blender. Thankfully, Blender's Text Editor lets us link with an external file, and a simple tool from Geometry Script can make the process more seamless:</p>
<ol>
<li>Open a <em>Text Editor</em> space.</li>
<li>Click the open button in the top of the editor, and navigate to your Python file.</li>
<li>Click the gear icon or press <em>N</em>, and uncheck <em>Make Internal</em>. This will ensure that changes made outside of Blender can be easily brought in.</li>
<li>Click <em>Open Text</em>.</li>
</ol>
<p><imgsrc="setup/../images/open_file.png"alt="A screenshot of Blender's file picker, with the Make Internal checkbox unchecked."/></p>
<olstart="5">
<li>At the top right of the Text Editor, open the <em>Geometry Script</em> menu and enable <em>Auto Resolve</em>. Enabling this feature will make the text data-block in Blender update every time you save the file outside of Blender.</li>
</ol>
<p><imgsrc="setup/../images/auto_resolve.png"alt="A screenshot of the Geometry Script menu with Auto Resolve checked"/></p>
<olstart="6">
<li>To enable hot reload, open the <em>Text</em> menu and enable <em>Live Edit</em>. This will re-run your Geometry Script whenever it changes, updating the node tree live.</li>
</ol>
<p><imgsrc="setup/../images/live_edit.png"alt="A screenshot of the Text menu with Live Edit checked"/></p>
<p>And that's it! You're setup to start writing scripts. In the next section we'll take a look at the API, and all of the things you can do with it.</p>
<p>Creating Geometry Scripts can be as easy or complex as you want for your project.
Throughout this guide, scripts will be displayed alongside the generated nodes to provide context on how a script relates to the underlying nodes.</p>
<p>Setting up an editor for <ahref="api/../setup/external-editing.html">external editing</a> is recommended when writing scripts, but <ahref="api/../setup/internal-editing-basics.html">internal editing inside Blender</a> will suffice for the simple examples shown here.</p>
<p>The first step when writing is script is importing the <code>geometry_script</code> module. There a are a few ways of doing this:</p>
<h2id="import-all-names-recommended"><aclass="header"href="#import-all-names-recommended">Import All Names (Recommended)</a></h2>
<p>This will import every type and function available into your script. It can make it easy to discover what's available with code completion, and makes the scripts more terse.</p>
<p>Node trees are created by decorating a function with <code>@tree</code>. Let's try creating a simple tree function.</p>
<blockquote>
<p>The code samples for the rest of the book assume you are importing all names with <code>from geometry_script import *</code>. However, if you are using a namespaced import, simply prefix the functions and types with <code>geometry_script</code> or your custom name.</p>
</blockquote>
<pre><codeclass="language-python">@tree
def cube_tree():
...
</code></pre>
<p>By default, the name of your function will be used as the name of the generated node tree. However, you can specify a custom name by passing a string to <code>@tree</code>:</p>
<p>All arguments in a tree function must be annotated with a valid socket type. These types are provided by Geometry Script, and are not equivalent to Python's built-in types. Let's add a size argument to our Cube Tree.</p>
<p>Because scripts are converted to Geometry Node trees, you typically cannot use default Python types as arguments. In some cases, they will be automatically converted for you, but in general you will be dealing with socket types.</p>
<h2id="what-is-a-socket"><aclass="header"href="#what-is-a-socket">What is a socket?</a></h2>
<p>A socket is any input or output on a node. Take the <em>Cube</em> node for example:</p>
<p>This node has 4 input sockets, and 1 output socket.</p>
<ul>
<li>Input Sockets
<ul>
<li>Size: <code>Vector</code></li>
<li>Vertices X: <code>Int</code></li>
<li>Vertices Y: <code>Int</code></li>
<li>Vertices Z: <code>Int</code></li>
</ul>
</li>
<li>Output Sockets
<ul>
<li>Mesh: <code>Geometry</code></li>
</ul>
</li>
</ul>
<p>A socket does not represent a value itself. For example, the <code>Size</code> socket does not necessarily represent the value <code>(1, 1, 1)</code>. Instead, it can be connected to another node as an input, giving it a dynamic value.</p>
<p>When we write scripts, we typically deal with socket types, not concrete values like <code>(1, 1, 1)</code>. Take this script for example:</p>
<p>The <code>size</code> argument creates a input socket with the type <code>Vector</code>. This is then connected to the <code>size</code> socket of the <em>Cube</em> node.</p>
<p>Our script does not run every time the node tree is evaluated. It only runs once to create the node tree. Therefore, we have no way of knowing what value <code>size</code> has when the script runs, because it is dynamic.</p>
<p>Sockets are great for passing values between nodes. A socket type like <code>Geometry</code> does not represent concrete vertices, edges, and faces. Instead, it represents the input or output socket of a node. This lets us use it to create connections between different nodes, by passing the output of one node to the input of another.</p>
<p>Sockets cannot be read for their concrete value. A <code>Float</code> socket type does not equal <code>5</code> or <code>10</code> or <code>3.14</code> to our script. It only represents the socket of a node. If you try to <code>print(...)</code> a socket, you will receive a generic reference type with no underlying value.</p>
<h2id="why-use-sockets"><aclass="header"href="#why-use-sockets">Why use sockets?</a></h2>
<p>You might be wondering, "if you can't access the value of a socket, what can you do with it?"</p>
<p>Geometry Script provides many helpful additions that make working with sockets about as easy as working with a concrete value.</p>
<p>Socket types can be used to perform math operations. The proper <em>Math</em> node will be created automatically for you, so you can focus on writing a script and not thinking about sockets. If you use <code>Float</code> or <code>Int</code> it will create a <em>Math</em> node, and if you use a <code>Vector</code> it will create a <em>Vector Math</em> node.</p>
<p>Socket types can be compared with Python comparison operators. A <em>Compare</em> node will be created with the correct inputs and options specified.</p>
<p>While the <code>Vector</code> type does not equate to three concrete components, such as <code>(1, 2, 3)</code>, you can still access the <code>x</code>, <code>y</code>, and <code>z</code> components as sockets. A <em>Separate XYZ</em> node will be created with the correct inputs and outputs specified.</p>
<p>Node functions are automatically generated for the Blender version you are using. This means every node will be available from geometry script.</p>
<p>This means that when future versions of Blender add new nodes, they will all be available in Geometry Script without updating the add-on.</p>
<p>To see all of the node functions available in your Blender version, open the <em>Geometry Script</em> menu in the <em>Text Editor</em> and click <em>Open Documentation</em>.</p>
<p>This will open the automatically generated docs page with a list of every available node and it's inputs and outputs.</p>
<h2id="how-nodes-are-mapped"><aclass="header"href="#how-nodes-are-mapped">How nodes are mapped</a></h2>
<p>All nodes are mapped to functions in the same way, so even without the documentation you can decifer what a node will equate to. Using an <ahref="api/basics/../../setup/external-editing.html">IDE with code completion</a> makes this even easier.</p>
<p>The general process is:</p>
<ol>
<li>Convert the node name to snake case.</li>
<li>Add a keyword argument (in snake case) for each property and input.</li>
<li>If the node has a single output, return the socket type, otherwise return an object with properties for each output name.</li>
</ol>
<blockquote>
<p>Properties and inputs are different types of argument. A property is a value that cannot be connected to a socket. These are typically enums (displayed in the UI as a dropdown), with specific string values expected. Check the documentation for a node to see what the possible values are for a property.</p>
<p>Many nodes have enum properties. For example, the math node lets you choose which operation to perform. You can pass a string to specify the enum case to use. But a safer way to set these values is with the autogenerated enum types. The enums are namespaced to the name of the node in PascalCase:</p>
<pre><codeclass="language-python"># Access it by Node.Enum Name.Case
<p>Some nodes use the same input name multiple times. For example, the <em>Math</em> node has three inputs named <code>value</code>. To specify each value, pass a tuple for the input:</p>
<pre><codeclass="language-python">result = capture_attribute(data_type=CaptureAttribute.DataType.BOOLEAN, geometry=cube_geo) # Specify a property and an input
<p>A Geometry Script can have more than one tree function. Each tree function is a node group, and tree functions can be used in other tree functions to create <em>Node Group</em> nodes.</p>
<h2id="functions-vs-node-groups"><aclass="header"href="#functions-vs-node-groups">Functions vs Node Groups</a></h2>
<p>You do not have to mark a function with <code>@tree(...)</code>. If you don't, function calls are treated as normal in Python. No checks are made against the arguments. Any nodes created in the callee will be placed in the caller's tree.</p>
<pre><codeclass="language-python">def instance_grid(instance: Geometry): # Not marked with `@tree(...)`
<p>The above example would place the <em>Grid</em>, <em>Mesh to Points</em>, and <em>Instance on Points</em> nodes in the main "Cube Grid" tree. It could be rewritten as:</p>
<p>Python has support for <ahref="https://wiki.python.org/moin/Generators">generators</a> using the <code>yield</code> keyword.</p>
<p>Geometry Script tree functions can be represented as generators to output multiple values. If every generated value is <code>Geometry</code>, the values are automatically connected to a <em>Join Geometry</em> node and output as a single mesh.</p>
<p>The first output is always displayed when using a <em>Geometry Nodes</em> modifier. Ensure it is a <code>Geometry</code> socket type, unless you are using the function as a node group.</p>
<p>There are a couple of problems with this. Firstly, the function signature is getting long. This can make it harder to visually parse the script. And, if we want to use the same arguments in another tree and pass them through to <code>terrain</code>, we need to make sure to keep everything up to date.</p>
<p>This is where input groups come in. An input group is class that contains properties annotated with valid socket types.</p>
<p>To create an input group, declare a new class that derives from <code>InputGroup</code>.</p>
<h2id="input-group-prefix"><aclass="header"href="#input-group-prefix">Input Group Prefix</a></h2>
<p>If you use the same <code>InputGroup</code> multiple times, you need to provide a prefix. Otherwise, inputs with duplicate names will be created on your tree.</p>
<p>To do this, use square brackets next to the annotation with a string for the prefix.</p>
<p>To improve the usability of these nodes, <code>capture(...)</code> and <code>transfer(...)</code> methods are provided on <code>Geometry</code> that simply take the attribute and any other optional arguments.</p>
The safest way to use named attributes is with the <code>Attribute</code> class.</p>
<p>Create a named attribute with a data type and optional domain, then use the <code>store(...)</code>, <code>exists()</code>, and <code>__call__(...)</code> methods to use it.</p>
<pre><codeclass="language-python"># Create the attribute
my_custom_attribute = Attribute(
"my_custom_attribute",
NamedAttribute.DataType.FLOAT, # declare the data type once
<p>In Blender 3.4+, transfer attribute was replaced with a few separate nodes: <em>Sample Index</em>, <em>Sample Nearest</em>, and <em>Sample Nearest Surface</em>.</p>
<p>To avoid inputting data types and geometry manually, you can use the custom <code>Geometry</code> subscript.</p>
<p>The structure for these subscripts is:</p>
<pre><codeclass="language-python">geometry[value : index or sample position : domain, mode, domain]
</code></pre>
<p>Only the value argument is required. Other arguments can be supplied as needed.</p>
<p>The <em>Boolean Math</em> node gives access to common boolean operations, such as <code>AND</code>, <code>NOT</code>, <code>XOR</code>, etc.</p>
<p>However, it can be cumbersome to use the <code>boolean_math</code> function in complex boolean expressions.</p>
<pre><codeclass="language-python"># Check if the two values equal, or if the first is true.
x = False
y = True
return boolean_math(
operation=BooleanMath.Operation.OR
boolean=(
boolean_math(
operation=BooleanMath.Operation.XNOR # Equal
boolean=(x, y)
),
x
)
)
</code></pre>
<p>A few operators are available to make boolean math easier and more readable.</p>
<pre><codeclass="language-python"># Check if the two values equal, or if the first is true.
x = False
y = True
return (x == y) | x
</code></pre>
<p>The operators available are:</p>
<ul>
<li><code>==</code> - <code>XNOR</code></li>
<li><code>!=</code> - <code>XOR</code></li>
<li><code>|</code> - <code>OR</code></li>
<li><code>&</code> - <code>AND</code></li>
<li><code>~</code> - <code>NOT</code></li>
</ul>
<blockquote>
<p>You <em>cannot</em> use the built-in Python keywords <code>and</code>, <code>or</code>, and <code>not</code>. You must use the custom operators above to create <em>Boolean Math</em> nodes.</p>
<p>This API is subject to change as future builds of Blender with simulation nodes are released.</p>
</blockquote>
<p>The <code>geometry-nodes-simulation</code> branch of Blender 3.5 includes support for "simulation nodes".</p>
<p>Using a <em>Simulation Input</em> and <em>Simulation Output</em> node, you can create effects that change over time.</p>
<p>As a convenience, the <code>@simulation</code> decorator is provided to make simulation node blocks easier to create.</p>
<pre><codeclass="language-python">@simulation
def move_over_time(
geometry: Geometry, # the first input must be `Geometry`
speed: Float,
dt: SimulationInput.DeltaTime, # Automatically passes the delta time on any argument annotated with `SimulationInput.DeltaTime`.
elapsed: SimulationInput.ElapsedTime, # Automatically passes the elapsed time
) -> Geometry:
return geometry.set_position(
offset=combine_xyz(x=speed)
)
</code></pre>
<p>Every frame the argument <code>geometry</code> will be set to the geometry from the previous frame. This allows the offset to accumulate over time.</p>
<p>The <code>SimulationInput.DeltaTime</code>/<code>SimulationInput.ElapsedTime</code> types mark arguments that should be given the outputs from the <em>Simulation Input</em> node.</p>
<p>Create a base mesh. I'll be using a Monkey primitive.</p>
<p><imgsrc="tutorials/./monkey.png"alt=""/></p>
<p>Next, create a new script. Setting up an <ahref="tutorials/../setup/external-editing.html">external editor</a> is recommended.</p>
<p>Import Geometry Script, and create a basic tree builder function. We'll add a <code>geometry</code> argument and annotate it with the <code>Geometry</code> type to receive our base mesh (in this case, a monkey).</p>
<p>Add a new argument <code>resolution: Float</code>. Give it a default value of <code>0.2</code>. This value will be used throughout the script to configure spacing and voxel density.</p>
<h2id="mesh-to-volume"><aclass="header"href="#mesh-to-volume">Mesh to Volume</a></h2>
<p>We want to convert the mesh to a hollow volume, so only the outside of the mesh has voxel instances. This will improve the performance of our script.</p>
<p>Use the <code>mesh_to_volume</code> function on the base mesh to convert it to a volume.</p>
<h2id="volume-to-points"><aclass="header"href="#volume-to-points">Volume to Points</a></h2>
<p>Next, we need to create points to instance each voxel cube on. Use <code>distribute_points_in_volume</code> with the mode set to <code>DENSITY_GRID</code> to create a uniform distribution of points.</p>
<p>Finally, use <code>instance_on_points</code> with a cube of size <code>resolution</code> to instance a cube on each point created from our mesh.</p>
<p>In this tutorial we'll create a dense grid of buildings, then cut away from them to place roads with curves. We'll also make use of a <ahref="tutorials/../api/advanced-scripting/generators.html">generator</a> to combine the buildings with the roads.</p>
<p>Run the script to create the tree, then add a <em>Geometry Nodes</em> modifier to your curve object and select the <em>City Builder</em> node group.</p>
<p>Next, we'll instance cubes on these points to serve as our buildings. We move the cube object up half its height so the buildings sit flat on the grid, and scale them randomly between the min and max sizes.</p>
<p>Using <code>curve_to_mesh</code>, we can turn the input curve into a flat mesh. We'll use the <code>yield</code> keyword to join the curve mesh and the building mesh automatically. Change the <code>building_points.instance_on_points</code> line to use <code>yield</code> for this to work.</p>
<p>But now the buildings are overlapping the road. We need to remove any point that falls within the road curve. We'll use <code>geometry_proximity</code> and <code>delete_geometry</code> to find and remove these invalid points.</p>