Skip to content

Commit ca69053

Browse files
committed
Update v3.0.9.0
2 parents 2c10f5b + 0056a71 commit ca69053

File tree

16 files changed

+667
-279
lines changed

16 files changed

+667
-279
lines changed

xivModdingFramework/Helpers/IOUtil.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ public static void CopyFolder(string sourcePath, string targetPath)
744744
sourcePath = MakeLongPath(sourcePath);
745745
targetPath = MakeLongPath(targetPath);
746746

747+
// \\?\ prefixed Long File Names will fail if there's double slashes in a path
748+
// Guarantee the source and target both have trailing slashes to avoid accidentally creating a double slash
749+
if (!sourcePath.EndsWith("\\")) sourcePath += "\\";
750+
if (!targetPath.EndsWith("\\")) targetPath += "\\";
751+
747752
Directory.CreateDirectory(targetPath);
748753

749754
//Now Create all of the directories
@@ -765,9 +770,11 @@ public static string MakeLongPath(string path)
765770
{
766771
if (!path.StartsWith("\\\\?\\"))
767772
{
773+
while (path.Contains("\\\\"))
774+
path = path.Replace("\\\\", "\\");
768775
path = "\\\\?\\" + path;
769776
}
770-
return path;
777+
return path.Replace("/", "\\");
771778
}
772779

773780
public static byte[] GetImageSharpPixels(Image<Bgra32> img)

xivModdingFramework/Materials/DataContainers/ShaderHelpers.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ private static void AddCustomNamesAndValues()
391391
UpdateConstantName(shKv.Key, 0xB5545FBB, "g_NormalScale", true);
392392
UpdateConstantName(shKv.Key, 0x800EE35F, "g_SheenRate", true);
393393
UpdateConstantName(shKv.Key, 0x1F264897, "g_SheenTintRate", true);
394+
UpdateConstantName(shKv.Key, 0xD925FF32, "g_ShadowAlphaThreshold", true);
395+
UpdateConstantName(shKv.Key, 0x5351646E, "g_ShadowPosOffset", true);
394396
UpdateConstantName(shKv.Key, 0xF490F76E, "g_SheenAperture", true);
395397
UpdateConstantName(shKv.Key, 0x50E36D56, "g_IrisRingColor", true);
396398
UpdateConstantName(shKv.Key, 0x66C93D3E, "g_IrisThickness", true);
@@ -516,6 +518,8 @@ public enum ESamplerId : uint
516518
g_Sampler = 0x88408C04,
517519
g_Sampler0 = 0x213CB439,
518520
g_Sampler1 = 0x563B84AF,
521+
g_SkySampler = 0xB4C285EF,
522+
g_FogWeightLutSampler = 0x6E231669,
519523

520524
};
521525

xivModdingFramework/Models/DataContainers/LevelOfDetail.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public EMeshType GetMeshType(int offset)
9797

9898
/// <summary>
9999
/// Unknown Usage
100+
/// This appears to be multiple individual byte values, with the first 2 being related to neck morph data
100101
/// </summary>
101102
public int Unknown7 { get; set; }
102103

xivModdingFramework/Models/DataContainers/MdlModelData.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public class MdlModelData
172172
/// </summary>
173173
public byte BgCrestChangeMaterialIndex { get; set; }
174174

175-
public byte Unknown12 { get; set; }
175+
public byte NeckMorphTableSize { get; set; }
176176

177177
/// <summary>
178178
/// Unknown Usage
@@ -241,7 +241,7 @@ public static MdlModelData Read(BinaryReader br)
241241
BgChangeMaterialIndex = br.ReadByte(),
242242
BgCrestChangeMaterialIndex = br.ReadByte(),
243243

244-
Unknown12 = br.ReadByte(),
244+
NeckMorphTableSize = br.ReadByte(),
245245
BoneSetSize = br.ReadInt16(),
246246

247247
Unknown13 = br.ReadInt16(),
@@ -278,7 +278,7 @@ public void Write(BinaryWriter br)
278278
br.Write((byte)Flags3);
279279
br.Write(BgChangeMaterialIndex);
280280
br.Write(BgCrestChangeMaterialIndex);
281-
br.Write(Unknown12);
281+
br.Write(NeckMorphTableSize);
282282
br.Write(BoneSetSize);
283283
br.Write(Unknown13);
284284
br.Write(Unknown14);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// xivModdingFramework
2+
// Copyright © 2018 Rafael Gonzalez - All Rights Reserved
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
using SharpDX;
18+
using System.Collections.Generic;
19+
20+
namespace xivModdingFramework.Models.DataContainers
21+
{
22+
/// <summary>
23+
/// Class representing a neck morph table entry in a model, which modifies a vertex on the neck seam of a character's body when active.
24+
/// Typically 10 of these entries exist on a head mesh.
25+
/// It is unclear how each entry relates to individual vertex on the body mesh.
26+
/// </summary>
27+
public class NeckMorphEntry
28+
{
29+
/// <summary>
30+
/// Relative adjustment of the Position of the vertex.
31+
/// Its unclear how this is applied, but it seems affected by the referenced bones.
32+
/// </summary>
33+
public Vector3 PositionAdjust { get; set; }
34+
35+
/// <summary>
36+
/// This value is unclear but preserving it is important.
37+
/// If it is incorrect, the vertex adjustments disappear when viewed from certain camera angles.
38+
/// For all known working examples, it just has the value 0x00006699.
39+
/// </summary>
40+
public uint Unknown { get; set; }
41+
42+
/// <summary>
43+
/// Relative adjustment of the Normal of the vertex.
44+
/// Its unclear how this is applied.
45+
/// </summary>
46+
public Vector3 NormalAdjust { get; set; }
47+
48+
/// <summary>
49+
/// A list of bones, stored as indexes in to MdlPathData.BoneList.
50+
/// For all known working examples, the referenced bones are ["j_kubi", "j_sebo_c"].
51+
/// If the incorrect bones are referenced, the neck behavior becomes erratic.
52+
/// NOTE: In the model file these are actually stored as indexes in to BoneSet 0.
53+
/// </summary>
54+
public List<short> Bones { get; set; }
55+
}
56+
}

xivModdingFramework/Models/DataContainers/XivMdl.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,10 @@ public class XivMdl
141141
/// This happens when the sum of all LoD mesh counts is less than the model data mesh count
142142
/// </remarks>
143143
public List<MeshData> ExtraMeshData { get; set; }
144+
145+
/// <summary>
146+
/// This data is present on heads and seems to affect the shape of the neck on the body mesh
147+
/// </summary>
148+
public List<NeckMorphEntry> NeckMorphTable { get; set; }
144149
}
145150
}

xivModdingFramework/Models/FileTypes/Mdl.cs

Lines changed: 113 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,6 @@ public static async Task<XivMdl> GetXivMdl(string mdlPath, bool getOriginal = fa
348348

349349
public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
350350
{
351-
352351
var xivMdl = new XivMdl { MdlPath = mdlPath };
353352
int totalNonNullMaterials = 0;
354353
var getShapeData = true;
@@ -894,7 +893,7 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
894893

895894
#endregion
896895

897-
#region Part Bone Sets & Padding
896+
#region Part Bone Sets
898897
// Bone index for Parts
899898
var partBoneSet = new BoneSet
900899
{
@@ -908,7 +907,43 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
908907
}
909908

910909
xivMdl.PartBoneSets = partBoneSet;
910+
#endregion
911911

912+
#region Neck Morph Data
913+
// Neck morph data (appears on face models new in Patch 7.1)
914+
xivMdl.NeckMorphTable = new List<NeckMorphEntry>();
915+
for (var i = 0; i < xivMdl.ModelData.NeckMorphTableSize; ++i)
916+
{
917+
var neckMorphDataEntry = new NeckMorphEntry
918+
{
919+
PositionAdjust = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
920+
Unknown = br.ReadUInt32(),
921+
NormalAdjust = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()),
922+
Bones = new List<short>()
923+
};
924+
byte[] neckBoneTable = br.ReadBytes(4);
925+
// Weird code alert:
926+
// - Most vanilla heads legitimately have a zero value in the second slot of the table.
927+
// - Female Hrothgar heads legitimately have a zero value in the first slot of the table.
928+
// - Values in the third and fourth slot seem to be unused, and also seem to use 0 as a padding or null value.
929+
// - However, at least one vanilla model seems to have a non-zero value in the third slot of the table.
930+
// Therefore, only in the third and fourth slot is a zero value treated as an early list terminator.
931+
// This means the table should always contain at least two bones.
932+
for (int j = 0; j < neckBoneTable.Length; ++j)
933+
{
934+
int boneset0_index = neckBoneTable[j];
935+
if (j >= 2 && boneset0_index == 0) break; // Treat this case as an early list terminator.
936+
// Resolve the bone to an index in the bone path table here, to make the in-memory representation a little more normal
937+
if (xivMdl.MeshBoneSets.Count > 0 && xivMdl.MeshBoneSets[0].BoneIndices.Count() > boneset0_index)
938+
{
939+
neckMorphDataEntry.Bones.Add(xivMdl.MeshBoneSets[0].BoneIndices[boneset0_index]);
940+
}
941+
}
942+
xivMdl.NeckMorphTable.Add(neckMorphDataEntry);
943+
}
944+
#endregion
945+
946+
#region Padding
912947
// Padding
913948
xivMdl.PaddingSize = br.ReadByte();
914949
xivMdl.PaddedBytes = br.ReadBytes(xivMdl.PaddingSize);
@@ -970,7 +1005,8 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
9701005
}
9711006
else {
9721007
if (xivMdl.LoDList[0].VertexDataOffset < br.BaseStream.Position
973-
|| (xivMdl.LoDList[0].VertexDataOffset % 8 != br.BaseStream.Position % 8))
1008+
|| (xivMdl.LoDList[0].VertexDataOffset % 8 != br.BaseStream.Position % 8)
1009+
&& xivMdl.LoDList[1].VertexDataSize == 0)
9741010
{
9751011

9761012
var delta = (int)(xivMdl.LoDList[0].VertexDataOffset - br.BaseStream.Position);
@@ -982,7 +1018,7 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
9821018
}
9831019

9841020

985-
var lodNum = 0;
1021+
var lodNum = 0;
9861022
var totalMeshNum = 0;
9871023
foreach (var lod in xivMdl.LoDList)
9881024
{
@@ -994,16 +1030,12 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
9941030
{
9951031
throw new Exception("Failed to parse some meshes in previous LoD level.");
9961032
}
997-
998-
// Seek to the start of the LoD.
999-
br.BaseStream.Seek(lod.VertexDataOffset, SeekOrigin.Begin);
1000-
var LoDStart = br.BaseStream.Position;
10011033

10021034
var mIdx = 0;
10031035

10041036
foreach (var meshData in meshDataList)
10051037
{
1006-
MdlVertexReader.ReadVertexData(br, meshData, lod.VertexDataOffset, lod.IndexDataOffset);
1038+
MdlVertexReader.ReadVertexData(mdlData, meshData, lod.VertexDataOffset, lod.IndexDataOffset);
10071039

10081040
mIdx++;
10091041
totalMeshNum++;
@@ -2986,8 +3018,10 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
29863018
basicModelBlock.Add(bgChangeIdx);
29873019
basicModelBlock.Add(crestChangeIdx);
29883020

2989-
// More Unknowns
2990-
basicModelBlock.Add(ogModelData.Unknown12);
3021+
// Using neck morph data from original modal
3022+
// The field currently named LevelOfDetail.Unknown7 also contains this number, and also gets copied from the original model
3023+
var neckMorphTableSizePointer = basicModelBlock.Count; // we want to reset this to 0 later if the neck data cannot be preserved
3024+
basicModelBlock.Add(ogModelData.NeckMorphTableSize);
29913025

29923026
// We fix this pointer later after bone table is done.
29933027
var boneSetSizePointer = basicModelBlock.Count;
@@ -3536,6 +3570,64 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
35363570

35373571
#endregion
35383572

3573+
// Neck Morph Data
3574+
#region Neck Morph Data
3575+
var neckMorphDataBlock = new List<byte>();
3576+
// Preserve the original model's neck morph data if present -- but update the bone references inside of it
3577+
// Bone references are made via BoneSet[0]
3578+
for (int i = 0; i < ogMdl.NeckMorphTable.Count; ++i)
3579+
{
3580+
// Extract the original data (except the bone list)
3581+
var positionAdjust = ogMdl.NeckMorphTable[i].PositionAdjust;
3582+
var unknown = ogMdl.NeckMorphTable[i].Unknown;
3583+
var normalAdjust = ogMdl.NeckMorphTable[i].NormalAdjust;
3584+
var bones = new List<byte>();
3585+
3586+
// Look up the originally referenced bone by name, and map it to the same bone in the imported model
3587+
for (int j = 0; j < ogMdl.NeckMorphTable[i].Bones.Count; ++j)
3588+
{
3589+
string ogBoneName = ogMdl.PathData.BoneList[ogMdl.NeckMorphTable[i].Bones[j]];
3590+
int boneset0Index = -1;
3591+
3592+
if (ttModel.MeshGroups.Count > 0)
3593+
{
3594+
boneset0Index = ttModel.MeshGroups[0].Bones.FindIndex(x => x == ogBoneName);
3595+
}
3596+
3597+
// If a bone can't be located in the new model, just discard all of the neck morph data
3598+
if (boneset0Index == -1 || boneset0Index > 255)
3599+
{
3600+
loggingFunction(true, "Could not match bones in neck morph data, so the data was discarded!");
3601+
// Reset the table size to 0 in the model header and drop the data
3602+
// (Two bytes are also cleared later on when writing the LODs)
3603+
basicModelBlock[neckMorphTableSizePointer] = 0;
3604+
neckMorphDataBlock = new List<byte>();
3605+
break;
3606+
}
3607+
3608+
bones.Add((byte)boneset0Index);
3609+
}
3610+
3611+
// Serialize
3612+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(positionAdjust.X));
3613+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(positionAdjust.Y));
3614+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(positionAdjust.Z));
3615+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(unknown));
3616+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(normalAdjust.X));
3617+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(normalAdjust.Y));
3618+
neckMorphDataBlock.AddRange(BitConverter.GetBytes(normalAdjust.Z));
3619+
3620+
// Bone list is always 4 bytes -- pad with zeroes
3621+
for (int j = 0; j < 4; ++j)
3622+
{
3623+
if (j < bones.Count)
3624+
neckMorphDataBlock.Add(bones[j]);
3625+
else
3626+
neckMorphDataBlock.Add(0);
3627+
}
3628+
}
3629+
#endregion
3630+
35393631
// Padding
35403632
#region Padding Data Block
35413633

@@ -3691,7 +3783,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
36913783
// This is the offset to the beginning of the vertex data
36923784
var combinedDataBlockSize = _MdlHeaderSize + vertexInfoBlock.Count + pathInfoBlock.Count + basicModelBlock.Count + unknownDataBlock0.Length + (60 * ogMdl.LoDList.Count) + extraMeshesBlock.Count + meshDataBlock.Count +
36933785
attributePathDataBlock.Count + (unknownDataBlock1?.Length ?? 0) + meshPartDataBlock.Count + unknownDataBlock2.Length + matPathOffsetDataBlock.Count + bonePathOffsetDataBlock.Count +
3694-
boneSetsBlock.Count + FullShapeDataBlock.Count + partBoneSetsBlock.Count + paddingDataBlock.Count + boundingBoxDataBlock.Count + boneBoundingBoxDataBlock.Count;
3786+
boneSetsBlock.Count + FullShapeDataBlock.Count + partBoneSetsBlock.Count + neckMorphDataBlock.Count + paddingDataBlock.Count + boundingBoxDataBlock.Count + boneBoundingBoxDataBlock.Count;
36953787

36963788
var lodDataBlock = new List<byte>();
36973789
List<int> indexStartInjectPointers = new List<int>();
@@ -3734,6 +3826,14 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
37343826
lodDataBlock.AddRange(BitConverter.GetBytes(ogMdl.LoDList[0].Unknown6));
37353827
lodDataBlock.AddRange(BitConverter.GetBytes(ogMdl.LoDList[0].Unknown7));
37363828

3829+
// If the neck morph data was discarded -- clear the morph counts here too
3830+
// (The first 2 bytes of what is currently named "Unknown7" seem to refer to the size of this table)
3831+
if (ogMdl.NeckMorphTable.Count > 0 && neckMorphDataBlock.Count == 0)
3832+
{
3833+
lodDataBlock[lodDataBlock.Count - 4] = 0;
3834+
lodDataBlock[lodDataBlock.Count - 3] = 0;
3835+
}
3836+
37373837
// Vertex & Index Sizes
37383838
lodDataBlock.AddRange(BitConverter.GetBytes(vertexDataSize));
37393839
lodDataBlock.AddRange(BitConverter.GetBytes(indexDataSize));
@@ -3770,6 +3870,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
37703870
modelDataBlock.AddRange(boneSetsBlock);
37713871
modelDataBlock.AddRange(FullShapeDataBlock);
37723872
modelDataBlock.AddRange(partBoneSetsBlock);
3873+
modelDataBlock.AddRange(neckMorphDataBlock);
37733874
modelDataBlock.AddRange(paddingDataBlock);
37743875
modelDataBlock.AddRange(boundingBoxDataBlock);
37753876
modelDataBlock.AddRange(boneBoundingBoxDataBlock);

0 commit comments

Comments
 (0)