Skip to content

Commit

Permalink
Connectability of shading attributes
Browse files Browse the repository at this point in the history
Added connectability as token valued metadata on attributes.
In certain shading models, there is a need to distinguish between the
inputs can vary over a surface and those that must be uniform. This is
accomplished in USD-shade by limiting the connectability of the input, by
setting the "connectability" metadata on the associated attribute.

Connectability of an Input can be set to "full" or "interfaceOnly".
 * "full "implies that  the Input can be connected to any other Input or Output.
 * "interfaceOnly" implies that the Input can only be connected to a NodeGraph
 Input (which represents an interface override, not a render-time dataflow
 connection), or another Input whose connectability is also "interfaceOnly".

When connectability isn't authored on a shading node, it defaults to "full"
on a Shader and to "interfaceOnly" on a NodeGraph. When the prim type
is unknown, the fallback value (which is set in the schema registry) is "full".

Added UsdShadeConnectableAPI::CanConnect to help clients determine if a
input or output *can* be connected to a given shading attribute.
UsdShadeConnectableAPI::ConnectToSource() no longer validates the
the attempted connection. It simply goes ahead and tries to author it.
Clients are responsible for calling CanConnect() themselves when they
care about validation.

Unit test update is coming soon.

(Internal change: 1727857)
  • Loading branch information
shriramiyer authored and pixar-oss committed Mar 24, 2017
1 parent 019a65f commit 10d510b
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 42 deletions.
147 changes: 110 additions & 37 deletions pxr/usd/lib/usdShade/connectableAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,116 @@ UsdShadeConnectableAPI::_IsCompatible(const UsdPrim &prim) const
return IsShader() || IsNodeGraph();
}

static bool
_CanConnectOutputToSource(const UsdShadeOutput &output,
const UsdAttribute &source,
std::string *reason)
{
if (not output.IsDefined()) {
if (reason) {
*reason = TfStringPrintf("Invalid output");
}
return false;
}

// Only outputs on node-graphs are connectable.
if (not UsdShadeConnectableAPI(output.GetPrim()).IsNodeGraph()) {
if (reason) {
*reason = "Output does not belong to a node-graph.";
}
return false;
}

if (source) {
// Ensure that the source prim is a descendent of the node-graph owning
// the output.
const SdfPath sourcePrimPath = source.GetPrim().GetPath();
const SdfPath outputPrimPath = output.GetPrim().GetPath();

if (not sourcePrimPath.HasPrefix(outputPrimPath)) {
if (reason) {
*reason = TfStringPrintf("Source of output '%s' on node-graph "
"at path <%s> is outside the node-graph: <%s>",
source.GetName().GetText(), outputPrimPath.GetText(),
sourcePrimPath.GetText());
}
return false;
}

}

return true;
}

bool
UsdShadeConnectableAPI::CanConnect(
const UsdShadeOutput &output,
const UsdAttribute &source)
{
std::string reason;
// The reason why a connection can't be made isn't exposed currently.
// We may want to expose it in the future, especially when we have
// validation in USD.
return _CanConnectOutputToSource(output, source, &reason);
}

bool
_CanConnectInputToSource(const UsdShadeInput &input,
const UsdAttribute &source,
std::string *reason)
{
if (not input.IsDefined()) {
if (reason) {
*reason = TfStringPrintf("Invalid input: %s",
input.GetAttr().GetPath().GetText());
}
return false;
}

if (not source) {
if (reason) {
*reason = TfStringPrintf("Invalid source: %s",
source.GetPath().GetText());
}
return false;
}

TfToken inputConnectability = input.GetConnectability();
if (inputConnectability == UsdShadeTokens->full) {
return true;
} else if (inputConnectability == UsdShadeTokens->interfaceOnly) {
if (UsdShadeInput::IsInput(source)) {
if (source.GetPrim().IsA<UsdShadeNodeGraph>()) {
return true;
}

TfToken sourceConnectability = UsdShadeInput(source).GetConnectability();
if (sourceConnectability == UsdShadeTokens->interfaceOnly) {
return true;
}
}
}

if (reason) {
*reason = TfStringPrintf("Input connectability is 'interfaceOnly' and "
"source does not have 'interfaceOnly' connectability.");
}

return false;
}

bool
UsdShadeConnectableAPI::CanConnect(
const UsdShadeInput &input,
const UsdAttribute &source)
{
std::string reason;
// The reason why a connection can't be made isn't exposed currently.
// We may want to expose it in the future, especially when we have
// validation in USD.
return _CanConnectInputToSource(input, source, &reason);
}

