mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 20:24:17 +01:00
Move a few things to export subdir
This commit is contained in:
parent
6a2b802196
commit
551c25a64c
5 changed files with 169 additions and 87 deletions
|
|
@ -6,15 +6,39 @@ using SharpGLTF.Geometry;
|
||||||
using SharpGLTF.Geometry.VertexTypes;
|
using SharpGLTF.Geometry.VertexTypes;
|
||||||
using SharpGLTF.IO;
|
using SharpGLTF.IO;
|
||||||
using SharpGLTF.Materials;
|
using SharpGLTF.Materials;
|
||||||
|
using SharpGLTF.Scenes;
|
||||||
|
|
||||||
namespace Penumbra.Import.Modules;
|
namespace Penumbra.Import.Models.Export;
|
||||||
|
|
||||||
public sealed class MeshConverter
|
public class MeshExporter
|
||||||
{
|
{
|
||||||
public static IMeshBuilder<MaterialBuilder>[] ToGltf(MdlFile mdl, byte lod, ushort meshIndex, Dictionary<string, int>? boneNameMap)
|
public class Mesh
|
||||||
{
|
{
|
||||||
var self = new MeshConverter(mdl, lod, meshIndex, boneNameMap);
|
private IMeshBuilder<MaterialBuilder>[] _meshes;
|
||||||
return self.BuildMesh();
|
private NodeBuilder[]? _joints;
|
||||||
|
|
||||||
|
public Mesh(IMeshBuilder<MaterialBuilder>[] meshes, NodeBuilder[]? joints)
|
||||||
|
{
|
||||||
|
_meshes = meshes;
|
||||||
|
_joints = joints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddToScene(SceneBuilder scene)
|
||||||
|
{
|
||||||
|
// TODO: throw if mesh has skinned vertices but no joints are available?
|
||||||
|
foreach (var mesh in _meshes)
|
||||||
|
if (_joints == null)
|
||||||
|
scene.AddRigidMesh(mesh, Matrix4x4.Identity);
|
||||||
|
else
|
||||||
|
scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, _joints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace bonenamemap with a gltfskeleton
|
||||||
|
public static Mesh Export(MdlFile mdl, byte lod, ushort meshIndex, GltfSkeleton? skeleton)
|
||||||
|
{
|
||||||
|
var self = new MeshExporter(mdl, lod, meshIndex, skeleton?.Names);
|
||||||
|
return new Mesh(self.BuildMeshes(), skeleton?.Joints);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const byte MaximumMeshBufferStreams = 3;
|
private const byte MaximumMeshBufferStreams = 3;
|
||||||
|
|
@ -22,14 +46,14 @@ public sealed class MeshConverter
|
||||||
private readonly MdlFile _mdl;
|
private readonly MdlFile _mdl;
|
||||||
private readonly byte _lod;
|
private readonly byte _lod;
|
||||||
private readonly ushort _meshIndex;
|
private readonly ushort _meshIndex;
|
||||||
private MdlStructs.MeshStruct Mesh => _mdl.Meshes[_meshIndex];
|
private MdlStructs.MeshStruct XivMesh => _mdl.Meshes[_meshIndex];
|
||||||
|
|
||||||
private readonly Dictionary<ushort, int>? _boneIndexMap;
|
private readonly Dictionary<ushort, int>? _boneIndexMap;
|
||||||
|
|
||||||
private readonly Type _geometryType;
|
private readonly Type _geometryType;
|
||||||
private readonly Type _skinningType;
|
private readonly Type _skinningType;
|
||||||
|
|
||||||
private MeshConverter(MdlFile mdl, byte lod, ushort meshIndex, Dictionary<string, int>? boneNameMap)
|
private MeshExporter(MdlFile mdl, byte lod, ushort meshIndex, Dictionary<string, int>? boneNameMap)
|
||||||
{
|
{
|
||||||
_mdl = mdl;
|
_mdl = mdl;
|
||||||
_lod = lod;
|
_lod = lod;
|
||||||
|
|
@ -49,7 +73,7 @@ public sealed class MeshConverter
|
||||||
private Dictionary<ushort, int> BuildBoneIndexMap(Dictionary<string, int> boneNameMap)
|
private Dictionary<ushort, int> BuildBoneIndexMap(Dictionary<string, int> boneNameMap)
|
||||||
{
|
{
|
||||||
// todo: BoneTableIndex of 255 means null? if so, it should probably feed into the attributes we assign...
|
// todo: BoneTableIndex of 255 means null? if so, it should probably feed into the attributes we assign...
|
||||||
var xivBoneTable = _mdl.BoneTables[Mesh.BoneTableIndex];
|
var xivBoneTable = _mdl.BoneTables[XivMesh.BoneTableIndex];
|
||||||
|
|
||||||
var indexMap = new Dictionary<ushort, int>();
|
var indexMap = new Dictionary<ushort, int>();
|
||||||
|
|
||||||
|
|
@ -66,16 +90,16 @@ public sealed class MeshConverter
|
||||||
return indexMap;
|
return indexMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMeshBuilder<MaterialBuilder>[] BuildMesh()
|
private IMeshBuilder<MaterialBuilder>[] BuildMeshes()
|
||||||
{
|
{
|
||||||
var indices = BuildIndices();
|
var indices = BuildIndices();
|
||||||
var vertices = BuildVertices();
|
var vertices = BuildVertices();
|
||||||
|
|
||||||
// TODO: handle submeshCount = 0
|
// TODO: handle SubMeshCount = 0
|
||||||
|
|
||||||
return _mdl.SubMeshes
|
return _mdl.SubMeshes
|
||||||
.Skip(Mesh.SubMeshIndex)
|
.Skip(XivMesh.SubMeshIndex)
|
||||||
.Take(Mesh.SubMeshCount)
|
.Take(XivMesh.SubMeshCount)
|
||||||
.Select(submesh => BuildSubMesh(submesh, indices, vertices))
|
.Select(submesh => BuildSubMesh(submesh, indices, vertices))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +107,7 @@ public sealed class MeshConverter
|
||||||
private IMeshBuilder<MaterialBuilder> BuildSubMesh(MdlStructs.SubmeshStruct submesh, IReadOnlyList<ushort> indices, IReadOnlyList<IVertexBuilder> vertices)
|
private IMeshBuilder<MaterialBuilder> BuildSubMesh(MdlStructs.SubmeshStruct submesh, IReadOnlyList<ushort> indices, IReadOnlyList<IVertexBuilder> vertices)
|
||||||
{
|
{
|
||||||
// Index indices are specified relative to the LOD's 0, but we're reading chunks for each mesh.
|
// Index indices are specified relative to the LOD's 0, but we're reading chunks for each mesh.
|
||||||
var startIndex = (int)(submesh.IndexOffset - Mesh.StartIndex);
|
var startIndex = (int)(submesh.IndexOffset - XivMesh.StartIndex);
|
||||||
|
|
||||||
var meshBuilderType = typeof(MeshBuilder<,,,>).MakeGenericType(
|
var meshBuilderType = typeof(MeshBuilder<,,,>).MakeGenericType(
|
||||||
typeof(MaterialBuilder),
|
typeof(MaterialBuilder),
|
||||||
|
|
@ -124,7 +148,7 @@ public sealed class MeshConverter
|
||||||
var shapeValues = _mdl.ShapeMeshes
|
var shapeValues = _mdl.ShapeMeshes
|
||||||
.Skip(shape.ShapeMeshStartIndex[_lod])
|
.Skip(shape.ShapeMeshStartIndex[_lod])
|
||||||
.Take(shape.ShapeMeshCount[_lod])
|
.Take(shape.ShapeMeshCount[_lod])
|
||||||
.Where(shapeMesh => shapeMesh.MeshIndexOffset == Mesh.StartIndex)
|
.Where(shapeMesh => shapeMesh.MeshIndexOffset == XivMesh.StartIndex)
|
||||||
.SelectMany(shapeMesh =>
|
.SelectMany(shapeMesh =>
|
||||||
_mdl.ShapeValues
|
_mdl.ShapeValues
|
||||||
.Skip((int)shapeMesh.ShapeValueOffset)
|
.Skip((int)shapeMesh.ShapeValueOffset)
|
||||||
|
|
@ -158,8 +182,8 @@ public sealed class MeshConverter
|
||||||
private IReadOnlyList<ushort> BuildIndices()
|
private IReadOnlyList<ushort> BuildIndices()
|
||||||
{
|
{
|
||||||
var reader = new BinaryReader(new MemoryStream(_mdl.RemainingData));
|
var reader = new BinaryReader(new MemoryStream(_mdl.RemainingData));
|
||||||
reader.Seek(_mdl.IndexOffset[_lod] + Mesh.StartIndex * sizeof(ushort));
|
reader.Seek(_mdl.IndexOffset[_lod] + XivMesh.StartIndex * sizeof(ushort));
|
||||||
return reader.ReadStructuresAsArray<ushort>((int)Mesh.IndexCount);
|
return reader.ReadStructuresAsArray<ushort>((int)XivMesh.IndexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IReadOnlyList<IVertexBuilder> BuildVertices()
|
private IReadOnlyList<IVertexBuilder> BuildVertices()
|
||||||
|
|
@ -172,7 +196,7 @@ public sealed class MeshConverter
|
||||||
for (var streamIndex = 0; streamIndex < MaximumMeshBufferStreams; streamIndex++)
|
for (var streamIndex = 0; streamIndex < MaximumMeshBufferStreams; streamIndex++)
|
||||||
{
|
{
|
||||||
streams[streamIndex] = new BinaryReader(new MemoryStream(_mdl.RemainingData));
|
streams[streamIndex] = new BinaryReader(new MemoryStream(_mdl.RemainingData));
|
||||||
streams[streamIndex].Seek(_mdl.VertexOffset[_lod] + Mesh.VertexBufferOffset[streamIndex]);
|
streams[streamIndex].Seek(_mdl.VertexOffset[_lod] + XivMesh.VertexBufferOffset[streamIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortedElements = _mdl.VertexDeclarations[_meshIndex].VertexElements
|
var sortedElements = _mdl.VertexDeclarations[_meshIndex].VertexElements
|
||||||
|
|
@ -183,7 +207,7 @@ public sealed class MeshConverter
|
||||||
var vertices = new List<IVertexBuilder>();
|
var vertices = new List<IVertexBuilder>();
|
||||||
|
|
||||||
var attributes = new Dictionary<MdlFile.VertexUsage, object>();
|
var attributes = new Dictionary<MdlFile.VertexUsage, object>();
|
||||||
for (var vertexIndex = 0; vertexIndex < Mesh.VertexCount; vertexIndex++)
|
for (var vertexIndex = 0; vertexIndex < XivMesh.VertexCount; vertexIndex++)
|
||||||
{
|
{
|
||||||
attributes.Clear();
|
attributes.Clear();
|
||||||
|
|
||||||
100
Penumbra/Import/Models/Export/ModelExporter.cs
Normal file
100
Penumbra/Import/Models/Export/ModelExporter.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
using Penumbra.GameData.Files;
|
||||||
|
using SharpGLTF.Scenes;
|
||||||
|
using SharpGLTF.Transforms;
|
||||||
|
|
||||||
|
namespace Penumbra.Import.Models.Export;
|
||||||
|
|
||||||
|
public class ModelExporter
|
||||||
|
{
|
||||||
|
public class Model
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
if (skeletonRoot != null)
|
||||||
|
scene.AddNode(skeletonRoot);
|
||||||
|
|
||||||
|
// Add all the meshes to the scene.
|
||||||
|
foreach (var mesh in _meshes)
|
||||||
|
mesh.AddToScene(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Model Export(MdlFile mdl, XivSkeleton? xivSkeleton)
|
||||||
|
{
|
||||||
|
var gltfSkeleton = xivSkeleton != null ? ConvertSkeleton(xivSkeleton) : null;
|
||||||
|
var meshes = ConvertMeshes(mdl, gltfSkeleton);
|
||||||
|
return new Model(meshes, gltfSkeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<MeshExporter.Mesh> ConvertMeshes(MdlFile mdl, GltfSkeleton? skeleton)
|
||||||
|
{
|
||||||
|
var meshes = new List<MeshExporter.Mesh>();
|
||||||
|
|
||||||
|
for (byte lodIndex = 0; lodIndex < mdl.LodCount; lodIndex++)
|
||||||
|
{
|
||||||
|
var lod = mdl.Lods[lodIndex];
|
||||||
|
|
||||||
|
// TODO: consider other types of mesh?
|
||||||
|
for (ushort meshOffset = 0; meshOffset < lod.MeshCount; meshOffset++)
|
||||||
|
{
|
||||||
|
var mesh = MeshExporter.Export(mdl, lodIndex, (ushort)(lod.MeshIndex + meshOffset), skeleton);
|
||||||
|
meshes.Add(mesh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return meshes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GltfSkeleton? ConvertSkeleton(XivSkeleton skeleton)
|
||||||
|
{
|
||||||
|
NodeBuilder? root = null;
|
||||||
|
var names = new Dictionary<string, int>();
|
||||||
|
var joints = new List<NodeBuilder>();
|
||||||
|
for (var boneIndex = 0; boneIndex < skeleton.Bones.Length; boneIndex++)
|
||||||
|
{
|
||||||
|
var bone = skeleton.Bones[boneIndex];
|
||||||
|
|
||||||
|
if (names.ContainsKey(bone.Name)) continue;
|
||||||
|
|
||||||
|
var node = new NodeBuilder(bone.Name);
|
||||||
|
names[bone.Name] = joints.Count;
|
||||||
|
joints.Add(node);
|
||||||
|
|
||||||
|
node.SetLocalTransform(new AffineTransform(
|
||||||
|
bone.Transform.Scale,
|
||||||
|
bone.Transform.Rotation,
|
||||||
|
bone.Transform.Translation
|
||||||
|
), false);
|
||||||
|
|
||||||
|
if (bone.ParentIndex == -1)
|
||||||
|
{
|
||||||
|
root = node;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent = joints[names[skeleton.Bones[bone.ParentIndex].Name]];
|
||||||
|
parent.AddNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Root = root,
|
||||||
|
Joints = joints.ToArray(),
|
||||||
|
Names = names,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
namespace Penumbra.Import.Models;
|
using SharpGLTF.Scenes;
|
||||||
|
|
||||||
// TODO: this should almost certainly live in gamedata. if not, it should at _least_ be adjacent to the model handling.
|
namespace Penumbra.Import.Models.Export;
|
||||||
public class Skeleton
|
|
||||||
|
public class XivSkeleton
|
||||||
{
|
{
|
||||||
public Bone[] Bones;
|
public Bone[] Bones;
|
||||||
|
|
||||||
public Skeleton(Bone[] bones)
|
public XivSkeleton(Bone[] bones)
|
||||||
{
|
{
|
||||||
Bones = bones;
|
Bones = bones;
|
||||||
}
|
}
|
||||||
|
|
@ -23,3 +24,10 @@ public class Skeleton
|
||||||
public Vector3 Translation;
|
public Vector3 Translation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct GltfSkeleton
|
||||||
|
{
|
||||||
|
public NodeBuilder Root;
|
||||||
|
public NodeBuilder[] Joints;
|
||||||
|
public Dictionary<string, int> Names;
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ using Dalamud.Plugin.Services;
|
||||||
using OtterGui.Tasks;
|
using OtterGui.Tasks;
|
||||||
using Penumbra.Collections.Manager;
|
using Penumbra.Collections.Manager;
|
||||||
using Penumbra.GameData.Files;
|
using Penumbra.GameData.Files;
|
||||||
using Penumbra.Import.Modules;
|
using Penumbra.Import.Models.Export;
|
||||||
using SharpGLTF.Scenes;
|
using SharpGLTF.Scenes;
|
||||||
using SharpGLTF.Transforms;
|
using SharpGLTF.Transforms;
|
||||||
|
|
||||||
|
|
@ -73,36 +73,18 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
||||||
|
|
||||||
public void Execute(CancellationToken cancel)
|
public void Execute(CancellationToken cancel)
|
||||||
{
|
{
|
||||||
|
var xivSkeleton = BuildSkeleton(cancel);
|
||||||
|
var model = ModelExporter.Export(_mdl, xivSkeleton);
|
||||||
|
|
||||||
var scene = new SceneBuilder();
|
var scene = new SceneBuilder();
|
||||||
|
model.AddToScene(scene);
|
||||||
|
|
||||||
var skeleton = BuildSkeleton(cancel);
|
var gltfModel = scene.ToGltf2();
|
||||||
if (skeleton != null)
|
gltfModel.SaveGLTF(_outputPath);
|
||||||
scene.AddNode(skeleton.Value.Root);
|
|
||||||
|
|
||||||
// TODO: group by LoD in output tree
|
|
||||||
for (byte lodIndex = 0; lodIndex < _mdl.LodCount; lodIndex++)
|
|
||||||
{
|
|
||||||
var lod = _mdl.Lods[lodIndex];
|
|
||||||
|
|
||||||
// TODO: consider other types of mesh?
|
|
||||||
for (ushort meshOffset = 0; meshOffset < lod.MeshCount; meshOffset++)
|
|
||||||
{
|
|
||||||
var meshBuilders = MeshConverter.ToGltf(_mdl, lodIndex, (ushort)(lod.MeshIndex + meshOffset), skeleton?.Names);
|
|
||||||
// TODO: use a value from the mesh converter for this check, rather than assuming that it has joints
|
|
||||||
foreach (var meshBuilder in meshBuilders)
|
|
||||||
if (skeleton == null)
|
|
||||||
scene.AddRigidMesh(meshBuilder, Matrix4x4.Identity);
|
|
||||||
else
|
|
||||||
scene.AddSkinnedMesh(meshBuilder, Matrix4x4.Identity, skeleton?.Joints);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = scene.ToGltf2();
|
|
||||||
model.SaveGLTF(_outputPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this should be moved to a seperate model converter or something
|
// TODO: this should be moved to a seperate model converter or something
|
||||||
private (NodeBuilder Root, NodeBuilder[] Joints, Dictionary<string, int> Names)? BuildSkeleton(CancellationToken cancel)
|
private XivSkeleton? BuildSkeleton(CancellationToken cancel)
|
||||||
{
|
{
|
||||||
if (_sklb == null)
|
if (_sklb == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -117,40 +99,7 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
||||||
var skeletonConverter = new SkeletonConverter();
|
var skeletonConverter = new SkeletonConverter();
|
||||||
var skeleton = skeletonConverter.FromXml(xml);
|
var skeleton = skeletonConverter.FromXml(xml);
|
||||||
|
|
||||||
// this is (less) atrocious
|
return skeleton;
|
||||||
NodeBuilder? root = null;
|
|
||||||
var names = new Dictionary<string, int>();
|
|
||||||
var joints = new List<NodeBuilder>();
|
|
||||||
for (var boneIndex = 0; boneIndex < skeleton.Bones.Length; boneIndex++)
|
|
||||||
{
|
|
||||||
var bone = skeleton.Bones[boneIndex];
|
|
||||||
|
|
||||||
if (names.ContainsKey(bone.Name)) continue;
|
|
||||||
|
|
||||||
var node = new NodeBuilder(bone.Name);
|
|
||||||
names[bone.Name] = joints.Count;
|
|
||||||
joints.Add(node);
|
|
||||||
|
|
||||||
node.SetLocalTransform(new AffineTransform(
|
|
||||||
bone.Transform.Scale,
|
|
||||||
bone.Transform.Rotation,
|
|
||||||
bone.Transform.Translation
|
|
||||||
), false);
|
|
||||||
|
|
||||||
if (bone.ParentIndex == -1)
|
|
||||||
{
|
|
||||||
root = node;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parent = joints[names[skeleton.Bones[bone.ParentIndex].Name]];
|
|
||||||
parent.AddNode(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return (root, joints.ToArray(), names);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(IAction? other)
|
public bool Equals(IAction? other)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
|
using Penumbra.Import.Models.Export;
|
||||||
|
|
||||||
namespace Penumbra.Import.Models;
|
namespace Penumbra.Import.Models;
|
||||||
|
|
||||||
// TODO: tempted to say that this living here is more okay? that or next to havok converter, wherever that ends up.
|
// TODO: tempted to say that this living here is more okay? that or next to havok converter, wherever that ends up.
|
||||||
public class SkeletonConverter
|
public class SkeletonConverter
|
||||||
{
|
{
|
||||||
public Skeleton FromXml(string xml)
|
public XivSkeleton FromXml(string xml)
|
||||||
{
|
{
|
||||||
var document = new XmlDocument();
|
var document = new XmlDocument();
|
||||||
document.LoadXml(xml);
|
document.LoadXml(xml);
|
||||||
|
|
@ -29,7 +30,7 @@ public class SkeletonConverter
|
||||||
.Select(values =>
|
.Select(values =>
|
||||||
{
|
{
|
||||||
var (transform, parentIndex, name) = values;
|
var (transform, parentIndex, name) = values;
|
||||||
return new Skeleton.Bone()
|
return new XivSkeleton.Bone()
|
||||||
{
|
{
|
||||||
Transform = transform,
|
Transform = transform,
|
||||||
ParentIndex = parentIndex,
|
ParentIndex = parentIndex,
|
||||||
|
|
@ -38,7 +39,7 @@ public class SkeletonConverter
|
||||||
})
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return new Skeleton(bones);
|
return new XivSkeleton(bones);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Get the main skeleton ID for a given skeleton document.</summary>
|
/// <summary>Get the main skeleton ID for a given skeleton document.</summary>
|
||||||
|
|
@ -57,14 +58,14 @@ public class SkeletonConverter
|
||||||
|
|
||||||
/// <summary>Read the reference pose transforms for a skeleton.</summary>
|
/// <summary>Read the reference pose transforms for a skeleton.</summary>
|
||||||
/// <param name="node">XML node for the skeleton.</param>
|
/// <param name="node">XML node for the skeleton.</param>
|
||||||
private Skeleton.Transform[] ReadReferencePose(XmlNode node)
|
private XivSkeleton.Transform[] ReadReferencePose(XmlNode node)
|
||||||
{
|
{
|
||||||
return ReadArray(
|
return ReadArray(
|
||||||
CheckExists(node.SelectSingleNode("array[@name='referencePose']")),
|
CheckExists(node.SelectSingleNode("array[@name='referencePose']")),
|
||||||
node =>
|
node =>
|
||||||
{
|
{
|
||||||
var raw = ReadVec12(node);
|
var raw = ReadVec12(node);
|
||||||
return new Skeleton.Transform()
|
return new XivSkeleton.Transform()
|
||||||
{
|
{
|
||||||
Translation = new(raw[0], raw[1], raw[2]),
|
Translation = new(raw[0], raw[1], raw[2]),
|
||||||
Rotation = new(raw[4], raw[5], raw[6], raw[7]),
|
Rotation = new(raw[4], raw[5], raw[6], raw[7]),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue