def assign_look_by_version(nodes, version_id): """Assign nodes a specific published look version by id. This assumes the nodes correspond with the asset. Args: nodes(list): nodes to assign look to version_id (bson.ObjectId): database id of the version Returns: None """ # Get representations of shader file and relationships look_representation = io.find_one({ "type": "representation", "parent": version_id, "name": "ma" }) json_representation = io.find_one({ "type": "representation", "parent": version_id, "name": "json" }) # See if representation is already loaded, if so reuse it. host = api.registered_host() representation_id = str(look_representation['_id']) for container in host.ls(): if (container['loader'] == "LookLoader" and container['representation'] == representation_id): log.info("Reusing loaded look ..") container_node = container['objectName'] break else: log.info("Using look for the first time ..") # Load file loaders = api.loaders_from_representation(api.discover(api.Loader), representation_id) Loader = next((i for i in loaders if i.__name__ == "LookLoader"), None) if Loader is None: raise RuntimeError("Could not find LookLoader, this is a bug") # Reference the look file with maya.maintained_selection(): container_node = pipeline.load(Loader, look_representation) # Get container members shader_nodes = cmds.sets(container_node, query=True) # Load relationships shader_relation = api.get_representation_path(json_representation) with open(shader_relation, "r") as f: relationships = json.load(f) # Assign relationships apply_shaders(relationships, shader_nodes, nodes)
def load_look(look, overload=False): """Load look subset if it's not been loaded """ representation = io.find_one({ "type": "representation", "parent": look["versionId"], "name": "LookDev" }) representation_id = str(representation["_id"]) is_loaded = False for container in lib.lsAttrs({ "id": AVALON_CONTAINER_ID, "loader": "LookLoader", "representation": representation_id }): if overload: is_loaded = True log.info("Overload look ..") break log.info("Reusing loaded look ..") return parse_container(container) if not is_loaded: # Not loaded log.info("Using look for the first time ..") loaders = api.loaders_from_representation(api.discover(api.Loader), representation_id) Loader = next((i for i in loaders if i.__name__ == "LookLoader"), None) if Loader is None: raise RuntimeError("Could not find LookLoader, this is a bug") container = api.load(Loader, representation, options={"overload": overload}) return container
def load(self, context, name, namespace, options): from avalon import api, pipeline from avalon.unreal import lib from avalon.unreal import pipeline as unreal_pipeline import unreal # Create directory for asset and avalon container root = "/Game/Avalon/Assets" asset = context.get('asset').get('name') suffix = "_CON" tools = unreal.AssetToolsHelpers().get_asset_tools() asset_dir, container_name = tools.create_unique_asset_name( "{}/{}".format(root, asset), suffix="") container_name += suffix unreal.EditorAssetLibrary.make_directory(asset_dir) libpath = self.fname with open(libpath, "r") as fp: data = json.load(fp) all_loaders = api.discover(api.Loader) for element in data: reference = element.get('_id') loaders = api.loaders_from_representation(all_loaders, reference) loader = None for l in loaders: if l.__name__ == "AnimationFBXLoader": loader = l break if not loader: continue instance_name = element.get('instance_name') api.load(loader, reference, namespace=instance_name, options=element) # Create Asset Container lib.create_avalon_container(container=container_name, path=asset_dir) data = { "schema": "openpype:container-2.0", "id": pipeline.AVALON_CONTAINER_ID, "asset": asset, "namespace": asset_dir, "container_name": container_name, "loader": str(self.__class__.__name__), "representation": context["representation"]["_id"], "parent": context["representation"]["parent"], "family": context["representation"]["context"]["family"] } unreal_pipeline.imprint("{}/{}".format(asset_dir, container_name), data) asset_content = unreal.EditorAssetLibrary.list_assets( asset_dir, recursive=True, include_folder=True) return asset_content
def load_package(filepath, name, namespace=None): """Load a package that was gathered elsewhere. A package is a group of published instances, possibly with additional data in a hierarchy. """ if namespace is None: # Define a unique namespace for the package namespace = os.path.basename(filepath).split(".")[0] unique_namespace(namespace) assert isinstance(namespace, basestring) # Load the setdress package data with open(filepath, "r") as fp: data = json.load(fp) # Load the setdress alembic hierarchy # We import this into the namespace in which we'll load the package's # instances into afterwards. alembic = filepath.replace(".json", ".abc") hierarchy = cmds.file(alembic, reference=True, namespace=namespace, returnNewNodes=True, groupReference=True, groupName="{}:{}".format(namespace, name), typ="Alembic") # Get the top root node (the reference group) root = "{}:{}".format(namespace, name) containers = [] all_loaders = api.discover(api.Loader) for representation_id, instances in data.items(): # Find the compatible loaders loaders = api.loaders_from_representation(all_loaders, representation_id) for instance in instances: container = _add(instance=instance, representation_id=representation_id, loaders=loaders, namespace=namespace, root=root) containers.append(container) # TODO: Do we want to cripple? Or do we want to add a 'parent' parameter? # Cripple the original avalon containers so they don't show up in the # manager # for container in containers: # cmds.setAttr("%s.id" % container, # "colorbleed.setdress.container", # type="string") # TODO: Lock all loaded nodes # This is to ensure the hierarchy remains unaltered by the artists # for node in nodes: # cmds.lockNode(node, lock=True) return containers + hierarchy
def update_scene(set_container, containers, current_data, new_data, new_file): """Updates the hierarchy, assets and their matrix Updates the following withing the scene: * Setdress hierarchy alembic * Matrix * Parenting * Representations It removes any assets which are not present in the new build data Args: set_container (dict): the setdress container of the scene containers (list): the list of containers under the setdress container current_data (dict): the current build data of the setdress new_data (dict): the new build data of the setdres Returns: processed_containers (list): all new and updated containers """ from colorbleed.maya.lib import DEFAULT_MATRIX, get_container_transforms set_namespace = set_container['namespace'] # Update the setdress hierarchy alembic set_root = get_container_transforms(set_container, root=True) set_hierarchy_root = cmds.listRelatives(set_root, fullPath=True)[0] set_hierarchy_reference = cmds.referenceQuery(set_hierarchy_root, referenceNode=True) new_alembic = new_file.replace(".json", ".abc") assert os.path.exists(new_alembic), "%s does not exist." % new_alembic with unlocked(cmds.listRelatives(set_root, ad=True, fullPath=True)): cmds.file(new_alembic, loadReference=set_hierarchy_reference, type="Alembic") identity = DEFAULT_MATRIX[:] processed_namespaces = set() processed_containers = list() new_lookup = _instances_by_namespace(new_data) old_lookup = _instances_by_namespace(current_data) for container in containers: container_ns = container['namespace'] # Consider it processed here, even it it fails we want to store that # the namespace was already available. processed_namespaces.add(container_ns) processed_containers.append(container['objectName']) if container_ns in new_lookup: root = get_container_transforms(container, root=True) if not root: log.error("Can't find root for %s", container['objectName']) continue old_instance = old_lookup.get(container_ns, {}) new_instance = new_lookup[container_ns] # Update the matrix # check matrix against old_data matrix to find local overrides current_matrix = cmds.xform(root, query=True, matrix=True, objectSpace=True) original_matrix = old_instance.get("matrix", identity) has_matrix_override = not matrix_equals(current_matrix, original_matrix) if has_matrix_override: log.warning("Matrix override preserved on %s", container_ns) else: new_matrix = new_instance.get("matrix", identity) cmds.xform(root, matrix=new_matrix, objectSpace=True) # Update the parenting if old_instance.get("parent", None) != new_instance["parent"]: parent = to_namespace(new_instance['parent'], set_namespace) if not cmds.objExists(parent): log.error("Can't find parent %s", parent) continue # Set the new parent cmds.lockNode(root, lock=False) root = cmds.parent(root, parent, relative=True) cmds.lockNode(root, lock=True) # Update the representation representation_current = container['representation'] representation_old = old_instance['representation'] representation_new = new_instance['representation'] has_representation_override = (representation_current != representation_old) if representation_new != representation_current: if has_representation_override: log.warning( "Your scene had local representation " "overrides within the set. New " "representations not loaded for %s.", container_ns) continue # We check it against the current 'loader' in the scene instead # of the original data of the package that was loaded because # an Artist might have made scene local overrides if new_instance['loader'] != container['loader']: log.warning( "Loader is switched - local edits will be " "lost. Removing: %s", container_ns) # Remove this from the "has been processed" list so it's # considered as new element and added afterwards. processed_containers.pop() processed_namespaces.remove(container_ns) api.remove(container) continue # Check whether the conversion can be done by the Loader. # They *must* use the same asset, subset and Loader for # `api.update` to make sense. old = io.find_one({"_id": io.ObjectId(representation_current)}) new = io.find_one({"_id": io.ObjectId(representation_new)}) is_valid = compare_representations(old=old, new=new) if not is_valid: log.error("Skipping: %s. See log for details.", container_ns) continue new_version = new["context"]["version"] api.update(container, version=new_version) else: # Remove this container because it's not in the new data log.warning("Removing content: %s", container_ns) api.remove(container) # Add new assets all_loaders = api.discover(api.Loader) for representation_id, instances in new_data.items(): # Find the compatible loaders loaders = api.loaders_from_representation(all_loaders, representation_id) for instance in instances: # Already processed in update functionality if instance['namespace'] in processed_namespaces: continue container = _add(instance=instance, representation_id=representation_id, loaders=loaders, namespace=set_container['namespace'], root=set_root) # Add to the setdress container cmds.sets(container, addElement=set_container['objectName']) processed_containers.append(container) return processed_containers
def _process(self, libpath, layout_container, container_name, representation, actions, parent): with open(libpath, "r") as fp: data = json.load(fp) scene = bpy.context.scene layout_collection = bpy.data.collections.new(container_name) scene.collection.children.link(layout_collection) all_loaders = api.discover(api.Loader) avalon_container = bpy.data.collections.get( blender.pipeline.AVALON_CONTAINERS) for element in data: reference = element.get('reference') family = element.get('family') loaders = api.loaders_from_representation(all_loaders, reference) loader = self._get_loader(loaders, family) if not loader: continue instance_name = element.get('instance_name') element_container = api.load(loader, reference, namespace=instance_name) if not element_container: continue avalon_container.children.unlink(element_container) layout_container.children.link(element_container) element_metadata = element_container.get( blender.pipeline.AVALON_PROPERTY) # Unlink the object's collection from the scene collection and # link it in the layout collection element_collection = element_metadata.get('obj_container') scene.collection.children.unlink(element_collection) layout_collection.children.link(element_collection) objects = element_metadata.get('objects') element_metadata['instance_name'] = instance_name objects_to_transform = [] creator_plugin = get_creator_by_name(self.animation_creator_name) if not creator_plugin: raise ValueError("Creator plugin \"{}\" was not found.".format( self.animation_creator_name)) if family == 'rig': for o in objects: if o.type == 'ARMATURE': objects_to_transform.append(o) # Create an animation subset for each rig o.select_set(True) asset = api.Session["AVALON_ASSET"] c = api.create(creator_plugin, name="animation_" + element_collection.name, asset=asset, options={"useSelection": True}, data={"dependencies": representation}) scene.collection.children.unlink(c) parent.children.link(c) o.select_set(False) break elif family == 'model': objects_to_transform = objects for o in objects_to_transform: self.set_transform(o, element.get('transform')) if actions: if o.type == 'ARMATURE': action = actions.get(instance_name, None) if action: if o.animation_data is None: o.animation_data_create() o.animation_data.action = action return layout_collection