static TfToken
_GetConnectionRelName(const TfToken &attrName)
{
Expand Down Expand Up @@ -176,15 +286,6 @@ _GetConnectionRel(
return UsdRelationship();
}

static
TfToken
_GetPropertyName(const TfToken &sourceName,
const UsdShadeAttributeType sourceType)
{
return TfToken(UsdShadeUtils::GetPrefixForAttributeType(sourceType) +
sourceName.GetString());
}

/* static */
bool
UsdShadeConnectableAPI::ConnectToSource(
Expand All @@ -194,34 +295,6 @@ UsdShadeConnectableAPI::ConnectToSource(
UsdShadeAttributeType const sourceType,
SdfValueTypeName typeName)
{
UsdShadeAttributeType shadingPropType =
UsdShadeUtils::GetBaseNameAndType(shadingProp.GetName()).second;
if (shadingPropType == UsdShadeAttributeType::Output) {
// Only outputs belonging to node-graphs are connectable. We don't allow
// connecting outputs of shaders as it's not meaningful.
//
// Note: this warning will not be issued if the prim is untyped or
// if the type is unknown.
if (UsdShadeConnectableAPI(shadingProp.GetPrim()).IsShader()) {
TF_WARN("Attempted to connect an output of a shader <%s> to <%s>.",
shadingProp.GetPath().GetText(),
source.GetPath().AppendProperty(
_GetPropertyName(sourceName, sourceType)).GetText());
return false;
}

// Ensure that the source prim is a descendent of the node-graph owning
// the output.
const SdfPath sourcePrimPath = source.GetPrim().GetPath();
const SdfPath outputOwnerPath = shadingProp.GetPrim().GetPath();
if (!sourcePrimPath.HasPrefix(outputOwnerPath)) {
TF_WARN("Source of output '%s' on node-graph at path <%s> is outside "
"the node-graph: <%s>", sourceName.GetText(),
outputOwnerPath.GetText(), sourcePrimPath.GetText());
// Issue a warning, but allow this connnection for now.
}
}

UsdPrim sourcePrim = source.GetPrim();
bool success = true;

Expand Down
25 changes: 25 additions & 0 deletions pxr/usd/lib/usdShade/connectableAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,31 @@ class UsdShadeConnectableAPI : public UsdSchemaBase
///
/// @{

/// Determines whether the given input can be connected to the given
/// source attribute, which can be an input or an output.
///
/// The result depends on the "connectability" of the input and the source
/// attributes and the types of prims they belong to.
///
/// \sa UsdShadeInput::SetConnectability
USDSHADE_API
static bool CanConnect(const UsdShadeInput &input,
const UsdAttribute &source);

/// Determines whether the given output can be connected to the given
/// source attribute, which can be an input or an output.
///
/// An output is considered to be connectable only if it belongs to a
/// node-graph. Shader outputs are not connectable.
///
/// \p source is an optional argument. If a valid UsdAttribute is supplied
/// for it, this method will return true only if the source attribute is
/// owned by a descendant of the node-graph owning the output.
///
USDSHADE_API
static bool CanConnect(const UsdShadeOutput &output,
const UsdAttribute &source=UsdAttribute());

/// Authors a connection for a given shading property \p shadingProp.
///
/// \p shadingProp can represent a parameter, an interface attribute or
Expand Down
45 changes: 45 additions & 0 deletions pxr/usd/lib/usdShade/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ using std::string;

TF_DEFINE_PRIVATE_TOKENS(
_tokens,
(connectability)
(renderType)
);

Expand Down Expand Up @@ -157,5 +158,49 @@ UsdShadeInput::IsInput(const UsdAttribute &attr)
UsdShadeTokens->inputs));
}

bool
UsdShadeInput::SetConnectability(const TfToken &connectability) const
{
return _attr.SetMetadata(_tokens->connectability, connectability);
}

TfToken
UsdShadeInput::GetConnectability() const
{
TfToken connectability;
_attr.GetMetadata(_tokens->connectability, &connectability);

// If there's no authored connectability, then check the owner of the
// input to see if it's a node-graph or a shader.
// If it's a node-graph, the default connectability is "interfaceOnly".
// If it's a shader, the default connectability is "full".
//
// If the owner's type is unknown, then get the fallback value from the
// schema registry.
//
if (connectability.IsEmpty()) {
UsdShadeConnectableAPI connectable(GetPrim());
if (connectable.IsNodeGraph())
return UsdShadeTokens->interfaceOnly;
else if (connectable.IsShader()) {
return UsdShadeTokens->full;
}
} else {
return connectability;
}

static const VtValue fallback = SdfSchema::GetInstance().GetFallback(
_tokens->connectability);

return fallback.IsHolding<TfToken>() ? fallback.UncheckedGet<TfToken>()
: UsdShadeTokens->full;
}

bool
UsdShadeInput::ClearConnectability() const
{
return _attr.ClearMetadata(_tokens->connectability);
}

PXR_NAMESPACE_CLOSE_SCOPE

42 changes: 42 additions & 0 deletions pxr/usd/lib/usdShade/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,48 @@ class UsdShadeInput
return lhs.GetAttr() == rhs.GetAttr();
}

// -------------------------------------------------------------------------
/// \name Connectability API
// -------------------------------------------------------------------------
/// @{

