public async Task <bool> SetMaterial(XivMtrl material, IItemModel item, MaterialEditorMode mode = MaterialEditorMode.EditSingle) { _material = material; _item = item; _mode = mode; return(await viewModel.SetMaterial(material, item, mode)); }
private static void ComputeColorsetBlending(XivMtrl mtrl, byte colorsetByte, byte[] colorSetData, Color baseDiffuse, Color baseSpecular, out Color newDiffuse, out Color newSpecular, out Color emissiveColor) { int rowNumber = colorsetByte / 17; int nextRow = rowNumber >= 15 ? 15 : rowNumber + 1; int blendAmount = (colorsetByte % 17); float fBlendAmount = blendAmount / 17.0f; Color diffuse1, diffuse2; Color spec1, spec2; Color emiss1, emiss2; // Byte offset to rows var row1Offset = Clamp(rowNumber * 16); var row2Offset = Clamp(nextRow * 16); diffuse1 = new Color(colorSetData[row1Offset + 0], colorSetData[row1Offset + 1], colorSetData[row1Offset + 2], (byte)255); diffuse2 = new Color(colorSetData[row2Offset + 0], colorSetData[row2Offset + 1], colorSetData[row2Offset + 2], (byte)255); spec1 = new Color(colorSetData[row1Offset + 4], colorSetData[row1Offset + 5], colorSetData[row1Offset + 6], (byte)255); spec2 = new Color(colorSetData[row2Offset + 4], colorSetData[row2Offset + 5], colorSetData[row2Offset + 6], (byte)255); emiss1 = new Color(colorSetData[row1Offset + 8], colorSetData[row1Offset + 9], colorSetData[row1Offset + 10], (byte)255); emiss2 = new Color(colorSetData[row2Offset + 8], colorSetData[row2Offset + 9], colorSetData[row2Offset + 10], (byte)255); // These are now our base values to multiply the base values by. Color diffuse = Blend(diffuse1, diffuse2, fBlendAmount); Color specular = Blend(spec1, spec2, fBlendAmount); Color emissive = Blend(emiss1, emiss2, fBlendAmount); newDiffuse = MultiplyColor(baseDiffuse, diffuse); newSpecular = MultiplyColor(baseSpecular, specular); emissiveColor = emissive; // Nothing to multiply by here. }
/// <summary> /// Converts a DDS file into a mtrl file and returns the raw data /// </summary> /// <param name="xivMtrl">The XivMtrl data of the original</param> /// <param name="ddsFileDirectory">The dds directory of the new ColorSet</param> /// <param name="item">The item</param> /// <returns>The raw mtrl data</returns> public byte[] DDStoMtrlData(XivMtrl xivMtrl, DirectoryInfo ddsFileDirectory, IItem item, XivLanguage lang) { var colorSetData = GetColorsetDataFromDDS(ddsFileDirectory); var colorSetExtraData = new byte[32]; // If the colorset size is 544, it contains extra data that must be imported try { colorSetExtraData = GetColorsetExtraDataFromDDS(ddsFileDirectory); } catch { colorSetExtraData = new byte[32]; } // Replace the color set data with the imported data xivMtrl.ColorSetData = colorSetData; xivMtrl.ColorSetDyeData = colorSetExtraData; if (xivMtrl.Unknown2.Length > 0) { // This byte enables the dye set if it's not already enabled. xivMtrl.Unknown2[0] = 12; } var _mtrl = new Mtrl(XivCache.GameInfo.GameDirectory); return(_mtrl.CreateMtrlFile(xivMtrl, item)); }
public async Task SetMaterial(XivMtrl mtrl, StainingTemplateFile dyeFile) { _mtrl = mtrl; DyeTemplateFile = dyeFile; _viewport = _view.ColorsetRowViewport; _viewport.BackgroundColor = System.Windows.Media.Colors.Gray; _viewport.Background = Brushes.Gray; if (_NeedLights) { _NeedLights = false; } _viewport.Camera.UpDirection = new System.Windows.Media.Media3D.Vector3D(0, 1, 0); _viewport.Camera.LookDirection = new System.Windows.Media.Media3D.Vector3D(0, 0, -1); _viewport.Camera.Position = new System.Windows.Media.Media3D.Point3D(0, 0, 3); if (TileTextureNormal == null) { var _tex = new Tex(XivCache.GameInfo.GameDirectory); TileTextureNormal = await _tex.GetTexData("chara/common/texture/-tile_n.tex"); TileTextureDiffuse = await _tex.GetTexData("chara/common/texture/-tile_d.tex"); } }
/// <summary> /// Converts a DDS file into a mtrl file and returns the raw data /// </summary> /// <param name="xivMtrl">The XivMtrl data of the original</param> /// <param name="ddsFileDirectory">The dds directory of the new ColorSet</param> /// <param name="item">The item</param> /// <returns>The raw mtrl data</returns> public byte[] DDStoMtrlData(XivMtrl xivMtrl, DirectoryInfo ddsFileDirectory, IItem item, XivLanguage lang) { var colorSetData = new List <Half>(); using (var br = new BinaryReader(File.OpenRead(ddsFileDirectory.FullName))) { // skip DDS header br.BaseStream.Seek(128, SeekOrigin.Begin); // color data is always 512 (4w x 16h = 64 x 8bpp = 512) // this reads 256 ushort values which is 256 x 2 = 512 for (var i = 0; i < 256; i++) { colorSetData.Add(new Half(br.ReadUInt16())); } } var colorSetExtraData = new byte[32]; // If the colorset size is 544, it contains extra data that must be imported if (xivMtrl.ColorSetDataSize == 544) { var flagsPath = Path.Combine(Path.GetDirectoryName(ddsFileDirectory.FullName), (Path.GetFileNameWithoutExtension(ddsFileDirectory.FullName) + ".dat")); if (File.Exists(flagsPath)) { colorSetExtraData = File.ReadAllBytes(flagsPath); //using (var br = new BinaryReader(File.OpenRead(flagsPath))) //{ // // The extra data after the colorset is always 32 bytes // // This reads 16 ushort values which is 16 x 2 = 32 // for (var i = 0; i < 16; i++) // { // colorSetData.Add(new Half(br.ReadUInt16())); // } //} } } // Replace the color set data with the imported data xivMtrl.ColorSetData = colorSetData; xivMtrl.ColorSetExtraData = colorSetExtraData; var mtrl = new Mtrl(_gameDirectory, xivMtrl.TextureTypePathList[0].DataFile, lang); return(mtrl.CreateMtrlFile(xivMtrl, item)); }
/// <summary> /// Imports a ColorSet file /// </summary> /// <param name="xivMtrl">The XivMtrl data of the original</param> /// <param name="ddsFileDirectory">The dds directory of the new ColorSet</param> /// <param name="item">The item</param> /// <param name="source">The source importing the file</param> /// <returns>The new offset</returns> public async Task <long> TexColorImporter(XivMtrl xivMtrl, DirectoryInfo ddsFileDirectory, IItem item, string source, XivLanguage lang) { var colorSetData = new List <Half>(); byte[] colorSetExtraData = null; colorSetData = GetColorsetDataFromDDS(ddsFileDirectory); colorSetExtraData = GetColorsetExtraDataFromDDS(ddsFileDirectory); // Replace the color set data with the imported data xivMtrl.ColorSetData = colorSetData; xivMtrl.ColorSetDyeData = colorSetExtraData; if (xivMtrl.Unknown2.Length > 0) { // This byte enables the dye set if it's not already enabled. xivMtrl.Unknown2[0] = 12; } var _mtrl = new Mtrl(XivCache.GameInfo.GameDirectory); return(await _mtrl.ImportMtrl(xivMtrl, item, source)); }
private async void SaveButton_Click(object sender, RoutedEventArgs e) { _material = await viewModel.SaveChanges(); Close(true); }
public async Task <bool> SetMaterial(XivMtrl material, IItemModel item, MaterialEditorMode mode) { if (material == null) { return(false); } _mode = mode; _material = material; _item = item; var gameDirectory = new DirectoryInfo(Properties.Settings.Default.FFXIV_Directory); _mtrl = new Mtrl(gameDirectory, item.DataFile, GetLanguage()); _index = new Index(gameDirectory); _modding = new Modding(gameDirectory); _gear = new Gear(gameDirectory, GetLanguage()); // Drop the multi functions down to singles if they only have one Material to edit anyways. if (_mode == MaterialEditorMode.EditMulti || _mode == MaterialEditorMode.NewMulti) { // This isn't an actual perfect check for if there's only one Variant, but doing so // would be a bit expensive here, and passing it through EditMulti isn't harmful anyways. var sameModelItems = await _item.GetSharedModelItems(); if (sameModelItems.Count == 1) { if (_mode == MaterialEditorMode.EditMulti) { _mode = MaterialEditorMode.EditSingle; } else { _mode = MaterialEditorMode.NewSingle; } } } /* * // Debug code for finding unknown Shader Parameters. * var unknowns = new List<ShaderParameterStruct>(); * foreach(var sp in material.ShaderParameterList) * { * if (!Enum.IsDefined(typeof(MtrlShaderParameterId), sp.ParameterID)) * { * unknowns.Add(sp); * } * } * if(unknowns.Count > 0) * { * // Debug line * var json = JsonConvert.SerializeObject(unknowns.ToArray()); * } */ // Update to new material name switch (_mode) { case MaterialEditorMode.EditSingle: _view.MaterialPathLabel.Text = _material.MTRLPath; break; case MaterialEditorMode.EditMulti: _view.MaterialPathLabel.Text = "Editing Multiple Materials: Material " + _material.GetMaterialIdentifier(); break; case MaterialEditorMode.NewSingle: _view.MaterialPathLabel.Text = "New Material"; break; case MaterialEditorMode.NewMulti: _view.MaterialPathLabel.Text = "New Materials"; break; } var shader = _material.GetShaderInfo(); var normal = _material.GetMapInfo(XivTexType.Normal); var diffuse = _material.GetMapInfo(XivTexType.Diffuse); var specular = _material.GetMapInfo(XivTexType.Specular); var multi = _material.GetMapInfo(XivTexType.Multi); var reflection = _material.GetMapInfo(XivTexType.Reflection); // Show Paths _view.NormalTextBox.Text = normal == null ? "" : normal.path; _view.SpecularTextBox.Text = specular == null ? "" : specular.path; _view.SpecularTextBox.Text = multi == null ? _view.SpecularTextBox.Text : multi.path; _view.DiffuseTextBox.Text = diffuse == null ? "" : diffuse.path; _view.DiffuseTextBox.Text = reflection == null ? _view.DiffuseTextBox.Text : reflection.path; // Add Other option if needed. if (shader.Shader == MtrlShader.Other) { _view.ShaderSource.Add(new KeyValuePair <MtrlShader, string>(MtrlShader.Other, "Other")); } // Show Settings _view.TransparencyComboBox.SelectedValue = shader.TransparencyEnabled; _view.BackfacesComboBox.SelectedValue = shader.RenderBackfaces; _view.ColorsetComboBox.SelectedValue = shader.HasColorset; _view.ShaderComboBox.SelectedValue = shader.Shader; _view.PresetComboBox.SelectedValue = shader.Preset; if (_mode == MaterialEditorMode.NewMulti) { // Bump up the material identifier letter. _newMaterialIdentifier = await GetNewMaterialIdentifier(); _view.MaterialPathLabel.Text = "New Materials: Material " + _newMaterialIdentifier; } else if (_mode == MaterialEditorMode.NewSingle) { _newMaterialIdentifier = await GetNewMaterialIdentifier(); _view.MaterialPathLabel.Text = "New Material: Material " + _newMaterialIdentifier; } // Get the mod entry. if (_mode == MaterialEditorMode.EditSingle || _mode == MaterialEditorMode.EditMulti) { var mod = await _modding.TryGetModEntry(_material.MTRLPath); if (mod != null && mod.enabled) { _view.DisableButton.IsEnabled = true; _view.DisableButton.Visibility = System.Windows.Visibility.Visible; } } return(true); }
public ModelTexture(DirectoryInfo gameDirectory, XivMtrl mtrlData) { _mtrlData = mtrlData; _gameDirectory = gameDirectory; }
/// <summary> /// Toggles Translucency for an item on or off /// </summary> /// <param name="xivMtrl">The XivMtrl containing the mtrl data</param> /// <param name="item">The item to toggle translucency for</param> /// <param name="translucencyEnabled">Flag determining if translucency is to be enabled or disabled</param> public void ToggleTranslucency(XivMtrl xivMtrl, IItem item, bool translucencyEnabled) { xivMtrl.ShaderNumber = !translucencyEnabled ? (short)0x0D : (short)0x1D; ImportMtrl(xivMtrl, item); }
/// <summary> /// Gets the customized texture map data for a model. /// Null custom model colors uses the defaults at ModelTexture.GetCustomColors(). /// </summary> /// <param name="gameDirectory"></param> /// <param name="mtrl"></param> /// <param name="colors"></param> /// <returns></returns> public static async Task <ModelTextureData> GetModelMaps(DirectoryInfo gameDirectory, XivMtrl mtrl, CustomModelColors colors = null) { var tex = new Tex(gameDirectory); return(await GetModelMaps(tex, mtrl)); }
/// <summary> /// Gets the materials for the model /// </summary> /// <returns>A dictionary containing the mesh number(key) and the associated texture data (value)</returns> public static async Task <Dictionary <string, ModelTextureData> > GetMaterials( DirectoryInfo gameDirectory, IItemModel item, XivMdl mdlData, XivRace race) { var textureDataDictionary = new Dictionary <string, ModelTextureData>(); var mtrlDictionary = new Dictionary <string, XivMtrl>(); var mtrl = new Mtrl(gameDirectory); var mtrlFilePaths = mdlData.PathData.MaterialList; var hasColorChangeShader = false; CustomModelColors customColor = new CustomModelColors(); WinColor winColor; var materialNum = 0; foreach (var mtrlFilePath in mtrlFilePaths) { var mtrlItem = new XivGenericItemModel { PrimaryCategory = item.PrimaryCategory, SecondaryCategory = item.SecondaryCategory, TertiaryCategory = item.TertiaryCategory, ModelInfo = new XivModelInfo { SecondaryID = item.ModelInfo.SecondaryID, PrimaryID = item.ModelInfo.PrimaryID, ModelKey = item.ModelInfo.ModelKey, ImcSubsetID = item.ModelInfo.ImcSubsetID }, Name = item.Name }; var modelID = mtrlItem.ModelInfo.PrimaryID; var bodyID = mtrlItem.ModelInfo.SecondaryID; var filePath = mtrlFilePath; if (!filePath.Contains("hou") && mtrlFilePath.Count(x => x == '/') > 1) { filePath = mtrlFilePath.Substring(mtrlFilePath.LastIndexOf("/")); } var typeChar = $"{mtrlFilePath[4]}{mtrlFilePath[9]}"; var raceString = ""; switch (typeChar) { // Character Body case "cb": var body = mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); if (!raceString.Equals("0901") && !raceString.Equals("1001") && !raceString.Equals("1101")) { var gender = 0; if (int.Parse(raceString.Substring(0, 2)) % 2 == 0) { gender = 1; } var settingsRace = GetSettingsRace(gender); race = settingsRace.Race; filePath = mtrlFilePath.Replace(raceString, race.GetRaceCode()).Replace(body, settingsRace.BodyID); body = settingsRace.BodyID; } mtrlItem = new XivGenericItemModel { PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Body, Name = XivStrings.Body, ModelInfo = new XivModelInfo { SecondaryID = int.Parse(body) } }; //winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Skin_Color); //customColor.SkinColor = new SharpDX.Color(winColor.R, winColor.G, winColor.B, winColor.A); break; // Face case "cf": bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("f") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem = new XivGenericItemModel { PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Face, Name = XivStrings.Face, ModelInfo = new XivModelInfo { SecondaryID = bodyID } }; break; // Hair case "ch": bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("h") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem = new XivGenericItemModel { PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Hair, Name = XivStrings.Hair, ModelInfo = new XivModelInfo { SecondaryID = bodyID } }; //winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Hair_Color); //customColor.HairColor = new SharpDX.Color(winColor.R, winColor.G, winColor.B, winColor.A); break; // Tail case "ct": var tempPath = mtrlFilePath.Substring(4); bodyID = int.Parse(tempPath.Substring(tempPath.IndexOf("t") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem = new XivGenericItemModel { PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Tail, Name = XivStrings.Tail, ModelInfo = new XivModelInfo { SecondaryID = bodyID } }; //winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Hair_Color); //customColor.HairColor = new SharpDX.Color(winColor.R, winColor.G, winColor.B, winColor.A); break; // Equipment case "ce": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("e") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem.ModelInfo.PrimaryID = modelID; break; // Accessory case "ca": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("a") + 1, 4)); raceString = mtrlFilePath.Substring(mtrlFilePath.IndexOf("c") + 1, 4); race = XivRaces.GetXivRace(raceString); mtrlItem.ModelInfo.PrimaryID = modelID; break; // Weapon case "wb": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("w") + 1, 4)); bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4)); mtrlItem.ModelInfo.PrimaryID = modelID; mtrlItem.ModelInfo.SecondaryID = bodyID; break; // Monster case "mb": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("_m") + 2, 4)); bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("b") + 1, 4)); mtrlItem.ModelInfo.PrimaryID = modelID; mtrlItem.ModelInfo.SecondaryID = bodyID; break; // DemiHuman case "de": modelID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("d") + 1, 4)); bodyID = int.Parse(mtrlFilePath.Substring(mtrlFilePath.IndexOf("e") + 1, 4)); mtrlItem.ModelInfo.PrimaryID = modelID; mtrlItem.ModelInfo.SecondaryID = bodyID; break; default: break; } var dxVersion = int.Parse(Settings.Default.DX_Version); var mtrlFile = filePath; if (mtrlFile.StartsWith("/")) { mtrlFile = mtrlFile.Remove(0, 1); } XivMtrl mtrlData = null; try { mtrlData = await mtrl.GetMtrlData(mtrlItem, mtrlFile, dxVersion); } catch (Exception e) { if (mtrlItem.ModelInfo.PrimaryID == item.ModelInfo.PrimaryID) { Console.WriteLine(e.Message); throw; } } if (mtrlData == null) { // Fall back to material data from the primary model. try { mtrlData = await mtrl.GetMtrlData(item, mtrlFile, dxVersion); } catch (Exception ee) { Console.WriteLine(ee.Message); throw; } } if (mtrlData == null) { continue; } if (mtrlData.Shader.Contains("colorchange")) { hasColorChangeShader = true; } mtrlDictionary.Add(mtrlFilePath, mtrlData); materialNum++; } foreach (var xivMtrl in mtrlDictionary) { if (hasColorChangeShader) { var modelMaps = await ModelTexture.GetModelMaps(gameDirectory, xivMtrl.Value); textureDataDictionary.Add(xivMtrl.Key, modelMaps); } else { if (item.SecondaryCategory.Equals(XivStrings.Face)) { var path = xivMtrl.Value.MTRLPath; customColor = new CustomModelColors(); if (path.Contains("_iri_")) { winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Iris_Color); } else if (path.Contains("_etc_")) { winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Etc_Color); } else { winColor = (WinColor)ColorConverter.ConvertFromString(Settings.Default.Skin_Color); } //customColor = new CustomModelColors(winColor.R, winColor.G, winColor.B, winColor.A); } var modelMaps = await ModelTexture.GetModelMaps(gameDirectory, xivMtrl.Value, customColor); textureDataDictionary.Add(xivMtrl.Key, modelMaps); } } return(textureDataDictionary); }
/// <summary> /// Gets the MTRL data for the given offset and path /// </summary> /// <param name="mtrlOffset">The offset to the mtrl in the dat file</param> /// <param name="mtrlPath">The full internal game path for the mtrl</param> /// <returns>XivMtrl containing all the mtrl data</returns> public XivMtrl GetMtrlData(int mtrlOffset, string mtrlPath, int dxVersion) { var dat = new Dat(_gameDirectory); var index = new Index(_gameDirectory); // Get uncompressed mtrl data var mtrlData = dat.GetType2Data(mtrlOffset, _dataFile); XivMtrl xivMtrl; using (var br = new BinaryReader(new MemoryStream(mtrlData))) { xivMtrl = new XivMtrl { Signature = br.ReadInt32(), FileSize = br.ReadInt16(), ColorSetDataSize = br.ReadUInt16(), MaterialDataSize = br.ReadUInt16(), TexturePathsDataSize = br.ReadUInt16(), TextureCount = br.ReadByte(), MapCount = br.ReadByte(), ColorSetCount = br.ReadByte(), UnknownDataSize = br.ReadByte(), TextureTypePathList = new List <TexTypePath>(), MTRLPath = mtrlPath }; var pathSizeList = new List <int>(); // get the texture path offsets xivMtrl.TexturePathOffsetList = new List <int>(xivMtrl.TextureCount); xivMtrl.TexturePathUnknownList = new List <short>(xivMtrl.TextureCount); for (var i = 0; i < xivMtrl.TextureCount; i++) { xivMtrl.TexturePathOffsetList.Add(br.ReadInt16()); xivMtrl.TexturePathUnknownList.Add(br.ReadInt16()); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.TexturePathOffsetList[i] - xivMtrl.TexturePathOffsetList[i - 1]); } } // get the map path offsets xivMtrl.MapPathOffsetList = new List <int>(xivMtrl.MapCount); xivMtrl.MapPathUnknownList = new List <short>(xivMtrl.MapCount); for (var i = 0; i < xivMtrl.MapCount; i++) { xivMtrl.MapPathOffsetList.Add(br.ReadInt16()); xivMtrl.MapPathUnknownList.Add(br.ReadInt16()); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.MapPathOffsetList[i - 1]); } else { pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.TexturePathOffsetList[xivMtrl.TextureCount - 1]); } } // get the color set offsets xivMtrl.ColorSetPathOffsetList = new List <int>(xivMtrl.ColorSetCount); xivMtrl.ColorSetPathUnknownList = new List <short>(xivMtrl.ColorSetCount); for (var i = 0; i < xivMtrl.ColorSetCount; i++) { xivMtrl.ColorSetPathOffsetList.Add(br.ReadInt16()); xivMtrl.ColorSetPathUnknownList.Add(br.ReadInt16()); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.ColorSetPathOffsetList[i - 1]); } else { pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.MapPathOffsetList[xivMtrl.MapCount - 1]); } } pathSizeList.Add(xivMtrl.TexturePathsDataSize - xivMtrl.ColorSetPathOffsetList[xivMtrl.ColorSetCount - 1]); var count = 0; // get the texture path strings xivMtrl.TexturePathList = new List <string>(xivMtrl.TextureCount); for (var i = 0; i < xivMtrl.TextureCount; i++) { var texturePath = Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", ""); var dx11FileName = Path.GetFileName(texturePath).Insert(0, "--"); if (index.FileExists(HashGenerator.GetHash(dx11FileName), HashGenerator.GetHash(Path.GetDirectoryName(texturePath).Replace("\\", "/")), _dataFile)) { texturePath = texturePath.Insert(texturePath.LastIndexOf("/") + 1, "--"); } xivMtrl.TexturePathList.Add(texturePath); count++; } // add the textures to the TextureTypePathList xivMtrl.TextureTypePathList.AddRange(GetTexNames(xivMtrl.TexturePathList, _dataFile)); // get the map path strings xivMtrl.MapPathList = new List <string>(xivMtrl.MapCount); for (var i = 0; i < xivMtrl.MapCount; i++) { xivMtrl.MapPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // get the color set path strings xivMtrl.ColorSetPathList = new List <string>(xivMtrl.ColorSetCount); for (var i = 0; i < xivMtrl.ColorSetCount; i++) { xivMtrl.ColorSetPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // If the mtrl file contains a color set, add it to the TextureTypePathList if (xivMtrl.ColorSetDataSize > 0) { var ttp = new TexTypePath { Path = mtrlPath, Type = XivTexType.ColorSet, DataFile = _dataFile }; xivMtrl.TextureTypePathList.Add(ttp); } var shaderPathSize = xivMtrl.MaterialDataSize - xivMtrl.TexturePathsDataSize; xivMtrl.Shader = Encoding.UTF8.GetString(br.ReadBytes(shaderPathSize)).Replace("\0", ""); xivMtrl.Unknown2 = br.ReadBytes(xivMtrl.UnknownDataSize); if (xivMtrl.ColorSetDataSize > 0) { // Color Data is always 512 (6 x 14 = 64 x 8bpp = 512) var colorDataSize = 512; xivMtrl.ColorSetData = new List <Half>(); for (var i = 0; i < colorDataSize / 2; i++) { xivMtrl.ColorSetData.Add(new Half(br.ReadUInt16())); } // If the color set is 544 in length, it has an extra 32 bytes at the end if (xivMtrl.ColorSetDataSize == 544) { xivMtrl.ColorSetExtraData = br.ReadBytes(32); } } xivMtrl.AdditionalDataSize = br.ReadUInt16(); xivMtrl.DataStruct1Count = br.ReadUInt16(); xivMtrl.DataStruct2Count = br.ReadUInt16(); xivMtrl.ParameterStructCount = br.ReadUInt16(); xivMtrl.ShaderNumber = br.ReadUInt16(); xivMtrl.Unknown3 = br.ReadUInt16(); xivMtrl.DataStruct1List = new List <DataStruct1>(xivMtrl.DataStruct1Count); for (var i = 0; i < xivMtrl.DataStruct1Count; i++) { xivMtrl.DataStruct1List.Add(new DataStruct1 { ID = br.ReadUInt32(), Unknown1 = br.ReadUInt32() }); } xivMtrl.DataStruct2List = new List <DataStruct2>(xivMtrl.DataStruct2Count); for (var i = 0; i < xivMtrl.DataStruct2Count; i++) { xivMtrl.DataStruct2List.Add(new DataStruct2 { ID = br.ReadUInt32(), Offset = br.ReadInt16(), Size = br.ReadInt16() }); } xivMtrl.ParameterStructList = new List <ParameterStruct>(xivMtrl.ParameterStructCount); for (var i = 0; i < xivMtrl.ParameterStructCount; i++) { xivMtrl.ParameterStructList.Add(new ParameterStruct { ID = br.ReadUInt32(), Unknown1 = br.ReadInt16(), Unknown2 = br.ReadInt16(), TextureIndex = br.ReadUInt32() }); } xivMtrl.AdditionalData = br.ReadBytes(xivMtrl.AdditionalDataSize); } return(xivMtrl); }
/// <summary> /// Toggles Translucency for an item on or off /// </summary> /// <param name="xivMtrl">The XivMtrl containing the mtrl data</param> /// <param name="item">The item to toggle translucency for</param> /// <param name="translucencyEnabled">Flag determining if translucency is to be enabled or disabled</param> /// <param name="source">The source/application that is writing to the dat.</param> public void ToggleTranslucency(XivMtrl xivMtrl, IItem item, bool translucencyEnabled, string source) { xivMtrl.ShaderNumber = !translucencyEnabled ? (ushort)0x0D : (ushort)0x1D; ImportMtrl(xivMtrl, item, source); }
public ModelTexture(Modding modding, XivMtrl mtrlData) { _modding = modding; _mtrlData = mtrlData; }
/// <summary> /// Imports an MTRL file /// </summary> /// <param name="xivMtrl">The XivMtrl containing the mtrl data</param> /// <param name="item">The item whos mtrl is being imported</param> public void ImportMtrl(XivMtrl xivMtrl, IItem item) { var mtrlBytes = new List <byte>(); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.Signature)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.FileSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.ColorSetDataSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.MaterialDataSize)); mtrlBytes.Add(xivMtrl.TexturePathsDataSize); mtrlBytes.Add(xivMtrl.Unknown); mtrlBytes.Add(xivMtrl.TextureCount); mtrlBytes.Add(xivMtrl.MapCount); mtrlBytes.Add(xivMtrl.ColorSetCount); mtrlBytes.Add(xivMtrl.Unknown1); foreach (var texPath in xivMtrl.TexturePathOffsetList) { mtrlBytes.AddRange(BitConverter.GetBytes(texPath)); } foreach (var mapPath in xivMtrl.MapPathOffsetList) { mtrlBytes.AddRange(BitConverter.GetBytes(mapPath)); } foreach (var colorPath in xivMtrl.ColorSetPathOffsetList) { mtrlBytes.AddRange(BitConverter.GetBytes(colorPath)); } foreach (var texPathString in xivMtrl.TexturePathList) { mtrlBytes.AddRange(Encoding.UTF8.GetBytes(texPathString)); mtrlBytes.Add(0); } foreach (var mapPathString in xivMtrl.MapPathList) { mtrlBytes.AddRange(Encoding.UTF8.GetBytes(mapPathString)); mtrlBytes.Add(0); } foreach (var colorSetPathString in xivMtrl.ColorSetPathList) { mtrlBytes.AddRange(Encoding.UTF8.GetBytes(colorSetPathString)); mtrlBytes.Add(0); } mtrlBytes.AddRange(Encoding.UTF8.GetBytes(xivMtrl.Shader)); mtrlBytes.Add(0); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.Unknown2)); foreach (var colorSetHalf in xivMtrl.ColorSetData) { mtrlBytes.AddRange(BitConverter.GetBytes(colorSetHalf.RawValue)); } mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.AdditionalDataSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.DataStruct1Count)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.DataStruct2Count)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.ParameterStructCount)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.ShaderNumber)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.Unknown3)); foreach (var dataStruct1 in xivMtrl.DataStruct1List) { mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct1.ID)); mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct1.Unknown1)); } foreach (var dataStruct2 in xivMtrl.DataStruct2List) { mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct2.ID)); mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct2.Offset)); mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct2.Size)); } foreach (var parameterStruct in xivMtrl.ParameterStructList) { mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.ID)); mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.Unknown1)); mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.Unknown2)); mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.TextureIndex)); } mtrlBytes.AddRange(xivMtrl.AdditionalData); var dat = new Dat(_gameDirectory); dat.ImportType2Data(mtrlBytes.ToArray(), item.Name, xivMtrl.MTRLPath, item.Category); }
/// <summary> /// Gets the MTRL data for the given item /// </summary> /// <remarks> /// It requires a race (The default is usually <see cref="XivRace.Hyur_Midlander_Male"/>) /// It also requires an mtrl part <see cref="GearInfo.GetPartList(IItemModel, XivRace)"/> (default is 'a') /// </remarks> /// <param name="itemModel">Item that contains model data</param> /// <param name="race">The race for the requested data</param> /// <param name="part">The Mtrl part </param> /// <returns></returns> public XivMtrl GetMtrlData(IItemModel itemModel, XivRace race, char part) { var index = new Index(_gameDirectory); var dat = new Dat(_gameDirectory); var itemType = ItemType.GetItemType(itemModel); // Get mtrl path var mtrlPath = GetMtrlPath(itemModel, race, part, itemType); // Get mtrl offset var mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlPath.Folder), HashGenerator.GetHash(mtrlPath.File), _dataFile); if (mtrlOffset == 0) { throw new Exception($"Could not find offest for {mtrlPath.Folder}/{mtrlPath.File}"); } // Get uncompressed mtrl data var mtrlData = dat.GetType2Data(mtrlOffset, _dataFile); XivMtrl xivMtrl; using (var br = new BinaryReader(new MemoryStream(mtrlData))) { xivMtrl = new XivMtrl { Signature = br.ReadInt32(), FileSize = br.ReadInt16(), ColorSetDataSize = br.ReadInt16(), MaterialDataSize = br.ReadInt16(), TexturePathsDataSize = br.ReadByte(), Unknown = br.ReadByte(), TextureCount = br.ReadByte(), MapCount = br.ReadByte(), ColorSetCount = br.ReadByte(), Unknown1 = br.ReadByte(), TextureTypePathList = new List <TexTypePath>(), MTRLPath = $"{mtrlPath.Folder}/{mtrlPath.File}" }; var pathSizeList = new List <int>(); // get the texture path offsets xivMtrl.TexturePathOffsetList = new List <int>(xivMtrl.TextureCount); for (var i = 0; i < xivMtrl.TextureCount; i++) { xivMtrl.TexturePathOffsetList.Add(br.ReadInt16()); br.ReadBytes(2); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.TexturePathOffsetList[i] - xivMtrl.TexturePathOffsetList[i - 1]); } } // get the map path offsets xivMtrl.MapPathOffsetList = new List <int>(xivMtrl.MapCount); for (var i = 0; i < xivMtrl.MapCount; i++) { xivMtrl.MapPathOffsetList.Add(br.ReadInt16()); br.ReadBytes(2); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.MapPathOffsetList[i - 1]); } else { pathSizeList.Add(xivMtrl.MapPathOffsetList[i] - xivMtrl.TexturePathOffsetList[xivMtrl.TextureCount - 1]); } } // get the color set offsets xivMtrl.ColorSetPathOffsetList = new List <int>(xivMtrl.ColorSetCount); for (var i = 0; i < xivMtrl.ColorSetCount; i++) { xivMtrl.ColorSetPathOffsetList.Add(br.ReadInt16()); br.ReadBytes(2); // add the size of the paths if (i > 0) { pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.ColorSetPathOffsetList[i - 1]); } else { pathSizeList.Add(xivMtrl.ColorSetPathOffsetList[i] - xivMtrl.MapPathOffsetList[xivMtrl.MapCount - 1]); } } pathSizeList.Add(xivMtrl.TexturePathsDataSize - xivMtrl.ColorSetPathOffsetList[xivMtrl.ColorSetCount - 1]); var count = 0; // get the texture path strings xivMtrl.TexturePathList = new List <string>(xivMtrl.TextureCount); for (var i = 0; i < xivMtrl.TextureCount; i++) { xivMtrl.TexturePathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // add the textures to the TextureTypePathList xivMtrl.TextureTypePathList.AddRange(GetTexNames(xivMtrl.TexturePathList, _dataFile)); // get the map path strings xivMtrl.MapPathList = new List <string>(xivMtrl.MapCount); for (var i = 0; i < xivMtrl.MapCount; i++) { xivMtrl.MapPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // get the color set path strings xivMtrl.ColorSetPathList = new List <string>(xivMtrl.ColorSetCount); for (var i = 0; i < xivMtrl.ColorSetCount; i++) { xivMtrl.ColorSetPathList.Add(Encoding.UTF8.GetString(br.ReadBytes(pathSizeList[count])).Replace("\0", "")); count++; } // If the mtrl file contains a color set, add it to the TextureTypePathList if (xivMtrl.ColorSetDataSize > 0) { var ttp = new TexTypePath { Path = mtrlPath.Folder + "/" + mtrlPath.File, Type = XivTexType.ColorSet, DataFile = _dataFile }; xivMtrl.TextureTypePathList.Add(ttp); } var shaderPathSize = xivMtrl.MaterialDataSize - xivMtrl.TexturePathsDataSize; xivMtrl.Shader = Encoding.UTF8.GetString(br.ReadBytes(shaderPathSize)).Replace("\0", ""); xivMtrl.Unknown2 = br.ReadInt32(); xivMtrl.ColorSetData = new List <Half>(); for (var i = 0; i < xivMtrl.ColorSetDataSize / 2; i++) { xivMtrl.ColorSetData.Add(new Half(br.ReadUInt16())); } xivMtrl.AdditionalDataSize = br.ReadInt16(); xivMtrl.DataStruct1Count = br.ReadInt16(); xivMtrl.DataStruct2Count = br.ReadInt16(); xivMtrl.ParameterStructCount = br.ReadInt16(); xivMtrl.ShaderNumber = br.ReadInt16(); xivMtrl.Unknown3 = br.ReadInt16(); xivMtrl.DataStruct1List = new List <DataStruct1>(xivMtrl.DataStruct1Count); for (var i = 0; i < xivMtrl.DataStruct1Count; i++) { xivMtrl.DataStruct1List.Add(new DataStruct1 { ID = br.ReadUInt32(), Unknown1 = br.ReadUInt32() }); } xivMtrl.DataStruct2List = new List <DataStruct2>(xivMtrl.DataStruct2Count); for (var i = 0; i < xivMtrl.DataStruct2Count; i++) { xivMtrl.DataStruct2List.Add(new DataStruct2 { ID = br.ReadUInt32(), Offset = br.ReadInt16(), Size = br.ReadInt16() }); } xivMtrl.ParameterStructList = new List <ParameterStruct>(xivMtrl.ParameterStructCount); for (var i = 0; i < xivMtrl.ParameterStructCount; i++) { xivMtrl.ParameterStructList.Add(new ParameterStruct { ID = br.ReadUInt32(), Unknown1 = br.ReadInt16(), Unknown2 = br.ReadInt16(), TextureIndex = br.ReadUInt32() }); } xivMtrl.AdditionalData = br.ReadBytes(xivMtrl.AdditionalDataSize); } return(xivMtrl); }
/// <summary> /// Retreives the raw pixel data for each texture, collated into a class to hold them. /// </summary> /// <returns>The texure map data</returns> private static async Task <TexMapData> GetTexMapData(Tex tex, XivMtrl mtrl) { var texMapData = new TexMapData(); // Use the function that returns proper sane reuslts. var ttps = mtrl.GetTextureTypePathList(); foreach (var ttp in ttps) { if (ttp.Type != XivTexType.ColorSet) { var texData = await tex.GetTexData(ttp); var imageData = await tex.GetImageData(texData); switch (ttp.Type) { case XivTexType.Diffuse: texMapData.Diffuse = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData }; break; case XivTexType.Specular: case XivTexType.Multi: case XivTexType.Skin: texMapData.Specular = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; case XivTexType.Normal: texMapData.Normal = new TexInfo { Width = texData.Width, Height = texData.Height, Data = imageData };; break; default: // Do not render textures that we do not know how to use break; } } } if (mtrl.ColorSetDataSize > 0) { var colorSetData = new List <byte>(); foreach (var half in mtrl.ColorSetData) { var colorByte = (byte)(half * 255); if (half > 1) { colorByte = 255; } colorSetData.Add(colorByte); } texMapData.ColorSet = new TexInfo { Width = 4, Height = 16, Data = colorSetData.ToArray() };; } return(texMapData); }
/// <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); }
private void CopyMaterialButton_Click(object sender, RoutedEventArgs e) { _copiedMaterial = _material; PasteMaterialButton.IsEnabled = true; }
/// <summary> /// Creates an MTRL file /// </summary> /// <param name="xivMtrl">The XivMtrl containing the mtrl data</param> /// <param name="item">The item</param> /// <returns>The new mtrl file byte data</returns> public byte[] CreateMtrlFile(XivMtrl xivMtrl, IItem item) { var mtrlBytes = new List <byte>(); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.Signature)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.FileSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.ColorSetDataSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.MaterialDataSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.TexturePathsDataSize)); mtrlBytes.Add(xivMtrl.TextureCount); mtrlBytes.Add(xivMtrl.MapCount); mtrlBytes.Add(xivMtrl.ColorSetCount); mtrlBytes.Add(xivMtrl.UnknownDataSize); for (int i = 0; i < xivMtrl.TexturePathOffsetList.Count; i++) { mtrlBytes.AddRange(BitConverter.GetBytes((short)xivMtrl.TexturePathOffsetList[i])); mtrlBytes.AddRange(BitConverter.GetBytes((short)xivMtrl.TexturePathUnknownList[i])); } for (int i = 0; i < xivMtrl.MapPathOffsetList.Count; i++) { mtrlBytes.AddRange(BitConverter.GetBytes((short)xivMtrl.MapPathOffsetList[i])); mtrlBytes.AddRange(BitConverter.GetBytes((short)xivMtrl.MapPathUnknownList[i])); } for (int i = 0; i < xivMtrl.ColorSetPathOffsetList.Count; i++) { mtrlBytes.AddRange(BitConverter.GetBytes((short)xivMtrl.ColorSetPathOffsetList[i])); mtrlBytes.AddRange(BitConverter.GetBytes((short)xivMtrl.ColorSetPathUnknownList[i])); } var pathStringList = new List <byte>(); foreach (var texPathString in xivMtrl.TexturePathList) { pathStringList.AddRange(Encoding.UTF8.GetBytes(texPathString)); pathStringList.Add(0); } foreach (var mapPathString in xivMtrl.MapPathList) { pathStringList.AddRange(Encoding.UTF8.GetBytes(mapPathString)); pathStringList.Add(0); } foreach (var colorSetPathString in xivMtrl.ColorSetPathList) { pathStringList.AddRange(Encoding.UTF8.GetBytes(colorSetPathString)); pathStringList.Add(0); } pathStringList.AddRange(Encoding.UTF8.GetBytes(xivMtrl.Shader)); pathStringList.Add(0); var paddingSize = xivMtrl.MaterialDataSize - pathStringList.Count; pathStringList.AddRange(new byte[paddingSize]); mtrlBytes.AddRange(pathStringList); mtrlBytes.AddRange(xivMtrl.Unknown2); foreach (var colorSetHalf in xivMtrl.ColorSetData) { mtrlBytes.AddRange(BitConverter.GetBytes(colorSetHalf.RawValue)); } if (xivMtrl.ColorSetDataSize == 544) { mtrlBytes.AddRange(xivMtrl.ColorSetExtraData); } mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.AdditionalDataSize)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.DataStruct1Count)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.DataStruct2Count)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.ParameterStructCount)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.ShaderNumber)); mtrlBytes.AddRange(BitConverter.GetBytes(xivMtrl.Unknown3)); foreach (var dataStruct1 in xivMtrl.DataStruct1List) { mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct1.ID)); mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct1.Unknown1)); } foreach (var dataStruct2 in xivMtrl.DataStruct2List) { mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct2.ID)); mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct2.Offset)); mtrlBytes.AddRange(BitConverter.GetBytes(dataStruct2.Size)); } foreach (var parameterStruct in xivMtrl.ParameterStructList) { mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.ID)); mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.Unknown1)); mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.Unknown2)); mtrlBytes.AddRange(BitConverter.GetBytes(parameterStruct.TextureIndex)); } mtrlBytes.AddRange(xivMtrl.AdditionalData); return(mtrlBytes.ToArray()); }
/// <summary> /// Sets the material and selects a given row (or row 0) /// </summary> /// <param name="mtrl"></param> /// <param name="row"></param> /// <returns></returns> public async Task SetMaterial(XivMtrl mtrl, int row = 0) { if (mtrl == null) { return; } _LOADING = true; var appStyle = ThemeManager.DetectAppStyle(Application.Current); Brush bgBrush = MainWindow.GetMainWindow().Background; Brush fgBrush = MainWindow.GetMainWindow().Foreground; DiffuseColorPicker.Background = bgBrush; DiffuseColorPicker.Foreground = fgBrush; SpecularColorPicker.Background = bgBrush; SpecularColorPicker.Foreground = fgBrush; EmissiveColorPicker.Background = bgBrush; EmissiveColorPicker.Foreground = fgBrush; DiffuseColorPicker.DropDownBackground = bgBrush; DiffuseColorPicker.HeaderForeground = fgBrush; DiffuseColorPicker.TabForeground = fgBrush; DiffuseColorPicker.TabBackground = bgBrush; DiffuseColorPicker.HeaderBackground = bgBrush; SpecularColorPicker.DropDownBackground = bgBrush; SpecularColorPicker.HeaderForeground = fgBrush; SpecularColorPicker.TabForeground = fgBrush; SpecularColorPicker.TabBackground = bgBrush; SpecularColorPicker.HeaderBackground = bgBrush; EmissiveColorPicker.DropDownBackground = bgBrush; EmissiveColorPicker.HeaderForeground = fgBrush; EmissiveColorPicker.TabForeground = fgBrush; EmissiveColorPicker.TabBackground = bgBrush; EmissiveColorPicker.HeaderBackground = bgBrush; try { DyeTemplateFile = await STM.GetStainingTemplateFile(false); DyeTemplateCollection.Clear(); DyePreviewIdBox.SelectedValue = -1; var keys = DyeTemplateFile.GetKeys(); DyeTemplateCollection.Add(new KeyValuePair <ushort, string>(0, "Undyable")); foreach (var key in keys) { DyeTemplateCollection.Add(new KeyValuePair <ushort, string>(key, key.ToString())); } if (CopiedRow == null) { PasteRowButton.IsEnabled = false; } else { PasteRowButton.IsEnabled = true; } var dyes = await STM.GetDyeNames(); PreviewDyeCollection.Clear(); PreviewDyeCollection.Add(new KeyValuePair <int, string>(-1, "Undyed")); for (ushort i = 0; i < 128; i++) { var name = "Dye " + i.ToString(); if (dyes.ContainsKey(i)) { name = dyes[i]; } PreviewDyeCollection.Add(new KeyValuePair <int, string>(i, name)); } DyePreviewIdBox.SelectedValue = -1; _mtrl = mtrl; await _vm.SetMaterial(_mtrl, DyeTemplateFile); await SetRow(row); for (int i = 0; i < 16; i++) { await UpdateRowVisual(i); } } catch (Exception ex) { FlexibleMessageBox.Show("Unable to load material into colorset editor.\n\nError: " + ex.Message, "Colorset Editor Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning); } _LOADING = false; }
/// <summary> /// Imports a ColorSet file /// </summary> /// <param name="xivMtrl">The XivMtrl data of the original</param> /// <param name="ddsFileDirectory">The dds directory of the new ColorSet</param> /// <param name="item">The item</param> /// <param name="source">The source importing the file</param> /// <returns>The new offset</returns> public async Task <int> TexColorImporter(XivMtrl xivMtrl, DirectoryInfo ddsFileDirectory, IItem item, string source, XivLanguage lang) { var colorSetData = new List <Half>(); byte[] colorSetExtraData = null; using (var br = new BinaryReader(File.OpenRead(ddsFileDirectory.FullName))) { // Check DDS type br.BaseStream.Seek(84, SeekOrigin.Begin); var texType = br.ReadInt32(); XivTexFormat textureType; if (DDSType.ContainsKey(texType)) { textureType = DDSType[texType]; } else { throw new Exception($"DDS Type ({texType}) not recognized. Expecting A16B16G16R16F."); } if (textureType != XivTexFormat.A16B16G16R16F) { throw new Exception($"Incorrect file type. Expected: A16B16G16R16F Given: {textureType}"); } // Skip past rest of the DDS header br.BaseStream.Seek(128, SeekOrigin.Begin); // color data is always 512 (4w x 16h = 64 x 8bpp = 512) // this reads 256 ushort values which is 256 x 2 = 512 for (var i = 0; i < 256; i++) { colorSetData.Add(new Half(br.ReadUInt16())); } } // If the colorset size is 544, it contains extra data that must be imported if (xivMtrl.ColorSetDataSize == 544) { var flagsPath = Path.Combine(Path.GetDirectoryName(ddsFileDirectory.FullName), (Path.GetFileNameWithoutExtension(ddsFileDirectory.FullName) + ".dat")); if (File.Exists(flagsPath)) { // The extra data after the colorset is always 32 bytes // This reads 16 ushort values which is 16 x 2 = 32 colorSetExtraData = File.ReadAllBytes(flagsPath); // If for whatever reason there is a .dat file but it's missing data if (colorSetExtraData.Length != 32) { // Set all dye modifiers to 0 (undyeable) colorSetExtraData = new byte[32]; } } else { // If .dat file is missing set all values to 0 (undyeable) colorSetExtraData = new byte[32]; } } // Replace the color set data with the imported data xivMtrl.ColorSetData = colorSetData; xivMtrl.ColorSetExtraData = colorSetExtraData; var mtrl = new Mtrl(_gameDirectory, xivMtrl.TextureTypePathList[0].DataFile, lang); return(await mtrl.ImportMtrl(xivMtrl, item, source)); }