2024-10-28 Geometry Nodes Workshop Notes

This workshop took place at the Blender head quarters in Amsterdam.

Present

  • Lukas Tönne
  • Hans Goudey
  • Jacques Lucke
  • Simon Thommes (afternoons)
  • Dalai Felinto (kickoff)
  • Falk David (sporadically)

Notes

These are the meeting notes we took during the workshop. There is also a blog post summarizing the different topics.


Use a declarative approach to separate the behavior a user wants from how it’s actually implemented. Users pass in “behaviors” to the solver which chooses how to evaluate them. Behaviors are implemented as closures or bundles.

Closures

A closure is basically a callback with some information stored inside.

The closure type cannot be statically propagated, so the types for closure creation and evaluation are separate on a low level. There should be high level tools that build on top of logged types to propagate types.

Closure input and output mapping can be based on identifiers (like node groups), because the closures and closure evaluation are built separately. The identifiers are exposed more publicly so it may not be possible to generate them automatically.

Mismatched closure types

  • Missing socket
    • Default value is used in evaluate node
  • Incorrect socket type
    • Implicit type conversion happens as possible, falling back to the default value.

There should be warning messages when the closure types don’t match.

Visualization

  • A double link might help to signify that data flows backwards for the closure evaluation.
    • On the other hand, it’s quite different from gizmos where the backward flow is for UI purposes rather than evaluation itself.

Bundles

On a low level, the types for the pack and unpack nodes are separate, meaning they have completely separate types. There should be high level tools to equalize types.

Many points from closures apply here too; conceptually they are a special case of closures with no inputs.

Bundles are syntactic sugar in the end, they should function the same as passing the links around separately (with some extra possibilities of runtime typing).

Bundles can be nested.

Bundles don’t have index access. We should avoid depending on the order of elements in a bundle. A bundle is like a dictionary while a list is like an array.

This relates to previous designs for extendable sockets (for example used for passing an arbitrary number of fields to the capture attribute node). Nodes that have extendable sockets (like the raycast node) should also process bundles.

Bundles will be useful for other node systems too (for example passing many PBR textures around at the same time). Shader nodes would have more constraints on the types, they must be statically known.

A useful operator would be a “Separate Closure” operator that would add a separate node based on the signature from the last evaluation.

Bundle Nodes

  • A node to retrieve items from a bundle based on an identifier: “Get Bundle Item”. It should have a “Remove” option that’s enabled by default which can be more efficient in some cases. It also needs a boolean output “Exists”
  • A “Set Bundle Item” node which adds an item to a bundle with a name and an identifier.
  • “Get Bundle Identifiers” with a bundle input and a string list output.
    • A list of strings is also useful to reference a group of attributes in many other places.

Visualization

Bundles can be visualized with a thicker “udon” link. The link can still just have a single color though-- it’s too complicated to try to display all the inner colors inside.

When it’s known statically, bundle links should be dashed if they contain fields.

Context Inputs

image

image

image

Reasoning

  • We have no way to override existing inputs like node tools inputs and the scene camera.
  • We have no good way to pass the hair system’s surface geometry in a flexible way.
  • There is a lot of boilerplate necessary to pass the same input to many places. Example video of a workaround by Erindale: https://youtu.be/xBM4BPpfbUg?si=K1WEfVgdbBZfiBe_&t=64
  • We need a more flexible replacement to the “Is Viewport” node, which is often used to control an interactive/quality switch.

A new “Context Input” node provides a way to retrieve data from an arbitrary higher level of the system.

Context values from builtin nodes should not be visible by default to avoid cluttering everything that uses a contextual builtin node.

Node group context inputs from builtin nodes should only be created when you actually use a socket. Removing a link from a socket doesn’t remove the group input.

In the modifier UI, if the context input is not overridden, an operator button displays to the right of the button to enable the overridee.

Group Input Defaults

Add a new output node that allows defining more complex defaults with nodes.
Design document: Group Default Node [Proposal] - HackMD

  • This has some problems when the default is configured when it wasn’t before-- the behavior of node group instances may change.
    • We propose to not specifically solve this problem for now. It’s not so different from the existing tradeoff when defaults are changed, and user feedback might make the proper solution more clear anyway. Moving forward with the implementation is more valuable.
  • For forward compatibility and workflow breakage:
    • The new group default node system should coexist with the existing field default dropdown in the node interface for several versions.
    • Once the old field default dropdown is removed (at a major version change), we should have a label directing the user to the new system for several more versions.

