Ejemplo n.º 1
0
def AddAlembicGeometry(importomaticNode, assetId, locationExpression=None):
    assetPlugin = AssetAPI.GetDefaultAssetPlugin()
    rootInstanceName = assetPlugin.getAssetDisplayName(assetId)
    
    node = NodegraphAPI.CreateNode('Group')
    node.setName(rootInstanceName)
    # This seems a bit odd to set the name and then retrieve it but Katana
    # will change the name to a unique name which we can then extract
    uniqueRootName = node.getName()
    node.setName(uniqueRootName)
    node.setType('Alembic')
    node.addOutputPort('out')
        
    alembicInNode = NodegraphAPI.CreateNode('Alembic_In', node)
    alembicInNode.getOutputPortByIndex(0).connect(node.getReturnPort('out'))

    # If the location parameter isn't
    # given try to intelligently guess one
    #
    if locationExpression:
        alembicInNode.getParameter('name').setExpression(locationExpression, True)
    else:
        baseLocation = "/root/world/geo"
        location = baseLocation + "/" + uniqueRootName
        alembicInNode.getParameter('name').setValue(location, 0)
        
    alembicInNode.getParameter('abcAsset').setValue(assetId, 0)

    assetInfoParam = node.getParameters().createChildGroup('assetInfo')

    findInstances(uniqueRootName, assetInfoParam, "")

    BuildScenegraph(node)

    return node
Ejemplo n.º 2
0
    def __popupVersions(self, item, local_pos, global_pos):
        currentColumnId = self.tree.columnAt(local_pos.x())
        currentColumnName = self.__headerLabels[currentColumnId]
        if currentColumnName == 'Version':
            itemData = item.getItemData()
            itemType = itemData['type']
            if itemType == 'assetTree':
                assetItem = itemData['assetItem']
                assetId = assetItem.getAssetId()
                if assetId != None:
                    assetPlugin = AssetAPI.GetDefaultAssetPlugin()
                    if assetPlugin.isAssetId(assetId):
                        versions = []
                        customTags = assetItem.getCustomVersionTagNames()
                        if customTags:
                            versions = customTags
                        versions.extend(assetPlugin.getAssetVersions(assetId))
                        if versions:
                            self.__versionPopup.clear()
                            for version in versions:
                                self.__versionPopup.addItem(version)

                            self.__versionPopup.popup(global_pos)
                            self.__versionPopup.show()
                        return True
        return False
Ejemplo n.º 3
0
 def __findAssetIdFromTreeItem(self, item):
     itemData = item.getItemData()
     itemType = itemData['type']
     if itemType == 'assetTree':
         assetItem = itemData['assetItem']
         assetId = assetItem.getAssetId()
         assetPlugin = AssetAPI.GetDefaultAssetPlugin()
         if assetPlugin.isAssetId(assetId):
             return assetId
     return
Ejemplo n.º 4
0
    def setValue(self, value):
        """Given an asset id. Set the Show, Shot, Asset and version in the UI."""

        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        if not assetPlugin.isAssetId(value):
            return
        
        assetFields = assetPlugin.getAssetFields(value, True)
        self.__showEdit.setText(assetFields["show"])
        self.__shotEdit.setText(assetFields["shot"])
        self.__assetEdit.setText(assetFields[AssetAPI.kAssetFieldName])
        self.__versionEdit.setText(assetFields.get(AssetAPI.kAssetFieldVersion, ""))
    def setAssetId(self, assetId):
        """
        Given an asset id decomposes in to a show, shot, name and 
        version fields and updates the UI to reflect that
        """
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        if not assetPlugin.isAssetId(assetId):
            return

        assetFields = assetPlugin.getAssetFields(assetId, True)
        for k in ("show", "shot", "name", "version"):
            self.__selectItem(k, assetFields[k])
Ejemplo n.º 6
0
 def setAssetId(self, assetId):
     """Given an asset ID, decompose in to a show, shot, asset name 
     and asset version and update the UI to reflect that.
     """
     assetPlugin = AssetAPI.GetDefaultAssetPlugin()
     if not assetPlugin.isAssetId(assetId):
         return
     
     assetFields = assetPlugin.getAssetFields(assetId, True)
     self.__showCombobox.setEditText(assetFields["show"])
     self.__shotCombobox.setEditText(assetFields["shot"])
     self.__assetCombobox.setEditText(assetFields[AssetAPI.kAssetFieldName])
     self.__versionCombobox.setEditText(assetFields.get(AssetAPI.kAssetFieldVersion,""))
