Skip to content

Commit bddd9e8

Browse files
committed
Always import models and export textures using appropriate racial material for the model.
1 parent e160bfd commit bddd9e8

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

xivModdingFramework/Materials/FileTypes/Mtrl.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,7 @@ private async Task<List<TexTypePath>> GetTexNames(IEnumerable<string> texPathLis
935935
private readonly Regex _raceRegex = new Regex("(c[0-9]{4})");
936936
private readonly Regex _weaponMatch = new Regex("(w[0-9]{4})");
937937
private readonly Regex _tailMatch = new Regex("(t[0-9]{4})");
938+
private readonly Regex _raceMatch = new Regex("(c[0-9]{4})");
938939
private readonly Regex _skinRegex = new Regex("^/mt_c([0-9]{4})b([0-9]{4})_.+\\.mtrl$");
939940
/// <summary>
940941
/// Resolves the MTRL path for a given MDL path.
@@ -945,13 +946,24 @@ private async Task<List<TexTypePath>> GetTexNames(IEnumerable<string> texPathLis
945946
/// <returns></returns>
946947
public string GetMtrlPath(string mdlPath, string mtrlName, int mtrlVariant = 1)
947948
{
948-
var match = _skinRegex.Match(mtrlName);
949-
949+
950+
951+
// So we have to do this step first.
952+
var mdlMatch = _raceMatch.Match(mdlPath);
953+
var mtrlMatch = _raceMatch.Match(mtrlName);
954+
955+
// Both Items have racial model information in their path, and the races DON'T match.
956+
if (mdlMatch.Success && mtrlMatch.Success && mdlMatch.Groups[1].Value != mtrlMatch.Groups[1].Value)
957+
{
958+
// In this case, we actually replace the race in the Material the race from the MODEL, which has priority.
959+
mtrlName = mtrlName.Replace(mtrlMatch.Groups[1].Value, mdlMatch.Groups[1].Value);
960+
}
961+
950962

951963
var mtrlFolder = "";
952964

953-
// Skin is the only known exception case. Skin materials are auto resolved out of their item root path
954-
// and into the character base directory.
965+
// Now then, skin materials resolve to their racial path, always.
966+
var match = _skinRegex.Match(mtrlName);
955967
if (match.Success)
956968
{
957969
var race = match.Groups[1].Value;
@@ -965,6 +977,7 @@ public string GetMtrlPath(string mdlPath, string mtrlName, int mtrlVariant = 1)
965977
// This a furniture item or something else that specifies an explicit full material path.
966978
// We can just return that.
967979
return mtrlName;
980+
968981
} else if (mdlPath.Contains("/face/f") || mdlPath.Contains("/zear/z")) {
969982

970983
// Faces and ears don't use material variants.
@@ -973,10 +986,11 @@ public string GetMtrlPath(string mdlPath, string mtrlName, int mtrlVariant = 1)
973986
var baseFolder = mdlFolder.Substring(0, mdlFolder.LastIndexOf("/"));
974987
mtrlFolder = baseFolder + "/material";
975988
}
989+
976990
else {
977991

978-
var mdlMatch = _raceRegex.Match(mdlPath);
979-
var mtrlMatch = _raceRegex.Match(mtrlName);
992+
mdlMatch = _raceRegex.Match(mdlPath);
993+
mtrlMatch = _raceRegex.Match(mtrlName);
980994

981995
// Both items have racaial information in their path, and the races DON'T match.
982996
if(mdlMatch.Success && mtrlMatch.Success && mdlMatch.Groups[1].Value != mtrlMatch.Groups[1].Value)
@@ -1008,6 +1022,7 @@ public string GetMtrlPath(string mdlPath, string mtrlName, int mtrlVariant = 1)
10081022
mdlPath = mdlPath.Replace(mdlMatch.Groups[1].Value, mtrlMatch.Groups[1].Value);
10091023
}
10101024

1025+
10111026
var mdlFolder = Path.GetDirectoryName(mdlPath);
10121027
mdlFolder = mdlFolder.Replace("\\", "/");
10131028

xivModdingFramework/Models/FileTypes/Mdl.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
using xivModdingFramework.Variants.FileTypes;
5454
using SixLabors.ImageSharp.Formats.Png;
5555
using System.Data;
56+
using System.Text.RegularExpressions;
5657

5758
namespace xivModdingFramework.Models.FileTypes
5859
{
@@ -1869,6 +1870,7 @@ public async Task ImportModel(IItemModel item, XivRace race, string path, ModelM
18691870
// Wrapping this in an await ensures we're run asynchronously on a new thread.
18701871
await Task.Run(async () =>
18711872
{
1873+
var filePath = currentMdl.MdlPath;
18721874

18731875
#region TTModel Loading
18741876
// Probably could stand to push this out to its own function later.
@@ -1910,12 +1912,15 @@ await Task.Run(async () =>
19101912
}
19111913
#endregion
19121914

1915+
19131916
var sane = TTModel.SanityCheck(ttModel, loggingFunction);
1914-
if(!sane)
1917+
if (!sane)
19151918
{
19161919
throw new InvalidDataException("Model was deemed invalid.");
19171920
}
19181921

1922+
1923+
19191924
// At this point we now have a fully populated TTModel entry.
19201925
// Time to pull in the Model Modifier for any extra steps before we pass
19211926
// it to the raw MDL creation function.
@@ -1956,6 +1961,10 @@ await Task.Run(async () =>
19561961
}
19571962
}
19581963

1964+
// Fix up the skin references, just because we can/it helps user expectation.
1965+
// Doesn't really matter as these get auto-resolved in game no matter what race they point to.
1966+
ModelModifiers.FixUpSkinReferences(ttModel, filePath, loggingFunction);
1967+
19591968
// Time to create the raw MDL.
19601969
loggingFunction(false, "Creating MDL file from processed data...");
19611970
var bytes = await MakeNewMdlFile(ttModel, currentMdl, loggingFunction);
@@ -1968,7 +1977,6 @@ await Task.Run(async () =>
19681977
var modEntry = await modding.TryGetModEntry(mdlPath);
19691978

19701979

1971-
var filePath = currentMdl.MdlPath;
19721980

19731981
if (!rawDataOnly)
19741982
{

xivModdingFramework/Models/Helpers/ModelModifiers.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,42 @@ public static void CalculateTangentsFromBinormals(TTModel model, Action<bool, st
10921092
}
10931093
}
10941094

1095+
1096+
1097+
/// <summary>
1098+
/// Fixes up the racial skin references in the model's materials.
1099+
/// this isn't actually necessary as the game will auto-resolve these regardless, but it's nice to do.
1100+
/// </summary>
1101+
/// <param name="model"></param>
1102+
/// <param name="newInternalPath"></param>
1103+
public static void FixUpSkinReferences(TTModel model, string newInternalPath, Action<bool, string> loggingFunction = null)
1104+
{
1105+
1106+
// Here we should to go in and correct any Skin Material references to point to the skin material for this race.
1107+
// It's not actually -NEEDED-, as the game will dynamically resolve them anyways to the player's skin material, but it's good for user expectation and sanity.
1108+
1109+
var raceRegex = new Regex("(c[0-9]{4})");
1110+
// So we have to do this step first.
1111+
var newRaceMatch = raceRegex.Match(newInternalPath);
1112+
1113+
if(!newRaceMatch.Success)
1114+
{
1115+
return;
1116+
}
1117+
1118+
loggingFunction(false, "Fixing up racial skin references...");
1119+
1120+
foreach (var m in model.MeshGroups)
1121+
{
1122+
var mtrlMatch = raceRegex.Match(m.Material);
1123+
if(mtrlMatch.Success)
1124+
{
1125+
m.Material.Replace(mtrlMatch.Groups[1].Value, newRaceMatch.Groups[1].Value);
1126+
}
1127+
}
1128+
1129+
}
1130+
10951131
/// <summary>
10961132
/// /dev/null function used when no logging parameter is supplied,
10971133
/// just to make sure we don't have to constantly null-check the logging functions.

0 commit comments

Comments
 (0)