Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ImDrawList UserCallback support #1526

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Dalamud.CorePlugin/Dalamud.CorePlugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,10 @@
<Private>false</Private>
</ProjectReference>
</ItemGroup>

<ItemGroup>
<None Update="Dalamud.CorePlugin.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
9 changes: 9 additions & 0 deletions Dalamud.CorePlugin/Dalamud.CorePlugin.json
Original file line number Diff line number Diff line change
@@ -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": []
}
4 changes: 2 additions & 2 deletions Dalamud.CorePlugin/PluginImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ public void Dispose()
/// </summary>
/// <param name="pluginInterface">Dalamud plugin interface.</param>
/// <param name="log">Logging service.</param>
public PluginImpl(DalamudPluginInterface pluginInterface, IPluginLog log)
public PluginImpl(DalamudPluginInterface pluginInterface, IDataManager dataManager, ITextureProvider textureProvider, IPluginLog log)
{
try
{
// this.InitLoc();
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;
Expand Down
59 changes: 56 additions & 3 deletions Dalamud.CorePlugin/PluginWindow.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -11,21 +17,59 @@ namespace Dalamud.CorePlugin
/// </summary>
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;

/// <summary>
/// Initializes a new instance of the <see cref="PluginWindow"/> class.
/// </summary>
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<TexFile>("chara/monster/m0361/obj/body/b0001/texture/v01_m0361b0001_n.tex")!);
this.callbackId = this.uiBuilder.AddImGuiDrawCmdUserCallback(this.DrawCmdUserCallback);
this.device = CppObject.FromPointer<Device>(uiBuilder.DeviceNativePointer);
this.deviceContext = CppObject.FromPointer<DeviceContext>(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);
}

/// <inheritdoc/>
public void Dispose()
{
this.uiBuilder.RemoveImGuiDrawCmdUserCallback(this.DrawCmdUserCallback);
this.tex.Dispose();
this.pixelShader.Dispose();
this.fontSampler.Dispose();
}

/// <inheritdoc/>
Expand All @@ -36,6 +80,15 @@ public override void OnOpen()
/// <inheritdoc/>
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);
}
}
}
12 changes: 11 additions & 1 deletion Dalamud/Interface/Internal/InterfaceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,20 @@ private InterfaceManager()
public ImGuiIOPtr LastImGuiIoPtr { get; set; }

/// <summary>
/// Gets the D3D11 device instance.
/// Gets the ImGuiRenderer from ImGuiScene.
/// </summary>
public IImGuiRenderer? ImGuiRenderer => this.scene?.Renderer;

/// <summary>
/// Gets the D3D11 device instance. Note that the reference count is not increased.
/// </summary>
public SharpDX.Direct3D11.Device? Device => this.scene?.Device;

/// <summary>
/// Gets the D3D11 device context. Note that the reference count is not increased.
/// </summary>
public DeviceContext? DeviceContext => this.scene?.DeviceContext;

/// <summary>
/// Gets the address handle to the main process window.
/// </summary>
Expand Down
64 changes: 61 additions & 3 deletions Dalamud/Interface/UiBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
using ImGuiNET;
using ImGuiScene;
using Serilog;
using SharpDX.Direct3D11;

namespace Dalamud.Interface;

Expand All @@ -32,6 +31,8 @@ public sealed class UiBuilder : IDisposable
private readonly InterfaceManager interfaceManager = Service<InterfaceManager>.Get();
private readonly GameFontManager gameFontManager = Service<GameFontManager>.Get();

private readonly Dictionary<IImGuiRenderer.DrawCmdUserCallbackDelegate, nint> drawCmdUserCallbackDelegates = new();

[ServiceManager.ServiceDependency]
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();

Expand Down Expand Up @@ -122,9 +123,34 @@ internal UiBuilder(string namespaceName)
public static ImFontPtr MonoFont => InterfaceManager.MonoFont;

/// <summary>
/// Gets the game's active Direct3D device.
/// Gets the game's active D3D11 device instance. Note that the reference count is not increased.
/// </summary>
public SharpDX.Direct3D11.Device? Device => this.InterfaceManagerWithScene?.Device;

/// <summary>
/// Gets the game's active D3D11 device context. Note that the reference count is not increased.
/// </summary>
public SharpDX.Direct3D11.DeviceContext? DeviceContext => this.InterfaceManagerWithScene?.DeviceContext;

/// <summary>
/// Gets the native pointer of the game's active D3D11 device instance.
/// Note that the reference count is not increased.
/// </summary>
public nint DeviceNativePointer => this.Device?.NativePointer ?? nint.Zero;

/// <summary>
/// Gets the native pointer of the game's active D3D11 device context.
/// Note that the reference count is not increased.
/// </summary>
public nint DeviceContextNativePointer => this.DeviceContext?.NativePointer ?? nint.Zero;

/// <summary>
/// Gets the ImGui DrawCmd callback for resetting the render parameters.
/// </summary>
public Device Device => this.InterfaceManagerWithScene.Device!;
/// <exception cref="InvalidOperationException">When UI is not yet ready.</exception>
public nint ImGuiResetDrawCmdUserCallback =>
this.interfaceManager.ImGuiRenderer?.ResetDrawCmdUserCallback
?? throw new InvalidOperationException("Renderer is not yet ready.");

/// <summary>
/// Gets the game's main window handle.
Expand Down Expand Up @@ -230,6 +256,36 @@ public bool CutsceneActive
private Task<InterfaceManager> InterfaceManagerWithSceneAsync =>
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync().ContinueWith(task => task.Result.Manager);

/// <summary>
/// Adds a callback that will be called on rendering a texture with <see cref="ImDrawCmd.UserCallback"/> set.
/// </summary>
/// <param name="cb">The callback.</param>
/// <returns>The value to use for <see cref="ImDrawCmd.UserCallback"/>.</returns>
/// <exception cref="InvalidOperationException">If UI is not yet ready.</exception>
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;
}

/// <summary>
/// Removes a callback that will be called on rendering a texture with <see cref="ImDrawCmd.UserCallback"/> set.
/// </summary>
/// <param name="cb">The callback.</param>
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);
}

/// <summary>
/// Loads an image from the specified file.
/// </summary>
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand Down