mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Some cleanup and using new features / intellisense recommendations.
This commit is contained in:
parent
51bb9cf7cd
commit
677c9bd801
5 changed files with 117 additions and 128 deletions
|
|
@ -13,24 +13,17 @@ namespace Penumbra.Import.Models.Export;
|
|||
|
||||
public class MeshExporter
|
||||
{
|
||||
public class Mesh
|
||||
public class Mesh(IEnumerable<IMeshBuilder<MaterialBuilder>> meshes, NodeBuilder[]? joints)
|
||||
{
|
||||
private IMeshBuilder<MaterialBuilder>[] _meshes;
|
||||
private NodeBuilder[]? _joints;
|
||||
|
||||
public Mesh(IMeshBuilder<MaterialBuilder>[] meshes, NodeBuilder[]? joints)
|
||||
{
|
||||
_meshes = meshes;
|
||||
_joints = joints;
|
||||
}
|
||||
|
||||
public void AddToScene(SceneBuilder scene)
|
||||
{
|
||||
foreach (var mesh in _meshes)
|
||||
if (_joints == null)
|
||||
foreach (var mesh in meshes)
|
||||
{
|
||||
if (joints == null)
|
||||
scene.AddRigidMesh(mesh, Matrix4x4.Identity);
|
||||
else
|
||||
scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, _joints);
|
||||
scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, joints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,9 +36,11 @@ public class MeshExporter
|
|||
private const byte MaximumMeshBufferStreams = 3;
|
||||
|
||||
private readonly MdlFile _mdl;
|
||||
private readonly byte _lod;
|
||||
private readonly ushort _meshIndex;
|
||||
private MdlStructs.MeshStruct XivMesh => _mdl.Meshes[_meshIndex];
|
||||
private readonly byte _lod;
|
||||
private readonly ushort _meshIndex;
|
||||
|
||||
private MdlStructs.MeshStruct XivMesh
|
||||
=> _mdl.Meshes[_meshIndex];
|
||||
|
||||
private readonly Dictionary<ushort, int>? _boneIndexMap;
|
||||
|
||||
|
|
@ -53,10 +48,10 @@ public class MeshExporter
|
|||
private readonly Type _materialType;
|
||||
private readonly Type _skinningType;
|
||||
|
||||
private MeshExporter(MdlFile mdl, byte lod, ushort meshIndex, Dictionary<string, int>? boneNameMap)
|
||||
private MeshExporter(MdlFile mdl, byte lod, ushort meshIndex, IReadOnlyDictionary<string, int>? boneNameMap)
|
||||
{
|
||||
_mdl = mdl;
|
||||
_lod = lod;
|
||||
_mdl = mdl;
|
||||
_lod = lod;
|
||||
_meshIndex = meshIndex;
|
||||
|
||||
if (boneNameMap != null)
|
||||
|
|
@ -76,11 +71,12 @@ public class MeshExporter
|
|||
if (_skinningType != typeof(VertexEmpty) && _boneIndexMap == null)
|
||||
Penumbra.Log.Warning($"Mesh {meshIndex} has skinned vertex usages but no bone information was provided.");
|
||||
|
||||
Penumbra.Log.Debug($"Mesh {meshIndex} using vertex types geometry: {_geometryType.Name}, material: {_materialType.Name}, skinning: {_skinningType.Name}");
|
||||
Penumbra.Log.Debug(
|
||||
$"Mesh {meshIndex} using vertex types geometry: {_geometryType.Name}, material: {_materialType.Name}, skinning: {_skinningType.Name}");
|
||||
}
|
||||
|
||||
/// <summary> Build a mapping between indices in this mesh's bone table (if any), and the glTF joint indices provdied. </summary>
|
||||
private Dictionary<ushort, int>? BuildBoneIndexMap(Dictionary<string, int> boneNameMap)
|
||||
/// <summary> Build a mapping between indices in this mesh's bone table (if any), and the glTF joint indices provided. </summary>
|
||||
private Dictionary<ushort, int>? BuildBoneIndexMap(IReadOnlyDictionary<string, int> boneNameMap)
|
||||
{
|
||||
// A BoneTableIndex of 255 means that this mesh is not skinned.
|
||||
if (XivMesh.BoneTableIndex == 255)
|
||||
|
|
@ -105,11 +101,10 @@ public class MeshExporter
|
|||
/// <summary> Build glTF meshes for this XIV mesh. </summary>
|
||||
private IMeshBuilder<MaterialBuilder>[] BuildMeshes()
|
||||
{
|
||||
var indices = BuildIndices();
|
||||
var indices = BuildIndices();
|
||||
var vertices = BuildVertices();
|
||||
|
||||
// NOTE: Index indices are specified relative to the LOD's 0, but we're reading chunks for each mesh, so we're specifying the index base relative to the mesh's base.
|
||||
|
||||
if (XivMesh.SubMeshCount == 0)
|
||||
return [BuildMesh($"mesh {_meshIndex}", indices, vertices, 0, (int)XivMesh.IndexCount)];
|
||||
|
||||
|
|
@ -117,7 +112,8 @@ public class MeshExporter
|
|||
.Skip(XivMesh.SubMeshIndex)
|
||||
.Take(XivMesh.SubMeshCount)
|
||||
.WithIndex()
|
||||
.Select(submesh => BuildMesh($"mesh {_meshIndex}.{submesh.Index}", indices, vertices, (int)(submesh.Value.IndexOffset - XivMesh.StartIndex), (int)submesh.Value.IndexCount))
|
||||
.Select(subMesh => BuildMesh($"mesh {_meshIndex}.{subMesh.Index}", indices, vertices,
|
||||
(int)(subMesh.Value.IndexOffset - XivMesh.StartIndex), (int)subMesh.Value.IndexCount))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
|
|
@ -161,7 +157,7 @@ public class MeshExporter
|
|||
}
|
||||
|
||||
var primitiveVertices = meshBuilder.Primitives.First().Vertices;
|
||||
var shapeNames = new List<string>();
|
||||
var shapeNames = new List<string>();
|
||||
|
||||
foreach (var shape in _mdl.Shapes)
|
||||
{
|
||||
|
|
@ -177,24 +173,28 @@ public class MeshExporter
|
|||
)
|
||||
.Where(shapeValue =>
|
||||
shapeValue.BaseIndicesIndex >= indexBase
|
||||
&& shapeValue.BaseIndicesIndex < indexBase + indexCount
|
||||
&& shapeValue.BaseIndicesIndex < indexBase + indexCount
|
||||
)
|
||||
.ToList();
|
||||
|
||||
if (shapeValues.Count == 0) continue;
|
||||
if (shapeValues.Count == 0)
|
||||
continue;
|
||||
|
||||
var morphBuilder = meshBuilder.UseMorphTarget(shapeNames.Count);
|
||||
shapeNames.Add(shape.ShapeName);
|
||||
|
||||
foreach (var shapeValue in shapeValues)
|
||||
{
|
||||
morphBuilder.SetVertex(
|
||||
primitiveVertices[gltfIndices[shapeValue.BaseIndicesIndex - indexBase]].GetGeometry(),
|
||||
vertices[shapeValue.ReplacingVertexIndex].GetGeometry()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
meshBuilder.Extras = JsonContent.CreateFrom(new Dictionary<string, object>() {
|
||||
{"targetNames", shapeNames}
|
||||
meshBuilder.Extras = JsonContent.CreateFrom(new Dictionary<string, object>()
|
||||
{
|
||||
{ "targetNames", shapeNames },
|
||||
});
|
||||
|
||||
return meshBuilder;
|
||||
|
|
@ -249,23 +249,25 @@ public class MeshExporter
|
|||
}
|
||||
|
||||
/// <summary> Read a vertex attribute of the specified type from a vertex buffer stream. </summary>
|
||||
private object ReadVertexAttribute(MdlFile.VertexType type, BinaryReader reader)
|
||||
private static object ReadVertexAttribute(MdlFile.VertexType type, BinaryReader reader)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
MdlFile.VertexType.Single3 => new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
|
||||
MdlFile.VertexType.Single4 => new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()),
|
||||
MdlFile.VertexType.UInt => reader.ReadBytes(4),
|
||||
MdlFile.VertexType.ByteFloat4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f),
|
||||
MdlFile.VertexType.UInt => reader.ReadBytes(4),
|
||||
MdlFile.VertexType.ByteFloat4 => new Vector4(reader.ReadByte() / 255f, reader.ReadByte() / 255f, reader.ReadByte() / 255f,
|
||||
reader.ReadByte() / 255f),
|
||||
MdlFile.VertexType.Half2 => new Vector2((float)reader.ReadHalf(), (float)reader.ReadHalf()),
|
||||
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf()),
|
||||
MdlFile.VertexType.Half4 => new Vector4((float)reader.ReadHalf(), (float)reader.ReadHalf(), (float)reader.ReadHalf(),
|
||||
(float)reader.ReadHalf()),
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary> Get the vertex geometry type for this mesh's vertex usages. </summary>
|
||||
private Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||
private static Type GetGeometryType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||
{
|
||||
if (!usages.ContainsKey(MdlFile.VertexUsage.Position))
|
||||
throw new Exception("Mesh does not contain position vertex elements.");
|
||||
|
|
@ -304,16 +306,16 @@ public class MeshExporter
|
|||
}
|
||||
|
||||
/// <summary> Get the vertex material type for this mesh's vertex usages. </summary>
|
||||
private Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||
private static Type GetMaterialType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||
{
|
||||
var uvCount = 0;
|
||||
if (usages.TryGetValue(MdlFile.VertexUsage.UV, out var type))
|
||||
uvCount = type switch
|
||||
{
|
||||
MdlFile.VertexType.Half2 => 1,
|
||||
MdlFile.VertexType.Half4 => 2,
|
||||
MdlFile.VertexType.Half2 => 1,
|
||||
MdlFile.VertexType.Half4 => 2,
|
||||
MdlFile.VertexType.Single4 => 2,
|
||||
_ => throw new Exception($"Unexpected UV vertex type {type}.")
|
||||
_ => throw new Exception($"Unexpected UV vertex type {type}."),
|
||||
};
|
||||
|
||||
var materialUsages = (
|
||||
|
|
@ -323,11 +325,11 @@ public class MeshExporter
|
|||
|
||||
return materialUsages switch
|
||||
{
|
||||
(2, true) => typeof(VertexColor1Texture2),
|
||||
(2, true) => typeof(VertexColor1Texture2),
|
||||
(2, false) => typeof(VertexTexture2),
|
||||
(1, true) => typeof(VertexColor1Texture1),
|
||||
(1, true) => typeof(VertexColor1Texture1),
|
||||
(1, false) => typeof(VertexTexture1),
|
||||
(0, true) => typeof(VertexColor1),
|
||||
(0, true) => typeof(VertexColor1),
|
||||
(0, false) => typeof(VertexEmpty),
|
||||
|
||||
_ => throw new Exception("Unreachable."),
|
||||
|
|
@ -377,7 +379,7 @@ public class MeshExporter
|
|||
}
|
||||
|
||||
/// <summary> Get the vertex skinning type for this mesh's vertex usages. </summary>
|
||||
private Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||
private static Type GetSkinningType(IReadOnlyDictionary<MdlFile.VertexUsage, MdlFile.VertexType> usages)
|
||||
{
|
||||
if (usages.ContainsKey(MdlFile.VertexUsage.BlendWeights) && usages.ContainsKey(MdlFile.VertexUsage.BlendIndices))
|
||||
return typeof(VertexJoints4);
|
||||
|
|
@ -400,7 +402,8 @@ public class MeshExporter
|
|||
var weights = ToVector4(attributes[MdlFile.VertexUsage.BlendWeights]);
|
||||
|
||||
var bindings = Enumerable.Range(0, 4)
|
||||
.Select(bindingIndex => {
|
||||
.Select(bindingIndex =>
|
||||
{
|
||||
// NOTE: I've not seen any files that throw this error that aren't completely broken.
|
||||
var xivBoneIndex = indices[bindingIndex];
|
||||
if (!_boneIndexMap.TryGetValue(xivBoneIndex, out var jointIndex))
|
||||
|
|
@ -417,44 +420,44 @@ public class MeshExporter
|
|||
|
||||
/// <summary> Clamps any tangent W value other than 1 to -1. </summary>
|
||||
/// <remarks> Some XIV models seemingly store -1 as 0, this patches over that. </remarks>
|
||||
private Vector4 FixTangentVector(Vector4 tangent)
|
||||
private static Vector4 FixTangentVector(Vector4 tangent)
|
||||
=> tangent with { W = tangent.W == 1 ? 1 : -1 };
|
||||
|
||||
/// <summary> Convert a vertex attribute value to a Vector2. Supported inputs are Vector2, Vector3, and Vector4. </summary>
|
||||
private Vector2 ToVector2(object data)
|
||||
private static Vector2 ToVector2(object data)
|
||||
=> data switch
|
||||
{
|
||||
Vector2 v2 => v2,
|
||||
Vector3 v3 => new Vector2(v3.X, v3.Y),
|
||||
Vector4 v4 => new Vector2(v4.X, v4.Y),
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid Vector2 input {data}")
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid Vector2 input {data}"),
|
||||
};
|
||||
|
||||
/// <summary> Convert a vertex attribute value to a Vector3. Supported inputs are Vector2, Vector3, and Vector4. </summary>
|
||||
private Vector3 ToVector3(object data)
|
||||
private static Vector3 ToVector3(object data)
|
||||
=> data switch
|
||||
{
|
||||
Vector2 v2 => new Vector3(v2.X, v2.Y, 0),
|
||||
Vector3 v3 => v3,
|
||||
Vector4 v4 => new Vector3(v4.X, v4.Y, v4.Z),
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}")
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}"),
|
||||
};
|
||||
|
||||
/// <summary> Convert a vertex attribute value to a Vector4. Supported inputs are Vector2, Vector3, and Vector4. </summary>
|
||||
private Vector4 ToVector4(object data)
|
||||
private static Vector4 ToVector4(object data)
|
||||
=> data switch
|
||||
{
|
||||
Vector2 v2 => new Vector4(v2.X, v2.Y, 0, 0),
|
||||
Vector2 v2 => new Vector4(v2.X, v2.Y, 0, 0),
|
||||
Vector3 v3 => new Vector4(v3.X, v3.Y, v3.Z, 1),
|
||||
Vector4 v4 => v4,
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}")
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid Vector3 input {data}"),
|
||||
};
|
||||
|
||||
/// <summary> Convert a vertex attribute value to a byte array. </summary>
|
||||
private byte[] ToByteArray(object data)
|
||||
private static byte[] ToByteArray(object data)
|
||||
=> data switch
|
||||
{
|
||||
byte[] value => value,
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid byte[] input {data}")
|
||||
_ => throw new ArgumentOutOfRangeException($"Invalid byte[] input {data}"),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,26 +6,17 @@ namespace Penumbra.Import.Models.Export;
|
|||
|
||||
public class ModelExporter
|
||||
{
|
||||
public class Model
|
||||
public class Model(List<MeshExporter.Mesh> meshes, GltfSkeleton? skeleton)
|
||||
{
|
||||
private List<MeshExporter.Mesh> _meshes;
|
||||
private GltfSkeleton? _skeleton;
|
||||
|
||||
public Model(List<MeshExporter.Mesh> meshes, GltfSkeleton? skeleton)
|
||||
{
|
||||
_meshes = meshes;
|
||||
_skeleton = skeleton;
|
||||
}
|
||||
|
||||
public void AddToScene(SceneBuilder scene)
|
||||
{
|
||||
// If there's a skeleton, the root node should be added before we add any potentially skinned meshes.
|
||||
var skeletonRoot = _skeleton?.Root;
|
||||
var skeletonRoot = skeleton?.Root;
|
||||
if (skeletonRoot != null)
|
||||
scene.AddNode(skeletonRoot);
|
||||
|
||||
// Add all the meshes to the scene.
|
||||
foreach (var mesh in _meshes)
|
||||
foreach (var mesh in meshes)
|
||||
mesh.AddToScene(scene);
|
||||
}
|
||||
}
|
||||
|
|
@ -64,10 +55,8 @@ public class ModelExporter
|
|||
NodeBuilder? root = null;
|
||||
var names = new Dictionary<string, int>();
|
||||
var joints = new List<NodeBuilder>();
|
||||
for (var boneIndex = 0; boneIndex < skeleton.Bones.Length; boneIndex++)
|
||||
foreach (var bone in skeleton.Bones)
|
||||
{
|
||||
var bone = skeleton.Bones[boneIndex];
|
||||
|
||||
if (names.ContainsKey(bone.Name)) continue;
|
||||
|
||||
var node = new NodeBuilder(bone.Name);
|
||||
|
|
@ -93,10 +82,10 @@ public class ModelExporter
|
|||
if (root == null)
|
||||
return null;
|
||||
|
||||
return new()
|
||||
return new GltfSkeleton
|
||||
{
|
||||
Root = root,
|
||||
Joints = joints.ToArray(),
|
||||
Joints = [.. joints],
|
||||
Names = names,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,9 @@ using SharpGLTF.Scenes;
|
|||
namespace Penumbra.Import.Models.Export;
|
||||
|
||||
/// <summary> Representation of a skeleton within XIV. </summary>
|
||||
public class XivSkeleton
|
||||
public class XivSkeleton(XivSkeleton.Bone[] bones)
|
||||
{
|
||||
public Bone[] Bones;
|
||||
|
||||
public XivSkeleton(Bone[] bones)
|
||||
{
|
||||
Bones = bones;
|
||||
}
|
||||
public Bone[] Bones = bones;
|
||||
|
||||
public struct Bone
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,17 +16,18 @@ public static unsafe class HavokConverter
|
|||
/// <param name="hkx"> A byte array representing the .hkx file. </param>
|
||||
public static string HkxToXml(byte[] hkx)
|
||||
{
|
||||
const hkSerializeUtil.SaveOptionBits options = hkSerializeUtil.SaveOptionBits.SerializeIgnoredMembers
|
||||
| hkSerializeUtil.SaveOptionBits.TextFormat
|
||||
| hkSerializeUtil.SaveOptionBits.WriteAttributes;
|
||||
|
||||
var tempHkx = CreateTempFile();
|
||||
File.WriteAllBytes(tempHkx, hkx);
|
||||
|
||||
var resource = Read(tempHkx);
|
||||
File.Delete(tempHkx);
|
||||
|
||||
if (resource == null) throw new Exception("Failed to read havok file.");
|
||||
|
||||
var options = hkSerializeUtil.SaveOptionBits.SerializeIgnoredMembers
|
||||
| hkSerializeUtil.SaveOptionBits.TextFormat
|
||||
| hkSerializeUtil.SaveOptionBits.WriteAttributes;
|
||||
if (resource == null)
|
||||
throw new Exception("Failed to read havok file.");
|
||||
|
||||
var file = Write(resource, options);
|
||||
file.Close();
|
||||
|
|
@ -41,17 +42,19 @@ public static unsafe class HavokConverter
|
|||
/// <param name="xml"> A string representing the .xml file. </param>
|
||||
public static byte[] XmlToHkx(string xml)
|
||||
{
|
||||
const hkSerializeUtil.SaveOptionBits options = hkSerializeUtil.SaveOptionBits.SerializeIgnoredMembers
|
||||
| hkSerializeUtil.SaveOptionBits.WriteAttributes;
|
||||
|
||||
var tempXml = CreateTempFile();
|
||||
File.WriteAllText(tempXml, xml);
|
||||
|
||||
var resource = Read(tempXml);
|
||||
File.Delete(tempXml);
|
||||
|
||||
if (resource == null) throw new Exception("Failed to read havok file.");
|
||||
|
||||
var options = hkSerializeUtil.SaveOptionBits.SerializeIgnoredMembers
|
||||
| hkSerializeUtil.SaveOptionBits.WriteAttributes;
|
||||
if (resource == null)
|
||||
throw new Exception("Failed to read havok file.");
|
||||
|
||||
g
|
||||
var file = Write(resource, options);
|
||||
file.Close();
|
||||
|
||||
|
|
@ -74,7 +77,7 @@ public static unsafe class HavokConverter
|
|||
var builtinTypeRegistry = hkBuiltinTypeRegistry.Instance();
|
||||
|
||||
var loadOptions = stackalloc hkSerializeUtil.LoadOptions[1];
|
||||
loadOptions->Flags = new() { Storage = (int)hkSerializeUtil.LoadOptionBits.Default };
|
||||
loadOptions->Flags = new hkFlags<hkSerializeUtil.LoadOptionBits, int> { Storage = (int)hkSerializeUtil.LoadOptionBits.Default };
|
||||
loadOptions->ClassNameRegistry = builtinTypeRegistry->GetClassNameRegistry();
|
||||
loadOptions->TypeInfoRegistry = builtinTypeRegistry->GetTypeInfoRegistry();
|
||||
|
||||
|
|
@ -92,37 +95,42 @@ public static unsafe class HavokConverter
|
|||
)
|
||||
{
|
||||
var tempFile = CreateTempFile();
|
||||
var path = Marshal.StringToHGlobalAnsi(tempFile);
|
||||
var oStream = new hkOstream();
|
||||
var path = Marshal.StringToHGlobalAnsi(tempFile);
|
||||
var oStream = new hkOstream();
|
||||
oStream.Ctor((byte*)path);
|
||||
|
||||
var result = stackalloc hkResult[1];
|
||||
|
||||
var saveOptions = new hkSerializeUtil.SaveOptions()
|
||||
{
|
||||
Flags = new() { Storage = (int)optionBits }
|
||||
Flags = new hkFlags<hkSerializeUtil.SaveOptionBits, int> { Storage = (int)optionBits },
|
||||
};
|
||||
|
||||
|
||||
var builtinTypeRegistry = hkBuiltinTypeRegistry.Instance();
|
||||
var classNameRegistry = builtinTypeRegistry->GetClassNameRegistry();
|
||||
var typeInfoRegistry = builtinTypeRegistry->GetTypeInfoRegistry();
|
||||
var classNameRegistry = builtinTypeRegistry->GetClassNameRegistry();
|
||||
var typeInfoRegistry = builtinTypeRegistry->GetTypeInfoRegistry();
|
||||
|
||||
try
|
||||
{
|
||||
var name = "hkRootLevelContainer";
|
||||
const string name = "hkRootLevelContainer";
|
||||
|
||||
var resourcePtr = (hkRootLevelContainer*)resource->GetContentsPointer(name, typeInfoRegistry);
|
||||
if (resourcePtr == null) throw new Exception("Failed to retrieve havok root level container resource.");
|
||||
if (resourcePtr == null)
|
||||
throw new Exception("Failed to retrieve havok root level container resource.");
|
||||
|
||||
var hkRootLevelContainerClass = classNameRegistry->GetClassByName(name);
|
||||
if (hkRootLevelContainerClass == null) throw new Exception("Failed to retrieve havok root level container type.");
|
||||
if (hkRootLevelContainerClass == null)
|
||||
throw new Exception("Failed to retrieve havok root level container type.");
|
||||
|
||||
hkSerializeUtil.Save(result, resourcePtr, hkRootLevelContainerClass, oStream.StreamWriter.ptr, saveOptions);
|
||||
}
|
||||
finally { oStream.Dtor(); }
|
||||
finally
|
||||
{
|
||||
oStream.Dtor();
|
||||
}
|
||||
|
||||
if (result->Result == hkResult.hkResultEnum.Failure) throw new Exception("Failed to serialize havok file.");
|
||||
if (result->Result == hkResult.hkResultEnum.Failure)
|
||||
throw new Exception("Failed to serialize havok file.");
|
||||
|
||||
return new FileStream(tempFile, FileMode.Open);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,16 +15,15 @@ public static class SkeletonConverter
|
|||
|
||||
var mainSkeletonId = GetMainSkeletonId(document);
|
||||
|
||||
var skeletonNode = document.SelectSingleNode($"/hktagfile/object[@type='hkaSkeleton'][@id='{mainSkeletonId}']");
|
||||
if (skeletonNode == null)
|
||||
throw new InvalidDataException($"Failed to find skeleton with id {mainSkeletonId}.");
|
||||
|
||||
var skeletonNode = document.SelectSingleNode($"/hktagfile/object[@type='hkaSkeleton'][@id='{mainSkeletonId}']")
|
||||
?? throw new InvalidDataException($"Failed to find skeleton with id {mainSkeletonId}.");
|
||||
var referencePose = ReadReferencePose(skeletonNode);
|
||||
var parentIndices = ReadParentIndices(skeletonNode);
|
||||
var boneNames = ReadBoneNames(skeletonNode);
|
||||
var boneNames = ReadBoneNames(skeletonNode);
|
||||
|
||||
if (boneNames.Length != parentIndices.Length || boneNames.Length != referencePose.Length)
|
||||
throw new InvalidDataException($"Mismatch in bone value array lengths: names({boneNames.Length}) parents({parentIndices.Length}) pose({referencePose.Length})");
|
||||
throw new InvalidDataException(
|
||||
$"Mismatch in bone value array lengths: names({boneNames.Length}) parents({parentIndices.Length}) pose({referencePose.Length})");
|
||||
|
||||
var bones = referencePose
|
||||
.Zip(parentIndices, boneNames)
|
||||
|
|
@ -33,9 +32,9 @@ public static class SkeletonConverter
|
|||
var (transform, parentIndex, name) = values;
|
||||
return new XivSkeleton.Bone()
|
||||
{
|
||||
Transform = transform,
|
||||
Transform = transform,
|
||||
ParentIndex = parentIndex,
|
||||
Name = name,
|
||||
Name = name,
|
||||
};
|
||||
})
|
||||
.ToArray();
|
||||
|
|
@ -63,14 +62,14 @@ public static class SkeletonConverter
|
|||
{
|
||||
return ReadArray(
|
||||
CheckExists(node.SelectSingleNode("array[@name='referencePose']")),
|
||||
node =>
|
||||
n =>
|
||||
{
|
||||
var raw = ReadVec12(node);
|
||||
var raw = ReadVec12(n);
|
||||
return new XivSkeleton.Transform()
|
||||
{
|
||||
Translation = new(raw[0], raw[1], raw[2]),
|
||||
Rotation = new(raw[4], raw[5], raw[6], raw[7]),
|
||||
Scale = new(raw[8], raw[9], raw[10]),
|
||||
Translation = new Vector3(raw[0], raw[1], raw[2]),
|
||||
Rotation = new Quaternion(raw[4], raw[5], raw[6], raw[7]),
|
||||
Scale = new Vector3(raw[8], raw[9], raw[10]),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
@ -82,11 +81,11 @@ public static class SkeletonConverter
|
|||
{
|
||||
var array = node.ChildNodes
|
||||
.Cast<XmlNode>()
|
||||
.Where(node => node.NodeType != XmlNodeType.Comment)
|
||||
.Select(node =>
|
||||
.Where(n => n.NodeType != XmlNodeType.Comment)
|
||||
.Select(n =>
|
||||
{
|
||||
var text = node.InnerText.Trim()[1..];
|
||||
// TODO: surely there's a less shit way to do this i mean seriously
|
||||
var text = n.InnerText.Trim()[1..];
|
||||
// TODO: surely there's a less shit way to do this I mean seriously
|
||||
return BitConverter.ToSingle(BitConverter.GetBytes(int.Parse(text, NumberStyles.HexNumber)));
|
||||
})
|
||||
.ToArray();
|
||||
|
|
@ -100,24 +99,20 @@ public static class SkeletonConverter
|
|||
/// <summary> Read the bone parent relations for a skeleton. </summary>
|
||||
/// <param name="node"> XML node for the skeleton. </param>
|
||||
private static int[] ReadParentIndices(XmlNode node)
|
||||
{
|
||||
// todo: would be neat to genericise array between bare and children
|
||||
return CheckExists(node.SelectSingleNode("array[@name='parentIndices']"))
|
||||
=> CheckExists(node.SelectSingleNode("array[@name='parentIndices']"))
|
||||
.InnerText
|
||||
.Split(new char[] { ' ', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Split((char[]) [' ', '\n'], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(int.Parse)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary> Read the names of bones in a skeleton. </summary>
|
||||
/// <param name="node"> XML node for the skeleton. </param>
|
||||
private static string[] ReadBoneNames(XmlNode node)
|
||||
{
|
||||
return ReadArray(
|
||||
=> ReadArray(
|
||||
CheckExists(node.SelectSingleNode("array[@name='bones']")),
|
||||
node => CheckExists(node.SelectSingleNode("string[@name='name']")).InnerText
|
||||
n => CheckExists(n.SelectSingleNode("string[@name='name']")).InnerText
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary> Read an XML tagfile array, converting it via the provided conversion function. </summary>
|
||||
/// <param name="node"> Tagfile XML array node. </param>
|
||||
|
|
@ -125,10 +120,9 @@ public static class SkeletonConverter
|
|||
private static T[] ReadArray<T>(XmlNode node, Func<XmlNode, T> convert)
|
||||
{
|
||||
var element = (XmlElement)node;
|
||||
var size = int.Parse(element.GetAttribute("size"));
|
||||
var array = new T[size];
|
||||
|
||||
var size = int.Parse(element.GetAttribute("size"));
|
||||
|
||||
var array = new T[size];
|
||||
foreach (var (childNode, index) in element.ChildNodes.Cast<XmlElement>().WithIndex())
|
||||
array[index] = convert(childNode);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue