def registerSubdividedSpaceOp(): """ Registers the SubdividedSpace Op using the NodeTypeBuilder. This is a helper class that takes care of registering a suitable node graph node type, and configuring it to call the supplied method in order to build the Op chain that is used to represent the node in the Op Graph. """ from Katana import Nodes3DAPI from Katana import FnAttribute, FnGeolibServices def buildOpChain(node, interface): """ Configures the Ops to represent the current state of the node. The calling mechanism in Node3D takes are of ensuring that the underlying Op network is only updated if Op Args and types have changed. @type node: C{Nodes3DAPI.NodeTypeBuilder.SubdividedSpace} @type interface: C[Nodes3DAPI.NodeTypeBuilder.BuildChainInterface} @param node: The node to build the Op and OpArgs from (ie: an instance of our node). @param interface: The interface that will configure the Ops in the underlying Op network. """ # Ensure the runtime doesn't error for us if there are no inputs on the # Node - as we want to allow it to exist in isolation. The default is 1 interface.setMinRequiredInputs(0) # We first need the current time for parameter value queries frameTime = interface.getGraphState().getTime() # Get the parameters from our node maxDepthParam = node.getParameter("maxDepth") subdivParam = node.getParameter("subdivisions") locationParam = node.getParameter("location") if not locationParam or not maxDepthParam or not subdivParam: raise RuntimeError("Missing node parameters, require " + "'location', 'maxdepth' and 'subdivisions'") # Build the Op args from our node argsGb = FnAttribute.GroupBuilder() argsGb.set("maxDepth", FnAttribute.IntAttribute(maxDepthParam.getValue(frameTime))) argsGb.set("subdivisions", FnAttribute.IntAttribute(subdivParam.getValue(frameTime))) # We want to use the StaticSceneCreate Op to build the parent # hierarchy, so that our op only has to worry about generating its # children. Its args are somewhat complex, but fortunately, there # is a helper class that makes it all much easier. rootLocation = locationParam.getValue(frameTime) sscb = FnGeolibServices.OpArgsBuilders.StaticSceneCreate() sscb.addSubOpAtLocation(rootLocation, "SubdividedSpace", argsGb.build()) interface.appendOp("StaticSceneCreate", sscb.build()) # Here we need to define the parameters for the node, and register the op # chain creation callback function nodeBuilder = Nodes3DAPI.NodeTypeBuilder("SubdividedSpace") # If we wanted to merge with incoming scene, we could simply allow the # node to have an input. Unless you delete locations in your Op, any # existing locations will pass-through. It is encouraged though to avoid # long chains of Ops, as it makes multi-threading and caching less # efficient, so for 'Generator' Ops, no input is preferable. # nodeBuilder.setInputPortNames( ("in",) ) # Parameters can be described by a group attribute paramGb = FnAttribute.GroupBuilder() paramGb.set("location", FnAttribute.StringAttribute("/root/world/geo/dividedSpace")) paramGb.set("maxDepth", FnAttribute.IntAttribute(4)) paramGb.set("subdivisions", FnAttribute.IntAttribute(1)) nodeBuilder.setParametersTemplateAttr(paramGb.build()) nodeBuilder.setHintsForParameter("location", {'widget': 'newScenegraphLocation'}) # Register our Op build function nodeBuilder.setBuildOpChainFnc(buildOpChain) # Create the new Node3D type nodeBuilder.build()
'instanceMode', { 'widget': 'popup', 'options': ['expanded', 'as sources and instances'], 'help': """ When using <i>expanded</i> USD instances will be unrolled as though the children of their masters were naturally present. </p> When using <i>as sources and instances</i>, masters will be created under a sibling of World named /Masters. Instanced USD prims will be of type "instance" and have no children. """, }) gb.set('prePopulate', FnAttribute.IntAttribute(1)) nb.setHintsForParameter( 'prePopulate', { 'widget': 'boolean', 'help': """ Controls USD pre-population. Pre-population loads all payloads and pre-populates the stage. Assuming the entire stage will be needed, this is more efficient since USD can use its internal multithreading. """, }) gb.set('verbose', 0) nb.setHintsForParameter(
def buildOpChain(self, interface): interface.setMinRequiredInputs(0) gb = FnAttribute.GroupBuilder() gb.set('fileName', interface.buildAttrFromParam(self.getParameter('fileName'))) gb.set('location', interface.buildAttrFromParam(self.getParameter('location'))) gb.set('isolatePath', interface.buildAttrFromParam(self.getParameter('isolatePath'))) gb.set('variants', interface.buildAttrFromParam(self.getParameter('variants'))) gb.set('ignoreLayerRegex', interface.buildAttrFromParam(self.getParameter('ignoreLayerRegex'))) gb.set( 'motionSampleTimes', interface.buildAttrFromParam(self.getParameter('motionSampleTimes'))) gb.set( 'verbose', interface.buildAttrFromParam(self.getParameter('verbose'), numberType=FnAttribute.IntAttribute)) gb.set('instanceMode', interface.buildAttrFromParam(self.getParameter('instanceMode'))) gb.set( 'prePopulate', interface.buildAttrFromParam(self.getParameter('prePopulate'), numberType=FnAttribute.IntAttribute)) sessionValues = ( interface.getGraphState().getDynamicEntry("var:pxrUsdInSession")) if isinstance(sessionValues, FnAttribute.GroupAttribute): gb.set('session', sessionValues) gb.set('system', interface.getGraphState().getOpSystemArgs()) gb.set('processStageWideQueries', FnAttribute.IntAttribute(1)) # our primary op in the chain that will create the root location sscb = FnGeolibServices.OpArgsBuilders.StaticSceneCreate(True) # check for any extra attributes or namespaces set downstream # via graph state extra = interface.getGraphState().getDynamicEntry( 'var:usdExtraAttributesOrNamespaces') if extra: gb.set('extraAttributesOrNamespaces', extra) argsOverride = interface.getGraphState().getDynamicEntry( 'var:pxrUsdInArgs') if isinstance(argsOverride, FnAttribute.GroupAttribute): gb.update(argsOverride) # add the PxrUsdIn op as a sub op pxrUsdInArgs = gb.build() sscb.addSubOpAtLocation( self.getScenegraphLocation(interface.getFrameTime()), 'PxrUsdIn', pxrUsdInArgs) sscb.setAttrAtLocation('/root', 'info.usdLoader', FnAttribute.StringAttribute('PxrUsdIn')) interface.appendOp('StaticSceneCreate', sscb.build())
def buildPxrUsdInOpArgsAtGraphState(self, graphState): gb = FnAttribute.GroupBuilder() frameTime = graphState.getTime() gb.set('fileName', self.getParameter('fileName').getValue(frameTime)) gb.set('location', self.getParameter('location').getValue(frameTime)) gb.set('isolatePath', self.getParameter('isolatePath').getValue(frameTime)) gb.set('variants', self.getParameter('variants').getValue(frameTime)) gb.set('ignoreLayerRegex', self.getParameter('ignoreLayerRegex').getValue(frameTime)) gb.set('motionSampleTimes', self.getParameter('motionSampleTimes').getValue(frameTime)) gb.set('verbose', int(self.getParameter('verbose').getValue(frameTime))) gb.set('instanceMode', self.getParameter('instanceMode').getValue(frameTime)) gb.set('prePopulate', int(self.getParameter('prePopulate').getValue(frameTime))) if self.getParameter('usePurposeBasedMaterialBinding').getValue(frameTime): purposes = [ "", ] for p in self.getParameter( 'additionalBindingPurposeNames').getChildren(): v = p.getValue(frameTime).strip() if v: purposes.append(v) gb.set('materialBindingPurposes', FnAttribute.StringAttribute(purposes, 1)) sessionValues = (graphState.getDynamicEntry("var:pxrUsdInSession")) if isinstance(sessionValues, FnAttribute.GroupAttribute): gb.set('session', sessionValues) gb.set('system', graphState.getOpSystemArgs()) gb.set('processStageWideQueries', FnAttribute.IntAttribute(1)) gb.set('setOpArgsToInfo', FnAttribute.IntAttribute(1)) # check for any extra attributes or namespaces set downstream # via graph state extra = graphState.getDynamicEntry('var:usdExtraAttributesOrNamespaces') if extra: gb.set('extraAttributesOrNamespaces', extra) argsOverride = graphState.getDynamicEntry('var:pxrUsdInArgs') if isinstance(argsOverride, FnAttribute.GroupAttribute): gb.update(argsOverride) pxrUsdInArgs = gb.build() return pxrUsdInArgs
def registerCubeMaker(): """ Registers a new CubMaker node type using the NodeTypeBuilder utility class. """ from Katana import Nodes3DAPI from Katana import FnAttribute def buildCubeMakerOpChain(node, interface): """ Defines the callback function used to create the Ops chain for the node type being registered. @type node: C{Nodes3DAPI.NodeTypeBuilder.CubeMaker} @type interface: C{Nodes3DAPI.NodeTypeBuilder.BuildChainInterface} @param node: The node for which to define the Ops chain @param interface: The interface providing the functions needed to set up the Ops chain for the given node. """ # Get the current frame time frameTime = interface.getGraphState().getTime() # Set the minimum number of input ports interface.setMinRequiredInputs(0) argsGb = FnAttribute.GroupBuilder() # Parse node parameters locationParam = node.getParameter('location') numberOfCubesParam = node.getParameter('numberOfCubes') rotateCubesParam = node.getParameter('rotateCubes') maxRotationParam = node.getParameter('maxRotation') if locationParam: location = locationParam.getValue(frameTime) # The base location is encoded using nested group attributes # defining a hierarchy where the elements in the location paths # are interleaved with group attributes named 'c' (for child). # The last element will contain a group attribute, named 'a', # which in turn will hold an attribute defining the number of # cubes to be generated. # See the Ops source code for more details locationPaths = location[1:].split('/')[1:] attrsHierarchy = \ '.'.join([x for t in zip(['c',] * len(locationPaths), locationPaths) for x in t]) argsGb.set(attrsHierarchy + '.a.numberOfCubes', FnAttribute.IntAttribute( numberOfCubesParam.getValue(frameTime))) if rotateCubesParam.getValue(frameTime) == 1: argsGb.set(attrsHierarchy + '.a.maxRotation', FnAttribute.DoubleAttribute( maxRotationParam.getValue(frameTime))) # Add the CubeMaker Op to the Ops chain interface.appendOp('CubeMaker', argsGb.build()) # Create a NodeTypeBuilder to register the new type nodeTypeBuilder = Nodes3DAPI.NodeTypeBuilder('CubeMaker') # Build the node's parameters gb = FnAttribute.GroupBuilder() gb.set('location', FnAttribute.StringAttribute('/root/world/geo/cubeMaker')) gb.set('numberOfCubes', FnAttribute.IntAttribute(20)) gb.set('rotateCubes', FnAttribute.IntAttribute(0)) gb.set('maxRotation', FnAttribute.DoubleAttribute(0)) # Set the parameters template nodeTypeBuilder.setParametersTemplateAttr(gb.build()) # Set parameter hints nodeTypeBuilder.setHintsForParameter('location', {'widget':'scenegraphLocation'}) nodeTypeBuilder.setHintsForParameter('numberOfCubes', {'int':True}) nodeTypeBuilder.setHintsForParameter('rotateCubes', {'widget':'boolean'}) nodeTypeBuilder.setHintsForParameter('maxRotation', {'conditionalVisOp':'equalTo', 'conditionalVisPath':'../rotateCubes', 'conditionalVisValue':1},) # Set the callback responsible to build the Ops chain nodeTypeBuilder.setBuildOpChainFnc(buildCubeMakerOpChain) # Build the new node type nodeTypeBuilder.build()
def registerArnoldYetiInOp(): """ Registers the ArnoldYeti Op using the NodeTypeBuilder. This is a helper class that takes care of registering a suitable node graph node type, and configuring it to call the supplied method in order to build the Op chain that is used to represent the node in the Op Graph. """ from Katana import Nodes3DAPI from Katana import FnAttribute, FnGeolibServices MODE_RIBBON = 0 MODE_THICK = 1 MODE_ORIENTED = 2 modeDict = { 'ribbon': MODE_RIBBON, 'thick': MODE_THICK, 'oriented': MODE_ORIENTED } def buildOpChain(node, interface): """ Configures the Ops to represent the current state of the node. The calling mechanism in Node3D takes are of ensuring that the underlying Op network is only updated if Op Args and types have changed. @type node: C{Nodes3DAPI.NodeTypeBuilder.SubdividedSpace} @type interface: C{Nodes3DAPI.NodeTypeBuilder.BuildChainInterface} @param node: The node to build the Op and OpArgs from (ie: an instance of our node). @param interface: The interface that will configure the Ops in the underlying Op network. """ # Ensure the runtime doesn't error for us if there are no inputs on the # Node - as we want to allow it to exist in isolation. The default is 1 interface.setMinRequiredInputs(0) # We first need the current time for parameter value queries frameTime = interface.getGraphState().getTime() # Pass these along through the ops so that the have shutter open/close and number of samples systemArgs = interface.getGraphState().getOpSystemArgs() # Get the parameters from our node locationParam = node.getParameter("location") filenameParam = node.getParameter("filename") proxyParam = node.getParameter("proxy") samplesParam = node.getParameter("samples") imageSearchPathParam = node.getParameter("imageSearchPath") disableBoundingboxParam = node.getParameter("disableBoundingbox") lengthParam = node.getParameter("length") densityParam = node.getParameter("density") minPixelWidthParam = node.getParameter("min_pixel_width") modeParam = node.getParameter("mode") widthParam = node.getParameter("width") verboseParam = node.getParameter("verbose") threadsParam = node.getParameter("threads") makeInteractiveParam = node.getParameter("makeInteractive") if not locationParam or not filenameParam: raise RuntimeError( "Missing node parameters, requires 'location' and 'filename'") # Copy array param values out to appropriate attributes samples = [] for ci in range(0, samplesParam.getNumChildren()): samples = samples + [ samplesParam.getChildByIndex(ci).getValue(frameTime) ] if samples is not None and len(samples) > 0: opSamplesParam = FnAttribute.FloatAttribute(samples) imageSearchPath = "" if imageSearchPathParam.getNumChildren() >= 1: imageSearchPath = imageSearchPathParam.getChildByIndex( ci).getValue(frameTime) for ci in range(1, imageSearchPathParam.getNumChildren()): imageSearchPath = imageSearchPath + ":" + imageSearchPathParam.getChildByIndex( ci).getValue(frameTime) opImageSearchPathParam = FnAttribute.StringAttribute(imageSearchPath) # Build the Op args from our node argsGb = FnAttribute.GroupBuilder() argsGb.set( "filename", FnAttribute.StringAttribute(filenameParam.getValue(frameTime))) argsGb.set("proxy", FnAttribute.StringAttribute(proxyParam.getValue(frameTime))) if samples is not None and len(samples) > 0: argsGb.set("samples", opSamplesParam) argsGb.set("length", FnAttribute.FloatAttribute(lengthParam.getValue(frameTime))) argsGb.set( "density", FnAttribute.FloatAttribute(densityParam.getValue(frameTime))) argsGb.set( "min_pixel_width", FnAttribute.FloatAttribute(minPixelWidthParam.getValue(frameTime))) argsGb.set("width", FnAttribute.FloatAttribute(widthParam.getValue(frameTime))) argsGb.set("imageSearchPath", opImageSearchPathParam) argsGb.set("verbose", FnAttribute.IntAttribute(verboseParam.getValue(frameTime))) argsGb.set("threads", FnAttribute.IntAttribute(threadsParam.getValue(frameTime))) argsGb.set("frame", FnAttribute.IntAttribute(int(round(frameTime)))) argsGb.set("mode", FnAttribute.IntAttribute(modeParam.getValue(frameTime))) argsGb.set("system", systemArgs) argsGb.set( "disableBoundingbox", FnAttribute.IntAttribute( disableBoundingboxParam.getValue(frameTime))) argsGb.set( "makeInteractive", FnAttribute.StringAttribute( makeInteractiveParam.getValue(frameTime))) argsGb.set("xform", interface.getTransformAsAttribute()) exclusiveAttrName, exclusiveAttr = interface.getExclusiveToNameAndAttribute( ) if exclusiveAttr is not None: argsGb.set("exclusiveTo", exclusiveAttr) # We want to use the StaticSceneCreate Op to build the parent # hierarchy, so that our op only has to worry about generating its # children. Its args are somewhat complex, but fortunately, there # is a helper class that makes it all much easier. rootLocation = locationParam.getValue(frameTime) sscb = FnGeolibServices.OpArgsBuilders.StaticSceneCreate() sscb.addSubOpAtLocation(rootLocation, "ArnoldYeti_In", argsGb.build()) interface.appendOp("StaticSceneCreate", sscb.build()) def getScenegraphLocation(node, frameTime): locationParam = node.getParameter("location") return locationParam.getValue(0.0) # Here we need to define the parameters for the node, and register the op # chain creation callback function nodeBuilder = Nodes3DAPI.NodeTypeBuilder("ArnoldYeti_In") # If we wanted to merge with incoming scene, we could simply allow the # node to have an input. Unless you delete locations in your Op, any # existing locations will pass-through. It is encouraged though to avoid # long chains of Ops, as it makes multi-threading and caching less # efficient, so for 'Generator' Ops, no input is preferable. # nodeBuilder.setInputPortNames( ("in",) ) # Parameters can be described by a group attribute paramGb = FnAttribute.GroupBuilder() paramGb.set("location", FnAttribute.StringAttribute("/root/world/geo/yetiProc")) paramGb.set("filename", FnAttribute.StringAttribute("")) paramGb.set("proxy", FnAttribute.StringAttribute("")) paramGb.set("density", FnAttribute.FloatAttribute(1.0)) paramGb.set("length", FnAttribute.FloatAttribute(1.0)) paramGb.set("imageSearchPath", FnAttribute.StringAttribute([], 1)) paramGb.set("min_pixel_width", FnAttribute.FloatAttribute(0.0)) paramGb.set("width", FnAttribute.FloatAttribute(1.0)) paramGb.set("mode", FnAttribute.IntAttribute(MODE_RIBBON)) paramGb.set("samples", FnAttribute.FloatAttribute([], 1)) paramGb.set("threads", FnAttribute.IntAttribute(0)) paramGb.set("verbose", FnAttribute.IntAttribute(2)) paramGb.set("disableBoundingbox", FnAttribute.IntAttribute(1)) nodeBuilder.addTransformParameters(paramGb) nodeBuilder.addMakeInteractiveParameter(paramGb) nodeBuilder.addInteractiveTransformCallbacks(paramGb) nodeBuilder.setParametersTemplateAttr(paramGb.build(), forceArrayNames=('samples', 'imageSearchPath')) nodeBuilder.setHintsForNode({ 'help': 'Create an Arnold procedural node suitable for invoking Peregrine Labs\' Yeti.' }) nodeBuilder.setHintsForParameter("location", {'widget': 'newScenegraphLocation'}) nodeBuilder.setHintsForParameter( "filename", { 'widget': 'assetIdInput', 'sequenceListing': False, 'fileTypes': 'fur', 'help': 'Requred; path to Yeti fur cache file' }) nodeBuilder.setHintsForParameter( "proxy", { 'widget': 'assetIdInput', 'sequenceListing': False, 'fileTypes': 'abc', 'help': 'Not Requred; path to Yeti fur alembic proxy file' }) nodeBuilder.setHintsForParameter( "density", {'help': 'Density scale for curve population'}) nodeBuilder.setHintsForParameter("length", {'help': 'Length scale for curves'}) nodeBuilder.setHintsForParameter( "imageSearchPath", { 'widget': 'sortableArray', 'help': 'Optional; colon-separated paths to images for curve operations' }) nodeBuilder.setHintsForParameter( "min_pixel_width", { 'help': 'Arnold min-pixel-width; typically between 0 and 1, can help reduce aliasing' }) nodeBuilder.setHintsForParameter( "mode", { 'widget': 'mapper', 'options': modeDict, 'help': 'Rendering mode of the curves; camera-facing ribbons, thick cylinders, or ribbons oriented by normal vectors.' }) nodeBuilder.setHintsForParameter( "width", {'help': 'Width/radius scale factor for curves'}) nodeBuilder.setHintsForParameter( "samples", { 'widget': 'sortableArray', 'help': 'Optional; frame-relative motion sample times (e.g -0.25, 0.25).' }) nodeBuilder.setHintsForParameter( "threads", {'help': 'Number of threads for curve generation'}) nodeBuilder.setHintsForParameter("verbose", {'help': 'Log level for Yeti'}) nodeBuilder.setHintsForParameter( 'disableBoundingbox', { 'widget': 'checkBox', 'help': 'disable caculating Yeti fur Boundingbox' }) # Register our Op build function nodeBuilder.setBuildOpChainFnc(buildOpChain) # Make this available for widgets and parameter expressions nodeBuilder.setGetScenegraphLocationFnc(getScenegraphLocation) # Create the new Node3D type nodeBuilder.build()
def buildOpChain(node, interface): """ Configures the Ops to represent the current state of the node. The calling mechanism in Node3D takes are of ensuring that the underlying Op network is only updated if Op Args and types have changed. @type node: C{Nodes3DAPI.NodeTypeBuilder.SubdividedSpace} @type interface: C{Nodes3DAPI.NodeTypeBuilder.BuildChainInterface} @param node: The node to build the Op and OpArgs from (ie: an instance of our node). @param interface: The interface that will configure the Ops in the underlying Op network. """ # Ensure the runtime doesn't error for us if there are no inputs on the # Node - as we want to allow it to exist in isolation. The default is 1 interface.setMinRequiredInputs(0) # We first need the current time for parameter value queries frameTime = interface.getGraphState().getTime() # Pass these along through the ops so that the have shutter open/close and number of samples systemArgs = interface.getGraphState().getOpSystemArgs() # Get the parameters from our node locationParam = node.getParameter("location") filenameParam = node.getParameter("filename") proxyParam = node.getParameter("proxy") samplesParam = node.getParameter("samples") imageSearchPathParam = node.getParameter("imageSearchPath") disableBoundingboxParam = node.getParameter("disableBoundingbox") lengthParam = node.getParameter("length") densityParam = node.getParameter("density") minPixelWidthParam = node.getParameter("min_pixel_width") modeParam = node.getParameter("mode") widthParam = node.getParameter("width") verboseParam = node.getParameter("verbose") threadsParam = node.getParameter("threads") makeInteractiveParam = node.getParameter("makeInteractive") if not locationParam or not filenameParam: raise RuntimeError( "Missing node parameters, requires 'location' and 'filename'") # Copy array param values out to appropriate attributes samples = [] for ci in range(0, samplesParam.getNumChildren()): samples = samples + [ samplesParam.getChildByIndex(ci).getValue(frameTime) ] if samples is not None and len(samples) > 0: opSamplesParam = FnAttribute.FloatAttribute(samples) imageSearchPath = "" if imageSearchPathParam.getNumChildren() >= 1: imageSearchPath = imageSearchPathParam.getChildByIndex( ci).getValue(frameTime) for ci in range(1, imageSearchPathParam.getNumChildren()): imageSearchPath = imageSearchPath + ":" + imageSearchPathParam.getChildByIndex( ci).getValue(frameTime) opImageSearchPathParam = FnAttribute.StringAttribute(imageSearchPath) # Build the Op args from our node argsGb = FnAttribute.GroupBuilder() argsGb.set( "filename", FnAttribute.StringAttribute(filenameParam.getValue(frameTime))) argsGb.set("proxy", FnAttribute.StringAttribute(proxyParam.getValue(frameTime))) if samples is not None and len(samples) > 0: argsGb.set("samples", opSamplesParam) argsGb.set("length", FnAttribute.FloatAttribute(lengthParam.getValue(frameTime))) argsGb.set( "density", FnAttribute.FloatAttribute(densityParam.getValue(frameTime))) argsGb.set( "min_pixel_width", FnAttribute.FloatAttribute(minPixelWidthParam.getValue(frameTime))) argsGb.set("width", FnAttribute.FloatAttribute(widthParam.getValue(frameTime))) argsGb.set("imageSearchPath", opImageSearchPathParam) argsGb.set("verbose", FnAttribute.IntAttribute(verboseParam.getValue(frameTime))) argsGb.set("threads", FnAttribute.IntAttribute(threadsParam.getValue(frameTime))) argsGb.set("frame", FnAttribute.IntAttribute(int(round(frameTime)))) argsGb.set("mode", FnAttribute.IntAttribute(modeParam.getValue(frameTime))) argsGb.set("system", systemArgs) argsGb.set( "disableBoundingbox", FnAttribute.IntAttribute( disableBoundingboxParam.getValue(frameTime))) argsGb.set( "makeInteractive", FnAttribute.StringAttribute( makeInteractiveParam.getValue(frameTime))) argsGb.set("xform", interface.getTransformAsAttribute()) exclusiveAttrName, exclusiveAttr = interface.getExclusiveToNameAndAttribute( ) if exclusiveAttr is not None: argsGb.set("exclusiveTo", exclusiveAttr) # We want to use the StaticSceneCreate Op to build the parent # hierarchy, so that our op only has to worry about generating its # children. Its args are somewhat complex, but fortunately, there # is a helper class that makes it all much easier. rootLocation = locationParam.getValue(frameTime) sscb = FnGeolibServices.OpArgsBuilders.StaticSceneCreate() sscb.addSubOpAtLocation(rootLocation, "ArnoldYeti_In", argsGb.build()) interface.appendOp("StaticSceneCreate", sscb.build())