Пример #1
0
    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
Пример #2
0
def get_fx_module_type(palette, description, modifier):
    """Return FXModule type

    Args:
        palette (str): XGen Legacy palette name
        description (str): XGen Legacy description name
        modifier (str): Name of an XGen modifier object

    Returns:
        (str)

    """
    return xg.fxModuleType(palette, description, modifier)
Пример #3
0
	def setRefWiresFrame(self, refWiresFrame= None):
		"""doc"""
		if not refWiresFrame:
			refWiresFrame = str(int(pm.PyNode('nucleus1').startFrame.get()))
		else:
			if pm.objExists('nucleus1'):
				pm.PyNode('nucleus1').startFrame.set(int(refWiresFrame))
		for palName in xg.palettes():
			for desc in xg.descriptions(palName):
				for fxm in xg.fxModules(palName, desc):
					if xg.fxModuleType(palName, desc, fxm) == 'AnimWiresFXModule':
						xg.setAttr('refWiresFrame', refWiresFrame, palName, desc, fxm)

		self.refresh('Full')
Пример #4
0
def set_refWires_frame(refWiresFrame, palette):
    """Setup refWireFrame to the descriptions that has animWire modifier

    Args:
        refWiresFrame (int, float): refWireFrame value
        palette (str): XGen Legacy palette name

    """
    refWiresFrame = str(int(refWiresFrame))
    palette = str(palette)

    for desc in xg.descriptions(palette):
        for fxm in xg.fxModules(palette, desc):
            if xg.fxModuleType(palette, desc, fxm) != "AnimWiresFXModule":
                continue
            xg.setAttr("refWiresFrame", refWiresFrame, palette, desc, fxm)
Пример #5
0
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)
Пример #6
0
def bake_description(palette, description):
    """Bake description with BakedGroomManagerFXModule

    Will create a bakedGroomManager module if not existing one.

    Args:
        palette (str): XGen Legacy palette name
        description (str): XGen Legacy description name

    """
    fxmod_typ = (lambda fxm: xg.fxModuleType(palette, description, fxm))

    for fxm in xg.fxModules(palette, description):
        if fxmod_typ(fxm) == "BakedGroomManagerFXModule":
            break
    else:
        # bake groom modifiers
        xg.addFXModule(palette, description, "BakedGroomManagerFXModule")

    xg.bakedGroomManagerBake(palette, description)
Пример #7
0
    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.")
Пример #8
0
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
Пример #9
0
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.'
Пример #10
0
def parse_objects(map_attr):
    """Parse attribute returned from `filePathEditor` into XGen object names

    (NOTE) Remember to refresh filePathEditor by calling
           `cmds.filePathEditor(refresh=True)`, or the
           fxmodule index might not return correctly.

    >>> cmds.filePathEditor(refresh=True)
    >>> maps = cmds.filePathEditor(q=1, listFiles="", withAttribute=1)
    ["descriptionShape.primitive.ClumpingFXModule(1).HeadPoint", ...]
    >>> parse_objects(maps[0])
    ('CY_Mon_Hair', 'description', 'Clumping2', 'pointDir', 0)

    Args:
        map_attr (str): An attribute path returned from `cmds.filePathEditor`

    Returns:
        tuple: Names of palette, description, object, attr, attr-index

    """
    address = map_attr.split(".")

    # The description shape name in `map_attr` string is a short name,
    # and since we only need the description transform short name, it
    # doesn't matter which shape node we get from `cmds.ls`.
    desc_shape = cmds.ls(address[0])[0]
    description = str(cmds.listRelatives(desc_shape,
                                         parent=True)[0])  # get short name
    palette = get_palette_by_description(description)

    if address[1] == "glRenderer":
        subtype = "GLRenderer"
    else:
        # primitive, generator
        subtype = xg.getActive(palette, description,
                               str(address[1].capitalize()))

    if len(address) < 4:
        # Example: descriptionShape.generator.mask

        attr = address[2]
        attr, attr_indx = _parse_attribute(attr, subtype)

        return palette, description, subtype, attr, attr_indx

    else:
        # Example: descriptionShape.primitive.ClumpingFXModule(1).HeadPoint

        modifier_cls, mod_indx = address[2][:-1].split("(")
        mod_indx = int(mod_indx)

        attr = address[3]
        attr, attr_indx = _parse_attribute(attr, subtype)

        try:
            module = xg.fxModules(palette, description)[mod_indx]
        except IndexError:
            raise IndexError("Object not found, possible `filePathEditor` "
                             "not refreshed: {}".format(map_attr))
        else:
            if xg.fxModuleType(palette, description, module) == modifier_cls:

                return palette, description, module, attr, attr_indx

        raise Exception("Object not found, this is a bug: {}".format(map_attr))
Пример #11
0
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)
Пример #12
0
	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
Пример #13
0
	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))