Asset Embedding

This is an ongoing design topic that isn’t discussed fully in this workshop. It’s necessary for the other designs to work though. We did decide that to solve data-block name collisions from multiple versions of the same asset, libraries would be created for those duplicate assets to give a namespace and avoid changing the ID name.

Solver Implementation

XPBD

  1. Find all colliders before the substep loop.
  2. Integrate forces to change velocities.
  3. Translate positions based on velocities.
  4. Collision detection “broad phase” prefilters colliders.
  5. Find actual contact points to build contact constraints.
    • There may be multiple contact points for a specific curve point.
  6. Alternate between Gauss-Seidel steps and Jacobi solver steps.
    • In the Gauss-Seidel step, every constraint is processed separately, but first separate contraint islands have to be found.
      • Without self collisions, all the points in one curve would be one constraint island.
    • Solving the constraints changes the positions directly.
  7. Calculate new velocities based on old and new positions.

We will need a graph coloring node.

Constraint Types

  • Collision
  • Edge length
  • Bending
  • Twist
  • Pinning
  • Friction

There are other types of constraints too.

Modifier UI

Parameters should be indepent from each other as much as possible

Possible panels:

  • Material Properties
    • Settings for the edge length and bend constraints (stiffness, damping, restitution)
  • Pinning
    • Surface pinning (enabled by default)
    • “Goal positions”
  • Collision
    • Collision collection
    • Self collision
  • Forces
    • Gravity (use scene gravity)
    • Wind
  • Quality
    • Solver Substeps
    • Collision substeps

Generic Modifier UI Features

Add items to the right click menu for modifier buttons:

  • Toggle Single/Attribute
  • Override Value (checkbox)
  • Switch Object/Collection (for geometry input socket type)
  • Hide Input (for a future input hiding feature)

Remove the attribute toggle from the modifier UI.

Node Group UI

The solver node is just a single node group.
Behaviors are separate nodes that are connected in as bundles/closures. Behaviors set up in separate nodes include:

  • Gravity
  • Colliders
  • Attachment

The solver node has some internal constraints built in, including:

  • Stiffness

Lists

Lists are necessary for implementing constraint evaluation with nodes because the solver needs to store values and do processing per constraint rather than per point.

The first version of lists would be very simple and just support plain arrays, no nesting and no fields.

List Nodes

  • List
    • Length (could be a field)
    • Field
  • Add Element
  • Remove Elements
  • Get Element
  • Concatenate Lists
  • Filter (gather)
    • Index list or selection field input
    • Different modes eventually (we will probably want a closure predicate)
  • Replace Elements
    • Index list or selection input
    • Two list inputs, one is the size of the indices/selection
    • A list output with replaced elements

What does the index mean when list and field contexts are combined?

To keep things simple we don’t have List of Fields, we only have Field of Lists (aka list field).

A syntactic sugar that would be useful is a multi-input socket that concatenate items to form a list.

Guides Workflow

Two workflows:

  1. Only sculpt the guides, all child hair is generated procedurally
  2. Guide hairs are selected/generated as a subset of original geometry

The biggest limitation is that we can’t store the fact that curves can’t be influenced by multiple guides. This is why we have a special builtin node for interpolating child positions rather than implementing it as a node group. Actually storing this multi-parent information is probably not so bad, something like 36 bytes per curve if each curve has 4 parents. This calls for lists (potentially nested lists), and potentially list attributes.

Other features are important for making the guides workflow work properly:

  1. Tangent space calculation
  2. Custom normals

For Each Geometry

Processes every unique geometry in an instance tree, creating the same behavior we have in current builtin nodes. The output node can have multiple geometry outputs. There can also be output attributes-- fields are evaluated on the output node. Non-instance geometry is processed in the same way, just like builtin nodes.

The instance component of each unique geometry isn’t passed to the inside of the zone. The zone doesn’t process non-top-level instances-- that use case isn’t addressed here.

Adding the instances as a separate input to the zone may be helpful.

There is some existing design work here: #123021 - Proposal: Instance's References Foreach Loop - blender - Blender Projects

Modal Node Tools

In Blender currently there are two types of modal operators:

  1. Operators based on the initial state (like bevel). These have redo panels.
  2. History dependent operators using the previous state at every modal step. These don’t have redo panels.

Theoretically it would be useful even for history dependent operators to have redo panels. Since the redo happens with a single “exec” call, it would require the context information (like mouse position) of every modal step to be stored as RNA lists.

