mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-18 14:44:29 +01:00
Merge branch 'master' into mdl-import
This commit is contained in:
commit
1a88cefd52
93 changed files with 2132 additions and 1744 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Tasks;
|
||||
using Penumbra.Collections.Manager;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Import.Models.Export;
|
||||
using Penumbra.Import.Models.Import;
|
||||
|
|
@ -9,21 +10,13 @@ using SharpGLTF.Schema2;
|
|||
|
||||
namespace Penumbra.Import.Models;
|
||||
|
||||
public sealed class ModelManager : SingleTaskQueue, IDisposable
|
||||
public sealed class ModelManager(IFramework framework, GamePathParser _parser) : SingleTaskQueue, IDisposable
|
||||
{
|
||||
private readonly IFramework _framework;
|
||||
private readonly IDataManager _gameData;
|
||||
private readonly ActiveCollectionData _activeCollectionData;
|
||||
private readonly IFramework _framework = framework;
|
||||
|
||||
private readonly ConcurrentDictionary<IAction, (Task, CancellationTokenSource)> _tasks = new();
|
||||
private bool _disposed = false;
|
||||
|
||||
public ModelManager(IFramework framework, IDataManager gameData, ActiveCollectionData activeCollectionData)
|
||||
{
|
||||
_framework = framework;
|
||||
_gameData = gameData;
|
||||
_activeCollectionData = activeCollectionData;
|
||||
}
|
||||
private bool _disposed;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
@ -33,26 +26,6 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
|||
_tasks.Clear();
|
||||
}
|
||||
|
||||
private Task Enqueue(IAction action)
|
||||
{
|
||||
if (_disposed)
|
||||
return Task.FromException(new ObjectDisposedException(nameof(ModelManager)));
|
||||
|
||||
Task task;
|
||||
lock (_tasks)
|
||||
{
|
||||
task = _tasks.GetOrAdd(action, action =>
|
||||
{
|
||||
var token = new CancellationTokenSource();
|
||||
var task = Enqueue(action, token.Token);
|
||||
task.ContinueWith(_ => _tasks.TryRemove(action, out var unused), CancellationToken.None);
|
||||
return (task, token);
|
||||
}).Item1;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public Task ExportToGltf(MdlFile mdl, SklbFile? sklb, string outputPath)
|
||||
=> Enqueue(new ExportToGltfAction(this, mdl, sklb, outputPath));
|
||||
|
||||
|
|
@ -62,29 +35,64 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
|||
return Enqueue(action).ContinueWith(_ => action.Out!);
|
||||
}
|
||||
|
||||
private class ExportToGltfAction : IAction
|
||||
/// <summary> Try to find the .sklb path for a .mdl file. </summary>
|
||||
/// <param name="mdlPath"> .mdl file to look up the skeleton for. </param>
|
||||
public string? ResolveSklbForMdl(string mdlPath)
|
||||
{
|
||||
private readonly ModelManager _manager;
|
||||
var info = _parser.GetFileInfo(mdlPath);
|
||||
if (info.FileType is not FileType.Model)
|
||||
return null;
|
||||
|
||||
private readonly MdlFile _mdl;
|
||||
private readonly SklbFile? _sklb;
|
||||
private readonly string _outputPath;
|
||||
|
||||
public ExportToGltfAction(ModelManager manager, MdlFile mdl, SklbFile? sklb, string outputPath)
|
||||
return info.ObjectType switch
|
||||
{
|
||||
_manager = manager;
|
||||
_mdl = mdl;
|
||||
_sklb = sklb;
|
||||
_outputPath = outputPath;
|
||||
ObjectType.Equipment => GamePaths.Skeleton.Sklb.Path(info.GenderRace, "base", 1),
|
||||
ObjectType.Accessory => GamePaths.Skeleton.Sklb.Path(info.GenderRace, "base", 1),
|
||||
ObjectType.Character when info.BodySlot is BodySlot.Body or BodySlot.Tail => GamePaths.Skeleton.Sklb.Path(info.GenderRace, "base",
|
||||
1),
|
||||
ObjectType.Character => throw new Exception($"Currently unsupported human model type \"{info.BodySlot}\"."),
|
||||
ObjectType.DemiHuman => GamePaths.DemiHuman.Sklb.Path(info.PrimaryId),
|
||||
ObjectType.Monster => GamePaths.Monster.Sklb.Path(info.PrimaryId),
|
||||
ObjectType.Weapon => GamePaths.Weapon.Sklb.Path(info.PrimaryId),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private Task Enqueue(IAction action)
|
||||
{
|
||||
if (_disposed)
|
||||
return Task.FromException(new ObjectDisposedException(nameof(ModelManager)));
|
||||
|
||||
Task task;
|
||||
lock (_tasks)
|
||||
{
|
||||
task = _tasks.GetOrAdd(action, a =>
|
||||
{
|
||||
var token = new CancellationTokenSource();
|
||||
var t = Enqueue(a, token.Token);
|
||||
t.ContinueWith(_ =>
|
||||
{
|
||||
lock (_tasks)
|
||||
{
|
||||
return _tasks.TryRemove(a, out var unused);
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
return (t, token);
|
||||
}).Item1;
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
private class ExportToGltfAction(ModelManager manager, MdlFile mdl, SklbFile? sklb, string outputPath)
|
||||
: IAction
|
||||
{
|
||||
public void Execute(CancellationToken cancel)
|
||||
{
|
||||
Penumbra.Log.Debug("Reading skeleton.");
|
||||
var xivSkeleton = BuildSkeleton(cancel);
|
||||
|
||||
Penumbra.Log.Debug("Converting model.");
|
||||
var model = ModelExporter.Export(_mdl, xivSkeleton);
|
||||
var model = ModelExporter.Export(mdl, xivSkeleton);
|
||||
|
||||
Penumbra.Log.Debug("Building scene.");
|
||||
var scene = new SceneBuilder();
|
||||
|
|
@ -92,16 +100,16 @@ public sealed class ModelManager : SingleTaskQueue, IDisposable
|
|||
|
||||
Penumbra.Log.Debug("Saving.");
|
||||
var gltfModel = scene.ToGltf2();
|
||||
gltfModel.SaveGLTF(_outputPath);
|
||||
gltfModel.SaveGLTF(outputPath);
|
||||
}
|
||||
|
||||
/// <summary> Attempt to read out the pertinent information from a .sklb. </summary>
|
||||
private XivSkeleton? BuildSkeleton(CancellationToken cancel)
|
||||
{
|
||||
if (_sklb == null)
|
||||
if (sklb == null)
|
||||
return null;
|
||||
|
||||
var xmlTask = _manager._framework.RunOnFrameworkThread(() => HavokConverter.HkxToXml(_sklb.Skeleton));
|
||||
var xmlTask = manager._framework.RunOnFrameworkThread(() => HavokConverter.HkxToXml(sklb.Skeleton));
|
||||
xmlTask.Wait(cancel);
|
||||
var xml = xmlTask.Result;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue