diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj index 67ca26dee2..d7eb8499cb 100644 --- a/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.csproj @@ -50,4 +50,10 @@ false + + + + Always + + diff --git a/Dalamud.CorePlugin/Dalamud.CorePlugin.json b/Dalamud.CorePlugin/Dalamud.CorePlugin.json new file mode 100644 index 0000000000..7db669a73c --- /dev/null +++ b/Dalamud.CorePlugin/Dalamud.CorePlugin.json @@ -0,0 +1,9 @@ +{ + "Author": "Dalamud Maintainers", + "Name": "CorePlugin", + "Punchline": "Testbed for developing Dalamud features.", + "Description": "Develop and debug internal Dalamud features using CorePlugin. You have full access to all types in Dalamud assembly.", + "InternalName": "CorePlugin", + "ApplicableVersion": "any", + "Tags": [] +} diff --git a/Dalamud.CorePlugin/PluginImpl.cs b/Dalamud.CorePlugin/PluginImpl.cs index ef99f6def6..f5ff92537f 100644 --- a/Dalamud.CorePlugin/PluginImpl.cs +++ b/Dalamud.CorePlugin/PluginImpl.cs @@ -56,7 +56,7 @@ public void Dispose() /// /// Dalamud plugin interface. /// Logging service. - public PluginImpl(DalamudPluginInterface pluginInterface, IPluginLog log) + public PluginImpl(DalamudPluginInterface pluginInterface, IDataManager dataManager, ITextureProvider textureProvider, IPluginLog log) { try { @@ -64,7 +64,7 @@ public PluginImpl(DalamudPluginInterface pluginInterface, IPluginLog log) this.Interface = pluginInterface; this.pluginLog = log; - this.windowSystem.AddWindow(new PluginWindow()); + this.windowSystem.AddWindow(new PluginWindow(pluginInterface.UiBuilder, dataManager, textureProvider)); this.Interface.UiBuilder.Draw += this.OnDraw; this.Interface.UiBuilder.OpenConfigUi += this.OnOpenConfigUi; diff --git a/Dalamud.CorePlugin/PluginWindow.cs b/Dalamud.CorePlugin/PluginWindow.cs index 27be82f41e..6430b04245 100644 --- a/Dalamud.CorePlugin/PluginWindow.cs +++ b/Dalamud.CorePlugin/PluginWindow.cs @@ -1,8 +1,14 @@ using System; -using System.Numerics; - +using System.IO; +using Dalamud.Interface; +using Dalamud.Interface.Internal; using Dalamud.Interface.Windowing; +using Dalamud.Plugin.Services; using ImGuiNET; +using Lumina.Data.Files; +using SharpDX; +using SharpDX.Direct3D11; +using Vector2 = System.Numerics.Vector2; namespace Dalamud.CorePlugin { @@ -11,21 +17,59 @@ namespace Dalamud.CorePlugin /// internal class PluginWindow : Window, IDisposable { + private readonly UiBuilder uiBuilder; + private readonly ITextureProvider textureProvider; + private readonly IDalamudTextureWrap tex; + private readonly IntPtr callbackId; + private readonly Device device; + private readonly DeviceContext deviceContext; + private readonly PixelShader pixelShader; + private readonly SamplerState fontSampler; + /// /// Initializes a new instance of the class. /// - public PluginWindow() + public PluginWindow(UiBuilder uiBuilder, IDataManager dataManager, ITextureProvider textureProvider) : base("CorePlugin") { + this.uiBuilder = uiBuilder; + this.textureProvider = textureProvider; this.IsOpen = true; this.Size = new Vector2(810, 520); this.SizeCondition = ImGuiCond.FirstUseEver; + + this.tex = this.textureProvider.GetTexture(dataManager.GetFile("chara/monster/m0361/obj/body/b0001/texture/v01_m0361b0001_n.tex")!); + this.callbackId = this.uiBuilder.AddImGuiDrawCmdUserCallback(this.DrawCmdUserCallback); + this.device = CppObject.FromPointer(uiBuilder.DeviceNativePointer); + this.deviceContext = CppObject.FromPointer(uiBuilder.DeviceContextNativePointer); + this.pixelShader = new PixelShader(this.device, File.ReadAllBytes(@"Z:\test.fxc")); + this.fontSampler = new SamplerState(this.device, new SamplerStateDescription + { + Filter = Filter.MinMagMipLinear, + AddressU = TextureAddressMode.Wrap, + AddressV = TextureAddressMode.Wrap, + AddressW = TextureAddressMode.Wrap, + MipLodBias = 0, + ComparisonFunction = Comparison.Always, + MinimumLod = 0, + MaximumLod = 0, + }); + } + + private void DrawCmdUserCallback(ImDrawDataPtr drawData, ImDrawCmdPtr drawCmd) + { + this.deviceContext.PixelShader.Set(this.pixelShader); + this.deviceContext.PixelShader.SetSampler(0, this.fontSampler); } /// public void Dispose() { + this.uiBuilder.RemoveImGuiDrawCmdUserCallback(this.DrawCmdUserCallback); + this.tex.Dispose(); + this.pixelShader.Dispose(); + this.fontSampler.Dispose(); } /// @@ -36,6 +80,15 @@ public override void OnOpen() /// public override void Draw() { + var drawList = ImGui.GetWindowDrawList(); + drawList.AddCallback(this.callbackId, nint.Zero); + ImGui.Image(this.tex.ImGuiHandle, new(512, 512), new(1, 0), new(2, 1)); + ImGui.SameLine(); + ImGui.Image(this.tex.ImGuiHandle, new(512, 512), new(2, 0), new(3, 1)); + ImGui.Image(this.tex.ImGuiHandle, new(512, 512), new(3, 0), new(4, 1)); + ImGui.SameLine(); + ImGui.Image(this.tex.ImGuiHandle, new(512, 512), new(4, 0), new(5, 1)); + drawList.AddCallback(this.uiBuilder.ImGuiResetDrawCmdUserCallback, nint.Zero); } } } diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index d5394fe8d9..1f2d85f070 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -149,10 +149,20 @@ private InterfaceManager() public ImGuiIOPtr LastImGuiIoPtr { get; set; } /// - /// Gets the D3D11 device instance. + /// Gets the ImGuiRenderer from ImGuiScene. + /// + public IImGuiRenderer? ImGuiRenderer => this.scene?.Renderer; + + /// + /// Gets the D3D11 device instance. Note that the reference count is not increased. /// public SharpDX.Direct3D11.Device? Device => this.scene?.Device; + /// + /// Gets the D3D11 device context. Note that the reference count is not increased. + /// + public DeviceContext? DeviceContext => this.scene?.DeviceContext; + /// /// Gets the address handle to the main process window. /// diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index dd2e5bad38..da1a4cecbd 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -16,7 +16,6 @@ using ImGuiNET; using ImGuiScene; using Serilog; -using SharpDX.Direct3D11; namespace Dalamud.Interface; @@ -32,6 +31,8 @@ public sealed class UiBuilder : IDisposable private readonly InterfaceManager interfaceManager = Service.Get(); private readonly GameFontManager gameFontManager = Service.Get(); + private readonly Dictionary drawCmdUserCallbackDelegates = new(); + [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); @@ -122,9 +123,34 @@ internal UiBuilder(string namespaceName) public static ImFontPtr MonoFont => InterfaceManager.MonoFont; /// - /// Gets the game's active Direct3D device. + /// Gets the game's active D3D11 device instance. Note that the reference count is not increased. + /// + public SharpDX.Direct3D11.Device? Device => this.InterfaceManagerWithScene?.Device; + + /// + /// Gets the game's active D3D11 device context. Note that the reference count is not increased. + /// + public SharpDX.Direct3D11.DeviceContext? DeviceContext => this.InterfaceManagerWithScene?.DeviceContext; + + /// + /// Gets the native pointer of the game's active D3D11 device instance. + /// Note that the reference count is not increased. + /// + public nint DeviceNativePointer => this.Device?.NativePointer ?? nint.Zero; + + /// + /// Gets the native pointer of the game's active D3D11 device context. + /// Note that the reference count is not increased. + /// + public nint DeviceContextNativePointer => this.DeviceContext?.NativePointer ?? nint.Zero; + + /// + /// Gets the ImGui DrawCmd callback for resetting the render parameters. /// - public Device Device => this.InterfaceManagerWithScene.Device!; + /// When UI is not yet ready. + public nint ImGuiResetDrawCmdUserCallback => + this.interfaceManager.ImGuiRenderer?.ResetDrawCmdUserCallback + ?? throw new InvalidOperationException("Renderer is not yet ready."); /// /// Gets the game's main window handle. @@ -230,6 +256,36 @@ public bool CutsceneActive private Task InterfaceManagerWithSceneAsync => Service.GetAsync().ContinueWith(task => task.Result.Manager); + /// + /// Adds a callback that will be called on rendering a texture with set. + /// + /// The callback. + /// The value to use for . + /// If UI is not yet ready. + public nint AddImGuiDrawCmdUserCallback(IImGuiRenderer.DrawCmdUserCallbackDelegate cb) + { + if (this.drawCmdUserCallbackDelegates.TryGetValue(cb, out var p)) + return p; + + if (this.interfaceManager.ImGuiRenderer is not { } renderer) + throw new InvalidOperationException("Renderer is not yet ready."); + + this.drawCmdUserCallbackDelegates.Add(cb, p = renderer.AddDrawCmdUserCallback(cb)); + return p; + } + + /// + /// Removes a callback that will be called on rendering a texture with set. + /// + /// The callback. + public void RemoveImGuiDrawCmdUserCallback(IImGuiRenderer.DrawCmdUserCallbackDelegate cb) + { + // Either it's not ready and nothing could have been already added yet, + // or it's destructing down and we don't care anymore + this.drawCmdUserCallbackDelegates.Remove(cb); + this.interfaceManager.ImGuiRenderer?.RemoveDrawCmdUserCallback(cb); + } + /// /// Loads an image from the specified file. /// @@ -397,6 +453,8 @@ void IDisposable.Dispose() this.interfaceManager.Draw -= this.OnDraw; this.interfaceManager.BuildFonts -= this.OnBuildFonts; this.interfaceManager.ResizeBuffers -= this.OnResizeBuffers; + foreach (var cb in this.drawCmdUserCallbackDelegates.Keys) + this.interfaceManager.ImGuiRenderer?.RemoveDrawCmdUserCallback(cb); } /// diff --git a/lib/ImGuiScene b/lib/ImGuiScene index 2f37349ffd..7dbaf3ba9f 160000 --- a/lib/ImGuiScene +++ b/lib/ImGuiScene @@ -1 +1 @@ -Subproject commit 2f37349ffd778561a1103a650683116c43edc86c +Subproject commit 7dbaf3ba9fd115b0e8755da1a1c30fe910b90ea6