-
Notifications
You must be signed in to change notification settings - Fork 411
Specification proposal for template elements
#2674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -157,6 +157,115 @@ Materials can inherit from other materials, to add or change shaders connected t | |
| Inheritance of material-type custom nodes is also allowed, so that new or changed input values can be applied on top of those specified in the inherited material. | ||
|
|
||
|
|
||
|
|
||
| ### TypeDef Token Elements | ||
|
|
||
| TypeDef token elements allow the definition of value strings with a consistent meaning across types, e.g. a `half` value that corresponds to "0.5" for the `float` type and "0.5, 0.5, 0.5" for the `vector3` type. References to TypeDef tokens are restricted to typed `value` attributes, and are bracketed by the `[` and `]` characters, e.g. `[half]`. Within a typed `value` attribute, TypeDef token substitutions are applied before all other token substitutions. | ||
|
|
||
| ```xml | ||
| <typedef name="float"> | ||
| <token name="zero" type="float" value="0.0" /> | ||
| <token name="half" type="float" value="0.5" /> | ||
| <token name="one" type="float" value="1.0" /> | ||
| </typedef> | ||
| <typedef name="color3" semantic="color" > | ||
| <token name="zero" type="color3" value="0.0, 0.0, 0.0" /> | ||
| <token name="half" type="color3" value="0.5, 0.5, 0.5" /> | ||
| <token name="one" type="color3" value="1.0, 1.0, 1.0" /> | ||
| </typedef> | ||
| <typedef name="color4" semantic="color"> | ||
| <token name="zero" type="color4" value="0.0, 0.0, 0.0, 0.0" /> | ||
| <token name="half" type="color4" value="0.5, 0.5, 0.5, 0.5" /> | ||
| <token name="one" type="color4" value="1.0, 1.0, 1.0, 1.0" /> | ||
| </typedef> | ||
| <typedef name="vector2" > | ||
| <token name="zero" type="vector2" value="0.0, 0.0" /> | ||
| <token name="half" type="vector2" value="0.5, 0.5" /> | ||
| <token name="one" type="vector2" value="1.0, 1.0" /> | ||
| </typedef> | ||
| <typedef name="vector3" > | ||
| <token name="zero" type="vector3" value="0.0, 0.0, 0.0" /> | ||
| <token name="half" type="vector3" value="0.5, 0.5, 0.5" /> | ||
| <token name="one" type="vector3" value="1.0, 1.0, 1.0" /> | ||
| </typedef> | ||
| <typedef name="vector4"> | ||
| <token name="zero" type="vector4" value="0.0, 0.0, 0.0, 0.0" /> | ||
| <token name="half" type="vector4" value="0.5, 0.5, 0.5, 0.5" /> | ||
| <token name="one" type="vector4" value="1.0, 1.0, 1.0, 1.0" /> | ||
| </typedef> | ||
| ``` | ||
|
|
||
| ### Template Elements | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The great news is that I think our proposals look pretty similar, so that should make things easy! For clarity perhaps I can enumerate what I see as the differences and we can discuss from there.... 1)
|
||
|
|
||
| Template elements allow a single template pattern to be used in instantiating an arbitrary number of elements at runtime, with each reference to the provided `key` being replaced with one of the corresponding strings in the `values` array. Each element within the scope of the `template` will be instantiated separately for each string in the `values` array. | ||
|
|
||
| References to the template `key` are expressed as the string value of the `key` bracketed by the `(` and `)` characters, e.g. `(keystring)`. Substitution of key-value pairs takes precedence over any other supported substitutions within the scope of a `template` element. | ||
|
|
||
| To support generic typed values, TypeDef `token` strings may be used in place of any literal value within a `template` element, bracketed by the `[` and `]` characters. At template expansion time, any typed value that corresponds to a TypeDef `token` will be replaced with the corresponding literal value for the given `token` and type. | ||
|
|
||
| Template elements may be nested to any depth, allowing for efficient authoring of combinatorial element templates. | ||
|
|
||
| The following example show how the full set of `nodedef` and `nodegraph` variations for the `contrast` node in MaterialX would be expressed using two `template` elements. | ||
|
|
||
| ```xml | ||
| <template name="T_contrast" key="type" values="float, color3, color4, vector2, vector3, vector4"> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll note that in my PR I haven't templatized the nodegraph elements yet - but I believe they could be similarly simplified.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Although this would be a clever way to further compress templates, to my eye it seems less visually clear than the proposed |
||
| <nodedef name="ND_contrast_(type)" node="contrast" nodegroup="adjustment"> | ||
| <input name="in" type="(type)" value="[zero]" /> | ||
| <input name="amount" type="(type)" value="[one]" /> | ||
| <input name="pivot" type="(type)" value="[half]" /> | ||
| <output name="out" type="(type)" defaultinput="in" /> | ||
| </nodedef> | ||
| <nodegraph name="NG_contrast_(type)" nodedef="ND_contrast_(type)"> | ||
| <subtract name="N_sub_(type)" type="(type)"> | ||
| <input name="in1" type="(type)" interfacename="in" /> | ||
| <input name="in2" type="(type)" interfacename="pivot" /> | ||
| </subtract> | ||
| <multiply name="N_mul_vector3" type="(type)"> | ||
| <input name="in1" type="(type)" nodename="N_sub_(type)" /> | ||
| <input name="in2" type="(type)" interfacename="amount" /> | ||
| </multiply> | ||
| <add name="N_add_(type)" type="(type)"> | ||
| <input name="in1" type="(type)" nodename="N_mul_(type)" /> | ||
| <input name="in2" type="(type)" interfacename="pivot" /> | ||
| </add> | ||
| <output name="out" type="(type)" nodename="N_add_(type)" /> | ||
| </nodegraph> | ||
| </template> | ||
|
|
||
| <template name="T_contrast_FA" key="type" values="color3, color4, vector2, vector3, vector4"> | ||
| <nodedef name="ND_contrast_(type)FA" node="contrast" nodegroup="adjustment"> | ||
| <input name="in" type="(type)" value="[zero]" /> | ||
| <input name="amount" type="float" value="1.0" /> | ||
| <input name="pivot" type="float" value="0.5" /> | ||
| <output name="out" type="(type)" defaultinput="in" /> | ||
| </nodedef> | ||
| <nodegraph name="NG_contrast_(type)FA" nodedef="ND_contrast_(type)FA"> | ||
| <subtract name="N_sub_(type)" type="(type)"> | ||
| <input name="in1" type="(type)" interfacename="in" /> | ||
| <input name="in2" type="float" interfacename="pivot" /> | ||
| </subtract> | ||
| <multiply name="N_mul_vector3" type="(type)"> | ||
| <input name="in1" type="(type)" nodename="N_sub_(type)" /> | ||
| <input name="in2" type="float" interfacename="amount" /> | ||
| </multiply> | ||
| <add name="N_add_(type)" type="(type)"> | ||
| <input name="in1" type="(type)" nodename="N_mul_(type)" /> | ||
| <input name="in2" type="float" interfacename="pivot" /> | ||
| </add> | ||
| <output name="out" type="(type)" nodename="N_add_(type)" /> | ||
| </nodegraph> | ||
| </template> | ||
| ``` | ||
|
|
||
| #### Proposed Template Implementation | ||
|
|
||
| When a document containing `template` elements is loaded through the MaterialX API, its templates are expanded to their full form through a built-in `Document::expandTemplates` method, which is invoked automatically at load-time in the same fashion as the existing `Document::upgradeVersion` method. This allows the document to be authored and stored on disk in its clearest and most compact form, while MaterialX runtimes and downstream clients can assume templates are fully expanded at load time, allowing them to operate as if no templates are present. | ||
|
|
||
| For an initial implementation of this feature, a proposed approach is to implement the `TemplateElement` class, the TypeDef form of the `Token` class, and the `Document::expandTemplates` method in the MaterialX runtime, and to add a single example of a `template` to the MaterialX data libraries (e.g. the `contrast` example above), in order to prove out the syntax and logic before committing to a full data library upgrade. This represents roughly 2-4 days of work for a developer that is familiar with the MaterialX C++ codebase. | ||
|
|
||
| An important property of this proposed implementation is that downstream clients such as OpenUSD will require no changes to support it, as the template expansion logic will be fully contained within existing MaterialX API calls. Clients that define custom MaterialX nodes will have the option of using `template` elements for efficiency and clarity, but will be under no obligation to do so, and they can effectively ignore the presence of template functionality if they choose. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I'm reading this - OpenUSD would be required to call
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm imagining that
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not how I read what you've written above - maybe it's worth making that idea more explicit in the proposed text.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, and I'll add a sentence to state this clearly in the proposal, since it's an important detail. |
||
|
|
||
|
|
||
| <p> <p><hr><p> | ||
|
|
||
| # Proposals: Stdlib Nodes<a id="propose-stdlib-nodes"></a> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
typespecification here feels unnecessary, given the scope of the element.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a completely fair point, and I mainly added this to be consistent with the concept of "typed values" in MaterialX, where the element itself has enough information for an accessor to interpret its combined
typeandvalueattributes as a typed value in the engine.It's certainly not a requirement here, but it seems consistent with how typed values are handled elsewhere in the MaterialX specification and API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are any of those other typed value use-cases encapsulated in a container element that concretely defines the type? if they are, then I think maybe there is precedent enough to retain this, otherwise I feel we should remove it for both brevity and lack of potential error/confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I see the perspective that you're outlining, it's worth noting that there are no typed MaterialX elements that state a
valueattribute without also stating thetypeattribute that clarifies its interpretation. This is what allows the API calls inTypedElementto be shared across a wide set ofElementsubclasses, since they all build upon the same expression of a typed value.From that perspective, it seems more robust to state both
valueandtypewithin thetokenelement itself, rather than making an exception when the parent of the element has a type-like name.