Ejemplo n.º 7
0
    def isLockable(self):
        """
        An asset is not lockable if there is no version
        information for it.
        """

        # An asset is lockable if it has
        # multiple versions

        assetId = self.__handler.getAssetId()
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        versions = assetPlugin.getAssetVersions(assetId)
        lockable = len(versions) != 0

        return lockable
Ejemplo n.º 8
0
    def getValue(self):
        """Get the asset ID from this browser window"""

        show = str(self.__showEdit.text())
        shot = str(self.__shotEdit.text())
        asset = str(self.__assetEdit.text())
        version = str(self.__versionEdit.text())

        assetFields = {"show" : show, 
                       "shot" : shot, 
                       AssetAPI.kAssetFieldName : asset, 
                       AssetAPI.kAssetFieldVersion : version}
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        
        assetId = assetPlugin.buildAssetId(assetFields)
        return assetId
Ejemplo n.º 9
0
 def __popupVersionsItemChosen(self, chosenVersion, meta):
     item = self.tree.itemAt(self.__lastTreePos)
     if item != None:
         itemData = item.getItemData()
         if itemData['type'] == 'assetTree':
             assetPlugin = AssetAPI.GetDefaultAssetPlugin()
             assetItem = itemData['assetItem']
             assetId = assetItem.getAssetId()
             if assetPlugin.isAssetId(assetId):
                 fields = assetPlugin.getAssetFields(assetId, False)
                 fields['version'] = str(chosenVersion)
                 assetIdWithVersion = assetPlugin.buildAssetId(fields)
                 assetItem.setAssetId(assetIdWithVersion)
     self.__versionPopup.clearFilterField()
     self.__versionPopup.clear()
     return
Ejemplo n.º 10
0
    def setAssetId(self, assetId):
        """Given an asset ID, decompose in to a show, shot, asset name
        and asset version and update the UI to reflect that.
        """
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        if not assetPlugin.isAssetId(assetId):
            return

        assetFields = assetPlugin.getAssetFields(assetId, True)
        self.__showCombobox.setEditText(assetFields["show"])
        self.__shotCombobox.setEditText(assetFields["shot"])
        self.__assetCombobox.setEditText(assetFields[AssetAPI.kAssetFieldName])
        self.__versionCombobox.setEditText(assetFields[AssetAPI.kAssetFieldVersion])

        if assetId.startswith("sandbox://"):
            self.__sandboxCheckBox.setCheckState(QtCore.Qt.Checked)
Ejemplo n.º 11
0
def FindPluginFromAssetIdType(assetId):
    """
    Given an asset id, find the type of the asset and look for a plugin
    that can open that type. 
    """

    # Get hold of the primary asset plugin
    #
    assetPlugin = AssetAPI.GetDefaultAssetPlugin()

    # Get hold of the asset type name
    # so that we can match it up with a plug-in
    # for loading that particular type
    #
    attrs = assetPlugin.getAssetAttributes(assetId, "version")
    if not attrs:
        log.error(
            "No attributes for asset '%s'. Ensure you are using the correct asset management plug-in for this asset id."
            % assetId)
        return None

    assetType = attrs["type"]

    # Look for the right importomatic plugin
    # for this type
    #
    plugin_name = CastingSheetConstants.TYPE_MAPPING.get(assetType, None)

    # Check that there is a plugin for this type
    #
    if not plugin_name:
        log.error(
            "Unable to find a plugin mapping to load the given asset type '%s'"
            % assetType)
        return None

    # Check that there is a plugin for this type
    #
    if not AssetModule.HasBatchCreateCallback(plugin_name):
        log.error(
            "Unable to find a plugin registered to load the given asset type '%s'"
            % assetType)
        return None

    # Load it
    #
    return plugin_name
Ejemplo n.º 12
0
    def setAssetId(self, assetId):
        """
        Given an asset id decomposes in to a show, shot, name and
        version fields and updates the UI to reflect that
        """
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        if not assetPlugin.isAssetId(assetId):
            return

        assetFields = assetPlugin.getAssetFields(assetId, True)
        for k in ("show", "shot", "name",  "version"):
            self.__selectItem(k, assetFields[k])

        currIndex = 0
        if assetFields.get("protocol", "") == "sandbox":
            currIndex = 1
        self.__tabWidget.setCurrentIndex(currIndex)
