def saveShadingEngine(shadingEngineObj, storagePlug): '''Save a connection to the shading engine node in the storage plug. This function unconditionally connects the shading engine to the storage plug. It also stores the name of the shading engine as a full name with the namespace path in the storage plug's node, if the shading engine is not in the main scene.''' # Store the shading engine message attribute fnEngine = OpenMaya.MFnDependencyNode(shadingEngineObj) enginePlug = fnEngine.findPlug('message', False) utils.connect(enginePlug, storagePlug) # Deal with fact that material might be in a referenced file. storageNode = storagePlug.node() storageStrPlug = plug.findPlug(storageNode, MaterialOverride.kShadingEngineNameLong) if fnEngine.isFromReferencedFile: if storageStrPlug is None: properties = {'type': 'String', 'connectable': False} storageStrPlug = plug.Plug.createAttribute( storageNode, MaterialOverride.kShadingEngineNameLong, MaterialOverride.kShadingEngineNameShort, properties, plug.kNotUndoable) storageStrPlug.value = fnEngine.name() elif storageStrPlug is not None: # Remove dynamic attribute. fn = OpenMaya.MFnDependencyNode(storageNode) fn.removeAttribute(storageStrPlug.plug.attribute())
def insert(self, target, nextOvr=None): '''Insert self in the override chain for given target, or start the chain if none exists.''' destinations = (OpenMaya.MFnDependencyNode(d.node()).userNode() for d in target.destinations()) firstApplyNode = next( (d for d in destinations if d and isinstance(d, ApplyConnectionOverride)), None) if not firstApplyNode: # no apply override chain on that target => self becomes the only apply override node for that target self.connectTarget(target) self.override().doSaveOriginal(target, self.getOriginalPlug()) return utils.transferPlug(firstApplyNode.getOriginalPlug(), self.getOriginalPlug()) # search where to insert if not nextOvr: prevAO, nextAO = firstApplyNode, None else: nextAO = (ao for ao in ApplyConnectionOverride.reverseGenerator( firstApplyNode) if ao.override() == nextOvr).next() prevAO = nextAO.prev() # insert between prevAO and nextAO if prevAO: utils.connect(prevAO.getNextPlug(), self.getPrevPlug()) if nextAO: utils.connect(self.getNextPlug(), nextAO.getPrevPlug()) else: # no nextAO => self becomes highest priority => must hold target prevAO.moveTargetTo(self)
def doAction(self, target, source): """ This method performs the override action for a given target and source. """ # NOTE: doAction should never add commands in the undo stack shaderPlug = utils.plugSrc(source) if shaderPlug: surfaceShader = shaderPlug.node() shadingEngine = target.node() # Break any connections to old surface shader fnShadingEngine = OpenMaya.MFnDependencyNode(shadingEngine) connections = fnShadingEngine.getConnections() for c in connections: if c.isDestination: fn = OpenMaya.MFnDependencyNode(c.source().node()) nodeClass = OpenMaya.MNodeClass(fn.typeName) if 'shader/surface' in nodeClass.classification: utils.disconnect(c.source(), c) # Make connections to new surface shader connections = ShaderOverride._getNewConnections( surfaceShader, shadingEngine) # Turn reference edits on if restoring the original. with handleRestoringOriginalCtx(): for c in connections: utils.connect(c[0], c[1])
def extract(self): '''Removes self from the apply override chain. This will trigger an update of the chain.''' prevAO, nextAO = self.prev(), self.next() if prevAO: utils.disconnect(prevAO.getNextPlug(), self.getPrevPlug()) if nextAO: utils.disconnect(self.getNextPlug(), nextAO.getPrevPlug()) elif prevAO: # no nextAO => prevAO becomes highest priority => must hold target self.moveTargetTo(prevAO) if prevAO and nextAO: utils.connect(prevAO.getNextPlug(), nextAO.getPrevPlug()) if not prevAO and not nextAO: # no other apply overrides => must restablish original connection with guard.StateGuardCtx(isRestoringOriginal, setRestoringOriginal, True): self.override().doAction(self._targetHandle.src(), self.getOriginalPlug()) else: # any apply override in the chain can trigger the update (prevAO or nextAO).update()
def _transferConnectedPlug(src, dst): if src.isDestination: fromSrc = utils.plugSrc(src) # Turn reference edits on if restoring the original. with handleRestoringOriginalCtx(): utils.connect(fromSrc, dst) elif src.isCompound and dst.isCompound: for idx in range(0, src.numChildren()): transferPlug(src.child(idx), dst.child(idx))
def _restoreConnection(self, dst): # If the source was in a referenced file, the connection to it may # have been broken. To deal with this case, we also stored a # string representation of the source. srcStrPlug = self._srcStrPlug() if srcStrPlug is not None: src = commonUtils.nameToPlug(srcStrPlug.value) if src is not None and not src.isNull: utils.connect(src, dst)
def _restoreOriginalPlug(self, original): # If the original shading engine was in a referenced file, the # connection to it may have been broken. To deal with this # case, we also stored the shading engine name as a string. originalStr = commonUtils.findPlug( self.thisMObject(), MaterialOverride.kShadingEngineNameLong) if originalStr is not None: originalNode = commonUtils.nameToNode(originalStr.asString()) if not originalNode.isNull(): fnEngine = OpenMaya.MFnDependencyNode(originalNode) enginePlug = fnEngine.findPlug('message', False) utils.connect(enginePlug, original)
def doSaveOriginal(self, target, storage): """ This method performs saving of original state for a given target and a storage plug for storing the state. """ # Find old surface shader fnShadingEngine = OpenMaya.MFnDependencyNode(target.node()) connections = fnShadingEngine.getConnections() for c in connections: if c.isDestination: fn = OpenMaya.MFnDependencyNode(c.source().node()) nodeClass = OpenMaya.MNodeClass(fn.typeName) if 'shader/surface' in nodeClass.classification: # Source surface shader found. # Use the message plug to save a reference to it. target = fn.findPlug('message', False) utils.connect(target, storage) break
def _createApplyOverride(self, target, nextOvr=None): name = self.name() + "_" + target.partialName( includeNodeName=True, includeNonMandatoryIndices=True, includeInstancedIndices=True) # Namespace qualifies from the original node name cannot be used # to rename the new apply node. Replace the namespace qualifiers with '_'. name = name.replace(':', '_').replace('.', '_') if utils.nameToUserNode(name): return ao = ApplyConnectionOverride.create(name) utils.connect(self._getEnabledPlug(), ao.getEnabledPlug()) ao.finalize(self._getAttrValuePlug()) ao.insert(target, nextOvr) return ao
def connect(self, src): '''Connect this destination to the argument source MPlug. If the source node is referenced, store a string representation of the source.''' # For non-referenced sources, simply connect. This will trivially # and obviously persist the connection information. utils.connect(src, self.dst()) # If src is referenced, record it as a string, as connections to # referenced objects are normally recorded as referenced edits, # which we intentionally turn off in render setup. srcNode = src.node() srcFn = OpenMaya.MFnDependencyNode(srcNode) if srcFn.isFromReferencedFile: srcStrPlug = self._srcStrPlug() if srcStrPlug is None: properties = {'type': 'String', 'connectable': False} srcStrPlug = plug.Plug.createAttribute( self._node, self._srcStrAttrNameLong, self._srcStrAttrNameShort, properties, plug.kNotUndoable) # Connection overrides represent a connection to a node, and # therefore don't need a DAG path to disambiguate between # different instances. However, because DAG nodes don't have # unique names, we do need a DAG path to disambiguate different # DAG nodes with the same name. if srcNode.hasFn(OpenMaya.MFn.kDagNode): srcPath = OpenMaya.MFnDagNode(srcNode).fullPathName() srcAttr = src.partialName(includeNodeName=False, includeNonMandatoryIndices=True, includeInstancedIndices=True, useAlias=False, useFullAttributePath=True, useLongNames=True) srcFullAttrName = srcPath + "." + srcAttr else: srcFullAttrName = src.name() srcStrPlug.value = srcFullAttrName
def setParent(self, parentListUserNode): """Set the list to which this item belongs. To remove this item from its list, the parent node argument must be None.""" # Get the parent list to which we belong, which is a destination plug, # and disconnect ourselves from the previous parent. parentListPlug = utils.findPlug(self, ListItem.parentList) utils.disconnectDst(parentListPlug) # If we're removing this item from its parent list, nothing more to do. if not parentListUserNode: return # We're setting a non-None parent. Get the listItems source plug # from the parent, and connect ourselves up to it, which makes us # an item in our parent's list. utils.connect( utils.findPlug(parentListUserNode, parentListUserNode._getListItemsAttr()), parentListPlug)
def setPrevious(self, item): previousPlug = utils.findPlug(self, ListItem.previous) nextPlug = utils.findPlug(item, ListItem.next) utils.connect(nextPlug, previousPlug)
def setNext(self, item): nextPlug = utils.findPlug(self, ListItem.next) previousPlug = utils.findPlug(item, ListItem.previous) utils.connect(nextPlug, previousPlug)