Files
gtav-src/tools_ng/techart/dcc/motionbuilder2014/python/RS/Utils/Namespace.py
T
2025-09-29 00:52:08 +02:00

474 lines
17 KiB
Python
Executable File

"""
Usage:
This module should only contain functionality related to working and preforming operations by namespace.
Author:
Ross George
"""
import re
import unbind
import pyfbsdk as mobu
from RS import Globals
from RS.Utils.Scene import Component
def Delete(targetNamespaces, deleteNamespaces=False, additionalComponents=()):
"""
Deletes everything inside the passed namespace (if it exists).
The MoBu version has a tendency to crash
Arguments:
targetNamespaces(list[string, etc.]): FBNamespace or the name of the namespace whose contents
should be deleted
deleteNamespace (boolean): Delete the namespace along with it's contents
additionalComponents (list): other components to include in the delete
"""
if not isinstance(targetNamespaces, (list, tuple, set)):
targetNamespaces = [targetNamespaces]
components = []
components.extend(additionalComponents)
for namespace in targetNamespaces:
if isinstance(namespace, basestring) and not Globals.Scene.NamespaceExist(namespace):
continue
elif isinstance(namespace, mobu.FBNamespace) and Component.IsDestroyed(namespace):
continue
elif isinstance(namespace, mobu.FBNamespace):
fbnamespace = namespace
namespace = namespace.LongName
elif isinstance(namespace, basestring):
fbnamespace = Globals.Scene.NamespaceGet(namespace)
for constraints in GetContentsByType(fbnamespace, mobu.FBConstraint):
constraints.Active = False
# Get all components under the namespace
if deleteNamespaces:
Globals.Scene.NamespaceDelete(namespace)
if not Component.IsDestroyed(fbnamespace) and not fbnamespace.HasObjectFlags(mobu.FBObjectFlag.kFBFlagDeletable):
fbnamespace.EnableObjectFlags(mobu.FBObjectFlag.kFBFlagDeletable)
fbnamespace.FBDelete()
Globals.Scene.NamespaceCleanup()
else:
Globals.Scene.NamespaceDeleteContent(namespace)
if additionalComponents:
Component.Delete(components, deleteNamespaces=deleteNamespaces)
def Rename(targetNamespace, newNamespace):
"""
Renames a namespace.
If the target namespace does not exist or the target
Arguments:
targetNamespace (string): name of the old namespace
newNamespace (string): new name to give to the old namespace
Return:
bool
"""
# get namespace components
targetFBNamespace = GetFBNamespace(targetNamespace)
if targetFBNamespace is None or targetNamespace == newNamespace:
return False
# There is a bug in mobu where setting the namespace of the same object with different casing does not
# get picked up. The work around is to rename the object something else first.
elif targetNamespace.lower() == newNamespace.lower():
targetFBNamespace.LongName = "{}__temp__".format(targetNamespace)
# now set the final correct namespace
targetFBNamespace.LongName = newNamespace
return targetFBNamespace.LongName == newNamespace
def Merge(targetNamespace, sourceNamespaces):
"""
Merges the contents of the source namespaces into the target namespace.
This will skip any components that already share the same name & type.
The source namespaces are then deleted along with their remaining components.
Arguments:
targetNamespace (pyfbsdk.FBNamespace): namespace to merge other namespaces into
sourceNamespaces (list): list of namespaces whose contents should be merged
"""
if isinstance(targetNamespace, basestring):
targetNamespace = Globals.Scene.NamespaceGet(targetNamespace)
if not isinstance(sourceNamespaces, (list, tuple)):
sourceNamespaces = [sourceNamespaces]
if not targetNamespace:
return False
# Map the contents of the namespace by their names
contents = {}
for content in GetContents(targetNamespace):
types = contents.setdefault(content.Name, [])
types.append(content)
deleteNamespaces = []
for sourceNamespace in sourceNamespaces:
if isinstance(sourceNamespace, basestring):
sourceNamespace = Globals.Scene.NamespaceGet(sourceNamespace)
if not sourceNamespace or sourceNamespace == targetNamespace or Component.IsDestroyed(sourceNamespace):
continue
Add(targetNamespace, GetAllContents(sourceNamespace), contents)
deleteNamespaces.append(sourceNamespace)
# Delete the namespaces and any extra components that couldn't be shared
Delete(deleteNamespaces, deleteNamespaces=True)
return True
def Add(namespace, components, contents=None, flat=True):
"""
Adds components to a namespace.
Prevents duplicates objects to be added in the namespace.
Arguments:
namespace (pyfbsdk.FBNamespace): namespace to add objects to
components (list): list of FBComponents to add to the namespace
contents (dictionary): dictionary of the names of contents of the namespace. Keys are the name and the values
are objects of different types that share the same name.
flat (boolean): ignore nested namespaces and add all objects to the top namespace when True.
"""
if contents is None:
contents = {}
for content in GetContents(namespace):
types = contents.setdefault(content.Name, [])
types.append(content)
for component in components:
if component is None or Component.IsDestroyed(component):
continue
elif isinstance(component, mobu.FBNamespace) and flat:
continue
sourceNamespace = ""
if ":" in component.LongName:
sourceNamespace = GetNamespace(component)
# Components of different types can have the same name
# here we attempt to check if any component of the same types with the same name exist
types = contents.get(component.Name, [])
if not types or not any([isinstance(content, type(component)) for content in types]):
component.ProcessObjectNamespace(mobu.FBNamespaceAction.kFBReplaceNamespace,
sourceNamespace,
namespace.LongName
)
def LiteRename(targetNamespace, newNamespace):
"""
Deprecated
Arguments:
targetNamespace (string): name of the old namespace
newNamespace (string): new name to give to the old namespace
"""
Rename(targetNamespace, newNamespace)
def GetComponentMatchList(SourceNamespace,
TargetNamespace,
TypeList=[mobu.FBPlug],
DerivedTypes=True,
IncludeRegex=None,
ExcludeRegex=None,
TypeMatch=True):
"""
Returns a list of component matches between components from the source and target namespaces.
Matches are based off the name of the component and type. Exact type matching can be turned off.
Keyword Arguments:
Namespace:
The namespace to search in. None will search all components.
TypeList:
The type to match against.
DerivedTypes:
True if we are also interest in the derived types of the types in the TypeList
IncludeRegex:
Regular expression to match to for inclusion.
ExcludeRegex:
Regular expression to match to for exclusion.
Returns:
A list of ComponentMatchObjects which pairs matched components together.
"""
# Get all the components in the namespaces
sourceNamespaceComponents = Component.GetComponents(SourceNamespace,
TypeList,
DerivedTypes,
IncludeRegex,
ExcludeRegex)
targetNamespaceComponents = Component.GetComponents(TargetNamespace,
TypeList,
DerivedTypes,
IncludeRegex,
ExcludeRegex)
return Component.GetComponentMatchList(sourceNamespaceComponents,
targetNamespaceComponents,
TypeMatch)
def CopyData(SourceNamespace,
TargetNamespace,
TypeList=[mobu.FBPlug],
DerivedTypes=True,
IncludeRegex=None,
ExcludeRegex=None,
TypeMatch=True):
"""
Copies all property data
"""
# Get component matches between the namespaces
componentMatchList = GetComponentMatchList(SourceNamespace,
TargetNamespace,
TypeList,
DerivedTypes,
IncludeRegex,
ExcludeRegex,
TypeMatch
)
for componentMatch in componentMatchList:
# Protect against pose mismatch error
# Copying data between poses crashes mobu sometimes
componentMatch.CopyData()
def CopyAnimationData(SourceNamespace,
TargetNamespace,
TypeList=[mobu.FBPlug],
DerivedTypes=True,
IncludeRegex=None,
ExcludeRegex=None,
TypeMatch=True):
"""
Copies all the animation data from one namespace to another on all takes and layers
"""
# Get component matches between the namespaces
componentMatchList = GetComponentMatchList(SourceNamespace,
TargetNamespace,
TypeList,
DerivedTypes,
IncludeRegex,
ExcludeRegex,
TypeMatch
)
# Record the current take so we can return state
initialTake = Globals.System.CurrentTake
# Then we'll go through each of the takes
for take in Globals.Scene.Takes:
Globals.System.CurrentTake = take
# Get initial layer so we can return state
initialLayerIndex = take.GetCurrentLayer()
# Then each layer
for layerIndex in range( take.GetLayerCount() ):
take.SetCurrentLayer(layerIndex)
# Go through each of our component matches and excute a copy
for componentMatch in componentMatchList:
componentMatch.CopyAnimationData()
# Go back to previous layer
take.SetCurrentLayer(initialLayerIndex)
# Go back to previous take
Globals.System.CurrentTake = initialTake
class StateLocker(object):
"""
Locks a namespace ensuring that nothing can get added and nothing can get removed from it.
Experimental - WIP.
"""
def __init__(self):
self._namespaceMonitorSet = set()
self._componentRenameDict = dict()
self._revertAction = False
def _OnConnectionStateNotify(self, source, event):
if not self._revertAction:
if event.Action is mobu.FBConnectionAction.kFBPrefixRename:
nameSplit = event.Plug.LongName.rsplit(':',1)
if nameSplit > 1:
namespace = nameSplit[0]
if namespace in self._namespaceMonitorSet:
self._componentRenameDict[event.Plug] = event.Plug.LongName
return
if event.Action is mobu.FBConnectionAction.kFBPrefixRenamed:
# Check if we should revert the rename action because the object was previously in a protected namespace
if event.Plug in self._componentRenameDict:
self._revertAction = True
event.Plug.LongName = self._componentRenameDict[event.Plug]
del self._componentRenameDict[event.Plug]
self._revertAction = False
return
# Check if we should revert the rename action because the object was has just been put into a
# protected namespace
nameSplit = event.Plug.LongName.rsplit(':',1)
if nameSplit > 1:
namespace = nameSplit[0]
if namespace in self._namespaceMonitorSet:
self._revertAction = True
objectName = nameSplit[1]
event.Plug.LongName = objectName
self._revertAction = False
return
def Deactivate(self):
"""
Deactivates the namespace state locker.
"""
Globals.System.OnConnectionStateNotify.Remove(self._OnConnectionStateNotify)
def Activate(self):
"""
Activates the namespace state locker.
"""
Globals.System.OnConnectionStateNotify.Add(self._OnConnectionStateNotify)
def Lock(self, namespace):
"""
Locks the passed namespace.
"""
self._namespaceMonitorSet.add(namespace)
def Unlock(self, namespace):
"""
Unlocks the passed namespace.
"""
self._namespaceMonitorSet.remove(namespace)
StateLocker = StateLocker()
def GetNamespace(character):
"""
Gets the Namespace of the provided character
Arguments:
character (pyfbsdk.FBComponent): The character whose namespace you want
Return:
string
"""
if character is not None:
return ":".join(character.LongName.split(":")[:-1])
def GetFBNamespace(name):
return Globals.Scene.NamespaceGet(name)
def GetContents(namespace):
"""
Gets all the contents of a namespace
Arguments:
namespace (pyfbsdk.FBNamespace): namespace to use
Return:
list
"""
if namespace is None or Component.IsDestroyed(namespace):
return []
contentList = mobu.FBComponentList()
namespace.GetContentList(contentList)
return contentList
def GetContentsByType(namespace, objectType=None, exactType=True):
"""
Gets the contents of a namespace that are of an exact type
Arguments:
namespace (pyfbsdk.FBNamespace): namespace to use
objectType (pyfbsdk.FBComponent): class to filter contents by
exactType (boolean): search for exact type
Return:
list
"""
if namespace is None or Component.IsDestroyed(namespace):
return []
componentList = mobu.FBComponentList()
namespace.GetContentList(componentList, mobu.FBPlugModificationFlag.kFBPlugAllContent, True, objectType.TypeInfo, exactType)
return list(componentList)
def GetAllContents(namespace, depth=0):
"""
Gets all the contents of a namespace and any child namespace it may contain
Arguments:
namespace (pyfbsdk.FBNamespace): namespace to use
depth (int): depth of recursion
Return:
list
"""
children = []
if namespace is not None:
for index in xrange(namespace.GetContentCount()):
component = namespace.GetContent(index)
if isinstance(component, mobu.FBNamespace):
children[0:0] = GetAllContents(component, depth=depth + 1)
children.append(component)
return children
def FindComponentInNamespace(namespace, name=None, ignoreCase=False, objectType=None, exactType=True):
"""
Finds a component within the namespace
Arguments:
namespace (pyfbsdk.FBNamespace): namespace to look under
name (string): name of the object to find
ignoreCase (boolean): do a case insensitive search
Return:
pyfbsdk.FBComponent or None
"""
if name is not None and exactType is None:
for component in GetContents(namespace):
if re.match(name, component.Name, (0, re.I)[ignoreCase]):
return component
elif name is not None and objectType:
for component in GetContentsByType(namespace, objectType=objectType, exactType=exactType):
if re.match(name, component.Name, (0, re.I)[ignoreCase]):
return component
def RemoveNamespaceByType(namespaceString, componentType=None):
"""
strip namespace from any poses found url:bugstar:4494564
"""
#
namespace = GetFBNamespace(namespaceString)
if namespace is None or Component.IsDestroyed(namespace):
return []
namespaceContent = GetContentsByType(namespace, componentType, exactType=True)
for content in namespaceContent:
content.ProcessObjectNamespace(mobu.FBNamespaceAction.kFBRemoveAllNamespace, '', '')