/// \brief Set the connectability of the Input.
///
/// In certain shading data models, there is a need to distinguish which
/// inputs <b>can</b> vary over a surface from those that must be
/// <b>uniform</b>. This is accomplished in UsdShade by limiting the
/// connectability of the input. This is done by setting the
/// "connectability" metadata on the associated attribute.
///
///
/// Connectability of an Input can be set to UsdShadeTokens->full or
/// UsdShadeTokens->interfaceOnly.
///
/// \li <b>full</b> implies that the Input can be connected to any other
/// Input or Output.
/// \li <b>interfaceOnly</b> implies that the Input can only be connected to
/// a NodeGraph Input (which represents an interface override, not a
/// render-time dataflow connection), or another Input whose connectability
/// is also "interfaceOnly".
///
/// The default connectability of a node-graph interface input is
/// UsdShadeTokens->interfaceOnly.
/// The default connectability of a shader input is UsdShadeTokens->full.
///
/// \sa SetConnectability()
bool SetConnectability(const TfToken &connectability) const;

/// \brief Returns the connectability of the Input.
///
/// \sa SetConnectability()
TfToken GetConnectability() const;

/// \brief Clears any authored connectability on the Input.
///
bool ClearConnectability() const;

/// @}

private:
friend class UsdShadeConnectableAPI;

Expand Down
7 changes: 4 additions & 3 deletions pxr/usd/lib/usdShade/plugInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
{
"Info": {
"SdfMetadata": {
"arrayConnectionSize": {
"connectability": {
"appliesTo": [
"attributes"
],
"default": -1,
"default": "full",
"displayGroup": "Shading",
"type": "int"
"documentation": "Metadata authored on UsdShadeInput's to specify what they can be connected to. Can be either \"full\" or \"interfaceOnly\". \"full\" implies that the input can be connected to any other input or output. \"interfaceOnly\" implies that the input can only connect to a NodeGraph Input (which represents an interface override, not a render-time dataflow connection), or another Input whose connectability is also \"interfaceOnly\".",
"type": "token"
},
"outputName": {
"appliesTo": [
Expand Down
15 changes: 15 additions & 0 deletions pxr/usd/lib/usdShade/schema.usda
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ over "GLOBAL" (
UsdShadeMaterial.
"""
}
dictionary full = {
string doc= """Possible value for 'connectability' metadata on
a UsdShadeInput. When connectability of an input is set to
"full", it implies that it can be connected to any input or
output.
"""
}
dictionary interfaceOnly = {
string doc= """Possible value for 'connectability' metadata on
a UsdShadeInput. It implies that the input can only connect to
a NodeGraph Input (which represents an interface override, not
a render-time dataflow connection), or another Input whose
connectability is also 'interfaceOnly'.
"""
}
dictionary connectedSourceFor = {
string value = "connectedSourceFor:"
string doc = """The prefix on UsdShadeShader relationships
Expand Down
4 changes: 4 additions & 0 deletions pxr/usd/lib/usdShade/tokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ PXR_NAMESPACE_OPEN_SCOPE
(displacement) \
(displayColor) \
(displayOpacity) \
(full) \
((infoId, "info:id")) \
((inputs, "inputs:")) \
((interface_, "interface:")) \
(interfaceOnly) \
((interfaceRecipientsOf, "interfaceRecipientsOf:")) \
((lookBinding, "look:binding")) \
((materialBinding, "material:binding")) \
Expand Down Expand Up @@ -84,9 +86,11 @@ PXR_NAMESPACE_OPEN_SCOPE
/// \li <b>displacement</b> - Describes the displacement relationship terminal on a UsdShadeMaterial. Used to find the terminal UsdShadeShader describing the displacement of a UsdShadeMaterial.
/// \li <b>displayColor</b> - UsdShadePShader
/// \li <b>displayOpacity</b> - UsdShadePShader
/// \li <b>full</b> - Possible value for 'connectability' metadata on a UsdShadeInput. When connectability of an input is set to "full", it implies that it can be connected to any input or output.
/// \li <b>infoId</b> - UsdShadeShader
/// \li <b>inputs</b> - The prefix on shading attributes denoting an input.
/// \li <b>interface_</b> - The prefix on UsdShadeNodeGraph attributes denoting an interface attribute.
/// \li <b>interfaceOnly</b> - Possible value for 'connectability' metadata on a UsdShadeInput. It implies that the input can only connect to a NodeGraph Input (which represents an interface override, not a render-time dataflow connection), or another Input whose connectability is also 'interfaceOnly'.
/// \li <b>interfaceRecipientsOf</b> - The prefix on UsdShadeNodeGraph relationships denoting the target of an interface attribute.
/// \li <b>lookBinding</b> - The relationship name on non shading prims to denote a binding to a UsdShadeLook. This is a deprecated relationship and is superceded by material:binding.
/// \li <b>materialBinding</b> - The relationship name on non-shading prims to denote a binding to a UsdShadeMaterial.
Expand Down
Loading

0 comments on commit 10d510b

Please sign in to comment.