def check_cacheable_attr(): """checks if there is at least one cacheable attr """ root_nodes = auxiliary.get_root_nodes() root_node_has_cacheable_attr = any([ root_node.hasAttr('cacheable') and root_node.getAttr('cacheable') != '' and root_node.getAttr('cacheable') is not None for root_node in root_nodes ]) if not root_node_has_cacheable_attr: # check children nodes for root_node in root_nodes: child_has_cacheable = False for child_node in root_node.listRelatives(c=1): if child_node.hasAttr('cacheable') \ and child_node.getAttr('cacheable') != '' \ and child_node.getAttr('cacheable') is not None: child_has_cacheable = True break if not child_has_cacheable: raise PublishError( 'Please add <b>cacheable</b> attribute and set it to a ' '<b>proper name</b>!')
def get_local_root_nodes(cls): """returns the root nodes that are not referenced """ return [ node for node in auxiliary.get_root_nodes() if node.referenceFile() is None ]
def check_cacheable_attr(): """checks if there is at least one cacheable attr """ if not any([root_node.hasAttr('cacheable') and root_node.getAttr('cacheable') != '' and root_node.getAttr('cacheable') is not None for root_node in auxiliary.get_root_nodes()]): raise PublishError( 'Please add <b>cacheable</b> attribute and set it to a ' '<b>proper name</b>!' )
def check_cacheable_attr(): """checks if there is at least one cacheable attr """ if not any([ root_node.hasAttr('cacheable') and root_node.getAttr('cacheable') != '' and root_node.getAttr('cacheable') is not None for root_node in auxiliary.get_root_nodes() ]): raise PublishError( 'Please add <b>cacheable</b> attribute and set it to a ' '<b>proper name</b>!')
def check_if_root_nodes_have_no_transformation(): """checks if transform nodes directly under world have 0 transformations """ root_transform_nodes = auxiliary.get_root_nodes() non_freezed_root_nodes = [] for node in root_transform_nodes: t = node.t.get() r = node.r.get() s = node.s.get() if t.x != 0 or t.y != 0 or t.z != 0 \ or r.x != 0 or r.y != 0 or r.z != 0 \ or s.x != 1 or s.y != 1 or s.z != 1: non_freezed_root_nodes.append(node) if len(non_freezed_root_nodes): pm.select(non_freezed_root_nodes) raise PublishError( 'Please freeze the following node transformations:\n\n%s' % '\n'.join( map(lambda x: x.name(), non_freezed_root_nodes[:MAX_NODE_DISPLAY])))
def check_if_root_nodes_have_no_transformation(): """checks if transform nodes directly under world have 0 transformations """ root_transform_nodes = auxiliary.get_root_nodes() non_freezed_root_nodes = [] for node in root_transform_nodes: t = node.t.get() r = node.r.get() s = node.s.get() if t.x != 0 or t.y != 0 or t.z != 0 \ or r.x != 0 or r.y != 0 or r.z != 0 \ or s.x != 1 or s.y != 1 or s.z != 1: non_freezed_root_nodes.append(node) if len(non_freezed_root_nodes): pm.select(non_freezed_root_nodes) raise PublishError( 'Please freeze the following node transformations:\n\n%s' % '\n'.join( map(lambda x: x.name(), non_freezed_root_nodes[:MAX_NODE_DISPLAY]) ) )
def generate_ass(self): """generates the ASS representation of the current scene For Model Tasks the ASS is generated over the LookDev Task because it is not possible to assign a material to an object inside an ASS file. """ # before doing anything, check if this is a look dev task # and export the objects from the referenced files with their current # shadings, then replace all of the references to ASS repr and than # add Stand-in nodes and parent them under the referenced models # load necessary plugins pm.loadPlugin('mtoa') # disable "show plugin shapes" active_panel = auxiliary.Playblaster.get_active_panel() show_plugin_shapes = pm.modelEditor(active_panel, q=1, pluginShapes=1) pm.modelEditor(active_panel, e=1, pluginShapes=False) # validate the version first self.version = self._validate_version(self.version) self.open_version(self.version) task = self.version.task # export_command = 'arnoldExportAss -f "%(path)s" -s -mask 24 ' \ # '-lightLinks 0 -compressed -boundingBox ' \ # '-shadowLinks 0 -cam perspShape;' export_command = 'arnoldExportAss -f "%(path)s" -s -mask 60' \ '-lightLinks 1 -compressed -boundingBox ' \ '-shadowLinks 1 -cam perspShape;' # calculate output path output_path = \ os.path.join(self.version.absolute_path, 'Outputs/ass/')\ .replace('\\', '/') # check if all references have an ASS repr first refs_with_no_ass_repr = [] for ref in pm.listReferences(): if ref.version and not ref.has_repr('ASS'): refs_with_no_ass_repr.append(ref) if len(refs_with_no_ass_repr): raise RuntimeError( 'Please generate the ASS Representation of the references ' 'first!!!\n%s' % '\n'.join(map(lambda x: str(x.path), refs_with_no_ass_repr)) ) if self.is_look_dev_task(task): # in look dev files, we export the ASS files directly from the Base # version and parent the resulting Stand-In node to the parent of # the child node # load only Model references for ref in pm.listReferences(): v = ref.version load_ref = False if v: ref_task = v.task if self.is_model_task(ref_task): load_ref = True if load_ref: ref.load() ref.importContents() # Make all texture paths relative # replace all "$REPO#" from all texture paths first # # This is needed to properly render textures with any OS types_and_attrs = { 'aiImage': 'filename', 'file': 'fileTextureName', 'imagePlane': 'imageName' } for node_type in types_and_attrs.keys(): attr_name = types_and_attrs[node_type] for node in pm.ls(type=node_type): orig_path = node.getAttr(attr_name).replace("\\", "/") path = re.sub( r'(\$REPO[0-9/]+)', '', orig_path ) tx_path = self.make_tx(path) inputs = node.attr(attr_name).inputs(p=1) if len(inputs): # set the input attribute for input_node_attr in inputs: input_node_attr.set(tx_path) else: node.setAttr(attr_name, tx_path) # randomize all render node names # This is needed to prevent clashing of materials in a bigger scene for node in pm.ls(type=RENDER_RELATED_NODE_TYPES): if node.referenceFile() is None and \ node.name() not in READ_ONLY_NODE_NAMES: node.rename('%s_%s' % (node.name(), uuid.uuid4().hex)) nodes_to_ass_files = {} # export all root ass files as they are for root_node in auxiliary.get_root_nodes(): for child_node in root_node.getChildren(): # check if it is a transform node if not isinstance(child_node, pm.nt.Transform): continue if not auxiliary.has_shape(child_node): continue # randomize child node name # TODO: This is not working as intended, node names are like |NS:node1|NS:node2 # resulting a child_node_name as "node2" child_node_name = child_node\ .fullPath()\ .replace('|', '_')\ .split(':')[-1] child_node_full_path = child_node.fullPath() pm.select(child_node) child_node.rename('%s_%s' % (child_node.name(), uuid.uuid4().hex)) output_filename =\ '%s_%s.ass' % ( self.version.nice_name, child_node_name ) output_full_path = \ os.path.join(output_path, output_filename) # run the mel command pm.mel.eval( export_command % { 'path': output_full_path.replace('\\', '/') } ) nodes_to_ass_files[child_node_full_path] = \ '%s.gz' % output_full_path # print('%s -> %s' % ( # child_node_full_path, # output_full_path) # ) # reload the scene pm.newFile(force=True) self.open_version(self.version) # convert all references to ASS # we are doing it a little bit early here, but we need to for ref in pm.listReferences(): ref.to_repr('ASS') all_stand_ins = pm.ls(type='aiStandIn') for ass_node in all_stand_ins: ass_tra = ass_node.getParent() full_path = ass_tra.fullPath() if full_path in nodes_to_ass_files: ass_file_path = \ Repository.to_os_independent_path( nodes_to_ass_files[full_path] ) ass_node.setAttr('dso', ass_file_path) elif self.is_vegetation_task(task): # in vegetation files, we export the ASS files directly from the # Base version, also we use the geometry under "pfxPolygons" # and parent the resulting Stand-In nodes to the # pfxPolygons # load all references for ref in pm.listReferences(): ref.load() # Make all texture paths relative # replace all "$REPO#" from all texture paths first # # This is needed to properly render textures with any OS types_and_attrs = { 'aiImage': 'filename', 'file': 'fileTextureName', 'imagePlane': 'imageName' } for node_type in types_and_attrs.keys(): attr_name = types_and_attrs[node_type] for node in pm.ls(type=node_type): orig_path = node.getAttr(attr_name).replace("\\", "/") path = re.sub( r'(\$REPO[0-9/]+)', '', orig_path ) tx_path = self.make_tx(path) inputs = node.attr(attr_name).inputs(p=1) if len(inputs): # set the input attribute for input_node_attr in inputs: input_node_attr.set(tx_path) else: node.setAttr(attr_name, tx_path) # randomize all render node names # This is needed to prevent clashing of materials in a bigger scene for node in pm.ls(type=RENDER_RELATED_NODE_TYPES): if node.referenceFile() is None and \ node.name() not in READ_ONLY_NODE_NAMES: node.rename('%s_%s' % (node.name(), uuid.uuid4().hex)) # find the _pfxPolygons node pfx_polygons_node = pm.PyNode('kks___vegetation_pfxPolygons') for node in pfx_polygons_node.getChildren(): for child_node in node.getChildren(): #print('processing %s' % child_node.name()) child_node_name = child_node.name().split('___')[-1] pm.select(child_node) output_filename =\ '%s_%s.ass' % ( self.version.nice_name, child_node_name.replace(':', '_').replace('|', '_') ) output_full_path = \ os.path.join(output_path, output_filename) # run the mel command pm.mel.eval( export_command % { 'path': output_full_path.replace('\\', '/') } ) # generate an aiStandIn node and set the path ass_node = auxiliary.create_arnold_stand_in( path='%s.gz' % output_full_path ) ass_tra = ass_node.getParent() # parent the ass node under the current node # under pfx_polygons_node pm.parent(ass_tra, node) # set pivots rp = pm.xform(child_node, q=1, ws=1, rp=1) sp = pm.xform(child_node, q=1, ws=1, sp=1) # rpt = child_node.getRotatePivotTranslation() pm.xform(ass_node, ws=1, rp=rp) pm.xform(ass_node, ws=1, sp=sp) # ass_node.setRotatePivotTranslation(rpt) # delete the child_node pm.delete(child_node) # give it the same name with the original ass_tra.rename('%s' % child_node_name) # clean up other nodes pm.delete('kks___vegetation_pfxStrokes') pm.delete('kks___vegetation_paintableGeos') elif self.is_model_task(task): # convert all children of the root node # to an empty aiStandIn node # and save it as it is root_nodes = self.get_local_root_nodes() for root_node in root_nodes: for child_node in root_node.getChildren(): child_node_name = child_node.name() rp = pm.xform(child_node, q=1, ws=1, rp=1) sp = pm.xform(child_node, q=1, ws=1, sp=1) pm.delete(child_node) ass_node = auxiliary.create_arnold_stand_in(path='') ass_tra = ass_node.getParent() pm.parent(ass_tra, root_node) ass_tra.rename(child_node_name) # set pivots pm.xform(ass_tra, ws=1, rp=rp) pm.xform(ass_tra, ws=1, sp=sp) # because there will be possible material assignments # in look dev disable overrideShaders ass_node.setAttr('overrideShaders', False) # we definitely do not use light linking in our studio, # which seems to create more problems then it solves. ass_node.setAttr('overrideLightLinking', False) # convert all references to ASS for ref in pm.listReferences(): ref.to_repr('ASS') ref.load() # fix an arnold bug for node_name in ['initialShadingGroup', 'initialParticleSE']: node = pm.PyNode(node_name) node.setAttr("ai_surface_shader", (0, 0, 0), type="float3") node.setAttr("ai_volume_shader", (0, 0, 0), type="float3") # if this is an Exterior/Interior -> Layout -> Hires task flatten it is_exterior_or_interior_task = self.is_exterior_or_interior_task(task) if is_exterior_or_interior_task: # and import all of the references all_refs = pm.listReferences() while len(all_refs) != 0: for ref in all_refs: if not ref.isLoaded(): ref.load() ref.importContents() all_refs = pm.listReferences() # assign lambert1 to all GPU nodes pm.sets('initialShadingGroup', e=1, fe=auxiliary.get_root_nodes()) # now remove them from the group pm.sets('initialShadingGroup', e=1, rm=pm.ls()) # and to make sure that no override is enabled [node.setAttr('overrideLightLinking', False) for node in pm.ls(type='aiStandIn')] # clean up self.clean_up() # check if all aiStandIn nodes are included in # ArnoldStandInDefaultLightSet set try: arnold_stand_in_default_light_set = \ pm.PyNode('ArnoldStandInDefaultLightSet') except pm.MayaNodeError: # just create it arnold_stand_in_default_light_set = \ pm.createNode( 'objectSet', name='ArnoldStandInDefaultLightSet' ) pm.select(None) pm.sets( arnold_stand_in_default_light_set, fe=pm.ls(type='aiStandIn') ) # save the scene as {{original_take}}___ASS # use maya take_name = '%s%s%s' % ( self.base_take_name, Representation.repr_separator, 'ASS' ) v = self.get_latest_repr_version(take_name) self.maya_env.save_as(v) # export the root nodes under the same file if is_exterior_or_interior_task: pm.select(auxiliary.get_root_nodes()) pm.exportSelected( v.absolute_full_path, type='mayaAscii', force=True ) # new scene pm.newFile(force=True) # reset show plugin shapes option active_panel = auxiliary.Playblaster.get_active_panel() pm.modelEditor(active_panel, e=1, pluginShapes=show_plugin_shapes)
def generate_gpu(self): """generates the GPU representation of the current scene """ # validate the version first self.version = self._validate_version(self.version) self.open_version(self.version) # load necessary plugins pm.loadPlugin('gpuCache') pm.loadPlugin('AbcExport') pm.loadPlugin('AbcImport') # check if all references have an GPU repr first refs_with_no_gpu_repr = [] for ref in pm.listReferences(): if ref.version and not ref.has_repr('GPU'): refs_with_no_gpu_repr.append(ref) if len(refs_with_no_gpu_repr): raise RuntimeError( 'Please generate the GPU Representation of the references ' 'first!!!\n%s' % '\n'.join(map(lambda x: str(x.path), refs_with_no_gpu_repr)) ) # unload all references for ref in pm.listReferences(): ref.unload() # for local models generate an ABC file output_path = os.path.join( self.version.absolute_path, 'Outputs/alembic/' ).replace('\\', '/') abc_command = \ 'AbcExport -j "-frameRange %(start_frame)s ' \ '%(end_frame)s ' \ '-ro -stripNamespaces ' \ '-uvWrite ' \ '-wholeFrameGeo ' \ '-worldSpace ' \ '-root |%(node)s -file %(file_path)s";' gpu_command = \ 'gpuCache -startTime %(start_frame)s ' \ '-endTime %(end_frame)s ' \ '-optimize -optimizationThreshold 40000 ' \ '-writeMaterials ' \ '-directory "%(path)s" ' \ '-fileName "%(filename)s" ' \ '%(node)s;' start_frame = end_frame = int(pm.currentTime(q=1)) if not self.is_scene_assembly_task(self.version.task): if self.is_vegetation_task(self.version.task): # in vegetation files, we export the GPU files directly from # the Base version, also we use the geometry under # "pfxPolygons" and parent the resulting Stand-In nodes to the # pfxPolygons # load all references for ref in pm.listReferences(): ref.load() # find the _pfxPolygons node pfx_polygons_node = pm.PyNode('kks___vegetation_pfxPolygons') for node in pfx_polygons_node.getChildren(): for child_node in node.getChildren(): child_node_name = child_node.name().split('___')[-1] child_node_shape = child_node.getShape() child_node_shape_name = None if child_node_shape: child_node_shape_name = child_node_shape.name() pm.select(child_node) temp_output_fullpath = \ tempfile.mktemp().replace('\\', '/') temp_output_path, temp_output_filename = \ os.path.split(temp_output_fullpath) output_filename = '%s_%s' % ( self.version.nice_name, child_node_name.split(':')[-1] .replace(':', '_') .replace('|', '_') ) # run the mel command # check if file exists pm.mel.eval( gpu_command % { 'start_frame': start_frame, 'end_frame': end_frame, 'node': child_node.fullPath(), 'path': temp_output_path, 'filename': temp_output_filename } ) cache_file_full_path = \ os.path\ .join(output_path, output_filename + '.abc')\ .replace('\\', '/') # create the intermediate directories try: os.makedirs( os.path.dirname(cache_file_full_path) ) except OSError: # directory exists pass # now move in to its place shutil.move( temp_output_fullpath + '.abc', cache_file_full_path ) # set rotate and scale pivots rp = pm.xform(child_node, q=1, ws=1, rp=1) sp = pm.xform(child_node, q=1, ws=1, sp=1) #child_node.setRotatePivotTranslation([0, 0, 0]) # delete the child and add a GPU node instead pm.delete(child_node) # check if file exists and create nodes if os.path.exists(cache_file_full_path): gpu_node = pm.createNode('gpuCache') gpu_node_tra = gpu_node.getParent() pm.parent(gpu_node_tra, node) gpu_node_tra.rename(child_node_name) if child_node_shape_name is not None: gpu_node.rename(child_node_shape_name) pm.xform(gpu_node_tra, ws=1, rp=rp) pm.xform(gpu_node_tra, ws=1, sp=sp) gpu_node.setAttr( 'cacheFileName', cache_file_full_path, type="string" ) else: print('File not found!: %s' % cache_file_full_path) # clean up other nodes pm.delete('kks___vegetation_pfxStrokes') pm.delete('kks___vegetation_paintableGeos') else: root_nodes = self.get_local_root_nodes() if len(root_nodes): for root_node in root_nodes: # export each child of each root as separate nodes for child_node in root_node.getChildren(): # check if it is a transform node if not isinstance(child_node, pm.nt.Transform): continue if not auxiliary.has_shape(child_node): continue child_name = child_node.name() child_shape = child_node.getShape() child_shape_name = None if child_shape: child_shape_name = child_shape.name() child_full_path = \ child_node.fullPath()[1:].replace('|', '_') temp_output_fullpath = \ tempfile.mktemp().replace('\\', '/') temp_output_path, temp_output_filename = \ os.path.split(temp_output_fullpath) output_filename =\ '%s_%s' % ( self.version.nice_name, child_full_path ) # run the mel command # check if file exists pm.mel.eval( gpu_command % { 'start_frame': start_frame, 'end_frame': end_frame, 'node': child_node.fullPath(), 'path': temp_output_path, 'filename': temp_output_filename } ) cache_file_full_path = \ os.path\ .join( output_path, '%s.abc' % ( output_filename ) )\ .replace('\\', '/') # create the intermediate directories try: os.makedirs( os.path.dirname(cache_file_full_path) ) except OSError: # directory exists pass # now move in to its place shutil.move( temp_output_fullpath + '.abc', cache_file_full_path ) # set rotate and scale pivots rp = pm.xform(child_node, q=1, ws=1, rp=1) sp = pm.xform(child_node, q=1, ws=1, sp=1) # rpt = child_node.getRotatePivotTranslation() # delete the child and add a GPU node instead pm.delete(child_node) # check if file exists if os.path.exists(cache_file_full_path): gpu_node = pm.createNode('gpuCache') gpu_node_tra = gpu_node.getParent() pm.parent(gpu_node_tra, root_node) gpu_node_tra.rename(child_name) if child_shape_name is not None: gpu_node.rename(child_shape_name) pm.xform(gpu_node_tra, ws=1, rp=rp) pm.xform(gpu_node_tra, ws=1, sp=sp) # child_node.setRotatePivotTranslation(rpt) gpu_node.setAttr( 'cacheFileName', cache_file_full_path, type="string" ) # load all references again # convert all references to GPU logger.debug('converting all references to GPU') for ref in pm.listReferences(): # check if this is a Model reference ref.to_repr('GPU') ref.load() # if this is an Exterior/Interior -> Layout -> Hires task flatten it task = self.version.task is_exterior_or_interior_task = self.is_exterior_or_interior_task(task) if is_exterior_or_interior_task: logger.debug('importing all references') # and import all of the references all_refs = pm.listReferences() while len(all_refs) != 0: for ref in all_refs: if not ref.isLoaded(): ref.load() ref.importContents() all_refs = pm.listReferences() # assign lambert1 to all GPU nodes pm.sets('initialShadingGroup', e=1, fe=auxiliary.get_root_nodes()) # clean up self.clean_up() # 6. save the scene as {{original_take}}___GPU # use maya take_name = '%s%s%s' % ( self.base_take_name, Representation.repr_separator, 'GPU' ) v = self.get_latest_repr_version(take_name) self.maya_env.save_as(v) # export the root nodes under the same file if is_exterior_or_interior_task: logger.debug('exporting root nodes') pm.select(auxiliary.get_root_nodes()) pm.exportSelected( v.absolute_full_path, type='mayaAscii', force=True ) logger.debug('renewing scene') # clear scene pm.newFile(force=True)
def get_local_root_nodes(cls): """returns the root nodes that are not referenced """ return [node for node in auxiliary.get_root_nodes() if node.referenceFile() is None]