Elementor #3909

#!/usr/bin/python da . importazione dello spazio dei nomi Spazio dei nomi importa getpath importare os importazione sist importare ri importare shutil mondo di importazione importa fnmatch importazione proxy importa gui3d dal core import G Risorse di classe ( NameSpace ): """Questo spazio dei nomi racchiude tutte le chiamate correlate alla lettura e alla gestione delle risorse.""" def __init__ ( auto , api ): sé . api = api Spazio dei nomi . __init__ ( auto ) sé . traccia () sé . assetTypes = [ "materiale" , "modello" , "vestiti" , "capelli" , "denti" , "sopracciglia" , "ciglia" , "lingua" , "occhi" , "delega" , "pelle" , "posa" , "espressione" , "rig" , "bersaglio" , "node_setups_and_blender_specific" ] sé . extensionToType = dict () sé . extensionToType [ ".mhmat" ] = "materiale" sé . extensionToType [ ".mhclo" ] = "proxy" sé . extensionToType [ ".proxy" ] = "proxy" sé . extensionToType [ ".target" ] = "target" sé . extensionToType [ ".mhm" ] = "modelli" self.typeToExtension = {'material' : 'mhmat', 'models' : 'mhm', 'model' : 'mhm', 'clothes' : 'mhclo', 'hair' : 'mhclo', 'teeth' : 'mhclo', 'eyebrows' : 'mhclo', 'eyelashes' : 'mhclo', 'tongue' : 'mhclo', 'eyes' : 'mhclo', 'proxymeshes': 'proxy', 'target' : 'target', 'skin' : 'mhmat'} self.genericExtraKeys = ["tag"] self.genericKeys = ["name","description", "uuid"] self.genericCommentKeys = ["license","homepage","author"] self.proxyKeys = [ "basemesh", "obj_file", "max_pole", "material", "z_depth", "x_scale", "y_scale", "z_scale" ] self.materialKeys = [ "diffuseColor", "specularColor", "emissiveColor", "ambientColor", "diffuseTexture", "bumpmapTexture", "normalmapTexture", "displacementmapTexture", "specularmapTexture", "transparencymapTexture", "aomapTexture", "diffuseIntensity", "bumpMapIntensity", "normalMapIntensity", "displacementMapIntensity", "specularMapIntensity", "transparencyMapIntensity", "aoMapIntensity", "shininess", "opacity", "translucency", "shadeless", "wireframe", "transparent", "alphaToCoverage", "backfaceCull", "depthless", "castShadows", "receiveShadows", ] # There are also SSS settings, but I don't know if those actually works self.keyList = self.genericExtraKeys + self.genericCommentKeys + self.genericKeys +self.materialKeys + \ self.proxyKeys self.zDepth = {"Body": 31, "Underwear and lingerie": 39, "Socks and stockings": 43, "Shirt and trousers": 47, "Sweater": 50, "Indoor jacket": 53, "Shoes and boots": 57, "Coat": 61, "Backpack": 69 } def _parseGenericAssetInfo(self,fullPath): info = dict() fPath, ext = os.path.splitext(fullPath) basename = os.path.basename(fullPath) info["type"] = self.extensionToType[ext] info["absolute path"] = fullPath info["extension"] = ext info["basename"] = basename info["rawlines"] = [] info["location"] = os.path.dirname(fullPath) info["parentdir"] = os.path.basename(info["location"]) with open(fullPath, 'r', encoding='utf8') as f: contents = f.readlines() for line in contents: info["rawlines"].append(re.sub(r"[\x0a\x0d]+",'',line)) info["rawkeys"] = [] info["rawcommentkeys"] = [] for line in info["rawlines"]: m = re.match(r"^([a-zA-Z_]+)\s+(.*)$",line) if m: info["rawkeys"].append([m.group(1),m.group(2)]) m = re.match(r"^#\s+([a-zA-Z_]+)\s+(.*)$",line) if m: info["rawcommentkeys"].append([m.group(1),m.group(2)]) for genericExtraKeyName in self.genericExtraKeys: info[genericExtraKeyName] = set() for rawkey in info["rawkeys"]: rawKeyName = rawkey[0] rawKeyValue = rawkey[1] if rawKeyName == genericExtraKeyName: info[genericExtraKeyName].add(rawKeyValue) for genericKeyName in self.genericKeys: info[genericKeyName] = None for rawkey in info["rawkeys"]: rawKeyName = rawkey[0] rawKeyValue = rawkey[1] if rawKeyName == genericKeyName: info[genericKeyName] = rawKeyValue for genericCommentKeyName in self.genericCommentKeys: info[genericCommentKeyName] = None for commentKey in info["rawcommentkeys"]: commentKeyName = commentKey[0] commentKeyValue = commentKey[1] if commentKeyName == genericCommentKeyName: info[commentKeyName] = commentKeyValue return info def _parseProxyKeys(self,assetInfo): for pk in self.proxyKeys: assetInfo[pk] = None for k in assetInfo["rawkeys"]: key = k[0] value = k[1] if key == pk: assetInfo[pk] = value def _parseMaterialKeys(self,assetInfo): for pk in self.materialKeys: assetInfo[pk] = None for k in assetInfo["rawkeys"]: key = k[0] value = k[1] if key == pk: assetInfo[pk] = value def _addPertinentKeyInfo(self,assetInfo): pertinentKeys = list(self.genericKeys) pertinentExtraKeys = list(self.genericExtraKeys) pertinentCommentKeys = list(self.genericCommentKeys) if assetInfo["type"] == "proxy": pertinentKeys.extend(self.proxyKeys) if assetInfo["type"] == "material": pertinentKeys.extend(self.materialKeys) assetInfo["pertinentKeys"] = pertinentKeys assetInfo["pertinentExtraKeys"] = pertinentExtraKeys assetInfo["pertinentCommentKeys"] = pertinentCommentKeys def assetTitleToDirName(self, assetTitle): """Convert an asset title (as shown for example in a list) to a normalized file name""" normalizedTitle = assetTitle.strip() normalizedTitle = re.sub(r'_+', ' ', normalizedTitle) normalizedTitle = normalizedTitle.strip() normalizedTitle = re.sub(r'\s+', '_', normalizedTitle) normalizedTitle = re.sub(r'[*:,\[\]/\\\(\)]+', '', normalizedTitle) return normalizedTitle def getAssetTypes(self): """Returns a non-live list of known asset types""" return list(self.assetTypes) def getAssetLocation(self, assetTitle, assetType): """Get the full normal (user) path for an asset based on its title and type""" alreadyKosher = ["clothes", "hair", "teeth", "eyebrows", "eyelashes", "tongue", "eyes"] needsPlural = ["material", "model", "skin", "pose", "expression", "rig"] normalizedTitle = self.assetTitleToDirName(assetTitle) if assetType == "model": root = self.api.locations.getUserHomePath("models") return os.path.join(root, normalizedTitle) if assetType in alreadyKosher: root = self.api.locations.getUserDataPath(assetType) return os.path.join(root,normalizedTitle) if assetType in needsPlural: root = self.api.locations.getUserDataPath(assetType + "s") return os.path.join(root,normalizedTitle) if assetType == "proxy": return self.api.locations.getUserDataPath("proxymeshes") if assetType == "target": return self.api.locations.getUserDataPath("custom") if assetType == "model": return self.api.locations.getUserHomePath("models") raise ValueError("Could not convert title to location for asset with type",assetType) def openAssetFile(self, path, strip = False): """Opens an asset file and returns a hash describing it""" fullPath = self.api.locations.getUnicodeAbsPath(path) if not os.path.isfile(fullPath): return None info = self._parseGenericAssetInfo(fullPath) self._addPertinentKeyInfo(info) if info["type"] == "proxy": self._parseProxyKeys(info) if info["type"] == "material": self._parseMaterialKeys(info) thumbPath = os.path.splitext(path)[0] + ".thumb" if os.path.isfile(thumbPath): info["thumb_path"] = thumbPath else: info["thumb_path"] = None if strip: info.pop("rawlines",None) info.pop("rawkeys",None) info.pop("rawcommentkeys",None) return info def writeAssetFile(self, assetInfo, createBackup = True): """ This (over)writes the asset file named in the assetInfo's "absolute path" key. If createBackup is set to True, any pre-existing file will be backed up to it's former name + ".bak" """ if not assetInfo: raise ValueError('Cannot use None as assetInfo') ap = assetInfo["absolute path"] bak = ap + ".bak" if createBackup and os.path.isfile(ap): shutil.copy(ap,bak) with open(ap, 'w', encoding='utf8') as f: stillNeedToDumpCommentKeys = True writtenKeys = [] writtenCommentKeys = [] writtenExtraKeys = [] remainingKeys = list(assetInfo["pertinentKeys"]) remainingCommentKeys = list(assetInfo["pertinentCommentKeys"]) remainingExtraKeys = list(assetInfo["pertinentExtraKeys"]) for line in assetInfo["rawlines"]: allowWrite = True m = re.match(r"^([a-zA-Z_]+)\s+(.*)$",line) if m: # If this is the first line without a hash sign, we want to # dump the remaining comment keys before doing anything else if stillNeedToDumpCommentKeys: if len(remainingCommentKeys) > 0: for key in remainingCommentKeys: if not assetInfo[key] is None: f.write("# " + key + " " + assetInfo[key] + "\x0a") stillNeedToDumpCommentKeys = False key = m.group(1) if key in remainingKeys: allowWrite = False if not assetInfo[key] is None: f.write(key + " " + assetInfo[key] + "\x0a") writtenKeys.append(key) remainingKeys.remove(key) if key in remainingExtraKeys: allowWrite = False if not assetInfo[key] is None and len(assetInfo[key]) > 0 and not key in writtenExtraKeys: for val in assetInfo[key]: f.write(key + " " + val + "\x0a") writtenExtraKeys.append(key) remainingExtraKeys.remove(key) if key in writtenExtraKeys: allowWrite = False m = re.match(r"^#\s+([a-zA-Z_]+)\s+(.*)$",line) if m: key = m.group(1) if key in remainingCommentKeys: allowWrite = False if not assetInfo[key] is None: f.write("# " + key + " " + assetInfo[key] + "\x0a") writtenCommentKeys.append(key) remainingCommentKeys.remove(key) if allowWrite: f.write(line + "\x0a") if len(remainingKeys) > 0: for key in remainingKeys: if not assetInfo[key] is None: f.write(key + " " + assetInfo[key] + "\x0a") if len(remainingExtraKeys) > 0: for key in remainingExtraKeys: if not assetInfo[key] is None and len(assetInfo[key]) > 0: for val in assetInfo[key]: f.write(key + " " + val + "\x0a") return True def materialToHash(self, material): """Convert a material object to a hash containing all its settings""" output = {} fn = os.path.abspath(material.filename) # meta output["name"] = material.name output["description"] = material.description output["materialFile"] = fn # colors output["ambientColor"] = material.ambientColor.values output["diffuseColor"] = material.diffuseColor.values output["specularColor"] = material.specularColor.values output["emissiveColor"] = material.emissiveColor.values # textures output["diffuseTexture"] = material.diffuseTexture output["bumpMapTexture"] = material.bumpMapTexture output["normalMapTexture"] = material.normalMapTexture output["displacementMapTexture"] = material.displacementMapTexture output["specularMapTexture"] = material.specularMapTexture output["transparencyMapTexture"] = material.transparencyMapTexture output["aoMapTexture"] = material.aoMapTexture # texture intensities output["bumpMapIntensity"] = material.bumpMapIntensity output["normalMapIntensity"] = material.normalMapIntensity output["displacementMapIntensity"] = material.displacementMapIntensity output["specularMapIntensity"] = material.specularMapIntensity output["transparencyMapIntensity"] = material.transparencyMapIntensity output["aoMapIntensity"] = material.aoMapIntensity # subsurface output["sssEnabled"] = material.sssEnabled output["sssRScale"] = material.sssRScale output["sssGScale"] = material.sssGScale output["sssBScale"] = material.sssBScale # various output["uvMap"] = material.uvMap output["shininess"] = material.shininess output["opacity"] = material.opacity output["translucency"] = material.translucency output["shadeless"] = material.shadeless output["wireframe"] = material.wireframe output["transparent"] = material.transparent output["alphaToCoverage"] = material.alphaToCoverage output["backfaceCull"] = material.backfaceCull output["depthless"] = material.depthless output["castShadows"] = material.castShadows output["receiveShadows"] = material.receiveShadows output["autoBlendSkin"] = material.autoBlendSkin # viewport color if material.usesViewPortColor(): output["viewPortColor"] = material.viewPortColor.values output["viewPortAlpha"] = material._viewPortAlpha for key in output.keys(): if output[key] is None: output[key] = "" definedKeys = [] for key in output: definedKeys.append(str(key).lower()) with open(fn, 'r', encoding='utf-8') as f: line = f.readline() while line: parsedLine = line.strip() if parsedLine and not parsedLine.startswith("#") and not parsedLine.startswith("/"): match = re.search(r'^([a-zA-Z]+)\s+(.*)$', parsedLine) if match: key = match.group(1) value = match.group(2) if not key.lower() in definedKeys: # There was a key defined in the material file, but it has not been picked up # by MH. So we insert it in the produced hash and let the recipient decide # what to do with it, if anything output[key] = value line = f.readline() return output def _findMaterials(self,path): matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, '*.mhmat'): matches.append(os.path.join(root, filename)) return matches def _findProxies(self,path): basenames = [] matches = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, '*.mhpxy'): matches.append(os.path.join(root, filename)) basenames.append(os.path.basename(filename)) for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, '*.mhclo'): bn = os.path.basename(filename) if not bn in basenames: matches.append(os.path.join(root, filename)) return matches def getAvailableSystemSkins(self): """Get a list with full paths to all system skins (the MHMAT files)""" path = getpath.getSysDataPath("skins") return self._findMaterials(path) def getAvailableUserSkins(self): """Get a list with full paths to all user skins (the MHMAT files)""" path = getpath.getDataPath("skins") return self._findMaterials(path) def getAvailableSystemHair(self): """Get a list with full paths to all system hair (the MHCLO files)""" path = getpath.getSysDataPath("hair") return self._findProxies(path) def getAvailableUserHair(self): """Get a list with full paths to all user hair (the MHCLO files)""" path = getpath.getDataPath("hair") return self._findProxies(path) def getAvailableSystemEyebrows(self): """Get a list with full paths to all system eyebrows (the MHCLO files)""" path = getpath.getSysDataPath("eyebrows") return self._findProxies(path) def getAvailableUserEyebrows(self): """Get a list with full paths to all user eyebrows (the MHCLO files)""" path = getpath.getDataPath("eyebrows") return self._findProxies(path) def getAvailableSystemEyelashes(self): """Get a list with full paths to all system eyelashes (the MHCLO files)""" path = getpath.getSysDataPath("eyelashes") return self._findProxies(path) def getAvailableUserEyelashes(self): """Get a list with full paths to all user eyelashes (the MHCLO files)""" path = getpath.getDataPath("eyelashes") return self._findProxies(path) def getAvailableSystemClothes(self): """Get a list with full paths to all system clothes (the MHCLO files)""" path = getpath.getSysDataPath("clothes") return self._findProxies(path) def getAvailableUserClothes(self): """Get a list with full paths to all user clothes (the MHCLO files)""" path = getpath.getDataPath("clothes") return self._findProxies(path) def _equipProxy(self, category, tab, filename): tv = self.api.ui.getTaskView(category, tab) if tv is None: raise ValueError("Could not find taskview " + str(category) + "/" + str(tab)) tv.proxyFileSelected(filename) def _unequipProxy(self, category, tab, filename): tv = self.api.ui.getTaskView(category, tab) if tv is None: raise ValueError("Could not find taskview " + str(category) + "/" + str(tab)) tv.proxyFileDeselected(filename) def _getEquippedProxies(self, category, tab, onlyFirst=False): tv = self.api.ui.getTaskView(category, tab) if tv is None: raise ValueError("Could not find taskview " + str(category) + "/" + str(tab)) ps = tv.selectedProxies if onlyFirst: if ps is None or len(ps) < 1: return None return ps[0].file else: ret = [] if ps is None: return [] for p in ps: ret.append(p.file) return ret def equipHair(self, mhclofile): """Equip a MHCLO file with hair. This will automatically unequip previously equipped hair.""" self._equipProxy("Geometries","Hair",mhclofile) def unequipHair(self, mhclofile): """Unequip a MHCLO file with hair""" self._unequipProxy("Geometries", "Hair", mhclofile) def getEquippedHair(self): """Get the currently equipped hair, if any""" return self._getEquippedProxies("Geometries","Hair",onlyFirst=True) def equipEyebrows(self, mhclofile): """Equip a MHCLO file with eyebrows. This will automatically unequip previously equipped eyebrows.""" self._equipProxy("Geometries", "Eyebrows", mhclofile) def unequipEyebrows(self, mhclofile): """Unequip a MHCLO file with eyebrows""" self._unequipProxy("Geometries", "Eyebrows", mhclofile) def getEquippedEyebrows(self): """Get the currently equipped eyebrows, if any""" return self._getEquippedProxies("Geometries", "Eyebrows", onlyFirst=True) def equipEyelashes(self, mhclofile): """Equip a MHCLO file with eyelashes. This will automatically unequip previously equipped eyelashes.""" self._equipProxy("Geometries", "Eyelashes", mhclofile) def unequipEyelashes(self, mhclofile): """Unequip a MHCLO file with eyelashes""" self._unequipProxy("Geometries", "Eyelashes", mhclofile) def getEquippedEyelashes(self): """Get the currently equipped eyelashes, if any""" return self._getEquippedProxies("Geometries", "Eyelashes", onlyFirst=True) def equipClothes(self, mhclofile): """Equip a MHCLO file with clothes""" self._equipProxy("Geometries", "Clothes", mhclofile) def unequipClothes(self, mhclofile): """Unequip a MHCLO file with clothes""" self._unequipProxy("Geometries", "Clothes", mhclofile) def getEquippedClothes(self): """Get a list of all currently equipped clothes""" return self._getEquippedProxies("Geometries", "Clothes") def unequipAllClothes(self): """Unequip all clothes""" for c in self.getEquippedClothes(): self.unequipClothes(c)