예제 #1
0
        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...";
            }
        }
예제 #3
0
        /// <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);
        }