Пример #1
0
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()
Пример #2
0
    '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(
Пример #3
0
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())
Пример #4
0
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
Пример #5
0
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()
Пример #6
0
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()
Пример #7
0
    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())