Example #1
0
void
UsdImagingGprimAdapter::_DiscoverPrimvars(UsdGeomGprim const& gprim,
                                          SdfPath const& cachePath, 
                                          UsdShadeShader const& shader,
                                          UsdTimeCode time,
                                          UsdImagingValueCache* valueCache)
{
    // TODO: It might be convenient to implicitly wire up PtexFaceOffset and
    // PtexFaceIndex primvars.
    
    TF_DEBUG(USDIMAGING_SHADERS).Msg("\t Looking for <%s> primvars at <%s>\n",
                            gprim.GetPrim().GetPath().GetText(),
                            shader.GetPrim().GetPath().GetText());
    for (UsdShadeParameter const& param : shader.GetParameters()) {
        UsdShadeShader source;
        TfToken outputName;
        if (param.GetConnectedSource(&source, &outputName)) {
            UsdAttribute attr = source.GetIdAttr();
            TfToken id;
            if (not attr or not attr.Get(&id)) {
                continue;
            }
            TF_DEBUG(USDIMAGING_SHADERS).Msg("\t\t Param <%s> connected <%s>(%s)\n",
                            param.GetAttr().GetName().GetText(),
                            source.GetPath().GetText(),
                            id.GetText());
            if (id == UsdHydraTokens->HwPrimvar_1) {
                TfToken t;
                VtValue v;
                UsdGeomPrimvar primvarAttr;
                if (UsdHydraPrimvar(source).GetVarnameAttr().Get(&t, 
                                            UsdTimeCode::Default())) {
                    primvarAttr = gprim.GetPrimvar(t);
                    if (primvarAttr.ComputeFlattened(&v, time)) {
                        TF_DEBUG(USDIMAGING_SHADERS).Msg("Found primvar %s\n",
                            t.GetText());

                        UsdImagingValueCache::PrimvarInfo primvar;
                        primvar.name = t;
                        primvar.interpolation = primvarAttr.GetInterpolation();
                        valueCache->GetPrimvar(cachePath, t) = v;
                        _MergePrimvar(primvar, &valueCache->GetPrimvars(cachePath));
                    } else {
                        TF_DEBUG(USDIMAGING_SHADERS).Msg(
                            "\t\t No primvar on <%s> named %s\n",
                            gprim.GetPath().GetText(),
                            t.GetText());

                    }
                }
            } else {
                // Recursively look for more primvars
                _DiscoverPrimvars(gprim, cachePath, source, time, valueCache);
            }
        }
    }
}
static
void _ExtractPrimvarsFromNode(UsdShadeShader const & shadeNode, 
                             HdMaterialNode const & node, 
                             HdMaterialNetwork *materialNetwork)
{
    // Check if it is a node that reads primvars.
    // XXX : We could be looking at more stuff here like manifolds..
    if (node.identifier == TfToken("Primvar_3")) {
        // Extract the primvar name from the usd shade node
        // and store it in the list of primvars in the network
        UsdShadeInput nameAttrib = shadeNode.GetInput(TfToken("varname"));
        if (nameAttrib) {
            VtValue value;
            nameAttrib.Get(&value);
            if (value.IsHolding<std::string>()) {
                materialNetwork->primvars.push_back(
                    TfToken(value.Get<std::string>()));
            }
        }        
    }
}
// Walk the shader graph and emit nodes in topological order
// to avoid forward-references.
static
void _WalkGraph(UsdShadeShader const & shadeNode, 
               HdMaterialNetwork *materialNetwork,
               const TfTokenVector &shaderSourceTypes)
{
    // Store the path of the node
    HdMaterialNode node;
    node.path = shadeNode.GetPath();
    if (!TF_VERIFY(node.path != SdfPath::EmptyPath())) {
        return;
    }
    // If this node has already been found via another path, we do
    // not need to add it again.
    for (HdMaterialNode const& existingNode: materialNetwork->nodes) {
        if (existingNode.path == node.path) {
            return;
        }
    }

    // Visit the inputs of this node to ensure they are emitted first.
    const std::vector<UsdShadeInput> shadeNodeInputs = shadeNode.GetInputs();
    for (UsdShadeInput const& input: shadeNodeInputs) {
        // Check if this input is a connection and if so follow the path
        UsdShadeConnectableAPI source;
        TfToken sourceName;
        UsdShadeAttributeType sourceType;
        if (UsdShadeConnectableAPI::GetConnectedSource(input, 
                &source, &sourceName, &sourceType)) {
            // When we find a connection to a shading node output,
            // walk the upstream shading node.  Do not do this for
            // other sources (ex: a connection to a material
            // public interface parameter), since they are not
            // part of the shading node graph.
            if (sourceType == UsdShadeAttributeType::Output) {
                UsdShadeShader connectedNode(source);
                _WalkGraph(connectedNode, materialNetwork, shaderSourceTypes);
            }
        }
    }

    // Extract the identifier of the node
    TfToken id;
    if (!shadeNode.GetShaderId(&id)) {
        for (auto &sourceType : shaderSourceTypes) {
            if (SdrShaderNodeConstPtr n = 
                    shadeNode.GetShaderNodeForSourceType(sourceType)) {
                id = n->GetIdentifier();
                break;
            }
        }
    }

    if (!id.IsEmpty()) {
        node.identifier = id;

        // If a node is recognizable, we will try to extract the primvar 
        // names that is using since this can help render delegates 
        // optimize what what is needed from a prim when making data 
        // accessible for renderers.
        _ExtractPrimvarsFromNode(shadeNode, node, materialNetwork);
    } else {
        TF_WARN("UsdShade Shader without an id: %s.", node.path.GetText());
        node.identifier = TfToken("PbsNetworkMaterialStandIn_2");
    }

    // Add the parameters and the relationships of this node
    VtValue value;
    for (UsdShadeInput const& input: shadeNodeInputs) {
        // Check if this input is a connection and if so follow the path
        UsdShadeConnectableAPI source;
        TfToken sourceName;
        UsdShadeAttributeType sourceType;
        if (UsdShadeConnectableAPI::GetConnectedSource(input,
            &source, &sourceName, &sourceType)) {
            if (sourceType == UsdShadeAttributeType::Output) {
                // Store the relationship
                HdMaterialRelationship relationship;
                relationship.outputId = shadeNode.GetPath();
                relationship.outputName = input.GetBaseName();
                relationship.inputId = source.GetPath();
                relationship.inputName = sourceName;
                materialNetwork->relationships.push_back(relationship);
            } else if (sourceType == UsdShadeAttributeType::Input) {
                // Connected to an input on the public interface.
                // The source is not a node in the shader network, so
                // pull the value and pass it in as a parameter.
                if (UsdShadeInput connectedInput =
                    source.GetInput(sourceName)) {
                    if (connectedInput.Get(&value)) {
                        node.parameters[input.GetBaseName()] = value;
                    }
                }
            }
        } else {
            // Parameters detected, let's store it
            if (input.Get(&value)) {
                node.parameters[input.GetBaseName()] = value;
            }
        }
    }

    materialNetwork->nodes.push_back(node);
}