Ejemplo n.º 13
0
def _ReplaceTagsWithNumbers(assetId):
    """
    Given an asset id that looks like this:
    
    mock://foo/bar/latest
    
    replace the tag with the latest version
    
    mock://foo/bar/3
    """
    assetPlugin = AssetAPI.GetDefaultAssetPlugin()
    version = assetPlugin.resolveAssetVersion(assetId)
    fields = assetPlugin.getAssetFields(assetId, False)
    fields["version"] = version
    assetId = assetPlugin.buildAssetId(fields)

    return assetId
Ejemplo n.º 14
0
    def setValue(self, value):
        """
        Given an asset ID. Set the Show, Shot, Asset and version in the UI.
        """

        assetPlugin = AssetAPI.GetDefaultAssetPlugin()
        if not assetPlugin.isAssetId(value):
            return

        assetFields = assetPlugin.getAssetFields(value, True)
        self.__showEdit.setText(assetFields["show"])
        self.__shotEdit.setText(assetFields["shot"])
        self.__assetEdit.setText(assetFields[AssetAPI.kAssetFieldName])
        self.__versionEdit.setText(assetFields[AssetAPI.kAssetFieldVersion])

        if assetFields.get("protocol", "") == "sandbox":
            self.__protocolLabel.setText("sandbox://")
        else:
            self.__protocolLabel.setText("mock://")
Ejemplo n.º 15
0
    def getValue(self):
        """Get the asset ID from this browser window"""

        show = str(self.__showEdit.text())
        shot = str(self.__shotEdit.text())
        asset = str(self.__assetEdit.text())
        version = str(self.__versionEdit.text())

        protocol = "asset"
        if self.__protocolLabel.text() == "sandbox://":
            protocol = "sandbox"

        assetFields = {"protocol" : protocol,
                       "show" : show,
                       "shot" : shot,
                       AssetAPI.kAssetFieldName : asset,
                       AssetAPI.kAssetFieldVersion : version}
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()

        assetId = assetPlugin.buildAssetId(assetFields)
        return assetId
Ejemplo n.º 16
0
def SyncCastingSheet(node, assetId):
    """
    Sync the casting sheet node to the given asset.
    
    -------------------------------------------------------------------
    Syncing steps
    
    Delete the merge node.
    
    Iterate over all of child nodes and ...
       - If they are locked:
           - Move the current node to the 'graveyard' group node.
               We are guaranteed that there will not be a node with the same casting name
               in this group node because if there had been it would have been in the
               casting sheet.
    
       - If they are not locked
           - Delete them
    
    For every node in the 'graveyard' node create an entry in a graveyard map.
    
    Create a new merge node.
    
    Iterate over all of the entries in the casting sheet:
        - If an asset of the given instance name exists in the graveyard map then:
           - re-parent the node under the casting sheet and use it
           
        - Otherwise:
           - Create a new node with the plug-in callback system
    
        Then connect the node to the merge node.
        
    Save the casting sheet asset id in to the parameter castingInfo.asset
    """

    # Type check
    #
    nodeType = node.getType()
    if nodeType != "CastingSheet":
        log.error(
            "You are trying to delete a node that is not a casting sheet node")
        return

    # For the casting sheet we always
    # resolve the asset version in the asset id
    # immediately.
    #
    assetPlugin = AssetAPI.GetDefaultAssetPlugin()
    assetId = _ReplaceTagsWithNumbers(assetId)
    filepath = assetPlugin.resolveAsset(assetId)
    if not filepath:
        log.error(
            "Unable to resolve the casting sheet asset ID for '%s' in the casting sheet node '%s'"
            % (assetId, node.getName()))
        return

    # Check if we will be able to load
    # the asset ids casting sheet on file
    #
    if not _PopulatePreCondition(filepath):
        return

    # Ensure that the casting sheet node has a valid internal state
    #
    children = list(node.getChildren())
    if not children:
        log.error(
            "Casting sheet node '%s' is in an invalid state it has no merge node or graveyard node, remove it and create a new one"
            % node.getName())
        return

    # The graveyard node is always first, then the merge node.
    #
    graveyardNode = _GetGraveyardNode(node)
    mergeNode = _GetMergeNode(node)

    # This is done to support an assertion only.
    # It does not contribute to the functionality of this code.
    #
    takenGraveyardNames = set(
        (GetCastingName(child) for child in graveyardNode.getChildren()))

    # Get hold of the children before the
    # merge node is deleted
    #
    entries = _GetInputNodes(mergeNode)

    # In case the merge node doesn't exist
    #
    if mergeNode.getType() == "Merge":
        mergeNode.delete()

    # Everything else is a casting sheet entry
    #
    for child in entries:
        if IsLocked(child):
            assert GetCastingName(child) not in takenGraveyardNames
            child.setParent(graveyardNode)
        else:
            child.delete()

    # Create a casting name to node mapping
    #
    graveyard = [(GetCastingName(n), n) for n in graveyardNode.getChildren()]
    graveyard = dict(graveyard)

    # Populate the node with casting sheet entries
    #
    _Populate(filepath, node, graveyard)

    # Now store the asset id so that we know where this all came from
    #
    setCastingParam(node,
                    "asset",
                    assetId,
                    hintsDict={'widget': 'assetIdInput'})

    # Finally rebuild the layout
    #
    _LayoutContents(node)
