def test_Identifiers(self): self.assertFalse(Tf.IsValidIdentifier('')) self.assertTrue(Tf.IsValidIdentifier('hello9')) self.assertFalse(Tf.IsValidIdentifier('9hello')) self.assertTrue(Tf.IsValidIdentifier('hello_world')) self.assertTrue(Tf.IsValidIdentifier('HELLO_WORLD')) self.assertTrue(Tf.IsValidIdentifier('hello_world_1234')) self.assertFalse(Tf.IsValidIdentifier('hello_#world#_1234')) self.assertFalse(Tf.IsValidIdentifier('h e l l o')) self.assertEqual(Tf.MakeValidIdentifier(''), '_') self.assertEqual(Tf.MakeValidIdentifier('hello9'), 'hello9') self.assertEqual(Tf.MakeValidIdentifier('9hello'), '_hello') self.assertEqual(Tf.MakeValidIdentifier('hello_#world#_1234'), 'hello__world__1234') self.assertFalse(Tf.IsValidIdentifier('h e l l o'), 'h_e_l_l_o') self.assertFalse(Tf.IsValidIdentifier('!@#$%'), '_____')
def testRenameSpecialCharacter(self): # open twoSpheres.ma scene in testSamples mayaUtils.openTwoSpheresScene() # clear selection to start off cmds.select(clear=True) # select a USD object. mayaPathSegment = mayaUtils.createUfePathSegment( '|usdSphereParent|usdSphereParentShape') usdPathSegment = usdUtils.createUfePathSegment('/sphereXform/sphere') basePath = ufe.Path([mayaPathSegment, usdPathSegment]) usdSphereItem = ufe.Hierarchy.createItem(basePath) ufe.GlobalSelection.get().append(usdSphereItem) # get the USD stage stage = mayaUsd.ufe.getStage(str(mayaPathSegment)) # by default edit target is set to the Rootlayer. self.assertEqual(stage.GetEditTarget().GetLayer(), stage.GetRootLayer()) # rename with special chars newNameWithSpecialChars = '!@#%$@$=sph^e.re_*()<>}021|' cmds.rename(newNameWithSpecialChars) # get the prim pSphereItem = ufe.GlobalSelection.get().front() usdPrim = stage.GetPrimAtPath(str(pSphereItem.path().segments[1])) self.assertTrue(usdPrim) # prim names are not allowed to have special characters except '_' regex = re.compile('[@!#$%^&*()<>?/\|}{~:]') self.assertFalse(regex.search(usdPrim.GetName())) # rename starting with digits. newNameStartingWithDigit = '09123Potato' self.assertFalse(Tf.IsValidIdentifier(newNameStartingWithDigit)) cmds.rename(newNameStartingWithDigit) # get the prim pSphereItem = ufe.GlobalSelection.get().front() usdPrim = stage.GetPrimAtPath(str(pSphereItem.path().segments[1])) self.assertTrue(usdPrim) # prim names are not allowed to start with digits newValidName = Tf.MakeValidIdentifier(newNameStartingWithDigit) self.assertEqual(usdPrim.GetName(), newValidName)
def UpdateSchemaWithSdrNode(schemaLayer, sdrNode, renderContext="", overrideIdentifier=""): """ Updates the given schemaLayer with primSpec and propertySpecs from sdrNode metadata. A renderContext can be provided which is used in determining the shaderId namespace, which follows the pattern: "<renderContext>:<SdrShaderNodeContext>:shaderId". Note that we are using a node's context (SDR_NODE_CONTEXT_TOKENS) here to construct the shaderId namespace, so shader parsers should make sure to use appropriate SDR_NODE_CONTEXT_TOKENS in the node definitions. overrideIdentifier parameter is the identifier which should be used when the identifier of the node being processed differs from the one Sdr will discover at runtime, such as when this function is def a node constructed from an explicit asset path. This should only be used when clients know the identifier being passed is the true identifier which sdr Runtime will provide when querying using GetShaderNodeByIdentifierAndType, etc. It consumes the following attributes (that manifest as Sdr metadata) in addition to many of the standard Sdr metadata specified and parsed (via its parser plugin). Node Level Metadata: - "schemaName": Name of the new schema populated from the given sdrNode (Required) - "schemaKind": Specifies the UsdSchemaKind for the schema being populated from the sdrNode. (Note that this does not support multiple apply schema kinds). - "schemaBase": Base schema from which the new schema should inherit from. Note this defaults to "APISchemaBase" for an API schema or "Typed" for a concrete scheme. - "apiSchemasForAttrPruning": A list of core API schemas which will be composed together and any shared shader property from this prim definition is pruned from the resultant schema. - "typedSchemaForAttrPruning": A core typed schema which will be composed together with the apiSchemasForAttrPruning and any shared shader property from this prim definition is pruned from the resultant schema. If no typedSchemaForAttrPruning is provided then only the apiSchemasForAttrPruning are composed to create a prim definition. This will only be used when creating an APISchema. - "apiSchemaAutoApplyTo": The schemas to which the sdrNode populated API schema will autoApply to. - "apiSchemaCanOnlyApplyTo": If specified, the API schema generated from the sdrNode can only be validly applied to this set of schemas. - "providesUsdShadeConnectableAPIBehavior": Used to enable a connectability behavior for an API schema. - "isUsdShadeContainer": Only used when providesUsdShadeConnectableAPIBehavior is set to true. Marks the connectable prim as a UsdShade container type. - "requiresUsdShadeEncapsulation": Only used when providesUsdShadeConnectableAPIBehavior is set to true. Configures the UsdShade encapsulation rules governing its connectableBehavior. - "tfTypeNameSuffix": Class name which will get registered with TfType system. This gets appended to the domain name to register with TfType. - "schemaPropertyNSPrefixOverride": Node level metadata which can drive all node's properties namespace prefix. This can be useful for non connectable nodes which should not get UsdShade inputs and outputs namespace prefix. Property Level Metadata: - "usdVariability": Property level metadata which specifies a specific sdrNodeProperty should have its USD variability set to Uniform or Varying - "usdSuppressProperty": A property level metadata which determines if the property should be suppressed from translation from args to property spec. - "propertyNSPrefixOverride": Provides a way to override a property's namespace from the default (inputs:/outputs:) or from a node's schemaPropertyNSPrefixOverride metadata. Sdr Property Metadata to SdfPropertySpec Translations - A "null" value for Widget sdrProperty metadata translates to SdfPropertySpec Hidden metadata. - SdrProperty's Help metadata (Label metadata if Help metadata not provided) translates to SdfPropertySpec's Documentation string metadata. - SdrProperty's Page metadata translates to SdfPropertySpec's DisplayGroup metadata. - SdrProperty's Label metadata translates to SdfPropertySpec's DisplayName metadata. - SdrProperty's Options translates to SdfPropertySpec's AllowedTokens. - SdrProperty's Default value translates to SdfPropertySpec's Default value. - Connectable input properties translates to InterfaceOnly SdfPropertySpec's CONNECTABILITY. """ import distutils.util import os # Early exit on invalid parameters if not schemaLayer: Tf.Warn("No Schema Layer provided") return if sdrNode is None: # This is a workaround to iterate through invalid sdrNodes (nodes not # having any input or output properties). Currently these nodes return # false when queried for IsValid(). # Refer: pxr/usd/ndr/node.h#140-149 Tf.Warn("No valid sdrNode provided") return sdrNodeMetadata = sdrNode.GetMetadata() if SchemaDefiningKeys.SCHEMA_NAME not in sdrNodeMetadata: Tf.Warn("Sdr Node (%s) does not define a schema name metadata." \ %(sdrNode.GetName())) return schemaName = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_NAME] if not Tf.IsValidIdentifier(schemaName): Tf.RaiseRuntimeError("schemaName (%s) is an invalid identifier; " "Provide a valid USD identifer for schemaName, example (%s) " %(schemaName, Tf.MakeValidIdentifier(schemaName))) tfTypeNameSuffix = None if SchemaDefiningKeys.TF_TYPENAME_SUFFIX in sdrNodeMetadata: tfTypeNameSuffix = sdrNodeMetadata[SchemaDefiningKeys.TF_TYPENAME_SUFFIX] if not Tf.IsValidIdentifier(tfTypeNameSuffix): Tf.RaiseRuntimeError("tfTypeNameSuffix (%s) is an invalid " \ "identifier" %(tfTypeNameSuffix)) if SchemaDefiningKeys.SCHEMA_KIND not in sdrNodeMetadata: schemaKind = SchemaDefiningMiscConstants.TYPED_SCHEMA else: schemaKind = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_KIND] # Note: We are not working on dynamic multiple apply schemas right now. isAPI = schemaKind == SchemaDefiningMiscConstants.SINGLE_APPLY_SCHEMA # Fix schemaName and warn if needed if isAPI and \ not schemaName.endswith(SchemaDefiningMiscConstants.API_STRING): Tf.Warn("node metadata implies the generated schema being created is " "an API schema, fixing schemaName to reflect that") schemaName = schemaName + SchemaDefiningMiscConstants.API_STRING if isAPI and tfTypeNameSuffix and \ not tfTypeNameSuffix.endswith(SchemaDefiningMiscConstants.API_STRING): Tf.Warn("node metadata implies the generated schema being created " "is an API schema, fixing tfTypeNameSuffix to reflect that") tfTypeNameSuffix = tfTypeNameSuffix + \ SchemaDefiningMiscConstants.API_STRING if SchemaDefiningKeys.SCHEMA_BASE not in sdrNodeMetadata: Tf.Warn("No schemaBase specified in node metadata, defaulting to " "APISchemaBase for API schemas else Typed") schemaBase = SchemaDefiningMiscConstants.API_SCHEMA_BASE if isAPI \ else SchemaDefiningMiscConstants.TYPED_SCHEMA else: schemaBase = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_BASE] apiSchemaAutoApplyTo = None if SchemaDefiningKeys.API_SCHEMA_AUTO_APPLY_TO in sdrNodeMetadata: apiSchemaAutoApplyTo = \ sdrNodeMetadata[SchemaDefiningKeys.API_SCHEMA_AUTO_APPLY_TO] \ .split('|') apiSchemaCanOnlyApplyTo = None if SchemaDefiningKeys.API_SCHEMA_CAN_ONLY_APPLY_TO in sdrNodeMetadata: apiSchemaCanOnlyApplyTo = \ sdrNodeMetadata[SchemaDefiningKeys.API_SCHEMA_CAN_ONLY_APPLY_TO] \ .split('|') providesUsdShadeConnectableAPIBehavior = False if SchemaDefiningKeys.PROVIDES_USD_SHADE_CONNECTABLE_API_BEHAVIOR in \ sdrNodeMetadata: providesUsdShadeConnectableAPIBehavior = \ distutils.util.strtobool(sdrNodeMetadata[SchemaDefiningKeys. \ PROVIDES_USD_SHADE_CONNECTABLE_API_BEHAVIOR]) apiSchemasForAttrPruning = None if SchemaDefiningKeys.API_SCHEMAS_FOR_ATTR_PRUNING in sdrNodeMetadata: apiSchemasForAttrPruning = \ sdrNodeMetadata[SchemaDefiningKeys.API_SCHEMAS_FOR_ATTR_PRUNING] \ .split('|') typedSchemaForAttrPruning = "" if isAPI and \ SchemaDefiningKeys.TYPED_SCHEMA_FOR_ATTR_PRUNING in sdrNodeMetadata: typedSchemaForAttrPruning = \ sdrNodeMetadata[SchemaDefiningKeys.TYPED_SCHEMA_FOR_ATTR_PRUNING] schemaPropertyNSPrefixOverride = None if SchemaDefiningKeys.SCHEMA_PROPERTY_NS_PREFIX_OVERRIDE in sdrNodeMetadata: schemaPropertyNSPrefixOverride = \ sdrNodeMetadata[ \ SchemaDefiningKeys.SCHEMA_PROPERTY_NS_PREFIX_OVERRIDE] usdSchemaReg = Usd.SchemaRegistry() # determine if the node being processed provides UsdShade-Connectability, # this helps in determining what namespace to use and also to report error # if a non-connectable node has outputs properties, which is malformed. # - Does the node derive from a schemaBase which provides connectable # behavior. Warn if schemaPropertyNSPrefixOverride is also specified, as # these metadata won't be used. # - If no schemaBase then we default to UsdShade connectable node's # inputs:/outputs: namespace prefix, unless schemaPropertyNSPrefixOverride # is provided. # - We also report an error if schemaPropertyNSPrefixOverride is provided # and an output property is found on the node being processed. schemaBaseProvidesConnectability = UsdShade.ConnectableAPI. \ HasConnectableAPI(usdSchemaReg.GetTypeFromName(schemaBase)) if (len(sdrNode.GetOutputNames()) > 0 and \ schemaPropertyNSPrefixOverride is not None and \ not _IsNSPrefixConnectableAPICompliant( \ schemaPropertyNSPrefixOverride)): Tf.RaiseRuntimeError("Presence of (%s) output parameters contradicts " \ "the presence of schemaPropertyNSPrefixOverride (\"%s\"), as it " \ "is illegal for non-connectable nodes to contain output " \ "parameters, or shader nodes' outputs to not have the \"outputs\"" \ "namespace prefix." %(len(sdrNode.GetOutputNames()), \ schemaPropertyNSPrefixOverride)) if (schemaBaseProvidesConnectability and \ schemaPropertyNSPrefixOverride is not None and \ not _IsNSPrefixConnectableAPICompliant( \ schemaPropertyNSPrefixOverride)): Tf.Warn("Node %s provides UsdShade-Connectability as it derives from " \ "%s, schemaPropertyNSPrefixOverride \"%s\" will not be used." \ %(schemaName, schemaBase, schemaPropertyNSPrefixOverride)) # set schemaPropertyNSPrefixOverride to "inputs", assuming default # UsdShade Connectability namespace prefix schemaPropertyNSPrefixOverride = "inputs" primSpec = schemaLayer.GetPrimAtPath(schemaName) if (primSpec): # if primSpec already exist, remove entirely and recreate using the # parsed sdr node if primSpec.nameParent: del primSpec.nameParent.nameChildren[primSpec.name] else: del primSpec.nameRoot.nameChildren[primSpec.name] primSpec = Sdf.PrimSpec(schemaLayer, schemaName, Sdf.SpecifierClass, "" if isAPI else schemaName) primSpec.inheritPathList.explicitItems = ["/" + schemaBase] primSpecCustomData = {} if isAPI: primSpecCustomData["apiSchemaType"] = schemaKind if tfTypeNameSuffix: # Defines this classname for TfType system # can help avoid duplicate prefix with domain and className # Tf type system will automatically pick schemaName as tfTypeName if # this is not set! primSpecCustomData["className"] = tfTypeNameSuffix if apiSchemaAutoApplyTo: primSpecCustomData['apiSchemaAutoApplyTo'] = \ Vt.TokenArray(apiSchemaAutoApplyTo) if apiSchemaCanOnlyApplyTo: primSpecCustomData['apiSchemaCanOnlyApplyTo'] = \ Vt.TokenArray(apiSchemaCanOnlyApplyTo) if providesUsdShadeConnectableAPIBehavior: extraPlugInfo = { SchemaDefiningKeys.PROVIDES_USD_SHADE_CONNECTABLE_API_BEHAVIOR \ : True } for propKey in [SchemaDefiningKeys.IS_USD_SHADE_CONTAINER, \ SchemaDefiningKeys.REQUIRES_USD_SHADE_ENCAPSULATION]: if propKey in sdrNodeMetadata: # Since we want to assign the types for these to bool and # because in python boolean type is a subset of int, we need to # do following instead of assign the propValue directly. propValue = distutils.util.strtobool(sdrNodeMetadata[propKey]) extraPlugInfo[propKey] = bool(propValue) primSpecCustomData['extraPlugInfo'] = extraPlugInfo primSpec.customData = primSpecCustomData doc = sdrNode.GetHelp() if doc != "": primSpec.documentation = doc # gather properties from a prim definition generated by composing apiSchemas # provided by apiSchemasForAttrPruning metadata. primDefForAttrPruning = None if apiSchemasForAttrPruning: primDefForAttrPruning = usdSchemaReg.BuildComposedPrimDefinition( typedSchemaForAttrPruning, apiSchemasForAttrPruning) # Create attrSpecs from input parameters for propName in sdrNode.GetInputNames(): _CreateAttrSpecFromNodeAttribute(primSpec, sdrNode.GetInput(propName), primDefForAttrPruning, schemaPropertyNSPrefixOverride) # Create attrSpecs from output parameters # Note that we always want outputs: namespace prefix for output attributes. for propName in sdrNode.GetOutputNames(): _CreateAttrSpecFromNodeAttribute(primSpec, sdrNode.GetOutput(propName), primDefForAttrPruning, UsdShade.Tokens.outputs[:-1], False) # Create token shaderId attrSpec -- only for shader nodes if (schemaBaseProvidesConnectability or \ schemaPropertyNSPrefixOverride is None or \ _IsNSPrefixConnectableAPICompliant(schemaPropertyNSPrefixOverride)): shaderIdAttrName = Sdf.Path.JoinIdentifier( \ [renderContext, sdrNode.GetContext(), PropertyDefiningKeys.SHADER_ID]) shaderIdAttrSpec = Sdf.AttributeSpec(primSpec, shaderIdAttrName, Sdf.ValueTypeNames.Token, Sdf.VariabilityUniform) # Since users shouldn't need to be aware of shaderId attribute, we put # this in "Internal" displayGroup. shaderIdAttrSpec.displayGroup = \ PropertyDefiningKeys.INTERNAL_DISPLAY_GROUP # Use the identifier if explicitly provided, (it could be a shader node # queried using an explicit path), else use sdrNode's registered # identifier. nodeIdentifier = overrideIdentifier if overrideIdentifier else \ sdrNode.GetIdentifier() shaderIdAttrSpec.default = nodeIdentifier # Extra attrSpec schemaBasePrimDefinition = \ Usd.SchemaRegistry().FindConcretePrimDefinition(schemaBase) if schemaBasePrimDefinition and \ SchemaDefiningMiscConstants.NodeDefAPI in \ schemaBasePrimDefinition.GetAppliedAPISchemas(): infoIdAttrSpec = Sdf.AttributeSpec(primSpec, \ UsdShade.Tokens.infoId, Sdf.ValueTypeNames.Token, \ Sdf.VariabilityUniform) infoIdAttrSpec.default = nodeIdentifier schemaLayer.Save()
def CreateModelStage(assetName, assetIdentifier=None, kind=Kind.Tokens.component, filesToReference=None, variantSetName=None, defaultVariantSelection=None): # Preconditions.... if not Tf.IsValidIdentifier(assetName): print "assetName '%s' must be a valid identifier. Aborting." % \ assetName return None if variantSetName and not Tf.IsValidIdentifier(variantSetName): print "variantSetName '%s' must be a valid identifier. Aborting." % \ variantSetName return None if filesToReference and len(filesToReference) > 1 and not variantSetName: # For now, we only allow multiple files to reference if we're switching # them with a variantSet. We can relax this restriction when we can # make internal payload arcs (bug #119960) print "Cannot create multiple-file-reference without a variantSet. Aborting" return None if not Kind.Registry.IsA(kind, Kind.Tokens.model): print "kind '%s' is not a valid model kind, which must be one of:" % \ kind print Kind.Registry.GetAllKinds() return None # Create the root file for the stage, and make it ASCII text. # We need some nicer sugar for this. fileName = assetName + ".usd" rootLayer = Sdf.Layer.CreateNew(fileName, args={'format': 'usda'}) stage = Usd.Stage.Open(rootLayer) # Name the root prim after the asset. Don't give it a type, since we # want that to come from referenced files. Make it be the "default prim" # so that we can reference the resulting file without specifiying a # prim path rootPath = Sdf.Path.absoluteRootPath modelRootPrim = stage.DefinePrim(rootPath.AppendChild(assetName)) stage.SetDefaultPrim(modelRootPrim) modelAPI = Usd.ModelAPI(modelRootPrim) modelAPI.SetKind(kind) # See http://openusd.org/docs/api/class_usd_model_a_p_i.html#details # for more on assetInfo modelAPI.SetAssetName(assetName) modelAPI.SetAssetIdentifier(assetIdentifier or fileName) # Add a class named after the asset, and make the asset inherit from it. # This is not necessary for a valid asset, and the class-naming is a Pixar # convention. But always having a class associated with each asset is # extremely useful for non-destructively editing many referenced or # instanced assets of the same type. classPrim = stage.CreateClassPrim( rootPath.AppendChild("_class_" + assetName)) modelRootPrim.GetInherits().AddInherit(classPrim.GetPath()) if not filesToReference: # weird edge case... we're done return stage elif len(filesToReference) == 1 and not variantSetName: # The other, more plausible edge case: we're just wrapping # some other file (e.g. alembic) in order to give it a payload # and other proper USD trappings - no variants modelRootPrim.GetPayloads().AddPayload(Sdf.Payload( filesToReference[0])) return stage # OK, we're making a variantSet, and we are going to vary the payload # in each variant varSet = modelRootPrim.GetVariantSet(variantSetName) for variantFile in filesToReference: import os variantName = os.path.splitext(os.path.basename(variantFile))[0] # If we didn't specify a default selection, choose the first one if not defaultVariantSelection: defaultVariantSelection = variantName varSet.AddVariant(variantName) varSet.SetVariantSelection(variantName) # The context object makes all edits "go inside" the variant we # just created. with varSet.GetVariantEditContext(): modelRootPrim.GetPayloads().AddPayload(Sdf.Payload(variantFile)) # Now put the variantSet into the state we want it to be in by default varSet.SetVariantSelection(defaultVariantSelection) return stage
def UpdateSchemaWithSdrNode(schemaLayer, sdrNode): """ Updates the given schemaLayer with primSpec and propertySpecs from sdrNode metadata. It consume the following attributes (that manifest as Sdr metadata) in addition to many of the standard Sdr metadata specified and parsed (via its parser plugin). Node Level Metadata: - "schemaName": Name of the new schema populated from the given sdrNode (Required) - "schemaKind": Specifies the UsdSchemaKind for the schema being populated from the sdrNode. (note that this does not support multi-applied schema kinds). - "schemaBase": Base schema from which the new schema should inherit from. Note this defaults to "APISchemaBase" for an api schema or "Typed" for a concrete scheme. - "usdSchemaClass": Specified the equivalent schema directly generated by USD (sourceType: USD). This is used to make sure duplicate properties already specified in the USD schema are not populated in the new API schema. Note this is only used when we are dealing with an API schema. - "apiSchemaAutoApplyTo": The Schemas to which the sdrNode populated (API) schema will autoApply to. - "tfTypeNameSuffix": Class name which will get registered with TfType system. This gets appended to the domain name to register with TfType. Property Level Metadata: USD_VARIABILITY = A property level metadata, which specified a specific sdrNodeProperty should its usd variability set to Uniform or Varying. """ # Early exit on invalid parameters if not schemaLayer: Tf.Warn("No Schema Layer provided") return if not sdrNode: Tf.Warn("No valid sdrNode provided") return sdrNodeMetadata = sdrNode.GetMetadata() if not sdrNodeMetadata.has_key(SchemaDefiningKeys.SCHEMA_NAME): Tf.Warn("Sdr Node does not define a schema name metadata.") return schemaName = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_NAME] if not Tf.IsValidIdentifier(schemaName): Tf.RaiseRuntimeError( "schemaName (%s) is an invalid identifier; " "Provide a valid USD identifer for schemaName, example (%s) " % (schemaName, Tf.MakeValidIdentifier(schemaName))) tfTypeNameSuffix = None if sdrNodeMetadata.has_key(SchemaDefiningKeys.TF_TYPENAME_SUFFIX): tfTypeNameSuffix = sdrNodeMetadata[ SchemaDefiningKeys.TF_TYPENAME_SUFFIX] if not Tf.IsValidIdentifier(tfTypeNameSuffix): Tf.RaiseRuntimeError("tfTypeNameSuffix (%s) is an invalid " \ "identifier" %(tfTypeNameSuffix)) if not sdrNodeMetadata.has_key(SchemaDefiningKeys.SCHEMA_KIND): schemaKind = SchemaDefiningMiscConstants.TYPED_SCHEMA else: schemaKind = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_KIND] # Note: We are not working on dynamic multiapply schemas right now. isAPI = schemaKind == SchemaDefiningMiscConstants.SINGLE_APPLY_SCHEMA # Fix schemaName and warn if needed if isAPI and \ not schemaName.endswith(SchemaDefiningMiscConstants.API_STRING): Tf.Warn("node metadata implies the generated schema being created is " "an API schema, fixing schemaName to reflect that") schemaName = schemaName + SchemaDefiningMiscConstants.API_STRING if isAPI and tfTypeNameSuffix and \ not tfTypeNameSuffix.endswith(SchemaDefiningMiscConstants.API_STRING): Tf.Warn("node metadata implies the generated schema being created " "is an API schema, fixing tfTypeNameSuffix to reflect that") tfTypeNameSuffix = tfTypeNameSuffix + \ SchemaDefiningMiscConstants.API_STRING if not sdrNodeMetadata.has_key(SchemaDefiningKeys.SCHEMA_BASE): Tf.Warn("No schemaBase specified in node metadata, defaulting to " "APISchemaBase for API schemas else Typed") schemaBase = SchemaDefiningMiscConstants.API_SCHEMA_BASE if isAPI \ else SchemaDefiningMiscConstants.TYPED_SCHEMA else: schemaBase = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_BASE] apiSchemaAutoApplyTo = None if sdrNodeMetadata.has_key(SchemaDefiningKeys.API_SCHEMA_AUTO_APPLY_TO): apiSchemaAutoApplyTo = \ sdrNodeMetadata[SchemaDefiningKeys.API_SCHEMA_AUTO_APPLY_TO] \ .split('|') usdSchemaClass = None if isAPI and sdrNodeMetadata.has_key(SchemaDefiningKeys.USD_SCHEMA_CLASS): usdSchemaClass = \ sdrNodeMetadata[SchemaDefiningKeys.USD_SCHEMA_CLASS] primSpec = schemaLayer.GetPrimAtPath(schemaName) if (primSpec): # if primSpec already exist, remove entirely and recreate using the # parsed sdr node if primSpec.nameParent: del primSpec.nameParent.nameChildren[primSpec.name] else: del primSpec.nameRoot.nameChildren[primSpec.name] primSpec = Sdf.PrimSpec(schemaLayer, schemaName, Sdf.SpecifierClass, "" if isAPI else schemaName) primSpec.inheritPathList.explicitItems = ["/" + schemaBase] primSpecCustomData = {} if isAPI: primSpecCustomData["apiSchemaType"] = schemaKind if tfTypeNameSuffix: # Defines this classname for TfType system # can help avoid duplicate prefix with domain and className # Tf type system will automatically pick schemaName as tfTypeName if # this is not set! primSpecCustomData["className"] = tfTypeNameSuffix if apiSchemaAutoApplyTo: primSpecCustomData['apiSchemaAutoApplyTo'] = \ Vt.TokenArray(apiSchemaAutoApplyTo) primSpec.customData = primSpecCustomData doc = sdrNode.GetHelp() if doc != "": primSpec.documentation = doc # gather properties from node directly generated from USD (sourceType: USD) # Use the usdSchemaClass tag when the generated schema being defined is an # API schema usdSchemaNode = None if usdSchemaClass: reg = Sdr.Registry() usdSchemaNode = reg.GetNodeByIdentifierAndType( usdSchemaClass, SchemaDefiningMiscConstants.USD_SOURCE_TYPE) # Create attrSpecs from input parameters for propName in sdrNode.GetInputNames(): _CreateAttrSpecFromNodeAttribute(primSpec, sdrNode.GetInput(propName), usdSchemaNode) # Create attrSpecs from output parameters for propName in sdrNode.GetOutputNames(): _CreateAttrSpecFromNodeAttribute(primSpec, sdrNode.GetOutput(propName), usdSchemaNode, False) schemaLayer.Save()
def UpdateSchemaWithSdrNode(schemaLayer, sdrNode, renderContext="", overrideIdentifier=""): """ Updates the given schemaLayer with primSpec and propertySpecs from sdrNode metadata. A renderContext can be provided which is used in determining the shaderId namespace, which follows the pattern: "<renderContext>:<SdrShaderNodeContext>:shaderId". Note that we are using a node's context (SDR_NODE_CONTEXT_TOKENS) here to construct the shaderId namespace, so shader parsers should make sure to use appropriate SDR_NODE_CONTEXT_TOKENS in the node definitions. overrideIdentifier parameter is the identifier which should be used when the identifier of the node being processed differs from the one Sdr will discover at runtime, such as when this function is def a node constructed from an explicit asset path. This should only be used when clients know the identifier being passed is the true identifier which sdr Runtime will provide when querying using GetShaderNodeByNameAndType, etc. It consumes the following attributes (that manifest as Sdr metadata) in addition to many of the standard Sdr metadata specified and parsed (via its parser plugin). Node Level Metadata: - "schemaName": Name of the new schema populated from the given sdrNode (Required) - "schemaKind": Specifies the UsdSchemaKind for the schema being populated from the sdrNode. (Note that this does not support multiple apply schema kinds). - "schemaBase": Base schema from which the new schema should inherit from. Note this defaults to "APISchemaBase" for an API schema or "Typed" for a concrete scheme. - "usdSchemaClass": Specifies the equivalent schema directly generated by USD (sourceType: USD). This is used to make sure duplicate properties already specified in the USD schema are not populated in the new API schema. Note this is only used when we are dealing with an API schema. - "apiSchemaAutoApplyTo": The schemas to which the sdrNode populated API schema will autoApply to. - "apiSchemaCanOnlyApplyTo": If specified, the API schema generated from the sdrNode can only be validly applied to this set of schemas. - "providesUsdShadeConnectableAPIBehavior": Used to enable a connectability behavior for an API schema. - "isUsdShadeContainer": Only used when providesUsdShadeConnectableAPIBehavior is set to true. Marks the connectable prim as a UsdShade container type. - "requiresUsdShadeEncapsulation": Only used when providesUsdShadeConnectableAPIBehavior is set to true. Configures the UsdShade encapsulation rules governing its connectableBehavior. - "tfTypeNameSuffix": Class name which will get registered with TfType system. This gets appended to the domain name to register with TfType. Property Level Metadata: - USD_VARIABILITY: Property level metadata which specifies a specific sdrNodeProperty should have its USD variability set to Uniform or Varying - USD_SUPPRESS_PROPERTY: A property level metadata which determines if the property should be suppressed from translation from args to property spec. Sdr Property Metadata to SdfPropertySpec Translations - A "null" value for Widget sdrProperty metadata translates to SdfPropertySpec Hidden metadata. - SdrProperty's Help metadata (Label metadata if Help metadata not provided) translates to SdfPropertySpec's Documentation string metadata. - SdrProperty's Page metadata translates to SdfPropertySpec's DisplayGroup metadata. - SdrProperty's Label metadata translates to SdfPropertySpec's DisplayName metadata. - SdrProperty's Options translates to SdfPropertySpec's AllowedTokens. - SdrProperty's Default value translates to SdfPropertySpec's Default value. - Connectable input properties translates to InterfaceOnly SdfPropertySpec's CONNECTABILITY. """ import distutils.util import os # Early exit on invalid parameters if not schemaLayer: Tf.Warn("No Schema Layer provided") return if not sdrNode: Tf.Warn("No valid sdrNode provided") return sdrNodeMetadata = sdrNode.GetMetadata() if SchemaDefiningKeys.SCHEMA_NAME not in sdrNodeMetadata: Tf.Warn("Sdr Node (%s) does not define a schema name metadata." \ %(sdrNode.GetName())) return schemaName = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_NAME] if not Tf.IsValidIdentifier(schemaName): Tf.RaiseRuntimeError( "schemaName (%s) is an invalid identifier; " "Provide a valid USD identifer for schemaName, example (%s) " % (schemaName, Tf.MakeValidIdentifier(schemaName))) tfTypeNameSuffix = None if SchemaDefiningKeys.TF_TYPENAME_SUFFIX in sdrNodeMetadata: tfTypeNameSuffix = sdrNodeMetadata[ SchemaDefiningKeys.TF_TYPENAME_SUFFIX] if not Tf.IsValidIdentifier(tfTypeNameSuffix): Tf.RaiseRuntimeError("tfTypeNameSuffix (%s) is an invalid " \ "identifier" %(tfTypeNameSuffix)) if SchemaDefiningKeys.SCHEMA_KIND not in sdrNodeMetadata: schemaKind = SchemaDefiningMiscConstants.TYPED_SCHEMA else: schemaKind = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_KIND] # Note: We are not working on dynamic multiple apply schemas right now. isAPI = schemaKind == SchemaDefiningMiscConstants.SINGLE_APPLY_SCHEMA # Fix schemaName and warn if needed if isAPI and \ not schemaName.endswith(SchemaDefiningMiscConstants.API_STRING): Tf.Warn("node metadata implies the generated schema being created is " "an API schema, fixing schemaName to reflect that") schemaName = schemaName + SchemaDefiningMiscConstants.API_STRING if isAPI and tfTypeNameSuffix and \ not tfTypeNameSuffix.endswith(SchemaDefiningMiscConstants.API_STRING): Tf.Warn("node metadata implies the generated schema being created " "is an API schema, fixing tfTypeNameSuffix to reflect that") tfTypeNameSuffix = tfTypeNameSuffix + \ SchemaDefiningMiscConstants.API_STRING if SchemaDefiningKeys.SCHEMA_BASE not in sdrNodeMetadata: Tf.Warn("No schemaBase specified in node metadata, defaulting to " "APISchemaBase for API schemas else Typed") schemaBase = SchemaDefiningMiscConstants.API_SCHEMA_BASE if isAPI \ else SchemaDefiningMiscConstants.TYPED_SCHEMA else: schemaBase = sdrNodeMetadata[SchemaDefiningKeys.SCHEMA_BASE] apiSchemaAutoApplyTo = None if SchemaDefiningKeys.API_SCHEMA_AUTO_APPLY_TO in sdrNodeMetadata: apiSchemaAutoApplyTo = \ sdrNodeMetadata[SchemaDefiningKeys.API_SCHEMA_AUTO_APPLY_TO] \ .split('|') apiSchemaCanOnlyApplyTo = None if SchemaDefiningKeys.API_SCHEMA_CAN_ONLY_APPLY_TO in sdrNodeMetadata: apiSchemaCanOnlyApplyTo = \ sdrNodeMetadata[SchemaDefiningKeys.API_SCHEMA_CAN_ONLY_APPLY_TO] \ .split('|') providesUsdShadeConnectableAPIBehavior = False if SchemaDefiningKeys.PROVIDES_USD_SHADE_CONNECTABLE_API_BEHAVIOR in \ sdrNodeMetadata: providesUsdShadeConnectableAPIBehavior = \ distutils.util.strtobool(sdrNodeMetadata[SchemaDefiningKeys. \ PROVIDES_USD_SHADE_CONNECTABLE_API_BEHAVIOR]) usdSchemaClass = None if isAPI and SchemaDefiningKeys.USD_SCHEMA_CLASS in sdrNodeMetadata: usdSchemaClass = \ sdrNodeMetadata[SchemaDefiningKeys.USD_SCHEMA_CLASS] primSpec = schemaLayer.GetPrimAtPath(schemaName) if (primSpec): # if primSpec already exist, remove entirely and recreate using the # parsed sdr node if primSpec.nameParent: del primSpec.nameParent.nameChildren[primSpec.name] else: del primSpec.nameRoot.nameChildren[primSpec.name] primSpec = Sdf.PrimSpec(schemaLayer, schemaName, Sdf.SpecifierClass, "" if isAPI else schemaName) primSpec.inheritPathList.explicitItems = ["/" + schemaBase] primSpecCustomData = {} if isAPI: primSpecCustomData["apiSchemaType"] = schemaKind if tfTypeNameSuffix: # Defines this classname for TfType system # can help avoid duplicate prefix with domain and className # Tf type system will automatically pick schemaName as tfTypeName if # this is not set! primSpecCustomData["className"] = tfTypeNameSuffix if apiSchemaAutoApplyTo: primSpecCustomData['apiSchemaAutoApplyTo'] = \ Vt.TokenArray(apiSchemaAutoApplyTo) if apiSchemaCanOnlyApplyTo: primSpecCustomData['apiSchemaCanOnlyApplyTo'] = \ Vt.TokenArray(apiSchemaCanOnlyApplyTo) if providesUsdShadeConnectableAPIBehavior: extraPlugInfo = { SchemaDefiningKeys.PROVIDES_USD_SHADE_CONNECTABLE_API_BEHAVIOR \ : True } for propKey in [SchemaDefiningKeys.IS_USD_SHADE_CONTAINER, \ SchemaDefiningKeys.REQUIRES_USD_SHADE_ENCAPSULATION]: if propKey in sdrNodeMetadata: # Since we want to assign the types for these to bool and # because in python boolean type is a subset of int, we need to # do following instead of assign the propValue directly. propValue = distutils.util.strtobool(sdrNodeMetadata[propKey]) extraPlugInfo[propKey] = bool(propValue) primSpecCustomData['extraPlugInfo'] = extraPlugInfo primSpec.customData = primSpecCustomData doc = sdrNode.GetHelp() if doc != "": primSpec.documentation = doc # gather properties from node directly generated from USD (sourceType: USD) # Use the usdSchemaClass tag when the generated schema being defined is an # API schema usdSchemaNode = None if usdSchemaClass: reg = Sdr.Registry() if usdSchemaClass.endswith(SchemaDefiningMiscConstants.API_STRING): # This usd schema is an API schema, we need to extract the shader # identifier from its primDef's shaderId field. primDef = Usd.SchemaRegistry().FindAppliedAPIPrimDefinition( usdSchemaClass) if primDef: # We are dealing with USD source type here, hence no render # context is required but we can still borrow node context # information from the sdrNode in question, since the usd source # type node should also belong to the same context. shaderIdAttrName = Sdf.Path.JoinIdentifier( \ sdrNode.GetContext(), PropertyDefiningKeys.SHADER_ID) sdrIdentifier = primDef.GetAttributeFallbackValue( shaderIdAttrName) if sdrIdentifier != "": usdSchemaNode = reg.GetNodeByIdentifierAndType( sdrIdentifier, SchemaDefiningMiscConstants.USD_SOURCE_TYPE) else: Tf.Warn("No sourceId authored for '%s'." % (usdSchemaClass)) else: Tf.Warn("Illegal API schema provided for the usdSchemaClass " "metadata. No prim definition registered for '%s'" % (usdSchemaClass)) else: usdSchemaNode = reg.GetNodeByIdentifierAndType( usdSchemaClass, SchemaDefiningMiscConstants.USD_SOURCE_TYPE) # Create attrSpecs from input parameters for propName in sdrNode.GetInputNames(): _CreateAttrSpecFromNodeAttribute(primSpec, sdrNode.GetInput(propName), usdSchemaNode) # Create attrSpecs from output parameters for propName in sdrNode.GetOutputNames(): _CreateAttrSpecFromNodeAttribute(primSpec, sdrNode.GetOutput(propName), usdSchemaNode, False) # Create token shaderId attrSpec shaderIdAttrName = Sdf.Path.JoinIdentifier( \ [renderContext, sdrNode.GetContext(), PropertyDefiningKeys.SHADER_ID]) shaderIdAttrSpec = Sdf.AttributeSpec(primSpec, shaderIdAttrName, Sdf.ValueTypeNames.Token, Sdf.VariabilityUniform) # Since users shouldn't need to be aware of shaderId attribute, we put this # in "Internal" displayGroup. shaderIdAttrSpec.displayGroup = PropertyDefiningKeys.INTERNAL_DISPLAY_GROUP # Use the identifier if explicitly provided, (it could be a shader node # queried using an explicit path), else use sdrNode's registered identifier. nodeIdentifier = overrideIdentifier if overrideIdentifier else \ sdrNode.GetIdentifier() shaderIdAttrSpec.default = nodeIdentifier # Extra attrSpec schemaBasePrimDefinition = \ Usd.SchemaRegistry().FindConcretePrimDefinition(schemaBase) if schemaBasePrimDefinition and \ SchemaDefiningMiscConstants.NodeDefAPI in \ schemaBasePrimDefinition.GetAppliedAPISchemas(): infoIdAttrSpec = Sdf.AttributeSpec(primSpec, \ UsdShade.Tokens.infoId, Sdf.ValueTypeNames.Token, \ Sdf.VariabilityUniform) infoIdAttrSpec.default = nodeIdentifier schemaLayer.Save()