mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
SuzanneWalker
This commit is contained in:
parent
e8e87cc6cb
commit
b7edf521b6
4 changed files with 461 additions and 6 deletions
|
|
@ -1 +1 @@
|
||||||
Subproject commit db421413a15c48c63eb883dbfc2ac863c579d4c6
|
Subproject commit 821194d0650a2dac98b7cbba9ff4a79e32b32d4d
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Lumina.Data.Parsing;
|
||||||
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.Models.Export;
|
using Penumbra.Import.Models.Export;
|
||||||
|
using SharpGLTF.Geometry;
|
||||||
|
using SharpGLTF.Geometry.VertexTypes;
|
||||||
|
using SharpGLTF.Materials;
|
||||||
using SharpGLTF.Scenes;
|
using SharpGLTF.Scenes;
|
||||||
|
using SharpGLTF.Schema2;
|
||||||
|
|
||||||
namespace Penumbra.Import.Models;
|
namespace Penumbra.Import.Models;
|
||||||
|
|
||||||
|
|
@ -54,6 +59,12 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
||||||
public Task ExportToGltf(MdlFile mdl, SklbFile? sklb, string outputPath)
|
public Task ExportToGltf(MdlFile mdl, SklbFile? sklb, string outputPath)
|
||||||
=> Enqueue(new ExportToGltfAction(this, mdl, sklb, outputPath));
|
=> Enqueue(new ExportToGltfAction(this, mdl, sklb, outputPath));
|
||||||
|
|
||||||
|
public Task<MdlFile> ImportGltf()
|
||||||
|
{
|
||||||
|
var action = new ImportGltfAction();
|
||||||
|
return Enqueue(action).ContinueWith(_ => action.Out!);
|
||||||
|
}
|
||||||
|
|
||||||
private class ExportToGltfAction : IAction
|
private class ExportToGltfAction : IAction
|
||||||
{
|
{
|
||||||
private readonly ModelManager _manager;
|
private readonly ModelManager _manager;
|
||||||
|
|
@ -109,4 +120,430 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ImportGltfAction : IAction
|
||||||
|
{
|
||||||
|
public MdlFile? Out;
|
||||||
|
|
||||||
|
public ImportGltfAction()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelRoot Build()
|
||||||
|
{
|
||||||
|
// Build a super simple plane as a fake gltf input.
|
||||||
|
var material = new MaterialBuilder();
|
||||||
|
var mesh = new MeshBuilder<VertexPositionNormalTangent, VertexColor1Texture2, VertexJoints4>("mesh 0.0");
|
||||||
|
var prim = mesh.UsePrimitive(material);
|
||||||
|
var tangent = new Vector4(.5f, .5f, 0, 1);
|
||||||
|
var vert1 = new VertexBuilder<VertexPositionNormalTangent, VertexColor1Texture2, VertexJoints4>(
|
||||||
|
new VertexPositionNormalTangent(new Vector3(-1, 0, 1), Vector3.UnitY, tangent),
|
||||||
|
new VertexColor1Texture2(Vector4.One, Vector2.UnitY, Vector2.Zero),
|
||||||
|
new VertexJoints4([(0, 1), (0, 0), (0, 0), (0, 0)])
|
||||||
|
);
|
||||||
|
var vert2 = new VertexBuilder<VertexPositionNormalTangent, VertexColor1Texture2, VertexJoints4>(
|
||||||
|
new VertexPositionNormalTangent(new Vector3(1, 0, 1), Vector3.UnitY, tangent),
|
||||||
|
new VertexColor1Texture2(Vector4.One, Vector2.One, Vector2.Zero),
|
||||||
|
new VertexJoints4([(0, 1), (0, 0), (0, 0), (0, 0)])
|
||||||
|
);
|
||||||
|
var vert3 = new VertexBuilder<VertexPositionNormalTangent, VertexColor1Texture2, VertexJoints4>(
|
||||||
|
new VertexPositionNormalTangent(new Vector3(-1, 0, -1), Vector3.UnitY, tangent),
|
||||||
|
new VertexColor1Texture2(Vector4.One, Vector2.Zero, Vector2.Zero),
|
||||||
|
new VertexJoints4([(0, 1), (0, 0), (0, 0), (0, 0)])
|
||||||
|
);
|
||||||
|
var vert4 = new VertexBuilder<VertexPositionNormalTangent, VertexColor1Texture2, VertexJoints4>(
|
||||||
|
new VertexPositionNormalTangent(new Vector3(1, 0, -1), Vector3.UnitY, tangent),
|
||||||
|
new VertexColor1Texture2(Vector4.One, Vector2.UnitX, Vector2.Zero),
|
||||||
|
new VertexJoints4([(0, 1), (0, 0), (0, 0), (0, 0)])
|
||||||
|
);
|
||||||
|
prim.AddTriangle(vert2, vert3, vert1);
|
||||||
|
prim.AddTriangle(vert2, vert4, vert3);
|
||||||
|
var jKosi = new NodeBuilder("j_kosi");
|
||||||
|
var scene = new SceneBuilder();
|
||||||
|
scene.AddNode(jKosi);
|
||||||
|
scene.AddSkinnedMesh(mesh, Matrix4x4.Identity, [jKosi]);
|
||||||
|
var model = scene.ToGltf2();
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>) GetPositionWriter(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("POSITION", out var accessor))
|
||||||
|
throw new Exception("todo: some error about position being hard required");
|
||||||
|
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.Single3,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.Position,
|
||||||
|
};
|
||||||
|
|
||||||
|
IList<Vector3> values = accessor.AsVector3Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element,
|
||||||
|
(index, bytes) => WriteSingle3(values[index], bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: probably should sanity check that if there's weights or indexes, both are available? game is always symmetric
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>)? GetBlendWeightWriter(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("WEIGHTS_0", out var accessor))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.ByteFloat4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendWeights,
|
||||||
|
};
|
||||||
|
|
||||||
|
var values = accessor.AsVector4Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element,
|
||||||
|
(index, bytes) => WriteByteFloat4(values[index], bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this will need to take in a skeleton mapping of some kind so i can persist the bones used and wire up the joints correctly. hopefully by the "write vertex buffer" stage of building, we already know something about the skeleton.
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>)? GetBlendIndexWriter(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("JOINTS_0", out var accessor))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 0,
|
||||||
|
Type = (byte)MdlFile.VertexType.UInt,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.BlendIndices,
|
||||||
|
};
|
||||||
|
|
||||||
|
var values = accessor.AsVector4Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element,
|
||||||
|
(index, bytes) => WriteUInt(values[index], bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>)? GetNormalWriter(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("NORMAL", out var accessor))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 1,
|
||||||
|
Type = (byte)MdlFile.VertexType.Half4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.Normal,
|
||||||
|
};
|
||||||
|
|
||||||
|
var values = accessor.AsVector3Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element,
|
||||||
|
(index, bytes) => WriteHalf4(new Vector4(values[index], 0), bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>)? GetUvWriter(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("TEXCOORD_0", out var accessor1))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// We're omitting type here, and filling it in on return, as there's two different types we might use.
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 1,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.UV,
|
||||||
|
};
|
||||||
|
|
||||||
|
var values1 = accessor1.AsVector2Array();
|
||||||
|
|
||||||
|
if (!accessors.TryGetValue("TEXCOORD_1", out var accessor2))
|
||||||
|
return (
|
||||||
|
element with {Type = (byte)MdlFile.VertexType.Half2},
|
||||||
|
(index, bytes) => WriteHalf2(values1[index], bytes)
|
||||||
|
);
|
||||||
|
|
||||||
|
var values2 = accessor2.AsVector2Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element with {Type = (byte)MdlFile.VertexType.Half4},
|
||||||
|
(index, bytes) => {
|
||||||
|
var value1 = values1[index];
|
||||||
|
var value2 = values2[index];
|
||||||
|
WriteHalf4(new Vector4(value1.X, value1.Y, value2.X, value2.Y), bytes);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>)? GetTangent1Writer(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("TANGENT", out var accessor))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 1,
|
||||||
|
Type = (byte)MdlFile.VertexType.ByteFloat4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.Tangent1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var values = accessor.AsVector4Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element,
|
||||||
|
(index, bytes) => WriteByteFloat4(values[index], bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private (MdlStructs.VertexElement, Action<int, List<byte>>)? GetColorWriter(IReadOnlyDictionary<string, Accessor> accessors)
|
||||||
|
{
|
||||||
|
if (!accessors.TryGetValue("COLOR_0", out var accessor))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var element = new MdlStructs.VertexElement()
|
||||||
|
{
|
||||||
|
Stream = 1,
|
||||||
|
Type = (byte)MdlFile.VertexType.ByteFloat4,
|
||||||
|
Usage = (byte)MdlFile.VertexUsage.Color,
|
||||||
|
};
|
||||||
|
|
||||||
|
var values = accessor.AsVector4Array();
|
||||||
|
|
||||||
|
return (
|
||||||
|
element,
|
||||||
|
(index, bytes) => WriteByteFloat4(values[index], bytes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteSingle3(Vector3 input, List<byte> bytes)
|
||||||
|
{
|
||||||
|
bytes.AddRange(BitConverter.GetBytes(input.X));
|
||||||
|
bytes.AddRange(BitConverter.GetBytes(input.Y));
|
||||||
|
bytes.AddRange(BitConverter.GetBytes(input.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteUInt(Vector4 input, List<byte> bytes)
|
||||||
|
{
|
||||||
|
bytes.Add((byte)input.X);
|
||||||
|
bytes.Add((byte)input.Y);
|
||||||
|
bytes.Add((byte)input.Z);
|
||||||
|
bytes.Add((byte)input.W);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteByteFloat4(Vector4 input, List<byte> bytes)
|
||||||
|
{
|
||||||
|
bytes.Add((byte)Math.Round(input.X * 255f));
|
||||||
|
bytes.Add((byte)Math.Round(input.Y * 255f));
|
||||||
|
bytes.Add((byte)Math.Round(input.Z * 255f));
|
||||||
|
bytes.Add((byte)Math.Round(input.W * 255f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteHalf2(Vector2 input, List<byte> bytes)
|
||||||
|
{
|
||||||
|
bytes.AddRange(BitConverter.GetBytes((Half)input.X));
|
||||||
|
bytes.AddRange(BitConverter.GetBytes((Half)input.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteHalf4(Vector4 input, List<byte> bytes)
|
||||||
|
{
|
||||||
|
bytes.AddRange(BitConverter.GetBytes((Half)input.X));
|
||||||
|
bytes.AddRange(BitConverter.GetBytes((Half)input.Y));
|
||||||
|
bytes.AddRange(BitConverter.GetBytes((Half)input.Z));
|
||||||
|
bytes.AddRange(BitConverter.GetBytes((Half)input.W));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte TypeSize(MdlFile.VertexType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
MdlFile.VertexType.Single3 => 12,
|
||||||
|
MdlFile.VertexType.Single4 => 16,
|
||||||
|
MdlFile.VertexType.UInt => 4,
|
||||||
|
MdlFile.VertexType.ByteFloat4 => 4,
|
||||||
|
MdlFile.VertexType.Half2 => 4,
|
||||||
|
MdlFile.VertexType.Half4 => 8,
|
||||||
|
|
||||||
|
_ => throw new Exception($"Unhandled vertex type {type}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(CancellationToken cancel)
|
||||||
|
{
|
||||||
|
var model = Build();
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
// todo this'll need to check names and such. also loop. i'm relying on a single mesh here which is Wrong:tm:
|
||||||
|
var mesh = model.LogicalNodes
|
||||||
|
.Where(node => node.Mesh != null)
|
||||||
|
.Select(node => node.Mesh)
|
||||||
|
.First();
|
||||||
|
|
||||||
|
// todo check how many prims there are - maybe throw if more than one? not sure
|
||||||
|
var prim = mesh.Primitives[0];
|
||||||
|
|
||||||
|
var accessors = prim.VertexAccessors;
|
||||||
|
|
||||||
|
var rawWriters = new[] {
|
||||||
|
GetPositionWriter(accessors),
|
||||||
|
GetBlendWeightWriter(accessors),
|
||||||
|
GetBlendIndexWriter(accessors),
|
||||||
|
GetNormalWriter(accessors),
|
||||||
|
GetTangent1Writer(accessors),
|
||||||
|
GetColorWriter(accessors),
|
||||||
|
GetUvWriter(accessors),
|
||||||
|
};
|
||||||
|
|
||||||
|
var writers = new List<(MdlStructs.VertexElement, Action<int, List<byte>>)>();
|
||||||
|
var offsets = new byte[] {0, 0, 0};
|
||||||
|
foreach (var writer in rawWriters)
|
||||||
|
{
|
||||||
|
if (writer == null) continue;
|
||||||
|
var element = writer.Value.Item1;
|
||||||
|
writers.Add((
|
||||||
|
element with {Offset = offsets[element.Stream]},
|
||||||
|
writer.Value.Item2
|
||||||
|
));
|
||||||
|
offsets[element.Stream] += TypeSize((MdlFile.VertexType)element.Type);
|
||||||
|
}
|
||||||
|
var strides = offsets;
|
||||||
|
|
||||||
|
var streams = new List<byte>[3];
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
streams[i] = new List<byte>();
|
||||||
|
|
||||||
|
// todo: this is a bit lmao but also... probably the most sane option? getting the count that is
|
||||||
|
var vertexCount = prim.VertexAccessors["POSITION"].Count;
|
||||||
|
for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
|
||||||
|
{
|
||||||
|
foreach (var (element, writer) in writers)
|
||||||
|
{
|
||||||
|
writer(vertexIndex, streams[element.Stream]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// indices
|
||||||
|
var indexCount = prim.GetIndexAccessor().Count;
|
||||||
|
var indices = prim.GetIndices()
|
||||||
|
.SelectMany(index => BitConverter.GetBytes((ushort)index))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var dataBuffer = streams[0].Concat(streams[1]).Concat(streams[2]).Concat(indices);
|
||||||
|
|
||||||
|
var lod1VertLen = (uint)(streams[0].Count + streams[1].Count + streams[2].Count);
|
||||||
|
|
||||||
|
var mdl = new MdlFile()
|
||||||
|
{
|
||||||
|
Radius = 1,
|
||||||
|
// todo: lod calcs... probably handled in penum? we probably only need to think about lod0 for actual import workflow.
|
||||||
|
VertexOffset = [0, 0, 0],
|
||||||
|
IndexOffset = [lod1VertLen, 0, 0],
|
||||||
|
VertexBufferSize = [lod1VertLen, 0, 0],
|
||||||
|
IndexBufferSize = [(uint)indices.Length, 0, 0],
|
||||||
|
LodCount = 1,
|
||||||
|
BoundingBoxes = new MdlStructs.BoundingBoxStruct()
|
||||||
|
{
|
||||||
|
Min = [-1, 0, -1, 1],
|
||||||
|
Max = [1, 0, 1, 1],
|
||||||
|
},
|
||||||
|
VertexDeclarations = [new MdlStructs.VertexDeclarationStruct()
|
||||||
|
{
|
||||||
|
VertexElements = writers.Select(x => x.Item1).ToArray(),
|
||||||
|
}],
|
||||||
|
Meshes = [new MdlStructs.MeshStruct()
|
||||||
|
{
|
||||||
|
VertexCount = (ushort)vertexCount,
|
||||||
|
IndexCount = (uint)indexCount,
|
||||||
|
MaterialIndex = 0,
|
||||||
|
SubMeshIndex = 0,
|
||||||
|
SubMeshCount = 1,
|
||||||
|
BoneTableIndex = 0,
|
||||||
|
StartIndex = 0,
|
||||||
|
// todo: this will need to be composed down across multiple submeshes. given submeshes store contiguous buffers
|
||||||
|
VertexBufferOffset = [0, (uint)streams[0].Count, (uint)(streams[0].Count + streams[1].Count)],
|
||||||
|
VertexBufferStride = strides,
|
||||||
|
VertexStreamCount = 2,
|
||||||
|
}],
|
||||||
|
BoneTables = [new MdlStructs.BoneTableStruct()
|
||||||
|
{
|
||||||
|
BoneCount = 1,
|
||||||
|
// this needs to be the full 64. this should be fine _here_ with 0s because i only have one bone, but will need to be fully populated properly. in real files.
|
||||||
|
BoneIndex = new ushort[64],
|
||||||
|
}],
|
||||||
|
BoneBoundingBoxes = [
|
||||||
|
// new MdlStructs.BoundingBoxStruct()
|
||||||
|
// {
|
||||||
|
// Min = [
|
||||||
|
// -0.081672676f,
|
||||||
|
// -0.113717034f,
|
||||||
|
// -0.11905348f,
|
||||||
|
// 1.0f,
|
||||||
|
// ],
|
||||||
|
// Max = [
|
||||||
|
// 0.03941727f,
|
||||||
|
// 0.09845419f,
|
||||||
|
// 0.107391916f,
|
||||||
|
// 1.0f,
|
||||||
|
// ],
|
||||||
|
// },
|
||||||
|
|
||||||
|
// _would_ be nice if i didn't need to fill out this
|
||||||
|
new MdlStructs.BoundingBoxStruct()
|
||||||
|
{
|
||||||
|
Min = [0, 0, 0, 0],
|
||||||
|
Max = [0, 0, 0, 0],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
SubMeshes = [new MdlStructs.SubmeshStruct()
|
||||||
|
{
|
||||||
|
IndexOffset = 0,
|
||||||
|
IndexCount = (uint)indexCount,
|
||||||
|
AttributeIndexMask = 0,
|
||||||
|
BoneStartIndex = 0,
|
||||||
|
BoneCount = 1,
|
||||||
|
}],
|
||||||
|
|
||||||
|
// TODO pretty sure this is garbage data as far as textools functions
|
||||||
|
// game clearly doesn't rely on this, but the "correct" values are a listing of the bones used by each submesh
|
||||||
|
SubMeshBoneMap = [0],
|
||||||
|
|
||||||
|
Lods = [new MdlStructs.LodStruct()
|
||||||
|
{
|
||||||
|
MeshIndex = 0,
|
||||||
|
MeshCount = 1,
|
||||||
|
ModelLodRange = 0,
|
||||||
|
TextureLodRange = 0,
|
||||||
|
VertexBufferSize = lod1VertLen,
|
||||||
|
VertexDataOffset = 0,
|
||||||
|
IndexBufferSize = (uint)indexCount,
|
||||||
|
IndexDataOffset = lod1VertLen,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Bones = [
|
||||||
|
"j_kosi",
|
||||||
|
],
|
||||||
|
Materials = [
|
||||||
|
"/mt_c0201e6180_top_b.mtrl",
|
||||||
|
],
|
||||||
|
RemainingData = dataBuffer.ToArray(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Out = mdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(IAction? other)
|
||||||
|
{
|
||||||
|
if (other is not ImportGltfAction rhs)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
private ModEditWindow _edit;
|
private ModEditWindow _edit;
|
||||||
|
|
||||||
public readonly MdlFile Mdl;
|
public MdlFile Mdl { get; private set; }
|
||||||
private readonly List<string>[] _attributes;
|
private List<string>[] _attributes;
|
||||||
|
|
||||||
public List<Utf8GamePath>? GamePaths { get; private set ;}
|
public List<Utf8GamePath>? GamePaths { get; private set; }
|
||||||
public int GamePathIndex;
|
public int GamePathIndex;
|
||||||
|
|
||||||
public bool PendingIo { get; private set; } = false;
|
public bool PendingIo { get; private set; } = false;
|
||||||
|
|
@ -34,13 +34,19 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
_edit = edit;
|
_edit = edit;
|
||||||
|
|
||||||
Mdl = new MdlFile(bytes);
|
Initialize(new MdlFile(bytes));
|
||||||
_attributes = CreateAttributes(Mdl);
|
|
||||||
|
|
||||||
if (mod != null)
|
if (mod != null)
|
||||||
FindGamePaths(path, mod);
|
FindGamePaths(path, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(Mdl), nameof(_attributes))]
|
||||||
|
private void Initialize(MdlFile mdl)
|
||||||
|
{
|
||||||
|
Mdl = mdl;
|
||||||
|
_attributes = CreateAttributes(Mdl);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Valid
|
public bool Valid
|
||||||
=> Mdl.Valid;
|
=> Mdl.Valid;
|
||||||
|
|
@ -72,6 +78,12 @@ public partial class ModEditWindow
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Import()
|
||||||
|
{
|
||||||
|
// TODO: this needs to be fleshed out a bunch.
|
||||||
|
_edit._models.ImportGltf().ContinueWith(v => Initialize(v.Result));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Export model to an interchange format. </summary>
|
/// <summary> Export model to an interchange format. </summary>
|
||||||
/// <param name="outputPath"> Disk path to save the resulting file to. </param>
|
/// <param name="outputPath"> Disk path to save the resulting file to. </param>
|
||||||
public void Export(string outputPath, Utf8GamePath mdlPath)
|
public void Export(string outputPath, Utf8GamePath mdlPath)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ public partial class ModEditWindow
|
||||||
DrawExport(tab, disabled);
|
DrawExport(tab, disabled);
|
||||||
|
|
||||||
var ret = false;
|
var ret = false;
|
||||||
|
|
||||||
|
if (ImGui.Button("import test"))
|
||||||
|
{
|
||||||
|
tab.Import();
|
||||||
|
ret |= true;
|
||||||
|
}
|
||||||
|
|
||||||
ret |= DrawModelMaterialDetails(tab, disabled);
|
ret |= DrawModelMaterialDetails(tab, disabled);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue