From ed283afe2caa835cdbe6994c31852ff25510481d Mon Sep 17 00:00:00 2001 From: ackwell Date: Wed, 27 Dec 2023 01:44:24 +1100 Subject: [PATCH] async is a great idea lets do more of that --- Penumbra/Import/Models/ModelManager.cs | 98 ++++++++++++++----- .../UI/AdvancedWindow/ModEditWindow.Models.cs | 11 ++- 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/Penumbra/Import/Models/ModelManager.cs b/Penumbra/Import/Models/ModelManager.cs index 33ad9249..fbccf4b7 100644 --- a/Penumbra/Import/Models/ModelManager.cs +++ b/Penumbra/Import/Models/ModelManager.cs @@ -1,3 +1,4 @@ +using OtterGui.Tasks; using Penumbra.GameData.Files; using SharpGLTF.Geometry; using SharpGLTF.Geometry.VertexTypes; @@ -6,40 +7,89 @@ using SharpGLTF.Scenes; namespace Penumbra.Import.Models; -public sealed class ModelManager +public sealed class ModelManager : SingleTaskQueue, IDisposable { + private readonly ConcurrentDictionary _tasks = new(); + private bool _disposed = false; + public ModelManager() { // } - // TODO: Consider moving import/export onto an async queue, check ../textures/texturemanager - - public void ExportToGltf(/* MdlFile mdl, */string path) + public void Dispose() { - var mesh = new MeshBuilder("mesh"); + _disposed = true; + foreach (var (_, cancel) in _tasks.Values.ToArray()) + cancel.Cancel(); + _tasks.Clear(); + } - var material1 = new MaterialBuilder() - .WithDoubleSide(true) - .WithMetallicRoughnessShader() - .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 0, 1)); - var primitive1 = mesh.UsePrimitive(material1); - primitive1.AddTriangle(new VertexPosition(-10, 0, 0), new VertexPosition(10, 0, 0), new VertexPosition(0, 10, 0)); - primitive1.AddTriangle(new VertexPosition(10, 0, 0), new VertexPosition(-10, 0, 0), new VertexPosition(0, -10, 0)); - - var material2 = new MaterialBuilder() - .WithDoubleSide(true) - .WithMetallicRoughnessShader() - .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 1, 1)); - var primitive2 = mesh.UsePrimitive(material2); - primitive2.AddQuadrangle(new VertexPosition(-5, 0, 3), new VertexPosition(0, -5, 3), new VertexPosition(5, 0, 3), new VertexPosition(0, 5, 3)); + private Task Enqueue(IAction action) + { + if (_disposed) + return Task.FromException(new ObjectDisposedException(nameof(ModelManager))); - var scene = new SceneBuilder(); - scene.AddRigidMesh(mesh, Matrix4x4.Identity); + 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; + } - var model = scene.ToGltf2(); - model.SaveGLTF(path); + return task; + } - // TODO: Draw the rest of the owl. + public Task ExportToGltf(/* MdlFile mdl, */string path) + => Enqueue(new ExportToGltfAction(path)); + + private class ExportToGltfAction : IAction + { + private readonly string _path; + + public ExportToGltfAction(string path) + { + _path = path; + } + + public void Execute(CancellationToken token) + { + var mesh = new MeshBuilder("mesh"); + + var material1 = new MaterialBuilder() + .WithDoubleSide(true) + .WithMetallicRoughnessShader() + .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 0, 1)); + var primitive1 = mesh.UsePrimitive(material1); + primitive1.AddTriangle(new VertexPosition(-10, 0, 0), new VertexPosition(10, 0, 0), new VertexPosition(0, 10, 0)); + primitive1.AddTriangle(new VertexPosition(10, 0, 0), new VertexPosition(-10, 0, 0), new VertexPosition(0, -10, 0)); + + var material2 = new MaterialBuilder() + .WithDoubleSide(true) + .WithMetallicRoughnessShader() + .WithChannelParam(KnownChannel.BaseColor, KnownProperty.RGBA, new Vector4(1, 0, 1, 1)); + var primitive2 = mesh.UsePrimitive(material2); + primitive2.AddQuadrangle(new VertexPosition(-5, 0, 3), new VertexPosition(0, -5, 3), new VertexPosition(5, 0, 3), new VertexPosition(0, 5, 3)); + + var scene = new SceneBuilder(); + scene.AddRigidMesh(mesh, Matrix4x4.Identity); + + var model = scene.ToGltf2(); + model.SaveGLTF(_path); + } + + public bool Equals(IAction? other) + { + if (other is not ExportToGltfAction rhs) + return false; + + // TODO: compare configuration + return true; + } } } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index 80831dab..89497cfd 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -14,10 +14,11 @@ public partial class ModEditWindow { private const int MdlMaterialMaximum = 4; - private readonly ModelManager _models; - private readonly FileEditor _modelTab; + private readonly ModelManager _models; + private bool _pendingIo = false; + private string _modelNewMaterial = string.Empty; private readonly List _subMeshAttributeTagWidgets = []; @@ -34,9 +35,11 @@ public partial class ModEditWindow ); } - if (ImGui.Button("bingo bango")) + if (ImGuiUtil.DrawDisabledButton("bingo bango", Vector2.Zero, "description", _pendingIo)) { - _models.ExportToGltf("C:\\Users\\ackwell\\blender\\gltf-tests\\bingo.gltf"); + _pendingIo = true; + var task = _models.ExportToGltf("C:\\Users\\ackwell\\blender\\gltf-tests\\bingo.gltf"); + task.ContinueWith(_ => _pendingIo = false); } var ret = false;