public async Task SaveMulti() { var _imc = new Imc(XivCache.GameInfo.GameDirectory); var _index = new Index(XivCache.GameInfo.GameDirectory); // Get tokenized map info structs. // This will let us set them in the new Materials and // Detokenize them using the new paths. var mapInfos = _material.GetAllMapInfos(true); // Shader info likewise will be pumped into each new material. var shaderInfo = _material.GetShaderInfo(); // Add new Materials for shared model items. var oldMaterialIdentifier = _material.GetMaterialIdentifier(); var oldMtrlName = Path.GetFileName(_material.MTRLPath); // Ordering these by name ensures that we create textures for the new variants in the first // item alphabetically, just for consistency's sake. var sameModelItems = (await _item.GetSharedModelItems()).OrderBy(x => x.Name, new ItemNameComparer()); var oldVariantString = "/v" + _material.GetVariant().ToString().PadLeft(4, '0') + '/'; var modifiedVariants = new List <int>(); var mtrlReplacementRegex = "_" + oldMaterialIdentifier + ".mtrl"; var mtrlReplacementRegexResult = "_" + _newMaterialIdentifier + ".mtrl"; if (_mode == MaterialEditorMode.NewRace) { mtrlReplacementRegexResult = mtrlReplacementRegex; } var newMtrlName = oldMtrlName.Replace(mtrlReplacementRegex, mtrlReplacementRegexResult); var root = _item.GetRootInfo(); var imcEntries = new List <XivImc>(); var materialSets = new HashSet <byte>(); try { var imcInfo = await _imc.GetFullImcInfo(_item); imcEntries = imcInfo.GetAllEntries(root.Slot, true); materialSets = imcEntries.Select(x => x.MaterialSet).ToHashSet(); } catch { // Item doesn't use IMC entries, and thus only has a single variant. materialSets.Clear(); materialSets.Add(0); } // We need to save our non-existent base material once before we can continue. if (_mode == MaterialEditorMode.NewRace) { await _mtrl.ImportMtrl(_material, _item, XivStrings.TexTools); } var count = 0; var allItems = (await root.ToFullRoot().GetAllItems()); var matNumToItems = new Dictionary <int, List <IItemModel> >(); foreach (var i in allItems) { if (imcEntries.Count <= i.ModelInfo.ImcSubsetID) { continue; } var matSet = imcEntries[i.ModelInfo.ImcSubsetID].MaterialSet; if (!matNumToItems.ContainsKey(matSet)) { matNumToItems.Add(matSet, new List <IItemModel>()); } var saveItem = i; if (typeof(XivCharacter) == i.GetType()) { var temp = (XivCharacter)((XivCharacter)_item).Clone(); temp.Name = saveItem.SecondaryCategory; saveItem = temp; } matNumToItems[matSet].Add(saveItem); } var keys = matNumToItems.Keys.ToList(); foreach (var key in keys) { var list = matNumToItems[key]; matNumToItems[key] = list.OrderBy(x => x.Name, new ItemNameComparer()).ToList(); } // Load and modify all the MTRLs. foreach (var materialSetId in materialSets) { var variantPath = _mtrl.GetMtrlFolder(root, materialSetId); var oldMaterialPath = variantPath + "/" + oldMtrlName; var newMaterialPath = variantPath + "/" + newMtrlName; // Don't create materials for set 0. (SE sets the material ID to 0 when that particular set-slot doesn't actually exist as an item) if (materialSetId == 0 && imcEntries.Count > 0) { continue; } var dxVersion = 11; XivMtrl itemXivMtrl; // Get mtrl path if (await _index.FileExists(oldMaterialPath)) { // Use the Material from this variant as a base? itemXivMtrl = await _mtrl.GetMtrlData(_item, oldMaterialPath, dxVersion); } else { itemXivMtrl = await _mtrl.GetMtrlData(_item, _material.MTRLPath, dxVersion); } // If we're an item that doesn't use IMC variants, make sure we don't accidentally move the material around. if (materialSetId != 0) { // Shift the MTRL to the new variant folder. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, oldVariantString, "/v" + materialSetId.ToString().PadLeft(4, '0') + "/"); } if (_mode == MaterialEditorMode.NewMulti) { // Change the MTRL part identifier. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, mtrlReplacementRegex, mtrlReplacementRegexResult); } // Load the Shader Settings itemXivMtrl.SetShaderInfo(shaderInfo, true); // Loop our tokenized map infos and pump them back in // using the new modified material to detokenize them. foreach (var info in mapInfos) { itemXivMtrl.SetMapInfo(info.Usage, (MapInfo)info.Clone()); } IItem item; try { item = matNumToItems[materialSetId].First(); } catch { item = (await XivCache.GetFirstRoot(itemXivMtrl.MTRLPath)).GetFirstItem(); } count++; // Write the new Material await _mtrl.ImportMtrl(itemXivMtrl, item, XivStrings.TexTools); _view.SaveStatusLabel.Content = "Updated " + count + "/" + materialSets.Count + " Material Sets..."; } }
public async Task SaveMulti() { // Get tokenized map info structs. // This will let us set them in the new Materials and // Detokenize them using the new paths. var mapInfos = _material.GetAllMapInfos(true); // Shader info likewise will be pumped into each new material. var shaderInfo = _material.GetShaderInfo(); // Add new Materials for shared model items. var oldMaterialIdentifier = _material.GetMaterialIdentifier(); // Ordering these by name ensures that we create textures for the new variants in the first // item alphabetically, just for consistency's sake. var sameModelItems = (await _item.GetSharedModelItems()).OrderBy(x => x.Name, new ItemNameComparer()); var oldVariantString = "/v" + _material.GetVariant().ToString().PadLeft(4, '0') + '/'; var modifiedVariants = new List <int>(); var mtrlReplacementRegex = "_" + oldMaterialIdentifier + ".mtrl"; var mtrlReplacementRegexResult = "_" + _newMaterialIdentifier + ".mtrl"; // Load and modify all the MTRLs. foreach (var item in sameModelItems) { // Resolve this item's material variant. // - This isn't always the same as the item model variant, for some reason. // - So it has to be resolved manually. var variantMtrlPath = ""; var itemType = ItemType.GetPrimaryItemType(_item); variantMtrlPath = (await _mtrl.GetMtrlPath(item, _material.GetRace(), oldMaterialIdentifier, itemType)).Folder; var match = Regex.Match(variantMtrlPath, "/v([0-9]+)"); var variant = 0; if (match.Success) { variant = Int32.Parse(match.Groups[1].Value); } // Only modify each Variant once. if (modifiedVariants.Contains(variant)) { continue; } var dxVersion = 11; XivMtrl itemXivMtrl; // Get mtrl path -- TODO: Need support here for offhand item materials. // But Offhand support is basically completely broken anyways, so this can wait. itemXivMtrl = await _mtrl.GetMtrlData(_item, _material.GetRace(), oldMaterialIdentifier, dxVersion); // Shift the MTRL to the new variant folder. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, oldVariantString, "/v" + variant.ToString().PadLeft(4, '0') + "/"); if (_mode == MaterialEditorMode.NewMulti) { // Change the MTRL part identifier. itemXivMtrl.MTRLPath = Regex.Replace(itemXivMtrl.MTRLPath, mtrlReplacementRegex, mtrlReplacementRegexResult); } // Load the Shader Settings itemXivMtrl.SetShaderInfo(shaderInfo, true); // Loop our tokenized map infos and pump them back in // using the new modified material to detokenize them. foreach (var info in mapInfos) { itemXivMtrl.SetMapInfo(info.Usage, (MapInfo)info.Clone()); } // Write the new Material await _mtrl.ImportMtrl(itemXivMtrl, item, XivStrings.TexTools); modifiedVariants.Add(variant); _view.SaveStatusLabel.Content = "Updated " + modifiedVariants.Count + " Variants..."; } }
/// <summary> /// Gets the texture maps for the model /// </summary> /// <returns>The texture maps in byte arrays inside a ModelTextureData class</returns> public static async Task <ModelTextureData> GetModelMaps(Tex tex, XivMtrl mtrl, CustomModelColors colors = null) { // Use static values as needed. if (colors == null) { colors = GetCustomColors(); } var shaderInfo = mtrl.GetShaderInfo(); var mtrlMaps = mtrl.GetAllMapInfos(); var texMapData = await GetTexMapData(tex, mtrl); var dimensions = await EqualizeTextureSizes(texMapData); var diffuseMap = new List <byte>(); var normalMap = new List <byte>(); var specularMap = new List <byte>(); var emissiveMap = new List <byte>(); var alphaMap = new List <byte>(); var diffuseColorList = new List <Color>(); var specularColorList = new List <Color>(); var emissiveColorList = new List <Color>(); byte[] diffusePixels = null, specularPixels = null, normalPixels = null; if (texMapData.Normal != null) { normalPixels = texMapData.Normal.Data; } if (texMapData.Diffuse != null) { diffusePixels = texMapData.Diffuse.Data; } if (texMapData.Specular != null) { specularPixels = texMapData.Specular.Data; } if (normalPixels == null && diffusePixels == null) { // This material doesn't actually have any readable data. var empty = new ModelTextureData { Width = 0, Height = 0, Normal = new byte[0], Diffuse = new byte[0], Specular = new byte[0], Emissive = new byte[0], Alpha = new byte[0], MaterialPath = mtrl.MTRLPath.Substring(mtrl.MTRLPath.LastIndexOf('/')) }; return(empty); } var dataLength = normalPixels != null ? normalPixels.Length : diffusePixels.Length; await Task.Run(() => { for (var i = 3; i < dataLength; i += 4) { // Load the individual pixels into memory. Color baseNormalColor = new Color(127, 127, 255, 255); Color baseDiffuseColor = new Color(255, 255, 255, 255); Color baseSpecularColor = new Color(255, 255, 255, 255); if (normalPixels != null) { baseNormalColor = new Color(normalPixels[i - 3], normalPixels[i - 2], normalPixels[i - 1], normalPixels[i]); } if (diffusePixels != null) { baseDiffuseColor = new Color(diffusePixels[i - 3], diffusePixels[i - 2], diffusePixels[i - 1], diffusePixels[i]); } if (specularPixels != null) { baseSpecularColor = new Color(specularPixels[i - 3], specularPixels[i - 2], specularPixels[i - 1], specularPixels[i]); } byte colorsetValue = baseNormalColor.A; // Calculate real colors from the inputs and shader. Color normalColor, diffuseColor, specularColor; byte opacity; ComputeShaderColors(colors, shaderInfo, baseNormalColor, baseDiffuseColor, baseSpecularColor, out normalColor, out diffuseColor, out specularColor, out opacity); Color alphaColor = new Color(opacity, opacity, opacity, opacity); // Apply colorset if needed. (This could really be baked into ComputeShaderColors) Color emissiveColor = new Color(0, 0, 0, 0); if (mtrl.ColorSetData.Count > 0) { var cs = texMapData.ColorSet.Data; Color finalDiffuseColor, finalSpecularColor; ComputeColorsetBlending(mtrl, colorsetValue, cs, diffuseColor, specularColor, out finalDiffuseColor, out finalSpecularColor, out emissiveColor); diffuseColor = finalDiffuseColor; specularColor = finalSpecularColor; } // White out the opacity channels where appropriate. diffuseColor.A = opacity; specularColor.A = 255; normalColor.A = 255; diffuseMap.AddRange(BitConverter.GetBytes(diffuseColor.ToRgba())); specularMap.AddRange(BitConverter.GetBytes(specularColor.ToRgba())); emissiveMap.AddRange(BitConverter.GetBytes(emissiveColor.ToRgba())); alphaMap.AddRange(BitConverter.GetBytes(alphaColor.ToRgba())); normalMap.AddRange(BitConverter.GetBytes(normalColor.ToRgba())); } }); var modelTextureData = new ModelTextureData { Width = dimensions.Width, Height = dimensions.Height, Normal = normalMap.ToArray(), Diffuse = diffuseMap.ToArray(), Specular = specularMap.ToArray(), Emissive = emissiveMap.ToArray(), Alpha = alphaMap.ToArray(), MaterialPath = mtrl.MTRLPath.Substring(mtrl.MTRLPath.LastIndexOf('/')) }; return(modelTextureData); }