def exportCurves(descName, fxmName): # setup value = xg.getAttr("exportDir", palette, descName, fxmName) xg.setAttr("exportDir", str(value), palette, descName, fxmName) xg.setAttr("exportCurves", "true", palette, descName, fxmName) # Need to fill in the export faces to correct value xg.setAttr("exportFaces", "", palette, descName, fxmName) # export clumpCurves.mel pmc.mel.xgmNullRender(descName, percent=0) # get clumpCurves.mel file path curvesMelPath = xg.getAttr("_fullExportDir", palette, descName, fxmName) # Remove clumpCurves.mel's last cmd : "xgmMakeCurvesDynamic;" print("Reading curves mel. -> %s" % curvesMelPath) with open(curvesMelPath, "r") as mel_script: curvesMel = mel_script.readlines() cmdIndex = curvesMel.index("xgmMakeCurvesDynamic;\n") curvesMel[cmdIndex] = "" # execute it, and we will run our MakeCurvesDynamic later pmc.mel.eval("".join(curvesMel)) # restore xg.setAttr("exportCurves", "false", palette, descName, fxmName) xg.setAttr("exportFaces", "", palette, descName, fxmName)
def importDescription(self, palName, descName, version, binding=False): """ XGen description will imported without validator. When importing baked description, @binding set to False should be fine. """ xdscFileName = descName + '.xdsc' xdscFile = '/'.join( [self.paletteVerDir(palName, version), descName, xdscFileName]) if not os.path.isfile(xdscFile): pm.error('[XGen Hub] : .xdsc file is not exists. -> ' + xdscFile) return None self.clearPreview() # check if descriptions exists in current scene if descName in xg.descriptions(palName): # delete current description folder descDir = xg.expandFilepath('${DESC}', descName) if os.path.isdir(descDir): try: dir_util.remove_tree(descDir) except: pm.warning('[XGen Hub] : Dir may not remove. -> ' + descDir) # delete current description xg.deleteDescription(palName, descName) # IMPORT DESCRIPTION desc = base.importDescription(palName, xdscFile) # create imported descriptions folder dataPath = xg.getAttr('xgDataPath', palName) paletteRoot = xg.expandFilepath(dataPath, '') msxgApi.setupDescriptionFolder(paletteRoot, palName, desc) # wrap into maya nodes pm.mel.xgmWrapXGen(pal=palName, d=desc, gi=binding) # bind to selected geometry if binding: igdesc = xg.getAttr('groom', palName, desc) xg.modifyFaceBinding(palName, desc, 'Append', '', False, len(igdesc)) if igdesc: # set groom density and sampling method pm.setAttr(igdesc + '.density', 1) pm.setAttr(igdesc + '.interpStyle', 1) # import grooming as well self.importGrooming(palName, descName, version) # import guides as well self.importGuides(palName, descName, version) self.notifyMsg('Description Import Complete !', 0) return desc
def importAnimResult(self, palName, version, shotName): """ """ self.clearPreview() # get delta .xgd file deltaPath = self.paletteDeltaDir(palName, version, shotName) deltaFile = '/'.join([deltaPath, palName + '.xgd']) # import if os.path.isfile(deltaFile): if not self.importPalette(palName, version, False, False, True, [deltaFile]): return None else: pm.error('[XGen Hub] : .xgd file not exists. Import stopped. -> ' + deltaFile) return None # get wires.abc wiresAbc = {} for abc in os.listdir(deltaPath): abcPath = '/'.join([deltaPath, abc]) if os.path.isfile(abcPath): descName = abc.split('.')[0] wiresAbc[descName] = str(abcPath) # animWire turn off live mode to read .abc for desc in [ desc for desc in wiresAbc if desc in xg.descriptions(palName) ]: for fxm in [ fxm for fxm in xg.fxModules(palName, desc) if xg.fxModuleType( palName, desc, fxm) == 'AnimWiresFXModule' ]: if xg.getAttr('active', palName, desc, fxm) == 'true': xg.setAttr('liveMode', 'false', palName, desc, fxm) xg.setAttr('wiresFile', wiresAbc[desc], palName, desc, fxm) # set ref frame self.setRefWiresFrame(xg.getAttr(self.xgRefFrame, palName)) # assign shaders # render settings self.xgOutputSettings(palName) self.refresh('Full') self.notifyMsg('Anim Result Import Complete !', 0) return True
def set_abs_path(self, xgen_dir): if not xgg.Maya: return # palette is collection, use palettes to get collections first. palettes = xgen.palettes() for palette in palettes: # Use descriptions to get description of each collection descriptions = xgen.descriptions(palette) for description in descriptions: commaon_objs = xgen.objects(palette, description, True) fx_objs = xgen.fxModules(palette, description) objs = commaon_objs + fx_objs # Get active objs,e.g. SplinePrimtives for obj in objs: attrs = xgen.allAttrs(palette, description, obj) for attr in attrs: value = xgen.getAttr(attr, palette, description, obj) if "${DESC}" in value: print palette, description, obj, attr description_dir = os.path.join(xgen_dir, "collections", palette, description).replace("\\", "/") new_value = value.replace("${DESC}", description_dir) xgen.setAttr(attr, new_value, palette, description, obj) de = xgg.DescriptionEditor de.refresh("Full")
def parse_map_path(map_attr): """Parse attribute returned from `filePathEditor` into file path (NOTE) Remember to refresh filePathEditor by calling `cmds.filePathEditor(refresh=True)`, or the fxmodule index might not return correctly. Args: map_attr (str): An attribute path returned from `cmds.filePathEditor` Returns: str: File path tuple: Name of the attribute and it's parent objects """ palette, description, obj, attr, index = parse_objects(map_attr) expr_maps = parse_expr_maps(attr, palette, description, obj) if not expr_maps: # Not expression type path = xg.getAttr(attr, palette, description, obj) else: path = expr_maps[index].file parents = (palette, description, obj, attr, index) return path, parents
def get_xgen(task_id): _xgen_ = [] _file_name = cmds.file(q=True, sceneName=True) _file_path = os.path.dirname(_file_name) if not xgg.Maya: return _xgen_ _all_palettes = xg.palettes() _task = zfused_api.task.Task(task_id) _task_project_entity = _task.project_entity() _task_production_path = _task.production_path() # for _palette in _all_palettes: # _palette_dict ={} # _palette_xgen_filename =cmds.getAttr('{}.xgFileName'.format(_palette)) # _palette_path =os.path.join(_file_path,_palette_xgen_filename) # _palette_date =xg.getAttr('xgDataPath',_palette) # _palette_dict['_palette_name'] = _palette # _palette_dict['_palette_path'] = _palette_path # _palette_dict['_palette_date'] = _palette_date for _palette in _all_palettes: _palette_dict = {} _palette_xgen_filename = cmds.getAttr('{}.xgFileName'.format(_palette)) _palette_path = os.path.join(_task_production_path, _palette_xgen_filename).replace( '\\', '/') _palette_date = xg.getAttr('xgDataPath', _palette) _palette_dict['_palette_name'] = _palette _palette_dict['_palette_path'] = _palette_path _palette_dict['_palette_date'] = _palette_date _xgen_.append(_palette_dict) return _xgen_
def descControlMethod(self, palName, descName): """ Find out what instance method used by description to control primitives, and return type name: 'Guides' 'Attribute' 'Groom' """ # check instance method primitive = xg.getActive(palName, descName, 'Primitive') if xg.getAttr('iMethod', palName, descName, primitive): return 'Guides' else: if xg.getAttr('groom', palName, descName): return 'Groom' else: return 'Attribute'
def current_data_paths(palette, expand=False): paths = list() for path in xg.getAttr("xgDataPath", palette).split(";"): if expand: path = xg.expandFilepath(str(path), "") path = os.path.normpath(path).replace("\\", "/") paths.append(path) return paths
def description_ctrl_method(description): """ Find out what instance method used by description to control primitives, and return type name: 'Guides' 'Attribute' 'Groom' """ palette = get_palette_by_description(description) primitive = xg.getActive(palette, description, "Primitive") if xg.getAttr("iMethod", palette, description, primitive) == "1": return "Guides" else: # iMethod == "0" if xg.getAttr("groom", palette, description): return "Groom" else: return "Attribute"
def get_groom(description): """Return description's grooming node Args: description (str): XGen Legacy description name Returns: (str) """ palette = get_palette_by_description(description) return xg.getAttr("groom", palette, description)
def get_invalid(cls, instance): import xgenm as xg invalid = list() for description in instance.data["xgenDescriptions"]: palette = xg.palette(description) percent = float( xg.getAttr("percent", palette, description, "GLRenderer")) if not percent == 100: invalid.append(description) return invalid
def attachSlot(palette, desc, fxmName, descHairSysName): if not (str(xg.fxModuleType(palette, desc, fxmName)) == "AnimWiresFXModule"): return refwFrame = xg.getAttr("refWiresFrame", palette, desc, fxmName) if str(xg.getAttr("liveMode", palette, desc, fxmName)) == "false": wiresfile = xg.getAttr("wiresFile", palette, desc, fxmName) pmc.mel.xgmFindAttachment(d=desc, f=wiresfile, fm=int(refwFrame), m=fxmName) else: curves = getHairCurves(descHairSysName) if curves: # attach wires to curves cmds.select(curves, replace=True) pmc.mel.xgmFindAttachment(d=desc, fm=int(refwFrame), m=fxmName) # print('The following curves were attached: ', # [c.name() for c in curves]) else: cmds.warning("No curves selected. Nothing to attach.")
def list_fx_modules(description, activated=None): palette = get_palette_by_description(description) modules = xg.fxModules(palette, description) if activated is not None: state = "true" if activated else "false" matched = list() for fxm in modules: if xg.getAttr("active", palette, description, fxm) == state: matched.append(fxm) return matched else: return modules
def setPreviewInCam(self, palName, valueDict= None): """doc""" if valueDict: # restore value for desc in xg.descriptions(palName): xg.setAttr('inCameraOnly', valueDict[desc], palName, desc, 'GLRenderer') return None else: # set to false if no value in valueDict = {} for desc in xg.descriptions(palName): valueDict[desc] = xg.getAttr('inCameraOnly', palName, desc, 'GLRenderer') xg.setAttr('inCameraOnly', 'false', palName, desc, 'GLRenderer') return valueDict
def attachSlot(palName, descName, fxmName, descHairSysName): """doc""" if not xg.fxModuleType(palName, descName, fxmName) == 'AnimWiresFXModule': return refwFrame = xg.getAttr('refWiresFrame', palName, descName, fxmName) if xg.getAttr('liveMode', palName, descName, fxmName) == 'false': wiresfile = xg.getAttr('wiresFile', palName, descName, fxmName) pm.mel.xgmFindAttachment(d=descName, f=wiresfile, fm=int(refwFrame), m=fxmName) else: curves = getHairCurves(descHairSysName) if curves: # attach wires to curves pm.select(curves, r=1) pm.mel.xgmFindAttachment(d=descName, fm=int(refwFrame), m=fxmName) print 'The following curves were attached: ', [ c.name() for c in curves ] else: print 'No curves selected. Nothing to attach.'
def switch_data_path(palette, data_path): """Switch xgDataPath context Args: palette (str): XGen Legacy palette name data_path (str): xgDataPath """ origin = xg.getAttr("xgDataPath", palette) data_path = data_path.replace("\\", "/") try: xg.setAttr("xgDataPath", data_path, palette) yield finally: xg.setAttr("xgDataPath", origin, palette)
def exportCurvesMel(palName, descName, fxmName): """doc""" # setup value = xg.getAttr('exportDir', palName, descName, fxmName) xg.setAttr('exportDir', str(value), palName, descName, fxmName) xg.setAttr('exportCurves', 'true', palName, descName, fxmName) # # Need to fill in the export faces to correct value # xg.setAttr('exportFaces', '', palName, descName, fxmName) # export clumpCurves.mel pm.mel.xgmNullRender(descName, percent=0) # get clumpCurves.mel file path curvesMelPath = xg.getAttr('_fullExportDir', palName, descName, fxmName) # remove clumpCurves.mel's last cmd : "xgmMakeCurvesDynamic;" print 'Reading curves mel. -> ' + curvesMelPath curvesMel = open(curvesMelPath, 'r').readlines() cmdIndex = curvesMel.index('xgmMakeCurvesDynamic;\n') curvesMel[cmdIndex] = '' # execute it, and we will run our MakeCurvesDynamic later pm.mel.eval(''.join(curvesMel)) # restore xg.setAttr('exportCurves', 'false', palName, descName, fxmName) xg.setAttr('exportFaces', '', palName, descName, fxmName)
def bake_modules(palette, description): """Bake description's modifiers which data needs to be baked This bakes NoiseFXModule and MeshCutFXModule, also set ClumpingFXModule attribute 'cvAttr' to True for AnimModifiers. Args: palette (str): XGen Legacy palette name description (str): XGen Legacy description name """ fxmod_typ = (lambda fxm: xg.fxModuleType(palette, description, fxm)) fx_modules = xg.fxModules(palette, description) previous_clump = None # (NOTE) fxModules iterate from bottom to top for fxm in fx_modules: if fxmod_typ(fxm) == "ClumpingFXModule": # set the top clumpingMod cvAttr to True, for AnimModifiers # which needs clump if previous_clump: xg.setAttr("cvAttr", "false", palette, description, previous_clump) xg.setAttr("cvAttr", "true", palette, description, fxm) previous_clump = fxm if fxmod_typ(fxm) in ("NoiseFXModule", "MeshCutFXModule"): # temporarily turn off lod so we dont bake it in lod = xg.getAttr("lodFlag", palette, description) xg.setAttr("lodFlag", "false", palette, description) # change mode for bake xg.setAttr("mode", "2", palette, description, fxm) # bake the noise cmds.xgmNullRender(description, progress=True) # restore xg.setAttr("lodFlag", lod, palette, description) # change mode to baked xg.setAttr("mode", "1", palette, description, fxm)
def parse_expr_maps(attr, palette, description, object): """Return a list of map file data from expression in an object attribute Args: attr (str): Modifier attribute name palette (str): XGen Legacy palette name description (str): XGen Legacy description name object (str): Name of an XGen object Returns: (list): A list of `ExpressionUI.MapItem` object. Object attribute: name: Expression var name file: Map file path mode: Map file mode pos: Expression var in line position """ expr = xg.getAttr(attr, palette, description, object) return _parseMapString(expr)
def xgen_preview_all(palette): """Preview all XGen Legacy primitives instead of in view only Args: palette (str): XGen Legacy palette name """ origin_value = {} for desc in xg.descriptions(palette): origin_value[desc] = xg.getAttr("inCameraOnly", palette, desc, "GLRenderer") xg.setAttr("inCameraOnly", "false", palette, desc, "GLRenderer") try: yield finally: # restore value for desc in xg.descriptions(palette): xg.setAttr("inCameraOnly", origin_value[desc], palette, desc, "GLRenderer")
def is_modifier_under_bake_manager(palette, description, modifier): """Is this modifier stack under an active bake groom manager ? Args: palette (str): XGen Legacy palette name description (str): XGen Legacy description name modifier (str): Name of an XGen modifier object Returns: (bool) """ fxmod_typ = (lambda fxm: xg.fxModuleType(palette, description, fxm)) fx_modules = xg.fxModules(palette, description) bake_found = False for fxm in reversed(fx_modules): if fxm == modifier: return bake_found if fxmod_typ(fxm) == "BakedGroomManagerFXModule": if xg.getAttr("active", palette, description, fxm) == "true": bake_found = True
def exportFullPackage(self, palName, version, bake= False, anim= False): """ Export Palettes, Descriptions, Grooming, Guides, all together, even bake modifiers befoer export if needed. """ self.clearPreview() # bake modifiers generator = {} if bake: for desc in xg.descriptions(palName): # bake Noise modifiers # fxModules evals from bottom to top clumpModLast = '' for fxm in xg.fxModules(palName, desc): if xg.fxModuleType(palName, desc, fxm) == 'ClumpingFXModule': # set the top clumpingMod cvAttr to True, for anim modifiers which needs clump if clumpModLast: xg.setAttr('cvAttr', 'false', palName, desc, clumpModLast) xg.setAttr('cvAttr', 'true', palName, desc, fxm) clumpModLast = fxm if xg.fxModuleType(palName, desc, fxm) == 'NoiseFXModule': # temporarily turn off lod so we dont bake it in lod = xg.getAttr('lodFlag', palName, desc) xg.setAttr('lodFlag', 'false', palName, desc) # change mode for bake xg.setAttr('mode', '2', palName, desc, fxm) # bake the noise pm.mel.xgmNullRender(pb= desc) # restore xg.setAttr('lodFlag', lod, palName, desc) # change mode to baked xg.setAttr('mode', '1', palName, desc, fxm) # bake groom modifiers fxm = xg.addFXModule(palName, desc, 'BakedGroomManagerFXModule') xg.setAttr('active', 'true', palName, desc, fxm) xg.bakedGroomManagerBake(palName, desc) # set Generator to XPD generator[desc] = xg.getActive(palName, desc, 'Generator') xg.setActive(palName, desc, 'FileGenerator') # change to export version path and keep current workPath = xg.getAttr('xgDataPath', palName) workProj = xg.getAttr('xgProjectPath', palName) xg.setAttr('xgDataPath', self.paletteVerDir(palName, version, raw= True), palName) xg.setAttr('xgProjectPath', self.projPath, palName) # get resolved repo path dataPath = self.paletteVerDir(palName, version) # set [xgDogTag] attr for ANIM record branchName if anim: xg.setAttr('xgDogTag', version, palName) # export descriptions for desc in xg.descriptions(palName): dstDescDir = xg.expandFilepath('${DESC}', desc, True, True) expPath = dstDescDir + desc + '.xdsc' xg.exportDescription(palName, desc, expPath) # copy map files srcDescVar = workPath.replace('${PROJECT}', workProj) + '/${DESC}' srcDescDir = xg.expandFilepath(srcDescVar, desc) for mapDir in os.listdir(srcDescDir): srcMap = os.path.join(srcDescDir, mapDir) dstMap = os.path.join(dstDescDir, mapDir) if os.path.isdir(srcMap): dir_util._path_created = {} dir_util.copy_tree(srcMap, dstMap) # export palettes expPath = dataPath + '/' + palName + '.xgen' xg.exportPalette(palName, expPath) # export grooming for desc in xg.descriptions(palName): igdesc = xg.getAttr('groom', palName, desc) if igdesc: expPath = xg.expandFilepath('${DESC}/groom', desc, True, True) tpu = 5 sampling = 1 igDescr = xg.igDescription(desc) # export Attribute Map try: pm.waitCursor(state= True) # may have .ptx file handle lock issue pm.mel.iGroom(exportMaps= expPath, texelsPerUnit= tpu, instanceMethod= sampling, description= igDescr) finally: pm.waitCursor(state= False) # export Mask try: pm.waitCursor(state= True) # may have .ptx file handle lock issue pm.mel.iGroom(exportMask= expPath, texelsPerUnit= tpu, description= igDescr) finally: pm.waitCursor(state= False) # export Region try: pm.waitCursor(state= True) # may have .ptx file handle lock issue pm.mel.iGroom(exportRegion= expPath, texelsPerUnit= tpu, description= igDescr) finally: pm.waitCursor(state= False) # export Settings jsonPath = expPath + 'groomSettings.json' groomSettings = {}.fromkeys(['density', 'length', 'width']) for key in groomSettings: groomSettings[key] = pm.getAttr(igdesc + '.' + key) with open(jsonPath, 'w') as jsonFile: json.dump(groomSettings, jsonFile, indent=4) # export guides with undoable('exportGuides'): for desc in xg.descriptions(palName): # listGuides guides = xg.descriptionGuides(desc) if not guides: continue expPath = xg.expandFilepath('${DESC}', desc) pm.select(guides, r= 1) # guides to curves curves = pm.mel.xgmCreateCurvesFromGuides(0, True) # export as alembic if not pm.pluginInfo('AbcExport', q= 1, l= 1): pm.loadPlugin('AbcExport') abcCmds = '-frameRange 1 1 -uvWrite -worldSpace -dataFormat ogawa ' abcRoot = '-root ' + ' -root '.join([cur.longName() for cur in pm.ls(curves)]) abcPath = expPath + 'curves.abc' pm.mel.AbcExport(j= abcCmds + abcRoot + ' -file ' + abcPath) if anim: # save out hairSystem preset presetMel = [] for nodeType in ['nucleus', 'hairSystem', 'nRigid']: presetDict = self.ioAttrPreset(nodeType, True) presetMel.extend(presetDict.values()) # move preset file to version repo presetRepo = self.nDynPresetPath(palName, version) if not os.path.exists(presetRepo): os.makedirs(presetRepo) for prs in presetMel: dstPath = '/'.join([presetRepo, os.path.basename(prs)]) shutil.move(prs, dstPath) # create empty _shot_ folder shotDir = self.paletteDeltaDir(palName, version, '') if not os.path.exists(shotDir): os.makedirs(shotDir) # export snapshot for i in range(5): tmpPath = self.snapshotTmp % (i+1) if os.path.isfile(tmpPath): imgPath = self.snapshotImgPath(palName, version, str(i+1)) if not os.path.exists(os.path.dirname(imgPath)): os.makedirs(os.path.dirname(imgPath)) shutil.move(tmpPath, imgPath) # restore dataPath xg.setAttr('xgDataPath', workPath, palName) xg.setAttr('xgProjectPath', workProj, palName) # restore modifiers if bake: for desc in xg.descriptions(palName): # bake Noise modifiers for fxm in xg.fxModules(palName, desc): if xg.fxModuleType(palName, desc, fxm) == 'NoiseFXModule': # restore to live mode xg.setAttr('mode', '0', palName, desc, fxm) # remove bake groom modifiers for fxm in xg.fxModules(palName, desc): if xg.fxModuleType(palName, desc, fxm) == 'BakedGroomManagerFXModule': xg.removeFXModule(palName, desc, fxm) # restore Generator xg.setActive(palName, desc, generator[desc]) self.refresh('Full') self.notifyMsg('Collection Export Complete !', 0) return True
def _get_root_folder(self): """ Gets the xgen root folder :return: String with the xgen folder path """ return xg.getAttr('xgDataPath', str(self.collection_name))
def buildUI(self): layout = QVBoxLayout() self.setLayout(layout) descriptionGrpBox = QGroupBox(self.description) layout.addWidget(descriptionGrpBox) descriptionGrpBoxLayout = QVBoxLayout() descriptionGrpBox.setLayout(descriptionGrpBoxLayout) # Description Toolbar descriptionToolBar = QToolBar() descriptionToolBar.setIconSize(QSize(32, 32)) descriptionGrpBoxLayout.addWidget(descriptionToolBar) guideDisplayToggleAction = QAction( QIcon(os.path.join(XGEN_ICON_PATH, 'xgToggleGuide.png')), 'Guide Display Toggle', self) descriptionToolBar.addAction(guideDisplayToggleAction) xgPreviewRefreshAction = QAction( QIcon(os.path.join(XGEN_ICON_PATH, 'xgPreviewRefresh.png')), 'Refresh Primitive', self) descriptionToolBar.addAction(xgPreviewRefreshAction) xgPreviewClearAction = QAction( QIcon(os.path.join(XGEN_ICON_PATH, 'xgPreviewClear.png')), 'Clear Primitive', self) descriptionToolBar.addAction(xgPreviewClearAction) # Renderable Button self.renderableButton = QPushButton() renderablePixmap = QPixmap(os.path.join(ICON_PATH, 'renderable.png')) self.renderableIcon = QIcon(renderablePixmap) disRenderablePixmap = QPixmap( os.path.join(ICON_PATH, 'disRenderable.png')) self.disRenderableIcon = QIcon(disRenderablePixmap) self.renderableButton.setIcon(self.renderableIcon) self.renderableButton.setIconSize(renderablePixmap.rect().size()) self.renderableButton.setCheckable(True) self.renderableButton.setStyleSheet("background-color: white") descriptionGrpBoxLayout.addWidget(self.renderableButton) # Density Widget densityLayout = QHBoxLayout() descriptionGrpBoxLayout.addLayout(densityLayout) densityLabel = QLabel('Density') densityLayout.addWidget(densityLabel) densityValue = xg.getAttr("density", self.collection, self.description, "RandomGenerator") densityLineEdit = QLineEdit() densityLineEdit.setText(densityValue) densityLineEditValidator = QDoubleValidator(0.000, 3000.000, 6, densityLineEdit) densityLineEdit.setValidator(densityLineEditValidator) densityLayout.addWidget(densityLineEdit) # Connect Widgets guideDisplayToggleAction.triggered.connect( lambda: xg.toggleGuideDisplay(self.description)) xgPreviewRefreshAction.triggered.connect(lambda: pm.mel.eval( 'xgmPreview -progress {"%s"};' % self.description)) xgPreviewClearAction.triggered.connect(lambda: pm.mel.eval( 'xgmPreview -clean {"%s"};' % self.description)) densityLineEdit.returnPressed.connect( lambda: self.setDensity(densityLineEdit.text())) self.renderableButton.toggled.connect( lambda checked: self.renderableButtonToggledSlot(checked))
def linkHairSystem(self, palName): """doc""" self.clearPreview() nHairAttrs = { # This will cause Maya crash if the dyn-curves shape is weried # xgen NullRender might generate weired shaped curve if scene is too large #'noStretch': 1, 'stretchResistance': 600, 'compressionResistance': 100, 'startCurveAttract': 0.3, 'mass': 0.05 } # get active AnimWire module list animWireDict = {} refWiresFrame = '' for desc in xg.descriptions(palName): for fxm in xg.fxModules(palName, desc): if xg.fxModuleType(palName, desc, fxm) == 'AnimWiresFXModule': if xg.getAttr('active', palName, desc, fxm) == 'true': refWiresFrame = xg.getAttr('refWiresFrame', palName, desc, fxm) hsysName = self.getHairSysName(desc) hsysTransforms = [str(hsys.getParent().name()) for hsys in pm.ls(type= 'hairSystem')] if hsysName in hsysTransforms: pm.warning('[XGen Hub] : description: %s has hairSystem [%s], skipped.' % (desc, hsysName)) else: animWireDict[desc] = fxm # build hairSystem for desc in animWireDict: fxm = animWireDict[desc] pm.warning('[XGen Hub] : Building hairSystem for description: %s, FXModule: %s' % (desc, fxm)) descHairSysName = self.getHairSysName(desc) msxgAwt.exportCurvesMel(palName, desc, fxm) meshPatch, hsys = msxgAwt.xgmMakeCurvesDynamic(descHairSysName, False) msxgAwt.nRigidRename(meshPatch, self.getRigidNameVar()) msxgAwt.attachSlot(palName, desc, fxm, descHairSysName) pm.warning('[XGen Hub] : Link hairSystem done.') # set some attributes for attr in nHairAttrs: hsys.setAttr(attr, nHairAttrs[attr]) # set follicles focGrp = pm.ls(desc + '_hairSystemFollicles', type= 'transform') if focGrp and focGrp[0].listRelatives(ad= 1, typ= 'follicle'): follicles = focGrp[0].listRelatives(ad= 1, typ= 'follicle') if palName == 'BossHair': for foc in follicles: foc.fixedSegmentLength.set(1) foc.segmentLength.set(20) # set rebuildCurve.rebuildType to update follicle changes cuv = pm.listConnections(foc, s= 1, d= 0, type= 'nurbsCurve', sh= 1) rebuildCuv = pm.listConnections(cuv, s= 1, d= 0, type= 'rebuildCurve')[0] rebuildCuv.rebuildType.set(0) if pm.objExists('nucleus1'): jobs = pm.scriptJob(lj= 1) for job in jobs: if 'nucleus1.startFrame' in job: pm.scriptJob(k= int(job.split(':')[0])) pm.scriptJob(ac= ['nucleus1.startFrame', self.setRefWiresFrame]) pm.PyNode('nucleus1').startFrame.set(int(refWiresFrame))
def build_hair_system(palette): """Build hair system and link to the descriptions that has animWire modifier Args: palette (str): XGen Legacy palette name """ def exportCurves(descName, fxmName): # setup value = xg.getAttr("exportDir", palette, descName, fxmName) xg.setAttr("exportDir", str(value), palette, descName, fxmName) xg.setAttr("exportCurves", "true", palette, descName, fxmName) # Need to fill in the export faces to correct value xg.setAttr("exportFaces", "", palette, descName, fxmName) # export clumpCurves.mel pmc.mel.xgmNullRender(descName, percent=0) # get clumpCurves.mel file path curvesMelPath = xg.getAttr("_fullExportDir", palette, descName, fxmName) # Remove clumpCurves.mel's last cmd : "xgmMakeCurvesDynamic;" print("Reading curves mel. -> %s" % curvesMelPath) with open(curvesMelPath, "r") as mel_script: curvesMel = mel_script.readlines() cmdIndex = curvesMel.index("xgmMakeCurvesDynamic;\n") curvesMel[cmdIndex] = "" # execute it, and we will run our MakeCurvesDynamic later pmc.mel.eval("".join(curvesMel)) # restore xg.setAttr("exportCurves", "false", palette, descName, fxmName) xg.setAttr("exportFaces", "", palette, descName, fxmName) def xgmMakeCurvesDynamic(descHairSysName, collide): """ Create nHairSystem with good name before MakeCurvesDynamic and without optionBox UI """ selection = pmc.ls(sl=True, long=True) # find hair holding mesh for later rigid body rename meshPatch = [] for dag in selection: if dag.getShape().type() == "mesh": meshPatch.append(dag.name()) # create the first time we hit a valid curve hsys = pmc.createNode("hairSystem") hsys.getParent().rename(descHairSysName) # we want uniform stiffness because the curves # are initially point locked to both ends pmc.removeMultiInstance(hsys.stiffnessScale[1], b=True) hsys.clumpWidth.set(0.00001) hsys.hairsPerClump.set(1) pmc.connectAttr("time1.outTime", hsys.currentTime) nucleus = pmc.mel.getActiveNucleusNode(False, True) pmc.mel.addActiveToNSystem(hsys, nucleus) pmc.connectAttr(nucleus + ".startFrame", hsys.startFrame) # select the hairSystem we just created and well named, # and maya won't create one when making curves dynamic selection.append(hsys) # re-select curves, mesh and hairSystem pmc.select(selection, replace=True) # trun on 'Collide With Mesh' pmc.optionVar( intValue=["makeCurvesDynamicCollideWithMesh", int(collide)]) # MakeCurvesDynamic callback mel.eval('makeCurvesDynamic 2 { "1", "0", "1", "1", "0"}') return meshPatch, hsys.name() def nRigidRename(meshPatch): # `meshPatch` is a list of geo long name renameDict = {} for rigid in cmds.ls(type="nRigid"): shapes = cmds.listConnections(rigid + ".inputMesh", shapes=True) if shapes and cmds.nodeType(shapes[0]) == "mesh": meshName = cmds.listRelatives(shapes[0], parent=True, fullPath=True)[0] if meshName in meshPatch: renameDict[rigid] = meshName # rename rigid body for rigidName in renameDict: rigid = cmds.ls(rigidName) if not rigid: continue cmds.rename( cmds.listRelatives(rigid[0], parent=True)[0], "%s_nRigid" % renameDict[rigidName]) def getHairCurves(descHairSysName): """List out curves which output from descHairSysName""" # since we had our nHairSystem well named, we can search it by name hsysList = cmds.ls(descHairSysName) if not hsysList: return curves = [] shapes = cmds.listRelatives(hsysList[0], shapes=True, fullPath=True) if cmds.nodeType(shapes[0]) == "hairSystem": # find curves hsys = shapes[0] follicles = cmds.listConnections(hsys + ".inputHair", shapes=True, type="follicle") for foll in follicles: curve = cmds.listConnections(foll + ".outCurve", shapes=True, type="nurbsCurve") curves.extend(curve) return curves def attachSlot(palette, desc, fxmName, descHairSysName): if not (str(xg.fxModuleType(palette, desc, fxmName)) == "AnimWiresFXModule"): return refwFrame = xg.getAttr("refWiresFrame", palette, desc, fxmName) if str(xg.getAttr("liveMode", palette, desc, fxmName)) == "false": wiresfile = xg.getAttr("wiresFile", palette, desc, fxmName) pmc.mel.xgmFindAttachment(d=desc, f=wiresfile, fm=int(refwFrame), m=fxmName) else: curves = getHairCurves(descHairSysName) if curves: # attach wires to curves cmds.select(curves, replace=True) pmc.mel.xgmFindAttachment(d=desc, fm=int(refwFrame), m=fxmName) # print('The following curves were attached: ', # [c.name() for c in curves]) else: cmds.warning("No curves selected. Nothing to attach.") # Start process preview_clear() get_hsys_name = (lambda desc: desc + "_hairSystem") nHairAttrs = { "stretchResistance": 600, "compressionResistance": 100, "startCurveAttract": 0.3, "mass": 0.05 } palette = str(palette) # get active AnimWire module list animWireDict = {} for desc in xg.descriptions(palette): for fxm in xg.fxModules(palette, desc): if xg.fxModuleType(palette, desc, fxm) != "AnimWiresFXModule": continue if xg.getAttr("active", palette, desc, fxm) == "true": hsysName = get_hsys_name(desc) hsysTransforms = [ cmds.listRelatives(hsys, parent=True)[0] for hsys in cmds.ls(type="hairSystem") ] if hsysName in hsysTransforms: cmds.warning("Description %s has hairSystem [%s], " "skipped." % (desc, hsysName)) else: animWireDict[desc] = fxm # build hairSystem for desc, fxm in animWireDict.items(): print("Building hairSystem for description: %s, FXModule: %s" "" % (desc, fxm)) fxm = animWireDict[desc] descHairSysName = get_hsys_name(desc) exportCurves(desc, fxm) # add patch to selection cmds.select(list_bound_geometry(desc), add=True) meshPatch, hsys = xgmMakeCurvesDynamic(descHairSysName, False) nRigidRename(meshPatch) attachSlot(palette, desc, fxm, descHairSysName) print("HairSystem linked.") # set some attributes for attr, val in nHairAttrs.items(): cmds.setAttr(hsys + "." + attr, val)
def getAnimBranch(self, palName): """doc""" return str(xg.getAttr('xgDogTag', palName))
def importGrooming(self, palName, descName= None, version= None): """ """ self.clearPreview() if descName: descs = [descName] else: descs = xg.descriptions(palName) # copy groom dir from versionRepo if @version has given if version: # check exists groomDesc = {} hasMissing = False for desc in descs: if xg.getAttr('groom', palName, desc): groomSource = '/'.join([self.paletteVerDir(palName, version), desc, 'groom']) if os.path.exists(groomSource): groomDesc[desc] = groomSource else: hasMissing = True msg = '[XGen Hub] : palette [%s] description [%s] version [%s] NOT exists. -> %s' pm.warning(msg % (palName, desc, version, groomSource)) # copy file if no missing if not hasMissing: for desc in groomDesc: src = groomDesc[desc] dst = '/'.join([self.paletteWipDir(palName), desc, 'groom']) if os.path.isdir(dst): try: dir_util.remove_tree(dst) except: pm.warning('[XGen Hub] : Dir may not remove. -> ' + dst) dir_util._path_created = {} dir_util.copy_tree(src, dst) else: pm.error('[XGen Hub] : Some data missing, Check ScriptEditor. grooming import stopped.') return None self.refresh() # IMPORT GROOMING # clear out autoExport path for preventing grooming auto export xg.setOptionVarString('igAutoExportFolder', '') for desc in descs: if xg.getAttr('groom', palName, desc): importPath = xg.expandFilepath('${DESC}/groom', desc) igDescr = xg.igDescription(desc) # import Attribute Map try: pm.waitCursor(state= True) pm.mel.iGroom(im= importPath, d= igDescr) finally: pm.waitCursor(state= False) # import Mask try: pm.waitCursor(state= True) pm.mel.iGroom(ik= importPath, d= igDescr) finally: pm.waitCursor(state= False) # import Region try: pm.waitCursor(state= True) pm.mel.iGroom(ir= importPath, d= igDescr) finally: pm.waitCursor(state= False) # restore default autoExport path xg.setOptionVarString('igAutoExportFolder', '${DESC}/groom') # IMPORT GROOM SETTINGS """ Currently only grab [density] setting, ['length', 'width'] will messed up imported grooming's map attribute """ for desc in descs: igdesc = xg.getAttr('groom', palName, desc) jsonPath = xg.expandFilepath('${DESC}/groom', desc) + 'groomSettings.json' if igdesc and os.path.isfile(jsonPath): groomSettings = {} with open(jsonPath) as jsonFile: groomSettings = json.load(jsonFile) for key in groomSettings: # grab [density] setting only if key == 'density': pm.setAttr(igdesc + '.' + key, groomSettings[key]) self.notifyMsg('Grooming Import Complete !', 0) return True
def importPalette(self, palName, version, binding= False, anim= False, asDelta= False, delta= []): """ ** NOT SUPPORT NAMESPACE ** XGen palette will imported without validator. [!!!] When importing [BAKED] palette, @binding set to False should be fine. """ xgenFileName = palName + '.xgen' xgenFile = str('/'.join([self.paletteVerDir(palName, version), xgenFileName])) if not os.path.isfile(xgenFile): self.notifyMsg('.xgen file is not exists.', 2) pm.error('[XGen Hub] : .xgen file is not exists. -> ' + xgenFile) return None if asDelta and not pm.sceneName(): self.notifyMsg('Please save the scene.', 2) return None self.clearPreview() # check if palette exists in current scene if palName in xg.palettes(): # delete current palette folder palDir = xg.expandFilepath(xg.getAttr('xgDataPath', palName), '') if os.path.isdir(palDir): try: dir_util.remove_tree(palDir) except: pm.warning('[XGen Hub] : Dir may not remove. -> ' + palDir) # delete current palette # this action might cry about 'None type object has no attr "previewer"' # when there is no xgen ui panel xg.deletePalette(palName) # IMPORT PALETTE palName = base.importPalette(xgenFile, delta, '') # update the palette with the current project xg.setAttr('xgProjectPath', str(pm.workspace(q= 1, rd= 1)), palName) dataPath = xg.paletteRootVar() + '/' + palName xg.setAttr('xgDataPath', dataPath, palName) # create imported palette folder paletteRoot = xg.expandFilepath(dataPath, '', True, True) # create all imported descriptions folder msxgApi.setupDescriptionFolder(paletteRoot, palName) # wrap into maya nodes palName = str(pm.mel.xgmWrapXGen(pal= palName, wp= binding, wlg= binding, gi= binding)) # copy maps from source descNames = xg.descriptions(palName) msxgApi.setupImportedMap(xgenFile, palName, descNames, self.projPath) # bind grooming descriptions to geometry if binding: for desc in descNames: igdesc = xg.getAttr('groom', palName, desc) if igdesc: # get groom dag node igdesc = xg.igActivateDescription(desc) # bind groom to geo pm.mel.igBindFromXGen(desc) # set groom density and sampling method pm.setAttr(igdesc + '.density', 1) pm.setAttr(igdesc + '.interpStyle', 1) # set all groom visible on xg.igSetDescriptionVisibility(True) # sync primitives tab attritube map path with auto export path xg.igSyncMaps(desc) # import grooming as well self.importGrooming(palName) # import as anim, build hairSystem if anim: # build hairSystem self.linkHairSystem(palName) # check preset dir exists presetLocalDir = str(pm.internalVar(userPresetsDir= 1)) presetRepo = self.nDynPresetPath(palName, version) if os.path.exists(presetRepo): # copy preset for prs in os.listdir(presetRepo): dstPath = presetLocalDir + prs prs = '/'.join([presetRepo, prs]) shutil.copyfile(prs, dstPath) # load preset # [note] nucleus preset will not be loaded during current devlope presetMel = [] for nodeType in ['hairSystem', 'nRigid']: presetDict = self.ioAttrPreset(nodeType, False) presetMel.extend(presetDict.values()) # dump preset for prs in presetMel: if os.path.isfile(prs): os.remove(prs) else: pm.warning('[XGen Hub] : nDynamic attribute presets folder not found.') if asDelta: dataPath = xg.getAttr('xgDataPath', palName) dataPath = dataPath + ';' + self.paletteVerDir(palName, version, raw= True) xg.setAttr('xgDataPath', dataPath, palName) # save scenes pm.saveFile(f= 1) # set export delta pm.setAttr(palName + '.xgExportAsDelta', 1) pm.warning('[XGen Hub] : Collection Import Complete !') self.notifyMsg('Collection Import Complete !', 0) return palName
def getAnimShotName(self, palName): """doc""" return str(xg.getAttr(self.xgShotAttr, palName))