Skip to content

Document inputs don't work when connected to NodeGraph inputs #2763

@frohnej-adsk

Description

@frohnej-adsk

I don't think this is mentioned explicitly in the specification but judging by PR #1453, documents are allowed to contain <input> elements.

  • Document inputs work if they feed into an input on a Node
  • Document inputs don't work if they feed into an input of a NodeGraph

Node input referencing a document input

This renders correctly:
Image

<?xml version="1.0"?>
<materialx version="1.39">
  <!-- Document input -->
  <input name="A" type="color3" value="0, 1, 0" />

  <add name="add1" type="color3" nodedef="ND_add_color3">
    <!-- Node input connected to document input is resolved correctly. -->
    <input name="in1" type="color3" interfacename="A" />
  </add>

  <convert name="convert1" type="surfaceshader" nodedef="ND_convert_color3_surfaceshader">
    <input name="in" type="color3" nodename="add1" />
  </convert>
  <surfacematerial name="surfacematerial1" type="material" nodedef="ND_surfacematerial">
    <input name="surfaceshader" type="surfaceshader" nodename="convert1" />
  </surfacematerial>
</materialx>

NodeGraph input referencing a document input

This renders black:
Image

<?xml version="1.0"?>
<materialx version="1.39">
  <!-- Document input -->
  <input name="A" type="color3" value="0, 1, 0" />

  <nodegraph name="compound1">
    <!-- NodeGraph input connected to document input. -->
    <!-- This isn't resolved correctly by `Input::getInterfaceInput()` -->
    <input name="B" type="color3" interfacename="A" />

    <add name="add1" type="color3" nodedef="ND_add_color3">
      <!-- Calling `Input::getInterfaceInput()` on this input returns input B. -->
      <!-- This isn't wrong, but it might be desirable to follow interfacenames -->
      <!-- recursively and return input A. -->
      <input name="in1" type="color3" interfacename="B" />
    </add>
    <output name="out" type="color3" nodename="add1" />
  </nodegraph>

  <convert name="convert1" type="surfaceshader" nodedef="ND_convert_color3_surfaceshader">
    <input name="in" type="color3" nodegraph="compound1" output="out" />
  </convert>
  <surfacematerial name="surfacematerial1" type="material" nodedef="ND_surfacematerial">
    <input name="surfaceshader" type="surfaceshader" nodename="convert1" />
  </surfacematerial>
</materialx>

It also prints a validation warning:

*** Validation warnings for B.mtlx ***
Interface name not found in referenced declaration: <input name="B" type="color3" interfacename="A">

Issue

The issue is mainly in Input::getInterfaceInput(), which calls getAncestorOfType<GraphElement>().

In the broken example, calling it on /compound1/B will return the first ancestor of type GraphElement: the parent /compound1. But we actually want to go to the grandparent /, so that we can find the input /A.

What would be the desired behavior? I haven't contributed before but I'd be happy to contribute a fix.

Suggestion A:

A simple fix could look like this:

    InputPtr Input::getInterfaceInput() const
    {
-       if (hasInterfaceName())
+       if (hasInterfaceName() && getParent()->getParent())
        {
-           ConstGraphElementPtr graph = getAncestorOfType<GraphElement>();
+           ConstGraphElementPtr graph = getParent()->getParent()->asA<GraphElement>();
            if (graph)
            {
                return graph->getInput(getInterfaceName());
            }
        }
        return nullptr;
    }

This makes the method work correctly, but it's not enough to fix the render: Calling it on /compound1/add1/in1 returns /compound1/B which has an interfacename attribute as well. The caller would be responsible to call getInterfaceInput() again on the returned input until the end of the chain is reached. Thus, it would require additional changes in other places.

Suggestion B:

Instead, the method could traverse the whole chain of interfacename connections. E.g. recursively:

InputPtr Input::getInterfaceInput() const
{
    if (!hasInterfaceName() || !getParent()->getParent())
    {
        return nullptr;
    }

    const auto graph = getParent()->getParent()->asA<GraphElement>();
    if (!graph)
    {
        return nullptr;
    }

    const auto interfaceInput = graph->getInput(getInterfaceName());
    if (!interfaceInput)
    {
        return nullptr;
    }
    const auto recursiveInput = interfaceInput->getInterfaceInput();

    return recursiveInput ? recursiveInput : interfaceInput;
}

This is enough to fix the render.

Other places that need fixing

I found two other places that'll need similar fixes:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions