/// <summary> /// Gets the first ancestor with a skin. /// </summary> /// <param name="race"></param> /// <returns></returns> public static XivRace GetSkinRace(this XivRace race) { var node = GetNode(race); if (node == null) { return(XivRace.Hyur_Midlander_Male); } // Roe F is very weird and uses Highlander F's skin materials, // but Midlander F's models. Blame SE hard-coding shit. if (node.Race == XivRace.Roegadyn_Female) { return(XivRace.Hyur_Highlander_Female); } if (node.HasSkin) { return(node.Race); } while (node.Parent != null) { node = node.Parent; if (node.HasSkin) { return(node.Race); } } return(XivRace.Hyur_Midlander_Male); }
/// <summary> /// Gets the Part for a given Character Item /// </summary> /// <remarks> /// For Body and Tail Character Items since they don't have Types /// </remarks> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <returns>A dictionary containging [</returns> public char[] GetPartForTextures(XivCharacter charaItem, XivRace race, int num) { var index = new Index(_gameDirectory); var folder = ""; var file = ""; var parts = new[] { 'a', 'b', 'c', 'd', 'e', 'f' }; if (charaItem.ItemCategory == XivStrings.Body) { folder = string.Format(XivStrings.BodyMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.BodyMtrlFile; } else if (charaItem.ItemCategory == XivStrings.Tail) { folder = string.Format(XivStrings.TailMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.TailMtrlFile; } var fileList = index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); return((from part in parts let mtrlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), part) where fileList.Contains(HashGenerator.GetHash(mtrlFile)) select part).ToArray()); }
/// <summary> /// Determines if this race is a parent of another given race or not. /// If the values are the same, it is considered TRUE by default. /// </summary> /// <param name="possibleParent"></param> /// <param name="possibleChild"></param> /// <returns></returns> public static bool IsParentOf(this XivRace possibleParent, XivRace possibleChild, bool allowSame = true) { CheckTree(); if (possibleChild == possibleParent && allowSame) { return(true); } var node = GetNode(possibleChild); if (node == null) { return(false); } while (node.Parent != null) { node = node.Parent; if (node.Race == possibleParent) { return(true); } } return(false); }
/// <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="mtrlFile">The Mtrl file</param> /// <returns>XivMtrl containing all the mtrl data</returns> public XivMtrl GetMtrlData(IItemModel itemModel, XivRace race, string mtrlFile, int dxVersion) { var index = new Index(_gameDirectory); var itemType = ItemType.GetItemType(itemModel); // Get mtrl path var mtrlFolder = GetMtrlFolder(itemModel, race, itemType); var mtrlStringPath = $"{mtrlFolder}/{mtrlFile}"; if (itemType == XivItemType.furniture) { mtrlStringPath = $"b{mtrlFile}"; mtrlFolder = Path.GetDirectoryName(mtrlStringPath).Replace("\\", "/"); mtrlFile = Path.GetFileName(mtrlStringPath); } // Get mtrl offset var mtrlOffset = index.GetDataOffset(HashGenerator.GetHash(mtrlFolder), HashGenerator.GetHash(mtrlFile), _dataFile); if (mtrlOffset == 0) { throw new Exception($"Could not find offest for {mtrlStringPath}"); } var mtrlData = GetMtrlData(mtrlOffset, mtrlStringPath, dxVersion); return(mtrlData); }
/// <summary> /// Gets the first ancestor with a skin. /// </summary> /// <param name="race"></param> /// <returns></returns> public static XivRace GetSkinRace(this XivRace race) { var node = GetNode(race); if (node == null) { return(XivRace.Hyur_Midlander_Male); } if (node.HasSkin) { return(node.Race); } while (node.Parent != null) { node = node.Parent; if (node.HasSkin) { return(node.Race); } } return(XivRace.Hyur_Midlander_Male); }
/// <summary> /// Gets the Display Name of the Race from the Resource file in order to support localization /// </summary> /// <param name="value">The enum value</param> /// <returns>The localized display name of the race</returns> public static string GetDisplayName(this XivRace value) { var rm = new ResourceManager(typeof(XivStrings)); var displayName = rm.GetString(value.ToString()); return(displayName); }
/// <summary> /// Gets the description from the enum value, in this case the Race Code /// </summary> /// <param name="value">The enum value</param> /// <returns>The race code</returns> public static string GetRaceCode(this XivRace value) { var field = value.GetType().GetField(value.ToString()); var attribute = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false); return(attribute.Length > 0 ? attribute[0].Description : value.ToString()); }
private static Dictionary <XivRace, ExtraSkeletonEntry> DeserializeEstData(byte[] data, uint dataVersion) { if (dataVersion == 1) { // Version 1 didn't include EST data, so just get the defaults. return(null); //// await Est.GetExtraSkeletonEntries(root); } // 6 Bytes per entry. int count = data.Length / 6; Dictionary <XivRace, ExtraSkeletonEntry> ret = new Dictionary <XivRace, ExtraSkeletonEntry>(count); for (int i = 0; i < count; i++) { int offset = i * 6; ushort raceCode = BitConverter.ToUInt16(data, offset); ushort setId = BitConverter.ToUInt16(data, offset + 2); ushort skelId = BitConverter.ToUInt16(data, offset + 4); XivRace race = GetXivRace(raceCode); ret.Add(race, new ExtraSkeletonEntry(race, setId, skelId)); } return(ret); }
private static string GetRaceCode(XivRace value) { FieldInfo field = value.GetType().GetField(value.ToString()); DescriptionAttribute[] attribute = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false); return(attribute.Length > 0 ? attribute[0].Description : value.ToString()); }
/// <summary> /// Applies the deformer to a model /// </summary> /// <param name="model">The model being deformed</param> /// <param name="currentRace">The current model race</param> /// <param name="targetRace">The target race to convert the model to</param> private static void ApplyDeformers(TTModel model, XivRace currentRace, XivRace targetRace) { // Current race is already parent node // Direct conversion // [ Current > (apply deform) > Target ] if (currentRace.IsDirectParentOf(targetRace)) { ModelModifiers.ApplyRacialDeform(model, targetRace); } // Target race is parent node of Current race // Convert to parent (invert deform) // [ Current > (apply inverse deform) > Target ] else if (targetRace.IsDirectParentOf(currentRace)) { ModelModifiers.ApplyRacialDeform(model, currentRace, true); } // Current race is not parent of Target Race and Current race has parent // Make a recursive call with the current races parent race // [ Current > (apply inverse deform) > Current.Parent > Recursive Call ] else if (currentRace.GetNode().Parent != null) { ModelModifiers.ApplyRacialDeform(model, currentRace, true); ApplyDeformers(model, currentRace.GetNode().Parent.Race, targetRace); } // Current race has no parent // Make a recursive call with the target races parent race // [ Target > (apply deform on Target.Parent) > Target.Parent > Recursive Call ] else { ModelModifiers.ApplyRacialDeform(model, targetRace.GetNode().Parent.Race); ApplyDeformers(model, targetRace.GetNode().Parent.Race, targetRace); } }
private static IItemModel GetHairModel(XivRace race, byte hair) { int raceCode = int.Parse(race.GetRaceCode()); foreach (IItem item in allItems) { if (item is IItemModel itemModel) { if (item.PrimaryCategory != "Character") { continue; } if (item.SecondaryCategory != "Hair") { continue; } if (itemModel.ModelInfo.PrimaryID != raceCode) { continue; } XivCharacter faceItem = (XivCharacter)itemModel.Clone(); faceItem.ModelInfo = new XivModelInfo(); faceItem.ModelInfo.SecondaryID = hair; return(faceItem); } } throw new Exception($"Failed to find hair model: {race}, {hair}"); }
private static IItemModel GetFaceModel(XivRace race, byte head, byte eyebrows, byte eyes, byte nose, byte jaw, byte mouth) { int raceCode = int.Parse(race.GetRaceCode()); foreach (IItem item in allItems) { if (item is IItemModel itemModel) { if (item.PrimaryCategory != "Character") { continue; } if (item.SecondaryCategory != "Face") { continue; } if (itemModel.ModelInfo.PrimaryID != raceCode) { continue; } XivCharacter faceItem = (XivCharacter)itemModel.Clone(); faceItem.ModelInfo = new XivModelInfo(); faceItem.ModelInfo.SecondaryID = head; return(faceItem); } } throw new Exception($"Failed to find face model: {race}, {head}"); }
private SimpleModPackEntries GetEntry(Mod mod, Modding modding) { if (mod.fullPath.Equals(string.Empty)) { return(null); } SimpleModPackEntries entry = new SimpleModPackEntries(); entry.Name = mod.name; entry.Category = mod.category; XivRace race = GetRace(mod.fullPath); entry.Race = race.GetDisplayName(); entry.Type = GetType(mod.fullPath); entry.Part = GetPart(mod.fullPath); entry.Num = GetNumber(mod.fullPath); entry.Map = GetMap(mod.fullPath); entry.Active = mod.enabled || mod.data.modOffset == mod.data.originalOffset; entry.ModEntry = mod; return(entry); }
/// <summary> /// Gets the Part for a given Character Item /// </summary> /// <remarks> /// For Body and Tail Character Items since they don't have Types /// </remarks> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <param name="variant">the variant for the mtrl folder</param> /// <returns>An array of characters containing the parts for the texture</returns> public async Task <char[]> GetPartForTextures(XivCharacter charaItem, XivRace race, int num, int variant = 1) { var folder = ""; var file = ""; var parts = Constants.Alphabet; if (charaItem.SecondaryCategory == XivStrings.Body) { folder = string.Format(XivStrings.BodyMtrlFolderVar, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), variant.ToString().PadLeft(4, '0')); file = XivStrings.BodyMtrlFile; } else if (charaItem.SecondaryCategory == XivStrings.Tail) { folder = string.Format(XivStrings.TailMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.TailMtrlFile; } else if (charaItem.SecondaryCategory == XivStrings.Ear) { folder = string.Format(XivStrings.EarsMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); file = XivStrings.EarsMtrlFile; } var fileList = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); return((from part in parts let mtrlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), part) where fileList.Contains(HashGenerator.GetHash(mtrlFile)) select part).ToArray()); }
/// <summary> /// Gets the full list of children for this node. /// </summary> /// <param name="race"></param> /// <param name="includeNPCs"></param> /// <returns></returns> public static List <XivRace> GetChildren(this XivRace race, bool includeNPCs = false) { CheckTree(); var node = GetNode(race); var name = node.Race.ToString(); // Skip NPCs if (name.Contains("NPC") && !includeNPCs) { return(new List <XivRace>()); } // Return ourselves if no children. if (node.Children == null || node.Children.Count == 0) { return new List <XivRace>() { race } } ; // Recursion for children. var children = new List <XivRace>(); foreach (var c in node.Children) { children.AddRange(GetChildren(c.Race, includeNPCs)); } // Final return. return(children); }
/// <summary> /// Gets the raw equipment or accessory deformation parameters file for a given race. /// </summary> /// <returns></returns> private async Task <List <byte> > LoadEquipmentDeformationFile(XivRace race, bool accessory = false) { var rootPath = accessory ? AccessoryDeformerParameterRootPath : EquipmentDeformerParameterRootPath; var fileName = rootPath + "c" + race.GetRaceCode() + "." + EquipmentDeformerParameterExtension; return(new List <byte>(await _dat.GetType2Data(fileName, false))); }
public AdvancedModelImportView(XivMdl xivMdl, IItemModel itemModel, XivRace selectedRace) { InitializeComponent(); var advancedImportViewModel = new AdvancedImportViewModel(xivMdl, itemModel, selectedRace, this); this.DataContext = advancedImportViewModel; }
private bool IsRaceEnabled(XivRace race) { if (_metadata.EqdpEntries.ContainsKey(race)) { return(_metadata.EqdpEntries[race].bit1); } return(false); }
/// <summary> /// Retrieves the base race enum value for this race/clan/gender race. /// Used for CMP files and a few other things. /// </summary> /// <param name="race"></param> /// <returns></returns> public static XivBaseRace GetBaseRace(this XivRace race) { switch (race) { case XivRace.Hyur_Midlander_Male: case XivRace.Hyur_Midlander_Female: case XivRace.Hyur_Midlander_Male_NPC: case XivRace.Hyur_Midlander_Female_NPC: case XivRace.Hyur_Highlander_Male: case XivRace.Hyur_Highlander_Female: case XivRace.Hyur_Highlander_Male_NPC: case XivRace.Hyur_Highlander_Female_NPC: return(XivBaseRace.Hyur); case XivRace.Elezen_Male: case XivRace.Elezen_Female: case XivRace.Elezen_Male_NPC: case XivRace.Elezen_Female_NPC: return(XivBaseRace.Elezen); case XivRace.Lalafell_Male: case XivRace.Lalafell_Female: case XivRace.Lalafell_Male_NPC: case XivRace.Lalafell_Female_NPC: return(XivBaseRace.Lalafell); case XivRace.Miqote_Male: case XivRace.Miqote_Female: case XivRace.Miqote_Male_NPC: case XivRace.Miqote_Female_NPC: return(XivBaseRace.Miqote); case XivRace.Roegadyn_Male: case XivRace.Roegadyn_Female: case XivRace.Roegadyn_Male_NPC: case XivRace.Roegadyn_Female_NPC: return(XivBaseRace.Roegadyn); case XivRace.AuRa_Male: case XivRace.AuRa_Female: case XivRace.AuRa_Male_NPC: case XivRace.AuRa_Female_NPC: return(XivBaseRace.AuRa); case XivRace.Viera_Male: case XivRace.Viera_Female: case XivRace.Viera_Male_NPC: case XivRace.Viera_Female_NPC: return(XivBaseRace.Viera); case XivRace.Hrothgar_Male: case XivRace.Hrothgar_Male_NPC: return(XivBaseRace.Hrothgar); default: return(XivBaseRace.Hyur); } }
/// <summary> /// Gets the Type of models for a given Character Item /// </summary> /// <param name="charaItem">The character item</param> /// <param name="race">The race</param> /// <param name="num">The character item number</param> /// <returns>A dictionary containging [</returns> public async Task <List <string> > GetTypeForModels(XivCharacter charaItem, XivRace race, int num) { var folder = ""; var file = ""; var typeDict = HairSlotAbbreviationDictionary; if (charaItem.ItemCategory == XivStrings.Body) { folder = string.Format(XivStrings.BodyMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = BodySlotAbbreviationDictionary; file = XivStrings.BodyMDLFile; } else if (charaItem.ItemCategory == XivStrings.Hair) { folder = string.Format(XivStrings.HairMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = HairSlotAbbreviationDictionary; file = XivStrings.HairMDLFile; } else if (charaItem.ItemCategory == XivStrings.Face) { folder = string.Format(XivStrings.FaceMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = FaceSlotAbbreviationDictionary; file = XivStrings.FaceMDLFile; } else if (charaItem.ItemCategory == XivStrings.Tail) { folder = string.Format(XivStrings.TailMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = TailSlotAbbreviationDictionary; file = XivStrings.TailMDLFile; } else if (charaItem.ItemCategory == XivStrings.Ears) { folder = string.Format(XivStrings.EarsMDLFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0')); typeDict = EarsSlotAbbreviationDictionary; file = XivStrings.EarsMDLFile; } var fileList = await _index.GetAllHashedFilesInFolder(HashGenerator.GetHash(folder), XivDataFile._04_Chara); var typeList = new List <string>(); foreach (var type in typeDict) { var mdlFile = string.Format(file, race.GetRaceCode(), num.ToString().PadLeft(4, '0'), type.Value); if (fileList.Contains(HashGenerator.GetHash(mdlFile))) { typeList.Add(type.Key); } } return(typeList); }
/// <summary> /// Gets the matrices for the bones used in the model /// </summary> /// <param name="boneList">List of bones used in the model</param> /// <param name="targetRace">Target Race to get the bone data for</param> /// <returns>A matrix array containing the pose data for each bone</returns> private Matrix[] GetMatrices(List <string> boneList, XivRace targetRace) { // Get the skeleton file for the target race var skeletonFile = Directory.GetCurrentDirectory() + $"/Skeletons/c{targetRace.GetRaceCode()}.skel"; var skeletonData = File.ReadAllLines(skeletonFile); // Deserialize the skeleton json and create a dictionary of all bones var skelData = new Dictionary <string, SkeletonData>(); foreach (var b in skeletonData) { if (b == "") { continue; } var j = JsonConvert.DeserializeObject <SkeletonData>(b); skelData.Add(j.BoneName, j); } // Add matrices for bones in the model to a list // Add missing bones if they exist in the model but not in the target race var matrixList = new List <Matrix>(); var missingBones = new List <string>(); foreach (var bone in boneList) { if (skelData.ContainsKey(bone)) { var matrix = new Matrix(skelData[bone].InversePoseMatrix); matrix.Invert(); matrixList.Add(matrix); } else { missingBones.Add(bone); } } // Show a warning when there is bones in the model that do not exist in the target race skeleton // The model will still be added, but with no bones animation will not work for that part if (missingBones.Count > 0) { var warning = new StringBuilder(); warning.AppendLine(); foreach (var bone in missingBones) { warning.AppendLine(bone); } FlexibleMessageBox.Show(string.Format(UIMessages.MissingBones, targetRace, warning), UIMessages.MissingBonesTitle, MessageBoxButtons.OK, MessageBoxIcon.Warning); } return(matrixList.ToArray()); }
/// <summary> /// Creates the Bones to be used in the format Helix Toolkit uses /// </summary> /// <param name="boneList">The list of bones in the model</param> /// <param name="targetRace">The target race to get the bones from</param> /// <returns>A list of Bone structures used by Helix Toolkit</returns> private List <Bone> MakeHelixBones(List <string> boneList, XivRace targetRace) { // Get the skeleton file for the target race var skeletonFile = Directory.GetCurrentDirectory() + $"/Skeletons/c{targetRace.GetRaceCode()}.skel"; var skeletonData = File.ReadAllLines(skeletonFile); // Deserialize the skeleton json and create a dictionary of all bones var boneDict = new Dictionary <int, SkeletonData>(); foreach (var b in skeletonData) { if (b == "") { continue; } var j = JsonConvert.DeserializeObject <SkeletonData>(b); boneDict.Add(j.BoneNumber, j); } // Add only the bones that are contained in the model including all parent bones var bonesInModel = new List <SkeletonData>(); foreach (var bone in boneDict) { if (!boneList.Contains(bone.Value.BoneName)) { continue; } bonesInModel.Add(bone.Value); AddBones(boneDict, bonesInModel, bone.Value); } // Create a bone list with the bones for the model in helix toolkit format var helixBoneList = new List <Bone>(); foreach (var bone in bonesInModel) { var bp = new Matrix(bone.InversePoseMatrix); bp.Invert(); helixBoneList.Add(new Bone { BindPose = bp, Name = bone.BoneName, ParentIndex = bone.BoneParent }); } return(helixBoneList); }
/// <summary> /// Determines if this race is a child of another given race or not. /// If the values are the same, it is considered TRUE by default. /// </summary> /// <param name="possibleChild"></param> /// <param name="possibleParent"></param> /// <returns></returns> public static bool IsChildOf(this XivRace possibleChild, XivRace possibleParent, bool allowSame = true) { CheckTree(); if (possibleChild == possibleParent && allowSame) { return(true); } var isParent = IsParentOf(possibleParent, possibleChild, allowSame); return(isParent); }
public AdvancedModelImportView(XivMdl xivMdl, IItemModel itemModel, XivRace selectedRace, bool fromWizard) { InitializeComponent(); _fromWizard = fromWizard; _viewModel = new AdvancedImportViewModel(xivMdl, itemModel, selectedRace, this, fromWizard); this.DataContext = _viewModel; if (fromWizard) { Title = FFXIV_TexTools.Resources.UIStrings.Advanced_Model_Options; ImportButton.Content = FFXIV_TexTools.Resources.UIStrings.Add; } }
/// <summary> /// Updates all models to the new skeleton /// </summary> /// <param name="previousRace">The original or previous race of the model</param> /// <param name="targetRace">The target race for the skeleton and model</param> public void UpdateSkin(XivRace race) { var shownModelList = new List <string>(); foreach (var model in shownModels) { shownModelList.Add(model.Key); } foreach (var model in shownModelList) { UpdateModel(shownModels[model].TtModel, shownModels[model].ModelTextureData, shownModels[model].ItemModel, race, race); } }
public AdvancedModelImportView(XivMdl xivMdl, IItemModel itemModel, XivRace selectedRace, bool fromWizard) { InitializeComponent(); _fromWizard = fromWizard; _viewModel = new AdvancedImportViewModel(xivMdl, itemModel, selectedRace, this, fromWizard); this.DataContext = _viewModel; if (fromWizard) { Title = "Advanced Model Options"; ImportButton.Content = "Add"; } }
/// <summary> /// Gets the internal FFXIV MTRL path for a given race's skin, using the tree as needed to find the appropriate ancestor skin. /// </summary> /// <param name="race"></param> /// <returns></returns> public static string GetSkinPath(this XivRace startingRace, int body = 1, string materialId = "a") { var race = GetSkinRace(startingRace); if (race != startingRace) { body = 1; } var bodyCode = body.ToString().PadLeft(4, '0'); var path = "/chara/human/c" + race.GetRaceCode() + "/obj/body/b" + bodyCode + "/material/v0001/mt_c" + race.GetRaceCode() + "b" + bodyCode + "_" + materialId + ".mtrl"; return(path); }
static void BatchExport() { _repo = new ExportRepository(Path.Combine(_repoPath, "repo")); // Gear var badGear = new HashSet <string>(new[] { "Doman Iron Hatchet", "Doman Iron Pickaxe", "Mammon Lucis", "Kurdalegon Lucis", "Rauni Lucis", "Kurdalegon Supra", "Rauni Supra", "SmallClothes Body", "SmallClothes Feet", "SmallClothes Legs" }); var gearList = _gear.GetGearList() .Where(g => !badGear.Contains(g.Name)); foreach (var item in gearList) { var primaryPath = EnsurePath(item.EquipSlotCategory.ToString(), item.ModelInfo); BatchExportItem(primaryPath, item, null, () => _gear.GetRacesForModels(item, item.DataFile)); if (item.SecondaryModelInfo.ModelID != 0) { var secondaryPath = EnsurePath(item.EquipSlotCategory.ToString(), item.SecondaryModelInfo); BatchExportItem(secondaryPath, item, item.SecondaryModelInfo, () => _gear.GetRacesForModels(item, item.DataFile)); } } // Minions var monsters = new XivRace[] { XivRace.Monster }; var minionList = _companions.GetMinionList(); foreach (var minion in minionList) { var modelKey = $"{minion.ModelInfo.ModelID}-{minion.ModelInfo.Body}-{minion.ModelInfo.Variant}"; var path = EnsurePath("minion", modelKey); BatchExportItem(path, minion, null, () => monsters); } // Mounts var mountList = _companions.GetMountList(); foreach (var mount in mountList) { var modelKey = $"{mount.ModelInfo.ModelID}-{mount.ModelInfo.Body}-{mount.ModelInfo.Variant}"; var path = EnsurePath("mount", modelKey); BatchExportItem(path, mount, null, () => monsters); } }
/// <summary> /// Updates all models to the new skeleton /// </summary> /// <param name="previousRace">The original or previous race of the model</param> /// <param name="targetRace">The target race for the skeleton and model</param> public void UpdateSkeleton(XivRace previousRace, XivRace targetRace) { var shownModelList = new List <string>(); foreach (var model in shownModels) { shownModelList.Add(model.Key); } // Apply racial transforms // This pretty much replaces every model by deleting and recreating them with the target race deforms foreach (var model in shownModelList) { UpdateModel(shownModels[model].TtModel, shownModels[model].ModelTextureData, shownModels[model].ItemModel, previousRace, targetRace); } }
/// <summary> /// Gets the matrices for the bones used in the model /// </summary> /// <param name="boneList">List of bones used in the model</param> /// <param name="targetRace">Target Race to get the bone data for</param> /// <returns>A matrix array containing the pose data for each bone</returns> private Matrix[] GetMatrices(XivRace targetRace) { // Get the skeleton, including all EX bones. var boneDict = GetBoneDictionary(targetRace); var matrixList = new List <Matrix>(); foreach (var kv in boneDict) { var matrix = new Matrix(kv.Value.InversePoseMatrix); matrix.Invert(); matrixList.Add(matrix); } return(matrixList.ToArray()); }