Skip to content

Commit 8c0fae0

Browse files
committed
Update 2.1.0.3
2 parents 29e8c5b + bddd9e8 commit 8c0fae0

File tree

5 files changed

+125
-25
lines changed

5 files changed

+125
-25
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/DataContainers/TTModel.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,39 @@ private Dictionary<string, SkeletonData> ResolveBoneHeirarchy()
11601160
return skelDict;
11611161
}
11621162

1163+
1164+
/// <summary>
1165+
/// Performs a basic sanity check on an incoming TTModel
1166+
/// Returns true if there were no errors or errors that were resolvable.
1167+
/// Returns false if the model was deemed insane.
1168+
/// </summary>
1169+
/// <param name="model"></param>
1170+
/// <param name="loggingFunction"></param>
1171+
/// <returns></returns>
1172+
public static bool SanityCheck(TTModel model, Action<bool, string> loggingFunction = null)
1173+
{
1174+
if (loggingFunction == null)
1175+
{
1176+
loggingFunction = ModelModifiers.NoOp;
1177+
}
1178+
1179+
if(model.MeshGroups.Count == 0)
1180+
{
1181+
loggingFunction(true, "Model has no data. - Model must have at least one valid Mesh Group.");
1182+
return false;
1183+
}
1184+
1185+
1186+
var Group0Valid = model.MeshGroups[0].Parts.Any(x => x.Vertices.Count > 0);
1187+
if(!Group0Valid)
1188+
{
1189+
loggingFunction(true, "Mesh Group 0 has no valid parts - Model must have at least one vertex in Mesh Group 0.");
1190+
return false;
1191+
}
1192+
1193+
return true;
1194+
}
1195+
11631196
#endregion
11641197
}
11651198
}

xivModdingFramework/Models/FileTypes/Mdl.cs

Lines changed: 25 additions & 13 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
{
@@ -424,6 +425,7 @@ public async Task<XivMdl> GetRawMdlData(string mdlPath, bool getOriginal = false
424425
if (modded)
425426
{
426427
offset = mod.data.originalOffset;
428+
modded = false;
427429
}
428430
}
429431

@@ -1868,6 +1870,7 @@ public async Task ImportModel(IItemModel item, XivRace race, string path, ModelM
18681870
// Wrapping this in an await ensures we're run asynchronously on a new thread.
18691871
await Task.Run(async () =>
18701872
{
1873+
var filePath = currentMdl.MdlPath;
18711874

18721875
#region TTModel Loading
18731876
// Probably could stand to push this out to its own function later.
@@ -1909,27 +1912,33 @@ await Task.Run(async () =>
19091912
}
19101913
#endregion
19111914

1912-
if (ttModel.MeshGroups.Count == 0)
1915+
1916+
var sane = TTModel.SanityCheck(ttModel, loggingFunction);
1917+
if (!sane)
19131918
{
1914-
throw new InvalidDataException("The imported model has no data.");
1919+
throw new InvalidDataException("Model was deemed invalid.");
19151920
}
1921+
1922+
1923+
19161924
// At this point we now have a fully populated TTModel entry.
19171925
// Time to pull in the Model Modifier for any extra steps before we pass
19181926
// it to the raw MDL creation function.
19191927

19201928
loggingFunction(false, "Merging in existing Attribute & Material Data...");
19211929

19221930
XivMdl ogMdl = null;
1923-
if (options.EnableShapeData && !ttModel.HasShapeData)
1931+
1932+
// Load the original model if we're actually going to need it.
1933+
var mod = await modding.TryGetModEntry(mdlPath);
1934+
if (mod != null)
19241935
{
1925-
// Load the original model if we're actually going to need it.
1926-
var mod = await modding.TryGetModEntry(mdlPath);
1927-
if (mod != null)
1928-
{
1929-
loggingFunction(false, "Loading original SE model to retrieve Shape Data...");
1930-
var ogOffset = mod.data.originalOffset;
1931-
ogMdl = await GetRawMdlData(item, IOUtil.GetRaceFromPath(mdlPath), submeshId, true);
1932-
}
1936+
loggingFunction(false, "Loading original SE model...");
1937+
var ogOffset = mod.data.originalOffset;
1938+
ogMdl = await GetRawMdlData(item, IOUtil.GetRaceFromPath(mdlPath), submeshId, true);
1939+
} else
1940+
{
1941+
ogMdl = currentMdl;
19331942
}
19341943

19351944
// Apply our Model Modifier options to the model.
@@ -1942,7 +1951,7 @@ await Task.Run(async () =>
19421951
loggingFunction(false, "Waiting on user...");
19431952

19441953
// Bool says whether or not we should continue.
1945-
var oldModel = TTModel.FromRaw(currentMdl);
1954+
var oldModel = TTModel.FromRaw(ogMdl);
19461955
bool cont = await intermediaryFunction(ttModel, oldModel);
19471956
if (!cont)
19481957
{
@@ -1952,6 +1961,10 @@ await Task.Run(async () =>
19521961
}
19531962
}
19541963

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+
19551968
// Time to create the raw MDL.
19561969
loggingFunction(false, "Creating MDL file from processed data...");
19571970
var bytes = await MakeNewMdlFile(ttModel, currentMdl, loggingFunction);
@@ -1964,7 +1977,6 @@ await Task.Run(async () =>
19641977
var modEntry = await modding.TryGetModEntry(mdlPath);
19651978

19661979

1967-
var filePath = currentMdl.MdlPath;
19681980

19691981
if (!rawDataOnly)
19701982
{

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.

xivModdingFramework/Models/ModelTextures/ModelTexture.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -541,18 +541,22 @@ private static void ComputeShaderColors(CustomModelColors colors, ShaderInfo in
541541

542542
// New diffuse starts from regular diffuse file.
543543
// Then factors in the player's skin color multiplied by the shader value.
544-
float skinInfluence = ByteToFloat(baseSpecular.R) * 0.5f;
545-
Color skinColor = MultiplyColor(colors.SkinColor, 1.0f);
544+
float skinInfluence = ByteToFloat(baseSpecular.R);
545+
var coloredSkin = MultiplyColor(baseDiffuse, colors.SkinColor);
546546

547-
newDiffuse = Blend(baseDiffuse, skinColor, skinInfluence);
547+
newDiffuse = Blend(baseDiffuse, coloredSkin, skinInfluence);
548548

549549
if (info.Preset == MtrlShaderPreset.Face)
550550
{
551551
// Face shaders also allow for lip color.
552-
Color lipColor = MultiplyColor(colors.LipColor, 1.0f);
552+
var coloredLip = MultiplyColor(baseDiffuse, colors.LipColor);
553553
float lipInfluence = ByteToFloat(baseSpecular.B);
554-
newDiffuse = Blend(newDiffuse, lipColor, lipInfluence);
555-
554+
newDiffuse = Blend(newDiffuse, coloredLip, lipInfluence);
555+
556+
// For lipstick, increase the specular value slightly.
557+
float specAmp = 1.0f + (lipInfluence * 0.25f);
558+
newSpecular = MultiplyColor(newSpecular, specAmp);
559+
556560
}
557561

558562

0 commit comments

Comments
 (0)