Making digital assets: handling internal attributes / groups

   1484   4   4
User Avatar
Member
50 posts
Joined: 8月 2009
Offline
I'm starting to get back into developing custom digital assets to fill some gaps in Houdini's out-of-box (and Labs) operator set. Many of these will be assets built from other nodes, particularly in SOPs, and I'll be creating and manipulating geometry attributes and groups internally within the asset. Whenever this happens, there's a risk that attribute and group names collide with existing names on the input geometry, potentially leading to undesirable behaviour within the asset. I've been wrestling with how to handle this in a way that is robust and transparent to the user, and thought I'd share my thinking here for comment. The options I've come up with so far are:

Ignore the problem
Use whatever attribute / group names make sense to me and overwrite any identically-named attributes / groups that the user has unwittingly supplied as input geometry. Not a particularly forgiving experience for the user, and could easily lead to confusion when things don't work as expected.

Obfuscate the internal names
I've lifted the lid on some Labs assets and noticed a tendency (but not quite frequent enough to be considered a rule) to prefix internal names with single or double underscores, a bit like how python 'hides' class internals. If this (or something similar) was an established practice that Houdini users knew and adhered to, this would probably work quite well and I wouldn't need to think any more on the problem. But it isn't, as far as I can tell (please correct if you know otherwise; the Labs assets may be trying to establish a precedent here). And there remains the possibility that a user who is unaware of such a convention or perhaps imports geometry from another package with conflicting attribute/group names that unknowingly interfere with the asset internals.

Raise an error / warning
With prefixed or otherwise obfuscated internal names for attributes/groups, for the rare occasions that a naming conflict does arise, the asset could simply raise an error or warning about a name collision. That puts the onus on the user to fix it by renaming one or more attribute/group names, which is better than silently ignoring the collision, but still feels like a suboptimal experience for a user in that position.

Procedurally rename internal names upon collision
This is the approach I've settled on thus far, and seems the most Houdini-like and acceptable from a user perspective, and introduces only moderate inconvenience to an asset developer. It works by branching the asset input geometry into a custom Python SOP, into which I list all the internal attribute and group names I want to use. The Python code then checks each name against the incoming geometry, and appends/increments the name by one if there is a collision - just like node names do when you copy-paste them or try to create a node name conflict - until it finds a unique name. The Python code then creates a dictionary attribute at the global geometry (detail) level with desired names as the keys (e.g. 'uv') and resulting names after collision-testing as the values (e.g. 'uv1').

Why store the mapping in an attribute? Well, the deconflicted names need to be referenced in string parameters of other nodes, and potentially in VEX or other Python code elsewhere in the asset, and there are established ways of doing this that don't require custom functions to be written. And the detail attribute itself does not propagate any further through the network, since it terminates that branch and is only indirectly referenced by the output geometry pipeline.

And so instead of referencing the original attribute / group names directly in node parameters (and any VEX/Python code within the asset), I instead retrieve the corresponding dictionary value from the detail attribute in the other branch, using the original name as key. Fully procedural, and guaranteed not to result in name collisions at runtime, regardless of whatever attribute or group names exist on the input geometry. It does, however, necessitate Python's slighty more verbose channel referencing syntax, because I haven't yet found a way to get Hscript / Expressions to work with dictionary attributes (again, would welcome any advice to the contrary).

With this approach, an asset developer can focus on getting the asset working right, using whatever internal attribute and group names make sense, and append these to the list of names in the custom Python SOP as they go. As development nears completion, one needs to then convert any hard-coded attribute/group names into references to the detail dictionary, but that's fairly trivial and could well be automated with a script (which I haven't bothered with owing to the relative simplicity of the assets I'm developing).

Anyway, that's probably enough detail on this admittedly niche topic for now. If you've made it this far, I'd be interested any thoughts / approaches to this problem you've come up with, and whether procedural renaming is the most sensible solution. If any devs are purusing the forum, perhaps there could be some built-in functionality to deal with attribute/group naming conflicts, that either builds on this solution or tackles it a completely different way.
User Avatar
Member
4520 posts
Joined: 2月 2012
Offline
I personally use either prefix or suffix with a distinct op name like vexsubdivide_ or polycarve_. I think this is unique enough that the odds of someone using these names is slim.

Anyway I never had issue with my convention above in production.

If you are more paranoid I guess you could also add a username and some unique numbers that would make it practically impossible to get a collision. Something like:

animatrix_(unique op id)_(op name)_ + attrib/group name

animatrix_718341_polycarve_ + attrib/group name
Senior FX TD @ Industrial Light & Magic
Get to the NEXT level in Houdini & VEX with Pragmatic VEX! [www.pragmatic-vfx.com]

youtube.com/@pragmaticvfx | patreon.com/animatrix | animatrix2k7.gumroad.com
User Avatar
Member
311 posts
Joined: 10月 2016
Offline
Hi Andrew,

another major context uses prefixes (and possibly suffixes) to prevent name conflicts when using referenced assets. So I’d suggest a simple approach with that.
Interested in character concepts, modeling, rigging, and animation. Related tool dev with Py and VEX.
User Avatar
Member
359 posts
Joined: 4月 2017
Offline
The double underscore is a pretty good precedent to follow and it's what I do for most of my tools. It's something that's sometimes used in Python as well; prefixing a variable with a double underscore automatically prepends the class name to the variable to avoid name conflicts (basically making it "private").

The nice thing about using this prefix is the easy cleanup; before the output node in your HDA, just drop down an Attribute Delete / Group Delete SOP and remove __* and you're done.
MOPs (Motion Operators for Houdini): http://www.motionoperators.com [www.motionoperators.com]
User Avatar
Member
1 posts
Joined: 12月 2020
Offline
toadstorm
The double underscore is a pretty good precedent to follow and it's what I do for most of my tools. ...
... easy cleanup; before the output node in your HDA, just drop down an Attribute Delete / Group Delete SOP and remove __* and you're done.

I know you have lots of dev experience with HDAs, but with respect for your work, please don't do this to us. When we go to use your HDA within our HDAs, and we have created __dblunder attributes, you'll end up deleting our private __attributes for us. You'll literally reach out of your HDAs and delete the users' __data when they try to use your assets.

Perhaps a better approach is, for each HDA, create a simple somewhat random, dev-conceived, 4+ digit UUID prefix:
__8362_attributename
__8362_pointcharacteristic

The chances of collision here are so low that you won't even need to check for them.

THEN at the end of your HDA you can safely Group Delete "__8362_*" and you're done.

This is the method I use. I truly wish that double underscores were a reserved attribute prefix and automatically scoped attributes to the current node context but until then we'll have to create our own inconsistent conventions.

A minor warning: Just be sure your UUIDs change per HDA (easy). Don't use an organization ID that spans all your nodes, "we here at Bro Co. always use __1337_ in our internals". You'll perform accidental deletions when nesting your own organizational nodes.
  • Quick Links