If boolean inputs have keymap items set up, they should not be displayed in the redo panel.

One issue with this design is that the keymap is defined as part of the asset. Usually for operators the keymap is defined at a completely different level. It should be possible to override the group’s keymap in the preferences keymap. The preferences could store keymaps associated with assets that are operators (rather than associated with operators directly). Local node tools that aren’t assets always have the default keymap that’s part of the node group.

Last Properties

The “Last properties” system doesn’t work for node tools because all node tools use the same operator. We really need a map from asset to the last properties for that asset, but it’s not clear where that should be stored (currently for regular operators it’s stored in the type). We don’t have a good solution for this yet.

Socket Shapes

We need socket shapes for:

  • Images
  • Grids
  • Fields
  • Dynamic
  • Single Value

Shader nodes wouldn’t need to change, at least not much. The compositor would change to use image sockets for images and it would open the opportunity to have sockets for many existing node properties.

In the same release we change the socket shapes, we will add features to improve the debugging experience, like tooltips describing the expected/current “structure type” and icons of the current data drawn on top of links.

Field Context Zone

This is a way to implement a custom field input, and a replacement for part of the older “geometry field” concept that we decided against. The zone has a geometry input and list outputs. Inside the zone, the geometry is the context geometry of the field evaluation. This resolves the duplication of nodes like “Sample Index” and “Evaluate at Index” which are the same, but one has a geometry input and one is a field input.

The attribute statistic node is another example where this would be useful.

Besides the geometry context, this would also work for volume grid and list contexts.

Old Particle System Features

  • Emission already handled by the hair system
  • Substeps (needs generic improvement of simulation/depsgraph evaluation)
  • Collisions
    • Quality More substeps specific to collision handling
    • Distance Try to avoid adding this option to the new system
    • Impulse Clamping Part of solver
    • Collision Collection Part of solver
  • Structure
    • Vertex Mass Try to configure as mass per length
    • Stiffness Should be configured independently from mass
    • Random Replaced with fields
    • Damping Part of solver
  • Volume
    • Air Drag Should be exposed somehow
    • The rest of the options can be skipped

Extra Modifier Evaluation Outputs

Modifier evaluation can also output a bundle. The object info node retrieves the bundle. Each modifier implicitly passes the bundle through if it has no bundle input or output. If a modifier node group does have a bundle input or output, it processes the previous bundle.

The extra data bundle could contain a closure. That could be useful for encoding a force field and its evaluation to increase shareability. There may be additional constraints on these closures like not depending on anonymous attributes or some fields that have difficult lifetime handling.

Internal Data Sockets

  • KD tree
  • BVH tree
  • Bake reference

These can use the same socket type / color: black.

Plan

24 Likes

Closures, lists, xpbd, and everything else! Amazing direction :slight_smile:

6 Likes

Hello

Closures

A closure is basically a callback with some information stored inside.

How is closure concept different from the concept of evaluating a nodegroup (say with a set of default values?)

There is a lot of boilerplate necessary to pass the same input to many places. Example video of a workaround by Erindale: https://youtu.be/xBM4BPpfbUg?si=K1WEfVgdbBZfiBe_&t=64

This could be done easily, and intuitively, if users could access object or scene custom properties directly from geometry node. erindale workflow could get covered that way.

for example:

Screenshot 2023-04-15 193848
was proposed in P106989

Then for the “override” idea, users could simply propose options in their nodegroup to override the value and that’s it.

2 Likes

See the blog post for why:

To create closures, we use a new closure zone. It’s a bit like creating a small local node group that can then be passed around. Just using existing node groups does not work, because we need the ability to pass data from the outside into the closure (like in all other zone types). Also, it’s good to have the ability to build the entire node tree in a single group to see everything at once.


This is also talked about in the blog post (regarding context inputs):

If the context value has not been provided by any node, it’s propagated up to the Geometry Nodes modifier where again users can choose to specify it. If not, we could support reading the value from a custom property of the object or scene.

2 Likes

Actually, with a “nodegroups as sockets” principle, this argument starts to get weak.

Users are already familiar with the concept of nodegroups, it’s user friendly

Here’s how the concept of “bundle” could be introduced visually

5 Likes

I’m not a fan of the rainbow mega noodle in the middle, it’s just far too much noise than is worthwhile. Just a single generic color would be fine in my books

3 Likes

I am stumbling in the same spot : don’t node groups let the user “pass data from the outside into it”? I don’t understand how that differs between the two

Otherwise fascinating blog post, I’m excited to see physics solvers being constructed with nodes up to a practical point, rather than a black box.

