Skip to content

PointInstancer : Add new node for making PointInstancer objects - #7077

Open
johnhaddon wants to merge 16 commits into
GafferHQ:mainfrom
johnhaddon:pointInstancerNode
Open

PointInstancer : Add new node for making PointInstancer objects#7077
johnhaddon wants to merge 16 commits into
GafferHQ:mainfrom
johnhaddon:pointInstancerNode

Conversation

@johnhaddon

Copy link
Copy Markdown
Member

This converts any points-based primitive to a PointInstancer, collecting prototypes and parenting them below. It works around the limitations of the USD instancing spec by allowing variations of the prototypes to be generated using time offsets and arbitrary context variables specified as primitive variables on the source points.

Like ContactSheet, the implementation is split between an internal C++ node and a user-facing node authored as a Box and exported via ExtensionAlgo. I'm pretty open to opinions about the user-facing design - questions on my mind include :

  • Is the shuffling of the transform primitive variables actually useful, or would we be better off making people do that beforehand?
  • Should the None prototype modes be removed or renamed? Would the add-variation-to-existing-instancer feature they enable be better off in a separate PrototypeVariations node?
  • Is the ordering of sections in the UI right? Should we do more to guide a user to making the minimal configuration to get useful output (specifying the prototype locations)?

As with ContactSheet and ContactSheetCore, we'll be combining a small C++ core with a node-network to implement the user-facing node.
This converts any points-based primitive to a PointInstancer, collecting prototypes and parenting them below. It works around the limitations of the USD instancing spec by allowing variations of the prototypes to be generated using time offsets and arbitrary context variables.

Fixes GafferHQ#6810
@johnhaddon johnhaddon self-assigned this Aug 5, 2026
@github-project-automation github-project-automation Bot moved this to Pending Review in Work in Progress Aug 5, 2026

@murraystevenson murraystevenson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks John, a few comments inline for what I managed to get through today. I've also pushed two fixups with spelling/typo corrections (seemed simpler to just fix them rather than make a bunch more comments).

Is the shuffling of the transform primitive variables actually useful, or would we be better off making people do that beforehand?

It may be useful, but it does feel secondary/tertiary to setting up your prototypes, their variations and any shading attributes. Maybe the Transform and IDs sections are better demoted to below Prototypes, Prototype Variation, and Attributes?

Should the None prototype modes be removed or renamed? Would the add-variation-to-existing-instancer feature they enable be better off in a separate PrototypeVariations node?

I haven't tried using it too much just yet, but the None modes do seem like they're muddying the waters of the node a bit. I'm also wondering about the current behaviour of the attributes plug when you're using the PointInstancer node to modify the prototypes of an existing point instancer. You need to remember to set attributes to * to ensure any previously included primitive variables aren't deleted, which seems easy to overlook. Maybe this would all be simpler with a dedicated PrototypeVariations node?

Comment thread src/GafferScene/PointInstancerCore.cpp Outdated
Comment thread src/GafferScene/PointInstancerCore.cpp
Comment thread src/GafferScene/PointInstancerCore.cpp Outdated
Comment thread src/GafferScene/PointInstancerCore.cpp Outdated
- `{timeOffset}` : The time offset.
- `{contextVariable}` : The value of a context variable.
- `{hash}` : A hash value that uniquely idenfies the protoype.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth mentioning the - -> n . -> _ transformations that occur to sanitise float values?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented in 87375a9.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this now just needs to be propagated through to the plug on the PointInstancer node, as it still has the old description baked in.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0485e43. And in f9dde0a I added a note to remind me to keep them in sync in future.

Comment thread src/GafferScene/PointInstancerCore.cpp Outdated
Comment thread src/GafferScene/PointInstancerCore.cpp Outdated
Comment thread python/GafferSceneTest/PointInstancerTest.py Outdated
__children["PointInstancer"]["Expression1"]["__expression"].setValue( 'parent["__out"]["p0"] = "prototypeRoots prototypeIndex P scale orientation instanceId invisibleIds " + parent["__in"]["p0"]\n' )
__children["Expression"]["__engine"].setValue( 'OSL' )
__children["Expression"]["__expression"].setValue( 'parent.__out.p0 = .25 * ( 1 + sin( time * 10 ) );' )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing a description for the PointInstancer node itself.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 665a7a2.

Maybe the Transform and IDs sections are better demoted to below Prototypes, Prototype Variation, and Attributes?

I did this in the same commit, as well as opening the Prototypes section by default, and adding a divider between the prototypes and prototype index plugs.

I also wonder if it's worth driving visibilityActivator rather than activator with the prototype mode plugs, to reduce clutter further?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this in the same commit, as well as opening the Prototypes section by default, and adding a divider between the prototypes and prototype index plugs.

Thanks, that is a great improvement to the layout.

I also wonder if it's worth driving visibilityActivator rather than activator with the prototype mode plugs, to reduce clutter further?

Yeah, visibilityActivator might be more appropriate in this situation, there is a fair jumble of plugs and I don't think we're too concerned about keeping the other plugs visible for discoverability. Yesterday, I was tempted to ask for the RandomPrimitiveVariables' "seed" plug to be promoted to make the Random instanceMode a little more useful, but didn't want to add to the clutter of plugs. If we switch to visibilityActivator maybe that's a worthwhile addition?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to visibilityActivator in 34b932c. Added seed in 1e54cfa.

Now it's looking less cluttered, I'm also wondering about adding a weighting feature to the random mode. I think we could collect weights from attributes on each prototype potentially. Maybe wait till someone asks for it though?

Comment thread python/GafferScene/PointInstancer.gfr
- Add description.
- Move prototype sections to top, and default first section open.
- Add divider between prototype and prototype indices sections.
Check interpolation for `timeOffsets` and `prototypeIndex`. The new `vertexVariable()` function also has the advantage of returning IndexedView directly instead of `optional<IndexedView>`, which simplifies later usage slightly.

At the same time, tighten up size checks for context variables. We don't want to accept Constant variables just because they happen to be the right length, because if the point count animates it will be super confusing for the variable to pop in and out of existence. There's a bit of tension here between error reporting and wildcards - we don't report errors so you can use `*` to use all vertex variables, but that means you don't get an error if you name `aConstantVariable` explicitly.
@johnhaddon

Copy link
Copy Markdown
Member Author

Thanks John, a few comments inline for what I managed to get through today.

Thanks Murray - I've pushed fixups for everything and noted them inline.

Maybe this would all be simpler with a dedicated PrototypeVariations node?

That is seeming pretty reasonable. I started prototyping such a node and the first thing that crops up is the layout of the prototypes on output.

For PointInstancer I felt it was pretty reasonable to be opinionated, enforcing that prototypes are always parented below the instancer, always in a flat list, etc. It's still possible to import a less/differently structured instancer from USD, or build one from primitives in Gaffer.

But for PrototypeVariations I wonder if a user might reasonably expect us to build the variations in-place, respecting the original layout of prototypes. Might it be better to lay them out as path/to/original/prototype/{variation} rather than conform them in the same way as PointInstancer? Worth getting this hashed out as it might affect the design of PointInstancerCore.

@johnhaddon

Copy link
Copy Markdown
Member Author

Testing this a bit today, I'm finding the attributes plug annoying. Maybe we default it to *, or just remove it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pending Review

Development

Successfully merging this pull request may close these issues.

2 participants