Ejemplo n.º 17
0
def AddScenegraphXmlGeometry(importomaticNode,
                             assetId,
                             locationExpression=None):

    filename = ''
    assetPlugin = AssetAPI.GetDefaultAssetPlugin()
    if assetPlugin.isAssetId(assetId):
        filename = assetPlugin.resolveAsset(assetId)
    else:
        return

    fileBase = os.path.basename(filename)
    rootParamName = os.path.splitext(fileBase)[0]

    # Parse XML to extract relevant information
    xmlTree = ET.parse(filename)
    xmlRoot = xmlTree.getroot()

    node = None

    # Find all instances of type reference
    rootInstanceList = xmlRoot.find("instanceList")
    if rootInstanceList is not None:
        rootInstance = rootInstanceList.find("instance")
        rootInstanceName = rootInstance.attrib["name"]
        rootInstanceType = rootInstance.attrib["type"]

        node = NodegraphAPI.CreateNode('Group')
        node.setName(rootParamName)
        # This seems a bit odd to set the name and then retrieve it but Katana
        # will change the name to a unique name which we can then extract
        uniqueRootName = node.getName()
        node.setType('ScenegraphXml')
        node.addOutputPort('out')

        xmlInNode = NodegraphAPI.CreateNode('ScenegraphXml_In', node)
        xmlInNode.getOutputPortByIndex(0).connect(node.getReturnPort('out'))

        # If the location parameter isn't
        # given try to intelligently guess one
        #
        if locationExpression:
            xmlInNode.getParameter('name').setExpression(
                locationExpression, True)
        else:
            baseLocation = xmlInNode.getParameter("name").getValue(0)
            location = baseLocation + '/' + uniqueRootName
            xmlInNode.getParameter('name').setValue(location, 0)

        xmlInNode.getParameter('asset').setValue(assetId, 0)
        basePath = os.path.dirname(filename) + os.sep

        assetInfoParam = node.getParameters().createChildGroup('assetInfo')
        assetInfoParam.createChildString('_ignore', 'False')
        assetInfoParam.createChildString('_sgPath', '/' + uniqueRootName)

        findInstances(rootInstanceList, assetInfoParam, node, basePath, "")

    BuildScenegraph(node)

    return node