We will try and implement as much of this as possible using generic geometry nodes. Some parts like collision detection and constraint grouping may require new built-in nodes for performance reasons. This will be decided when we get there.

@jacqueslucke so is the idea to let users wrangle their own physics solvers with reliance on such nodes?

2 Likes

It’s not a rainblow, imagine the links you want to “bundle” drawn aside closely together
I believe it gives a better visual/intuitive cue on the concept of multi links

Oh yea, I realize, I’m just saying in a lot of cases it will look rainbow. This example already has too much happening for my liking. If we get more links than just 4, that middle noodle would be like godzilla big.

1 Like

A lot of really exciting future plans there! I hope that the xpbd solvers and constraints will not be just group nodes, but dedicated nodes for speed reasons.

I have been toying with a geonodes xpbd setup for a while now, and while it is possible to do with the current atomic nodes, it is very slow as your sims scale up. (for context I am @redjam9 on twitter).

I can do maybe half a million granular particles and get less than a frame a second on a 20 thread machine for baking. Granted the blender devs are 10x smarter than I am, but I still think that performance and scalability, should be a focused on from the start.

Even a super simple (non pbd) geonodes particle setup struggles with a million or more particles. It is hard to know where the bottleneck is with this. Stability in geonodes with a high particle count also does get very flaky in that half million and upwards count in my experience.

Anyway, I am very keen to see how this progresses! Regular buildbot builds of the PBD PR would be nice to see.

4 Likes

I agree, plus displaying the cable colors in the link is not a useful information, considering that the user will still have to unpack the bundle to use it. With a single color only for bundles users will already know that the cable contains multiple data.

Question for the devs: so bundles are intended to be used like structs in C?

Yes, we want the system to be flexible enough for that sort of thing, for sure.

This is definitely something we should optimize in the future. There are few fundamental technical reasons why builtin nodes would be faster in general. Also if you’re running into crashes, please report a bug!

Yes, that’s a good analogy. Though technically they are more like Python dictionaries conceptually, since the item identifier plays an important role.

10 Likes

Just to brag a little: I’m extremely pleased ( :stuck_out_tongue: ) the GN team arrived at the same proposal I mocked up a long time ago: :smiley:

5 Likes

At the very least, the different colors show that is a connction of multiple types - not just one thick wire.

1 Like

This is a suggestion from a general user.

Default socket connection determination: If there is a connection to this default socket, the value of the connected node will be used as the default value. On the other hand, if there is no connection, the rule will be set to use the default value set in the old panel.

Coexistence with the old method: By allowing the default setting method in the old panel to coexist with the new default socket, you can gradually introduce the new default value setting method while continuing to operate existing node setups without any problems.

I’m always excited to see Blender developments.

1 Like

Wow, much going on.
I am excited about so many of the ideas. Focussing answer on closures.

Closures are fantastic news!

How is closure concept different from the concept of evaluating a nodegroup (say with a set of default values?)

Node group can be grouped themselves. But it is static. Once you set, it’s everywhere.

A closure is an encapsulated function. It does nothing unless you evaluate it. You can happily compose closures and create your own functionality.
Somewhere you could evaluate it. Or you could feed it to a group, which uses it to do something.

You can nest ngroups, evaluate something, or compose fields with groups. You can plug the ngroup’s output values. But you cannot plug the ngroup ‘itself’ into another ngroup.
NGroups need to fully contain their functionality. Menu’s could help to give additional options about what the group does with its input. It is still hardcoded. If functionality lacks, the ngroup needs to be extended.

Closures would improve ngroups flexibility. NGroups do not nescesarly need to contain the whole procedure. They can borrow some procedure from the outside. So, if you need to extend what could be done with an ngroup, closures could offer a chance that you can extend functionality of the ngroup without changing it.

1 Like

Can you explain that like I’m 5?

Doesn’t a nodegroup also do nothing unless it is evaluated?

Edit: Can a Nodegroup be thought of more like a macro and a closure more like a function?

1 Like

I like this concept and disagree that the individual colors are not useful information: In a situation where you have many different multinoodles, this would make things distinguishable.
Maybe it could be an options thing?
Or perhaps it could be easier solved by simply allowing for a custom in the input node.

If it were thick enough (turning it into more of a ribbon), it could even get some text attached inside it to give the option of labelling what this set of noodles is about. Although perhaps that’s just the job of the corresponding multi-reroute which surely would have such a naming option just like a regular reroute