Ejemplo n.º 18
0
    def buildBgeoInOpChain(node, interface):
        """
        Defines the callback function used to create the Ops chain for the
        node type being registered.

        @type node: C{Nodes3DAPI.NodeTypeBuilder.BgeoIn}
        @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')
        fileNameParam = node.getParameter('fileName')
        makeFacesetsParam = node.getParameter('makeFacesets')
        reportEmptyParam = node.getParameter('reportEmpty')
        computeBoundParam = node.getParameter('computePointCloudBound')
        createSubdParam = node.getParameter('createSubd')
        checkVersionParam = node.getParameter('checkVersion')

        # resolve file path so that it can include frame number replacement
        # i.e. %04d
        filePath = fileNameParam.getValue(frameTime)
        fileSequencePlugin = AssetAPI.GetDefaultFileSequencePlugin()
        if fileSequencePlugin and fileSequencePlugin.isFileSequence(filePath):
            fileSequence = fileSequencePlugin.getFileSequence(filePath)
            resolvedPath = fileSequence.getResolvedPath(int(frameTime))
        else:
            resolvedPath = filePath

        argsGb.set('fileName', FnAttribute.StringAttribute(resolvedPath))
        argsGb.set(
            'makeFacesets',
            FnAttribute.IntAttribute(makeFacesetsParam.getValue(frameTime)))
        argsGb.set(
            'reportEmpty',
            FnAttribute.IntAttribute(reportEmptyParam.getValue(frameTime)))
        argsGb.set(
            'computePointCloudBound',
            FnAttribute.IntAttribute(computeBoundParam.getValue(frameTime)))
        argsGb.set(
            'createSubd',
            FnAttribute.IntAttribute(createSubdParam.getValue(frameTime)))
        argsGb.set(
            'checkVersion',
            FnAttribute.IntAttribute(checkVersionParam.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, 'BgeoIn', argsGb.build())

        interface.appendOp('StaticSceneCreate', sscb.build())
Ejemplo n.º 19
0
    def __traverseItemTree(self,
                           assetItem,
                           treeParent,
                           selectedKeys,
                           openState,
                           node=None):
        item = AssetListViewItem(treeParent, '')
        key = node
        selectable = True
        if not node:
            try:
                key = assetItem.getItemKey()
                selectable = assetItem.isSelectable()
            except Exception as exception:
                log.exception('Error accessing asset item %s: %s' %
                              (assetItem, str(exception)))

        item.setItemData({
            'type': 'assetTree',
            'assetItem': assetItem,
            'key': key
        })
        if selectable:
            item.setSelected(key in selectedKeys)
            item.setFlags(
                QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsSelectable
                                    | QtCore.Qt.ItemIsEnabled))
        else:
            item.setFlags(QtCore.Qt.ItemFlags(QtCore.Qt.ItemIsEnabled))
        defaultState = assetItem.getDefaultOpenState()
        expanded = openState.get(key, defaultState)
        item.setExpanded(expanded)
        try:
            assetId = assetItem.getAssetId()
            if assetId:
                assetPlugin = AssetAPI.GetDefaultAssetPlugin()
                if assetPlugin.isAssetId(assetId):
                    fields = assetPlugin.getAssetFields(assetId, True)
                    version = fields.get('version', None)
                    resolvedVersion = assetPlugin.resolveAssetVersion(
                        assetId, throwOnError=True)
                    if version:
                        item.setText(VERSION_COLUMN, version)
                        item.setIcon(
                            VERSION_COLUMN,
                            UI4.Util.IconManager.GetIcon(
                                'Icons/heightAdjustDownHilite16.png'))
                    if resolvedVersion:
                        item.setText(RESOLVED_VERSION_COLUMN, resolvedVersion)
        except Exception as exception:
            log.exception('Error getting version of asset item %s: %s' %
                          (assetItem, str(exception)))

        try:
            assetItem.setItemState(item)
        except Exception as exception:
            log.exception('Error setting state of asset item %s: %s' %
                          (assetItem, str(exception)))

        if assetItem.isIgnorable() and assetItem.isIgnored():
            item.setIcon(NAME_COLUMN,
                         UI4.Util.IconManager.GetIcon('Icons/ignore16.png'))
        try:
            children = assetItem.getChildren()
            if children:
                for child in children:
                    self.__traverseItemTree(child, item, selectedKeys,
                                            openState)

        except Exception as exception:
            log.exception('Error traversing children of asset item %s: %s' %
                          (assetItem, str(exception)))

        try:
            assetItem.addNodeObservers(self.__addObserverNode)
        except Exception as exception:
            log.exception('Error adding node observer to asset item %s: %s' %
                          (assetItem, str(exception)))

        return item
    def getResult(self):
        assetFields = self.__widget.getAssetFields()
        assetPlugin = AssetAPI.GetDefaultAssetPlugin()

        return assetPlugin.buildAssetId(assetFields)
Ejemplo n.º 21
0
def AddCastingSheet(importomaticNode, assetId, locationExpression=None):
    """
    Load the chosen casting sheet specified by assetId to the location specified by locationExpression.
    """

    # We can't do anything without a casting sheet asset id

    if assetId == None:
        log.error("No asset id given for new casting sheet")
        return

    # Resolve the asset file path
    #
    # Get hold of the primary asset plugin
    # so that we can resolve the casting sheet asset.

    assetPlugin = AssetAPI.GetDefaultAssetPlugin()
    filepath = assetPlugin.resolveAsset(assetId)
    if not filepath:
        log.error("Unable to resolve the casting sheet asset ID for '%s'" %
                  assetId)
        return

    # Check if we will be able to load
    # the asset ids in the casting sheet on file

    if not _PopulatePreCondition(filepath):
        return

    # Store everything inside of the casting sheet group

    node = NodegraphAPI.CreateNode('Group')
    node.setName('CastingSheet')
    node.setType(CASTINGSHEET_TYPE)
    node.addOutputPort('out')
    returnOut = node.getReturnPort("out")

    # CastingSheet parameters - Scene graph location

    if locationExpression:
        setCastingStringParamExpression(
            node,
            "name",
            locationExpression,
            hintsDict={'widget': 'scenegraphLocation'})

    # If a locationExpression wasn't given
    # default to a sensible place

    else:
        location = '/root/world/geo/%s' % node.getName()
        setCastingParam(node,
                        "name",
                        location,
                        hintsDict={'widget': 'scenegraphLocation'})

    # Create our graveyard node
    # This should be created first because it
    # never gets deleted

    graveyard = NodegraphAPI.CreateNode('Group')
    graveyard.setName('Graveyard')
    graveyard.setParent(node)

    # Read the casting sheet and populate
    # the casting sheet node.

    _Populate(filepath, node)

    # Now store the asset id so that we know where this all came from

    setCastingParam(node,
                    "asset",
                    assetId,
                    hintsDict={'widget': 'assetIdInput'})

    # Finally adjust the layout in the nodegraph ui

    _LayoutContents(node)

    return node
Ejemplo n.º 22
0
def _Populate(filepath, node, graveyard={}):
    """
    Populate the casting sheet node with the contents of the casting sheet file
    pointed to by 'filepath'.
    
    Use the graveyard parameter to bring back locked nodes that were not part of the last
    set casting sheet version.  
    """

    # Create a new merge node
    #
    merge = NodegraphAPI.CreateNode('Merge')
    merge.setName('MergeCastingSheet')
    merge.setParent(node)
    mergeOut = merge.getOutputPort("out")

    # Connect the merge node
    # to this nodes output port
    #
    returnOut = node.getReturnPort("out")
    mergeOut.connect(returnOut)

    # Resolve the asset file path
    #
    assetPlugin = AssetAPI.GetDefaultAssetPlugin()

    # Iterate over all the entries
    # in the casting sheet loading
    # their respective nodes
    #
    GRID_WIDTH = 200
    GRID_Y_OFFSET = 200
    FIRST = 0
    ID, NAME = (0, 1)
    for index, asset in enumerate(CastingSheetIO.Iterator(filepath)):
        assetId = asset[ID]
        assetName = asset[NAME]

        # If it exists in the 'graveyard'.
        # bring it back to life
        #
        child = graveyard.get(assetName, None)

        if not child:

            # Get hold of a plug-in that can load this asset
            #
            plugin_name = FindPluginFromAssetIdType(assetId)

            # Ensure that a matching plug-in was found
            # for the asset type
            #
            if plugin_name == None:
                log.error(
                    "We were unable to find a plug-in to load the the asset '%s'."
                    % (assetId))

            else:

                # The location of the casting sheet asset in the scene graph
                #
                locationExpression = 'getParam("%s.castingInfo.name") + "/" + getNode(nodeName).getParent().castingInfo.name' % (
                    node.getName())

                # Load it in
                #
                child = AssetModule.TriggerBatchCreateCallback(
                    plugin_name, node, assetId, locationExpression)

                # We can try to set the name of the casting sheet item
                # but if there is already an item that exists in the scene
                # with that same name then we will be given a different name.
                #
                child.setName(assetName)

                # So store the casting sheet name on the node as a parameter.
                # We will use this later for synching.
                #
                setCastingParam(child, "name", assetName)
                setCastingParam(child, "locked", UNLOCK,
                                NodegraphAPI.Parameter.createChildNumber)

        if child:
            # Re-parent the node
            #
            child.setParent(node)

            # Merge this input in
            #
            entry = merge.addInputPort(assetName)
            main = child.getOutputPortByIndex(FIRST)

            main.connect(entry)

        else:
            log.warning(
                "We were unable to create a new casting node for the casting sheet entry '%s' at '%s'."
                % (assetName, assetId))