From abdcea3b71eb50448f942055b9d9aecaf71acd39 Mon Sep 17 00:00:00 2001 From: Patrick Dawson Date: Fri, 19 Apr 2024 22:11:45 +0200 Subject: [PATCH] wip --- .github/workflows/gdext.yml | 224 +++++++++++++++ Dear ImGui for Godot Demo.csproj | 2 +- addons/imgui-godot/ImGuiGodot/ImGuiGD.cs | 6 +- addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs | 138 ++++----- .../ImGuiGodot/Internal/BackendNative.cs | 10 +- .../ImGuiGodot/Internal/BackendNet.cs | 6 +- .../ImGuiGodot/Internal/CanvasRenderer.cs | 10 +- .../imgui-godot/ImGuiGodot/Internal/Input.cs | 12 +- .../ImGuiGodot/Internal/RdRenderer.cs | 1 - .../imgui-godot/ImGuiGodot/Internal/State.cs | 35 +-- .../imgui-godot/ImGuiGodot/Internal/Util.cs | 11 - .../ImGuiGodot/Internal/Viewports.cs | 28 +- gdext/godot-cpp | 2 +- gdext/include/imgui-godot.h | 86 +++--- gdext/src/CanvasRenderer.cpp | 178 +++++++++++- gdext/src/CanvasRenderer.h | 1 + gdext/src/Context.cpp | 272 ++++++++++++------ gdext/src/Context.h | 43 +-- gdext/src/DummyRenderer.h | 5 + gdext/src/Fonts.h | 2 +- gdext/src/ImGuiGD.cpp | 46 +-- gdext/src/ImGuiLayer.cpp | 120 ++++---- gdext/src/ImGuiLayer.h | 2 - gdext/src/ImGuiLayerHelper.cpp | 10 +- gdext/src/Input.cpp | 26 +- gdext/src/Input.h | 3 + gdext/src/RdRenderer.cpp | 18 ++ gdext/src/RdRenderer.h | 5 +- gdext/src/Renderer.h | 1 + gdext/src/Viewports.cpp | 4 +- gdext/src/main.cpp | 1 - 31 files changed, 930 insertions(+), 378 deletions(-) create mode 100644 .github/workflows/gdext.yml diff --git a/.github/workflows/gdext.yml b/.github/workflows/gdext.yml new file mode 100644 index 0000000..617aa2a --- /dev/null +++ b/.github/workflows/gdext.yml @@ -0,0 +1,224 @@ +name: 🦕 C++ GDExtension + +on: push + +env: + VCPKG_ROOT: "${{ github.workspace }}/vcpkg" + vcpkg_tag: 2024.03.25 + +jobs: + windows: + name: 🪟Windows + runs-on: windows-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Setup Python 3.x + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Setup scons + run: | + python -m pip install scons + scons --version + + - name: Get vcpkg + run: | + git clone -n https://github.com/microsoft/vcpkg + cd vcpkg + git checkout ${{ env.vcpkg_tag }} + .\bootstrap-vcpkg.bat + + - name: vcpkg install + run: | + cd gdext + ${{ env.VCPKG_ROOT }}\vcpkg install --triplet x64-windows-static + + - name: GDScript bindings + run: | + cd gdext + pip install ply + + - name: Build debug + run: | + cd gdext + scons debug_symbols=yes + + - name: Build release + run: | + cd gdext + scons debug_symbols=yes target=template_release + + - name: Upload binaries + uses: actions/upload-artifact@v3 + with: + name: gdext-windows + path: addons/imgui-godot/bin + + linux: + name: 🐧Linux + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Setup Python 3.x + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Setup scons + run: | + python -m pip install scons + scons --version + + - name: Get vcpkg + run: | + git clone -n https://github.com/microsoft/vcpkg + cd vcpkg + git checkout ${{ env.vcpkg_tag }} + ./bootstrap-vcpkg.sh + + - name: vcpkg install + run: | + cd gdext + ${{ env.VCPKG_ROOT }}/vcpkg install --triplet x64-linux + + - name: GDScript bindings + run: | + cd gdext + pip install ply + sudo apt install clang-format + + - name: Build debug + run: | + cd gdext + scons + + - name: Build release + run: | + cd gdext + scons target=template_release + + - name: Upload binaries + uses: actions/upload-artifact@v3 + with: + name: gdext-linux + path: addons/imgui-godot/bin + + macos: + name: 🍎macOS + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Setup Python 3.x + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Setup scons + run: | + python -m pip install scons + scons --version + + - name: Get vcpkg + run: | + git clone -n https://github.com/microsoft/vcpkg + cd vcpkg + git checkout ${{ env.vcpkg_tag }} + ./bootstrap-vcpkg.sh + + - name: vcpkg install + run: | + cd gdext + zsh scripts/vcpkg-macos.sh + + - name: GDScript bindings + run: | + cd gdext + pip install ply + brew install clang-format + + - name: Build debug + run: | + export PATH="$(brew --prefix llvm@15)/bin:$PATH" + echo $PATH + clang++ --version + cd gdext + scons + + - name: Build release + run: | + export PATH="$(brew --prefix llvm@15)/bin:$PATH" + echo $PATH + cd gdext + scons target=template_release + + - name: Upload binaries + uses: actions/upload-artifact@v3 + with: + name: gdext-macos + path: addons/imgui-godot/bin + + package: + name: 📦Package + runs-on: ubuntu-latest + needs: [windows, linux, macos] + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: actions/download-artifact@v3 + with: + path: addons/imgui-godot/bin + + - run: ls -R + working-directory: addons/imgui-godot/bin + + - name: Extract + run: | + cd addons/imgui-godot/bin + rm *.exp + rm *.lib + + - name: Upload PDBs + uses: actions/upload-artifact@v3 + with: + name: pdbs + path: addons/imgui-godot/bin/*.pdb + + - name: Prepare files + id: prep + run: | + ls -R + rm addons/imgui-godot/bin/*.pdb + cp -r gdext/include/*.h addons/imgui-godot/include/ + + env + imgui_ver=$(grep -m 1 "^#define IMGUI_VERSION " < gdext/imgui/imgui.h | awk '{ print $3 }' | sed 's/"//g') + godot_ver=$(grep -m 1 compatibility_minimum < addons/imgui-godot/imgui-godot-native.gdextension | sed 's/compatibility_minimum = //') + plugin_ver=$(grep -m 1 "version=" < addons/imgui-godot/plugin.cfg | sed 's/version=//' | sed 's/"//g') + pkgfn=imgui-godot-native-${plugin_ver}_godot-${godot_ver}_imgui-${imgui_ver} + echo $pkgfn + echo "pkgfn=$pkgfn" >> $GITHUB_OUTPUT + rm -rf gdext + + - name: Upload package + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.prep.outputs.pkgfn }} + path: | + . + !.git* diff --git a/Dear ImGui for Godot Demo.csproj b/Dear ImGui for Godot Demo.csproj index c199bbb..0599c45 100644 --- a/Dear ImGui for Godot Demo.csproj +++ b/Dear ImGui for Godot Demo.csproj @@ -1,4 +1,4 @@ - + net8.0 true diff --git a/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs b/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs index c737009..9e14866 100644 --- a/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs +++ b/addons/imgui-godot/ImGuiGodot/ImGuiGD.cs @@ -37,7 +37,11 @@ public static float Scale } } - public static bool Visible { get; set; } + public static bool Visible + { + get => _backend.Visible; + set => _backend.Visible = value; + } static ImGuiGD() { diff --git a/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs b/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs index 50f1710..5d92be7 100644 --- a/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs +++ b/addons/imgui-godot/ImGuiGodot/ImGuiLayer.cs @@ -1,6 +1,5 @@ using Godot; #if GODOT_PC -using ImGuiNET; namespace ImGuiGodot; @@ -11,44 +10,28 @@ public partial class ImGuiLayer : CanvasLayer private Window _window = null!; private Rid _subViewportRid; private Vector2I _subViewportSize = Vector2I.Zero; - private Rid _ci; + private Rid _canvasItem; private Transform2D _finalTransform = Transform2D.Identity; - private UpdateFirst _updateFirst = null!; + private ImGuiLayerHelper _helper = null!; + private bool _visible = true; public Node Signaler { get; private set; } = null!; - private sealed partial class UpdateFirst : Node + private sealed partial class ImGuiLayerHelper : Node { - private uint _counter = 0; - private ImGuiLayer _parent = null!; private Window _window = null!; public override void _Ready() { - _parent = (ImGuiLayer)GetParent(); + Name = "ImGuiLayerHelper"; + ProcessPriority = int.MinValue; + ProcessMode = ProcessModeEnum.Always; _window = GetWindow(); - _parent.VisibilityChanged += OnChangeVisibility; - OnChangeVisibility(); - } - - public override void _PhysicsProcess(double delta) - { - // call NewFrame occasionally if GUI isn't visible, to prevent leaks - if (unchecked(_counter++) % 60 == 0) - ImGui.NewFrame(); } public override void _Process(double delta) { Internal.State.Instance.Update(delta, _window.Size); } - - private void OnChangeVisibility() - { - _counter = 0; - bool vis = _parent.Visible; - SetProcess(vis); - SetPhysicsProcess(!vis); - } } public override void _EnterTree() @@ -58,12 +41,9 @@ public override void _EnterTree() CheckContentScale(); - ProcessPriority = int.MaxValue; - VisibilityChanged += OnChangeVisibility; - - _subViewportRid = Internal.Util.AddLayerSubViewport(this); - _ci = RenderingServer.CanvasItemCreate(); - RenderingServer.CanvasItemSetParent(_ci, GetCanvas()); + _subViewportRid = AddLayerSubViewport(this); + _canvasItem = RenderingServer.CanvasItemCreate(); + RenderingServer.CanvasItemSetParent(_canvasItem, GetCanvas()); Node cfgScene = ResourceLoader.Load("res://addons/imgui-godot/Config.tscn") .Instantiate(); @@ -75,74 +55,68 @@ public override void _EnterTree() Internal.State.Init(_window, _subViewportRid, cfg); - _updateFirst = new UpdateFirst - { - Name = "ImGuiLayer_UpdateFirst", - ProcessPriority = int.MinValue, - ProcessMode = ProcessModeEnum.Always, - }; - AddChild(_updateFirst); + _helper = new ImGuiLayerHelper(); + AddChild(_helper); Signaler = GetParent(); } public override void _Ready() { + ProcessPriority = int.MaxValue; + VisibilityChanged += OnChangeVisibility; OnChangeVisibility(); } public override void _ExitTree() { Internal.State.Instance.Dispose(); - RenderingServer.FreeRid(_ci); + RenderingServer.FreeRid(_canvasItem); RenderingServer.FreeRid(_subViewportRid); } private void OnChangeVisibility() { - if (Visible) + _visible = Visible; + if (_visible) { - ProcessMode = ProcessModeEnum.Always; + SetProcessInput(true); } else { - ProcessMode = ProcessModeEnum.Disabled; + SetProcessInput(false); Internal.State.Instance.Renderer.OnHide(); _subViewportSize = Vector2I.Zero; - RenderingServer.CanvasItemClear(_ci); - CallDeferred(nameof(FinishHide)); + RenderingServer.CanvasItemClear(_canvasItem); } } - private static void FinishHide() - { - ImGui.NewFrame(); - Internal.State.Instance.Render(); - } - public override void _Process(double delta) { - var winSize = _window.Size; - var ft = _window.GetFinalTransform(); - if (_subViewportSize != winSize || _finalTransform != ft) + if (_visible) { - // this is more or less how SubViewportContainer works - _subViewportSize = winSize; - _finalTransform = ft; - RenderingServer.ViewportSetSize( - _subViewportRid, - _subViewportSize.X, - _subViewportSize.Y); - Rid vptex = RenderingServer.ViewportGetTexture(_subViewportRid); - RenderingServer.CanvasItemClear(_ci); - RenderingServer.CanvasItemSetTransform(_ci, ft.AffineInverse()); - RenderingServer.CanvasItemAddTextureRect( - _ci, - new(0, 0, _subViewportSize.X, _subViewportSize.Y), - vptex); + var winSize = _window.Size; + var ft = _window.GetFinalTransform(); + if (_subViewportSize != winSize || _finalTransform != ft) + { + // this is more or less how SubViewportContainer works + _subViewportSize = winSize; + _finalTransform = ft; + RenderingServer.ViewportSetSize( + _subViewportRid, + _subViewportSize.X, + _subViewportSize.Y); + Rid vptex = RenderingServer.ViewportGetTexture(_subViewportRid); + RenderingServer.CanvasItemClear(_canvasItem); + RenderingServer.CanvasItemSetTransform(_canvasItem, ft.AffineInverse()); + RenderingServer.CanvasItemAddTextureRect( + _canvasItem, + new(0, 0, _subViewportSize.X, _subViewportSize.Y), + vptex); + } + + Signaler.EmitSignal("imgui_layout"); } - - Signaler.EmitSignal("imgui_layout"); Internal.State.Instance.Render(); } @@ -159,25 +133,23 @@ public override void _Input(InputEvent @event) } } - private void CheckContentScale() + private static Rid AddLayerSubViewport(Node parent) { - switch (_window.ContentScaleMode) - { - case Window.ContentScaleModeEnum.Disabled: - case Window.ContentScaleModeEnum.CanvasItems: - break; - case Window.ContentScaleModeEnum.Viewport: - PrintErrContentScale(); - break; - } + Rid svp = RenderingServer.ViewportCreate(); + RenderingServer.ViewportSetTransparentBackground(svp, true); + RenderingServer.ViewportSetUpdateMode(svp, RenderingServer.ViewportUpdateMode.Always); + RenderingServer.ViewportSetClearMode(svp, RenderingServer.ViewportClearMode.Always); + RenderingServer.ViewportSetActive(svp, true); + RenderingServer.ViewportSetParentViewport(svp, parent.GetWindow().GetViewportRid()); + return svp; } - private void PrintErrContentScale() + private void CheckContentScale() { - GD.PrintErr( - $"imgui-godot only supports content scale modes {Window.ContentScaleModeEnum.Disabled}" - + $" or {Window.ContentScaleModeEnum.CanvasItems}"); - GD.PrintErr($" current mode is {_window.ContentScaleMode}/{_window.ContentScaleAspect}"); + if (_window.ContentScaleMode == Window.ContentScaleModeEnum.Viewport) + { + GD.PrintErr("imgui-godot: scale mode `viewport` is unsupported"); + } } } #else diff --git a/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs b/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs index f35443d..994373d 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/BackendNative.cs @@ -27,14 +27,16 @@ private sealed class PropertyName public float JoyAxisDeadZone { - get => throw new System.NotImplementedException(); - set => throw new System.NotImplementedException(); + get => (float)_gd.Get(PropertyName.JoyAxisDeadZone); + set => _gd.Set(PropertyName.JoyAxisDeadZone, value); } + public float Scale { get; set; } = 1.0f; // TODO: make property + public bool Visible { - get => throw new System.NotImplementedException(); - set => throw new System.NotImplementedException(); + get => (bool)_gd.Get(PropertyName.Visible); + set => _gd.Set(PropertyName.Visible, value); } public void AddFont(FontFile fontData, int fontSize, bool merge) diff --git a/addons/imgui-godot/ImGuiGodot/Internal/BackendNet.cs b/addons/imgui-godot/ImGuiGodot/Internal/BackendNet.cs index 35ce28c..d1ebb4a 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/BackendNet.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/BackendNet.cs @@ -8,7 +8,11 @@ namespace ImGuiGodot.Internal; internal sealed class BackendNet : IBackend { - public float JoyAxisDeadZone { get; set; } = 0.15f; + public float JoyAxisDeadZone + { + get => State.Instance.Input.JoyAxisDeadZone; + set => State.Instance.Input.JoyAxisDeadZone = value; + } public float Scale { diff --git a/addons/imgui-godot/ImGuiGodot/Internal/CanvasRenderer.cs b/addons/imgui-godot/ImGuiGodot/Internal/CanvasRenderer.cs index 7cc02b6..0498c11 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/CanvasRenderer.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/CanvasRenderer.cs @@ -3,7 +3,6 @@ using ImGuiNET; using System; using System.Collections.Generic; -using System.Runtime.InteropServices; namespace ImGuiGodot.Internal; @@ -51,9 +50,6 @@ private void RenderOne(Rid vprid, ImDrawDataPtr drawData) ViewportData vd = _vpData[vprid]; Rid parent = vd.RootCanvasItem; - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(drawData.OwnerViewport.PlatformHandle) - .Target!; - if (!_canvasItemPools.ContainsKey(parent)) _canvasItemPools[parent] = []; @@ -129,10 +125,10 @@ private void RenderOne(Rid vprid, ImDrawDataPtr drawData) } var indices = new int[drawCmd.ElemCount]; - int idxOffset = (int)drawCmd.IdxOffset; - for (int i = idxOffset, j = 0; i < idxOffset + drawCmd.ElemCount; ++i, ++j) + uint idxOffset = drawCmd.IdxOffset; + for (uint i = idxOffset, j = 0; i < idxOffset + drawCmd.ElemCount; ++i, ++j) { - indices[j] = cmdList.IdxBuffer[i]; + indices[j] = cmdList.IdxBuffer[(int)i]; } Vector2[] cmdvertices = vertices; diff --git a/addons/imgui-godot/ImGuiGodot/Internal/Input.cs b/addons/imgui-godot/ImGuiGodot/Internal/Input.cs index cb8ae39..428c95b 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/Input.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/Input.cs @@ -8,6 +8,8 @@ namespace ImGuiGodot.Internal; internal sealed class Input(Window mainWindow) { + public float JoyAxisDeadZone { get; set; } = 0.15f; + internal SubViewport? PreviousSubViewport { get; set; } internal SubViewport? CurrentSubViewport { get; set; } internal System.Numerics.Vector2 CurrentSubViewportPos { get; set; } private Vector2 _mouseWheel = Vector2.Zero; @@ -80,6 +82,7 @@ public void Update(ImGuiIOPtr io) if (_hasMouse) UpdateMouse(io); + PreviousSubViewport = CurrentSubViewport; CurrentSubViewport = null; } @@ -94,6 +97,9 @@ public bool ProcessInput(InputEvent evt, Window window) if (CurrentSubViewport != null) { + if (CurrentSubViewport != PreviousSubViewport) + CurrentSubViewport.Notification((int)Node.NotificationVpMouseEnter); + var vpEvent = evt.Duplicate() as InputEvent; if (vpEvent is InputEventMouse mouseEvent) { @@ -104,6 +110,10 @@ public bool ProcessInput(InputEvent evt, Window window) } CurrentSubViewport.PushInput(vpEvent, true); } + else + { + PreviousSubViewport?.Notification((int)Node.NotificationVpMouseExit); + } bool consumed = false; @@ -185,7 +195,7 @@ public bool ProcessInput(InputEvent evt, Window window) { bool pressed = true; float v = jm.AxisValue; - if (Math.Abs(v) < ImGuiGD.JoyAxisDeadZone) + if (Math.Abs(v) < JoyAxisDeadZone) { v = 0f; pressed = false; diff --git a/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs b/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs index 53b82c8..2b3a52e 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/RdRenderer.cs @@ -155,7 +155,6 @@ public RdRenderer() public void InitViewport(Rid vprid) { - //RenderingServer.ViewportSetUpdateMode(vprid, RenderingServer.ViewportUpdateMode.Disabled); RenderingServer.ViewportSetClearMode(vprid, RenderingServer.ViewportClearMode.Never); } diff --git a/addons/imgui-godot/ImGuiGodot/Internal/State.cs b/addons/imgui-godot/ImGuiGodot/Internal/State.cs index ca021e6..fce7bc1 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/State.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/State.cs @@ -39,8 +39,8 @@ public State(Window mainWindow, Rid mainSubViewport, IRenderer renderer) var context = ImGui.CreateContext(); ImGui.SetCurrentContext(context); - var io = ImGui.GetIO(); + var io = ImGui.GetIO(); io.BackendFlags = ImGuiBackendFlags.HasGamepad | ImGuiBackendFlags.HasSetMousePos | @@ -72,31 +72,27 @@ public void Dispose() public static void Init(Window mainWindow, Rid mainSubViewport, Resource cfg) { if (IntPtr.Size != sizeof(ulong)) - { throw new PlatformNotSupportedException("imgui-godot requires 64-bit pointers"); - } - RendererType renderer = Enum.Parse((string)cfg.Get("Renderer")); + RendererType rendererType = Enum.Parse((string)cfg.Get("Renderer")); if (DisplayServer.GetName() == "headless") - { - renderer = RendererType.Dummy; - } + rendererType = RendererType.Dummy; // fall back to Canvas in OpenGL compatibility mode - if (renderer == RendererType.RenderingDevice + if (rendererType == RendererType.RenderingDevice && RenderingServer.GetRenderingDevice() == null) { - renderer = RendererType.Canvas; + rendererType = RendererType.Canvas; } // there's no way to get the actual current thread model, eg if --render-thread is used int threadModel = (int)ProjectSettings.GetSetting("rendering/driver/threads/thread_model"); - IRenderer internalRenderer; + IRenderer renderer; try { - internalRenderer = renderer switch + renderer = rendererType switch { RendererType.Dummy => new DummyRenderer(), RendererType.Canvas => new CanvasRenderer(), @@ -108,19 +104,19 @@ public static void Init(Window mainWindow, Rid mainSubViewport, Resource cfg) } catch (Exception e) { - if (renderer == RendererType.RenderingDevice) + if (rendererType == RendererType.RenderingDevice) { GD.PushWarning($"imgui-godot: falling back to Canvas renderer ({e.Message})"); - internalRenderer = new CanvasRenderer(); + renderer = new CanvasRenderer(); } else { GD.PushError("imgui-godot: failed to init renderer"); - internalRenderer = new DummyRenderer(); + renderer = new DummyRenderer(); } } - Instance = new(mainWindow, mainSubViewport, internalRenderer) + Instance = new(mainWindow, mainSubViewport, renderer) { Scale = (float)cfg.Get("Scale") }; @@ -133,18 +129,16 @@ public static void Init(Window mainWindow, Rid mainSubViewport, Resource cfg) for (int i = 0; i < fonts.Count; ++i) { var fontres = (Resource)fonts[i]; - var font = (FontFile)fontres.Get("FontData"); + var fontData = (FontFile)fontres.Get("FontData"); int fontSize = (int)fontres.Get("FontSize"); bool merge = (bool)fontres.Get("Merge"); if (i == 0) - ImGuiGD.AddFont(font, fontSize); + ImGuiGD.AddFont(fontData, fontSize); else - ImGuiGD.AddFont(font, fontSize, merge); + ImGuiGD.AddFont(fontData, fontSize, merge); } if ((bool)cfg.Get("AddDefaultFont")) - { ImGuiGD.AddFontDefault(); - } ImGuiGD.RebuildFontAtlas(); } @@ -181,7 +175,6 @@ public void Update(double delta, Vector2 displaySize) public void Render() { ImGui.Render(); - ImGui.UpdatePlatformWindows(); Renderer.Render(); //_inProcessFrame = false; diff --git a/addons/imgui-godot/ImGuiGodot/Internal/Util.cs b/addons/imgui-godot/ImGuiGodot/Internal/Util.cs index 4b65f45..690069e 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/Util.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/Util.cs @@ -23,16 +23,5 @@ static Util() il.Emit(OpCodes.Ret); ConstructRid = dm.CreateDelegate>(); } - - public static Rid AddLayerSubViewport(Node parent) - { - Rid svp = RenderingServer.ViewportCreate(); - RenderingServer.ViewportSetTransparentBackground(svp, true); - RenderingServer.ViewportSetUpdateMode(svp, RenderingServer.ViewportUpdateMode.Always); - RenderingServer.ViewportSetClearMode(svp, RenderingServer.ViewportClearMode.Always); - RenderingServer.ViewportSetActive(svp, true); - RenderingServer.ViewportSetParentViewport(svp, parent.GetWindow().GetViewportRid()); - return svp; - } } #endif diff --git a/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs b/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs index 7a5a342..dc4da98 100644 --- a/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs +++ b/addons/imgui-godot/ImGuiGodot/Internal/Viewports.cs @@ -21,7 +21,7 @@ public GodotImGuiWindow(ImGuiViewportPtr vp) { _gcHandle = GCHandle.Alloc(this); _vp = vp; - _vp.PlatformHandle = (IntPtr)_gcHandle; + _vp.PlatformUserData = (IntPtr)_gcHandle; Rect2I winRect = new(_vp.Pos.ToVector2I(), _vp.Size.ToVector2I()); @@ -66,7 +66,7 @@ public GodotImGuiWindow(ImGuiViewportPtr vp, Window gw, Rid mainSubViewport) { _gcHandle = GCHandle.Alloc(this); _vp = vp; - _vp.PlatformHandle = (IntPtr)_gcHandle; + _vp.PlatformUserData = (IntPtr)_gcHandle; GodotWindow = gw; _vp.RendererUserData = (IntPtr)mainSubViewport.Id; } @@ -275,65 +275,65 @@ private static void Godot_CreateWindow(ImGuiViewportPtr vp) private static void Godot_DestroyWindow(ImGuiViewportPtr vp) { - if (vp.PlatformHandle != IntPtr.Zero) + if (vp.PlatformUserData != IntPtr.Zero) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.Dispose(); - vp.PlatformHandle = IntPtr.Zero; + vp.PlatformUserData = IntPtr.Zero; } } private static void Godot_ShowWindow(ImGuiViewportPtr vp) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.ShowWindow(); } private static void Godot_SetWindowPos(ImGuiViewportPtr vp, Vector2 pos) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowPos(pos.ToVector2I()); } private static void Godot_GetWindowPos(ImGuiViewportPtr vp, out Vector2 pos) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; pos = window.GetWindowPos().ToImVec2(); } private static void Godot_SetWindowSize(ImGuiViewportPtr vp, Vector2 size) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowSize(size.ToVector2I()); } private static void Godot_GetWindowSize(ImGuiViewportPtr vp, out Vector2 size) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; size = window.GetWindowSize().ToImVec2(); } private static void Godot_SetWindowFocus(ImGuiViewportPtr vp) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowFocus(); } private static bool Godot_GetWindowFocus(ImGuiViewportPtr vp) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; return window.GetWindowFocus(); } private static bool Godot_GetWindowMinimized(ImGuiViewportPtr vp) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; return window.GetWindowMinimized(); } private static void Godot_SetWindowTitle(ImGuiViewportPtr vp, string title) { - var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformHandle).Target!; + var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowTitle(title); } } diff --git a/gdext/godot-cpp b/gdext/godot-cpp index 51c752c..98c143a 160000 --- a/gdext/godot-cpp +++ b/gdext/godot-cpp @@ -1 +1 @@ -Subproject commit 51c752c46b44769d3b6c661526c364a18ea64781 +Subproject commit 98c143a48365f3f3bf5f99d6289a2cb25e6472d1 diff --git a/gdext/include/imgui-godot.h b/gdext/include/imgui-godot.h index ec23fb6..3e34ec2 100644 --- a/gdext/include/imgui-godot.h +++ b/gdext/include/imgui-godot.h @@ -21,6 +21,7 @@ #endif #ifdef IGN_GDEXT +// GDExtension #pragma warning(push, 0) #include #include @@ -52,6 +53,7 @@ using godot::TypedArray; using godot::Vector2; using godot::Window; #else +// module #include "core/config/engine.h" #include "core/variant/callable.h" #include "scene/main/viewport.h" @@ -114,15 +116,15 @@ inline void ResetFonts() inline void SetJoyAxisDeadZone(float deadZone) { ERR_FAIL_COND(!detail::GET_IMGUIGD()); - static const StringName sn("SetJoyAxisDeadZone"); - detail::ImGuiGD->call(sn, deadZone); + static const StringName sn("JoyAxisDeadZone"); + detail::ImGuiGD->set(sn, deadZone); } inline void SetVisible(bool vis) { ERR_FAIL_COND(!detail::GET_IMGUIGD()); - static const StringName sn("SetVisible"); - detail::ImGuiGD->call(sn, vis); + static const StringName sn("Visible"); + detail::ImGuiGD->set(sn, vis); } inline void ToolInit() @@ -132,13 +134,6 @@ inline void ToolInit() detail::ImGuiGD->call(sn); } -inline bool SubViewport(SubViewport* svp) -{ - ERR_FAIL_COND_V(!detail::GET_IMGUIGD(), false); - static const StringName sn("SubViewport"); - return detail::ImGuiGD->call(sn, svp); -} - inline void SyncImGuiPtrs() { ERR_FAIL_COND(!detail::GET_IMGUIGD()); @@ -162,33 +157,6 @@ inline ImTextureID BindTexture(Texture2D* tex) { return reinterpret_cast(tex->get_rid().get_id()); } - -inline void Image(Texture2D* tex, const Vector2& size, const Vector2& uv0 = {0, 0}, const Vector2& uv1 = {1, 1}, - const Color& tint_col = {1, 1, 1, 1}, const Color& border_col = {0, 0, 0, 0}) -{ - ImGui::Image(BindTexture(tex), size, uv0, uv1, tint_col, border_col); -} - -inline void Image(const Ref& tex, const Vector2& size, const Vector2& uv0 = {0, 0}, - const Vector2& uv1 = {1, 1}, const Color& tint_col = {1, 1, 1, 1}, - const Color& border_col = {0, 0, 0, 0}) -{ - ImGui::Image(BindTexture(tex.ptr()), size, uv0, uv1, tint_col, border_col); -} - -inline bool ImageButton(const char* str_id, Texture2D* tex, const Vector2& size, const Vector2& uv0 = {0, 0}, - const Vector2& uv1 = {1, 1}, const Color& bg_col = {0, 0, 0, 0}, - const Color& tint_col = {1, 1, 1, 1}) -{ - return ImGui::ImageButton(str_id, BindTexture(tex), size, uv0, uv1, bg_col, tint_col); -} - -inline bool ImageButton(const char* str_id, const Ref& tex, const Vector2& size, const Vector2& uv0 = {0, 0}, - const Vector2& uv1 = {1, 1}, const Color& bg_col = {0, 0, 0, 0}, - const Color& tint_col = {1, 1, 1, 1}) -{ - return ImGui::ImageButton(str_id, BindTexture(tex.ptr()), size, uv0, uv1, bg_col, tint_col); -} #endif #ifdef IGN_GDEXT @@ -438,6 +406,46 @@ inline ImGuiKey ToImGuiKey(JoyButton btn) }; } #endif +} // namespace ImGui::Godot + +#ifndef IGN_EXPORT +// widgets +namespace ImGui { +inline bool SubViewport(SubViewport* svp) +{ + ERR_FAIL_COND_V(!Godot::detail::GET_IMGUIGD(), false); + static const StringName sn("SubViewport"); + return Godot::detail::ImGuiGD->call(sn, svp); +} + +inline void Image(Texture2D* tex, const Vector2& size, const Vector2& uv0 = {0, 0}, const Vector2& uv1 = {1, 1}, + const Color& tint_col = {1, 1, 1, 1}, const Color& border_col = {0, 0, 0, 0}) +{ + ImGui::Image(Godot::BindTexture(tex), size, uv0, uv1, tint_col, border_col); +} + +inline void Image(const Ref& tex, const Vector2& size, const Vector2& uv0 = {0, 0}, + const Vector2& uv1 = {1, 1}, const Color& tint_col = {1, 1, 1, 1}, + const Color& border_col = {0, 0, 0, 0}) +{ + ImGui::Image(Godot::BindTexture(tex.ptr()), size, uv0, uv1, tint_col, border_col); +} + +inline bool ImageButton(const char* str_id, Texture2D* tex, const Vector2& size, const Vector2& uv0 = {0, 0}, + const Vector2& uv1 = {1, 1}, const Color& bg_col = {0, 0, 0, 0}, + const Color& tint_col = {1, 1, 1, 1}) +{ + return ImGui::ImageButton(str_id, Godot::BindTexture(tex), size, uv0, uv1, bg_col, tint_col); +} + +inline bool ImageButton(const char* str_id, const Ref& tex, const Vector2& size, const Vector2& uv0 = {0, 0}, + const Vector2& uv1 = {1, 1}, const Color& bg_col = {0, 0, 0, 0}, + const Color& tint_col = {1, 1, 1, 1}) +{ + return ImGui::ImageButton(str_id, Godot::BindTexture(tex.ptr()), size, uv0, uv1, bg_col, tint_col); +} +} // namespace ImGui +#endif #define IMGUI_GODOT_MODULE_INIT() \ extern "C" { \ @@ -448,5 +456,3 @@ inline ImGuiKey ToImGuiKey(JoyButton btn) ImGui::SetAllocatorFunctions(afunc, ffunc, nullptr); \ } \ } - -} // namespace ImGui::Godot diff --git a/gdext/src/CanvasRenderer.cpp b/gdext/src/CanvasRenderer.cpp index 4163807..b312cef 100644 --- a/gdext/src/CanvasRenderer.cpp +++ b/gdext/src/CanvasRenderer.cpp @@ -18,14 +18,173 @@ struct CanvasRenderer::Impl std::unordered_map> canvasItemPools; std::unordered_map vpData; + + PackedVector2Array vertices; + PackedColorArray colors; + PackedVector2Array uvs; + PackedInt32Array indices; + + void RenderOne(RID vprid, ImDrawData* drawData); + void ClearCanvasItems(RID rootci); + void ClearCanvasItems(); }; -CanvasRenderer::CanvasRenderer() +void CanvasRenderer::Impl::RenderOne(RID vprid, ImDrawData* drawData) +{ + RenderingServer* RS = RenderingServer::get_singleton(); + ViewportData& vd = vpData.find(vprid)->second; + RID parent = vd.rootCanvasItem; + + if (!canvasItemPools.contains(parent)) + canvasItemPools[parent] = {}; + + std::vector& children = canvasItemPools[parent]; + + // allocate our CanvasItem pool as needed + int neededNodes = 0; + for (ImDrawList* drawList : drawData->CmdLists) + { + const auto& cmdBuf = drawList->CmdBuffer; + neededNodes += cmdBuf.size(); + for (const ImDrawCmd& cmd : cmdBuf) + { + if (cmd.ElemCount == 0) + --neededNodes; + } + } + + while (children.size() < neededNodes) + { + RID newChild = RS->canvas_item_create(); + RS->canvas_item_set_parent(newChild, parent); + RS->canvas_item_set_draw_index(newChild, children.size()); + children.push_back(newChild); + } + + // trim unused nodes + while (children.size() > neededNodes) + { + RS->free_rid(children.back()); + children.pop_back(); + } + + // render + drawData->ScaleClipRects(ImGui::GetIO().DisplayFramebufferScale); + int nodeN = 0; + + for (ImDrawList* cmdList : drawData->CmdLists) + { + int nVert = cmdList->VtxBuffer.size(); + + vertices.resize(nVert); + colors.resize(nVert); + uvs.resize(nVert); + + for (int i = 0; i < cmdList->VtxBuffer.size(); ++i) + { + const ImDrawVert& v = cmdList->VtxBuffer[i]; + vertices[i] = Vector2(v.pos.x, v.pos.y); + // need to reverse the color bytes + uint32_t rgba = v.col; + float r = (rgba & 0xFF) / 255.f; + rgba >>= 8; + float g = (rgba & 0xFF) / 255.f; + rgba >>= 8; + float b = (rgba & 0xFF) / 255.f; + rgba >>= 8; + float a = (rgba & 0xFF) / 255.f; + colors[i] = Color(r, g, b, a); + uvs[i] = Vector2(v.uv.x, v.uv.y); + } + + for (const ImDrawCmd& drawCmd : cmdList->CmdBuffer) + { + if (drawCmd.ElemCount == 0) + continue; + + indices.resize(drawCmd.ElemCount); + uint32_t idxOffset = drawCmd.IdxOffset; + for (uint32_t i = idxOffset, j = 0; i < idxOffset + drawCmd.ElemCount; ++i, ++j) + { + indices[j] = cmdList->IdxBuffer[i]; + } + + PackedVector2Array cmdvertices = vertices; + PackedColorArray cmdcolors = colors; + PackedVector2Array cmduvs = uvs; + if (drawCmd.VtxOffset > 0) + { + // slow implementation of RendererHasVtxOffset + int localSize = cmdList->VtxBuffer.size() - drawCmd.VtxOffset; + cmdvertices = vertices.slice(drawCmd.VtxOffset, localSize); + cmdcolors = colors.slice(drawCmd.VtxOffset, localSize); + cmduvs = uvs.slice(drawCmd.VtxOffset, localSize); + } + + RID child = children[nodeN++]; + + RID texrid = make_rid(drawCmd.GetTexID()); + RS->canvas_item_clear(child); + Transform2D xform(1.f, 0.f, 0.f, 1.f, 0.f, 0.f); // identity + if (drawData->DisplayPos.x != 0.f || drawData->DisplayPos.y != 0.f) + { + xform = xform.translated(drawData->DisplayPos).inverse(); + } + RS->canvas_item_set_transform(child, xform); + RS->canvas_item_set_clip(child, true); + RS->canvas_item_set_custom_rect(child, + true, + Rect2(drawCmd.ClipRect.x, + drawCmd.ClipRect.y, + drawCmd.ClipRect.z - drawCmd.ClipRect.x, + drawCmd.ClipRect.w - drawCmd.ClipRect.y)); + + RS->canvas_item_add_triangle_array(child, indices, cmdvertices, cmdcolors, cmduvs, {}, {}, texrid); + } + } +} + +void CanvasRenderer::Impl::ClearCanvasItems(RID rootci) +{ + auto it = canvasItemPools.find(rootci); + if (it == canvasItemPools.end()) + return; + + RenderingServer* RS = RenderingServer::get_singleton(); + for (RID ci : it->second) + { + RS->free_rid(ci); + } +} + +void CanvasRenderer::Impl::ClearCanvasItems() +{ + for (const auto& kv : canvasItemPools) + { + ClearCanvasItems(kv.first); + } + canvasItemPools.clear(); +} + +CanvasRenderer::CanvasRenderer() : impl(std::make_unique()) { } CanvasRenderer::~CanvasRenderer() { + RenderingServer* RS = RenderingServer::get_singleton(); + impl->ClearCanvasItems(); + for (const auto& kv : impl->vpData) + { + const auto& vd = kv.second; + RS->free_rid(vd.rootCanvasItem); + RS->free_rid(vd.canvas); + } +} + +bool CanvasRenderer::Init() +{ + return true; } void CanvasRenderer::InitViewport(RID vprid) @@ -41,14 +200,31 @@ void CanvasRenderer::InitViewport(RID vprid) void CanvasRenderer::CloseViewport(RID vprid) { + auto it = impl->vpData.find(vprid); + if (it == impl->vpData.end()) + return; + + RenderingServer* RS = RenderingServer::get_singleton(); + Impl::ViewportData& vd = it->second; + impl->ClearCanvasItems(vd.rootCanvasItem); + + RS->free_rid(vd.rootCanvasItem); + RS->free_rid(vd.canvas); } void CanvasRenderer::Render() { + auto& pio = ImGui::GetPlatformIO(); + for (ImGuiViewport* vp : pio.Viewports) + { + RID vprid = make_rid(vp->RendererUserData); + impl->RenderOne(vprid, vp->DrawData); + } } void CanvasRenderer::OnHide() { + impl->ClearCanvasItems(); } } // namespace ImGui::Godot diff --git a/gdext/src/CanvasRenderer.h b/gdext/src/CanvasRenderer.h index c3ed6de..3b35a7f 100644 --- a/gdext/src/CanvasRenderer.h +++ b/gdext/src/CanvasRenderer.h @@ -22,6 +22,7 @@ class CanvasRenderer : public Renderer return "godot4_canvas"; } + bool Init() override; void InitViewport(RID vprid) override; void CloseViewport(RID vprid) override; void Render() override; diff --git a/gdext/src/Context.cpp b/gdext/src/Context.cpp index 8963532..e141ac8 100644 --- a/gdext/src/Context.cpp +++ b/gdext/src/Context.cpp @@ -1,11 +1,8 @@ #include "Context.h" +#include "CanvasRenderer.h" #include "common.h" #include -#pragma warning(push, 0) - -#pragma warning(pop) - using namespace godot; namespace ImGui::Godot { @@ -21,104 +18,213 @@ Context* GetContext() return ctx.get(); } -void Init(godot::Window* mainWindow, RID canvasItem, const Ref& cfg) +Context::Context(Window* mainWindow, RID mainSubViewport, std::unique_ptr r) { - // re-init not allowed - if (ctx) - return; + renderer = std::move(r); + input = std::make_unique(mainWindow); + fonts = std::make_unique(); - ctx = std::make_unique(); - ctx->mainWindow = mainWindow; - ctx->ci = canvasItem; - ctx->input = std::make_unique(ctx->mainWindow); + if (ImGui::GetCurrentContext()) + ImGui::DestroyContext(); - int32_t screenDPI = DisplayServer::get_singleton()->screen_get_dpi(); - ctx->dpiFactor = std::max(1, screenDPI / 96); - ctx->scaleToDPI = ProjectSettings::get_singleton()->get_setting("display/window/dpi/allow_hidpi"); + ImGuiContext* context = ImGui::CreateContext(); + ImGui::SetCurrentContext(context); ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize = ctx->mainWindow->get_size(); + io.BackendFlags = ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_HasSetMousePos | + ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_RendererHasVtxOffset | + ImGuiBackendFlags_RendererHasViewports; io.BackendPlatformName = PlatformName; + io.BackendRendererName = renderer->Name(); - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; - io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; - io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; + viewports = std::make_unique(mainWindow, mainSubViewport); +} - Array fonts = cfg->get("Fonts"); - bool addDefaultFont = cfg->get("AddDefaultFont"); - float scale = cfg->get("Scale"); - String iniFilename = cfg->get("IniFilename"); - String rendererName = cfg->get("Renderer"); +Context::~Context() +{ + // RenderingServer::get_singleton()->free_rid(ci); + // RenderingServer::get_singleton()->free_rid(svp); + if (ImGui::GetCurrentContext()) + ImGui::DestroyContext(); +} - SetIniFilename(iniFilename); +void Init(godot::Window* mainWindow, RID mainSubViewport, const Ref& cfg) +{ + // re-init not allowed + // if (ctx) + // return; + DisplayServer* DS = DisplayServer::get_singleton(); RenderingServer* RS = RenderingServer::get_singleton(); + ProjectSettings* PS = ProjectSettings::get_singleton(); - ctx->headless = DisplayServer::get_singleton()->get_name() == "headless"; + RendererType rendererType; - if (!ctx->headless && !RS->get_rendering_device()) - { - ctx->headless = true; - UtilityFunctions::printerr("imgui-godot requires RenderingDevice"); - } + String rendererName = cfg->get("Renderer"); + if (rendererName == "Dummy") + rendererType = RendererType::Dummy; + else if (rendererName == "Canvas") + rendererType = RendererType::Canvas; + else + rendererType = RendererType::RenderingDevice; + + if (DS->get_name() == "headless") + rendererType = RendererType::Dummy; + + // fall back to Canvas in OpenGL compatibility mode + if (rendererType == RendererType::RenderingDevice && !RS->get_rendering_device()) + rendererType = RendererType::Canvas; + + // there's no way to get the actual current thread model, eg if --render-thread is used + int threadModel = PS->get_setting("rendering/driver/threads/thread_model"); - if (ctx->headless || rendererName == "Dummy") + std::unique_ptr renderer; + switch (rendererType) { - ctx->renderer = std::make_unique(); + case RendererType::Dummy: + renderer = std::make_unique(); + break; + case RendererType::Canvas: + renderer = std::make_unique(); + break; + case RendererType::RenderingDevice: + renderer = threadModel == 2 ? std::make_unique() : std::make_unique(); + break; } - else + + if (!renderer->Init()) { - int threadModel = ProjectSettings::get_singleton()->get_setting("rendering/driver/threads/thread_model"); -#ifdef DEBUG_ENABLED - if (Engine::get_singleton()->is_editor_hint()) - threadModel = 0; -#endif - if (threadModel == 2) - ctx->renderer = std::make_unique(); + if (rendererType == RendererType::RenderingDevice) + { + UtilityFunctions::push_warning("imgui-godot: falling back to Canvas renderer"); + renderer = std::make_unique(); + } else - ctx->renderer = std::make_unique(); + { + UtilityFunctions::push_error("imgui-godot: failed to init renderer"); + renderer = std::make_unique(); + } + renderer->Init(); } - io.BackendRendererName = ctx->renderer->Name(); - - Object* igl = Engine::get_singleton()->get_singleton("ImGuiLayer"); - RS->connect("frame_pre_draw", Callable(igl, "on_frame_pre_draw")); - ctx->svp = RS->viewport_create(); - RS->viewport_set_transparent_background(ctx->svp, true); - RS->viewport_set_update_mode(ctx->svp, RenderingServer::VIEWPORT_UPDATE_ALWAYS); - RS->viewport_set_clear_mode(ctx->svp, RenderingServer::VIEWPORT_CLEAR_NEVER); - RS->viewport_set_active(ctx->svp, true); - RS->viewport_set_parent_viewport(ctx->svp, ctx->mainWindow->get_viewport_rid()); + ctx = std::make_unique(mainWindow, mainSubViewport, std::move(renderer)); + ctx->scale = cfg->get("Scale"); + ctx->renderer->InitViewport(mainSubViewport); - ctx->fonts = std::make_unique(); + String iniFilename = cfg->get("IniFilename"); + if (iniFilename.length() > 0) + SetIniFilename(iniFilename); + Array fonts = cfg->get("Fonts"); for (int i = 0; i < fonts.size(); ++i) { Ref fontres = fonts[i]; - Ref font = fontres->get("FontData"); + Ref fontData = fontres->get("FontData"); int fontSize = fontres->get("FontSize"); bool merge = fontres->get("Merge"); - AddFont(font, fontSize, i > 0 && merge); + if (i == 0) + AddFont(fontData, fontSize); + else + AddFont(fontData, fontSize, merge); } - if (addDefaultFont) + if (cfg->get("AddDefaultFont")) AddFontDefault(); - RebuildFontAtlas(scale); - - ctx->viewports = std::make_unique(ctx->mainWindow, ctx->svp); + RebuildFontAtlas(); + + // ctx = std::make_unique(); + // ctx->mainWindow = mainWindow; + // ctx->ci = canvasItem; + // ctx->input = std::make_unique(ctx->mainWindow); + // + // int32_t screenDPI = DisplayServer::get_singleton()->screen_get_dpi(); + // ctx->dpiFactor = std::max(1, screenDPI / 96); + // ctx->scaleToDPI = ProjectSettings::get_singleton()->get_setting("display/window/dpi/allow_hidpi"); + // + // ImGuiIO& io = ImGui::GetIO(); + // io.DisplaySize = ctx->mainWindow->get_size(); + // + // io.BackendPlatformName = PlatformName; + // + // io.BackendFlags |= ImGuiBackendFlags_HasGamepad; + // io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; + // io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; + // io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; + // io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; + // io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; + // + // Array fonts = cfg->get("Fonts"); + // bool addDefaultFont = cfg->get("AddDefaultFont"); + // float scale = cfg->get("Scale"); + // String iniFilename = cfg->get("IniFilename"); + // String rendererName = cfg->get("Renderer"); + // + // SetIniFilename(iniFilename); + // + // RenderingServer* RS = RenderingServer::get_singleton(); + // + // ctx->headless = DisplayServer::get_singleton()->get_name() == "headless"; + // + // if (!ctx->headless && !RS->get_rendering_device()) + // { + // ctx->headless = true; + // UtilityFunctions::printerr("imgui-godot requires RenderingDevice"); + // } + // + // if (ctx->headless || rendererName == "Dummy") + // { + // ctx->renderer = std::make_unique(); + // } + // else + // { + // int threadModel = ProjectSettings::get_singleton()->get_setting("rendering/driver/threads/thread_model"); + // #ifdef DEBUG_ENABLED + // if (Engine::get_singleton()->is_editor_hint()) + // threadModel = 0; + // #endif + // if (threadModel == 2) + // ctx->renderer = std::make_unique(); + // else + // ctx->renderer = std::make_unique(); + // } + // ctx->renderer = std::make_unique(); + // io.BackendRendererName = ctx->renderer->Name(); + // + // Object* igl = Engine::get_singleton()->get_singleton("ImGuiLayer"); + // RS->connect("frame_pre_draw", Callable(igl, "on_frame_pre_draw")); + // + // ctx->svp = RS->viewport_create(); + // RS->viewport_set_transparent_background(ctx->svp, true); + // RS->viewport_set_update_mode(ctx->svp, RenderingServer::VIEWPORT_UPDATE_ALWAYS); + // RS->viewport_set_clear_mode(ctx->svp, RenderingServer::VIEWPORT_CLEAR_NEVER); + // RS->viewport_set_active(ctx->svp, true); + // RS->viewport_set_parent_viewport(ctx->svp, ctx->mainWindow->get_viewport_rid()); + // + // ctx->fonts = std::make_unique(); + // + // for (int i = 0; i < fonts.size(); ++i) + // { + // Ref fontres = fonts[i]; + // Ref font = fontres->get("FontData"); + // int fontSize = fontres->get("FontSize"); + // bool merge = fontres->get("Merge"); + // AddFont(font, fontSize, i > 0 && merge); + // } + // if (addDefaultFont) + // AddFontDefault(); + // RebuildFontAtlas(scale); + // + // ctx->viewports = std::make_unique(ctx->mainWindow, ctx->svp); } -void Update(double delta) +void Update(double delta, Vector2 displaySize) { ImGuiIO& io = ImGui::GetIO(); - io.DisplaySize = ctx->mainWindow->get_size(); + io.DisplaySize = displaySize; io.DeltaTime = static_cast(delta); - if (!ctx->headless) - ctx->input->Update(); + // if (!ctx->headless) + ctx->input->Update(); gdscache->OnNewFrame(); ImGui::NewFrame(); @@ -137,13 +243,13 @@ void ProcessNotification(int what) void Render() { - RenderingServer* RS = RenderingServer::get_singleton(); - godot::Vector2i winSize = ctx->mainWindow->get_size(); - RS->viewport_set_size(ctx->svp, winSize.x, winSize.y); - RID vptex = RS->viewport_get_texture(ctx->svp); - RS->canvas_item_clear(ctx->ci); - RS->canvas_item_set_transform(ctx->ci, ctx->mainWindow->get_final_transform().affine_inverse()); - RS->canvas_item_add_texture_rect(ctx->ci, godot::Rect2(0, 0, winSize.x, winSize.y), vptex); + // RenderingServer* RS = RenderingServer::get_singleton(); + // godot::Vector2i winSize = ctx->mainWindow->get_size(); + // RS->viewport_set_size(ctx->svp, winSize.x, winSize.y); + // RID vptex = RS->viewport_get_texture(ctx->svp); + // RS->canvas_item_clear(ctx->ci); + // RS->canvas_item_set_transform(ctx->ci, ctx->mainWindow->get_final_transform().affine_inverse()); + // RS->canvas_item_add_texture_rect(ctx->ci, godot::Rect2(0, 0, winSize.x, winSize.y), vptex); ImGui::Render(); ImGui::UpdatePlatformWindows(); @@ -179,33 +285,27 @@ void AddFontDefault() ctx->fonts->Add(nullptr, 13, false); } -void RebuildFontAtlas(float scale) +void RebuildFontAtlas() { - ctx->fonts->RebuildFontAtlas(ctx->scaleToDPI ? ctx->dpiFactor * scale : scale); + ctx->fonts->RebuildFontAtlas(ctx->scale); } void SetIniFilename(const String& fn) { ImGuiIO& io = ImGui::GetIO(); + static std::vector iniFilename; if (fn.length() > 0) { std::string globalfn = ProjectSettings::get_singleton()->globalize_path(fn).utf8().get_data(); - ctx->iniFilename.resize(globalfn.length() + 1); - std::copy(globalfn.begin(), globalfn.end(), ctx->iniFilename.begin()); - ctx->iniFilename.back() = '\0'; - io.IniFilename = ctx->iniFilename.data(); + iniFilename.resize(globalfn.length() + 1); + std::copy(globalfn.begin(), globalfn.end(), iniFilename.begin()); + iniFilename.back() = '\0'; + io.IniFilename = iniFilename.data(); } else io.IniFilename = nullptr; } -void SetVisible(bool visible) -{ - CanvasLayer* igl = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiLayer")); - ERR_FAIL_COND(!igl); - igl->set_visible(visible); -} - void OnFramePreDraw() { ctx->renderer->OnFramePreDraw(); diff --git a/gdext/src/Context.h b/gdext/src/Context.h index ed53a80..21e5c1f 100644 --- a/gdext/src/Context.h +++ b/gdext/src/Context.h @@ -34,32 +34,38 @@ using namespace godot; namespace ImGui::Godot { + +enum class RendererType +{ + Dummy, + Canvas, + RenderingDevice, +}; + struct Context { - Window* mainWindow = nullptr; - std::unique_ptr renderer; - std::unique_ptr input; - std::unique_ptr fonts; + // Window* mainWindow = nullptr; std::unique_ptr viewports; - RID svp; - RID ci; - Ref fontTexture; - bool headless = false; - int dpiFactor = 1; - bool scaleToDPI = false; - std::vector iniFilename; + std::unique_ptr fonts; + std::unique_ptr input; + std::unique_ptr renderer; + float scale = 1.0f; + + // RID svp; + // RID ci; + // Ref fontTexture; + // bool headless = false; + // int dpiFactor = 1; + // bool scaleToDPI = false; - ~Context() - { - RenderingServer::get_singleton()->free_rid(ci); - RenderingServer::get_singleton()->free_rid(svp); - } + Context(Window* mainWindow, RID mainSubViewport, std::unique_ptr r); + ~Context(); }; Context* GetContext(); void Init(Window* mainWindow, RID canvasItem, const Ref& config); -void Update(double delta); +void Update(double delta, Vector2 displaySize); bool ProcessInput(const Ref& evt, Window* window); void ProcessNotification(int what); void Render(); @@ -68,9 +74,10 @@ void Connect(const Callable& callable); void ResetFonts(); void AddFont(const Ref& fontFile, int fontSize, bool merge = false); void AddFontDefault(); -void RebuildFontAtlas(float scale); +void RebuildFontAtlas(); void SetIniFilename(const String& fn); void SetVisible(bool visible); +bool IsVisible(); bool SubViewportWidget(SubViewport* svp); diff --git a/gdext/src/DummyRenderer.h b/gdext/src/DummyRenderer.h index 98ff155..8b791d6 100644 --- a/gdext/src/DummyRenderer.h +++ b/gdext/src/DummyRenderer.h @@ -19,6 +19,11 @@ class DummyRenderer : public Renderer return "godot4_dummy"; } + bool Init() override + { + return true; + } + void InitViewport(RID vprid) override { } diff --git a/gdext/src/Fonts.h b/gdext/src/Fonts.h index 02d3a33..e412159 100644 --- a/gdext/src/Fonts.h +++ b/gdext/src/Fonts.h @@ -13,7 +13,7 @@ class Fonts ~Fonts(); void Reset(); - void Add(Ref fontData, int fontSize, bool merge); + void Add(Ref fontData, int fontSize, bool merge = false); void RebuildFontAtlas(float scale); private: diff --git a/gdext/src/ImGuiGD.cpp b/gdext/src/ImGuiGD.cpp index c63bba6..3653e6a 100644 --- a/gdext/src/ImGuiGD.cpp +++ b/gdext/src/ImGuiGD.cpp @@ -38,24 +38,24 @@ void ImGuiGD::_bind_methods() ClassDB::bind_method(D_METHOD("GetFontPtrs"), &ImGuiGD::GetFontPtrs); } -//void ImGuiGD::InitEditor(Node* parent) +// void ImGuiGD::InitEditor(Node* parent) //{ -//#ifdef DEBUG_ENABLED -// if (!Engine::get_singleton()->is_editor_hint()) -// return; +// #ifdef DEBUG_ENABLED +// if (!Engine::get_singleton()->is_editor_hint()) +// return; // -// if (!Engine::get_singleton()->has_singleton("ImGuiRoot")) -// { -// String resPath = "res://addons/imgui-godot-native/ImGuiGodot.tscn"; -// if (ResourceLoader::get_singleton()->exists(resPath)) -// { -// Ref scene = ResourceLoader::get_singleton()->load(resPath); -// if (scene.is_valid()) -// parent->add_child(scene->instantiate()); -// } -// } -//#endif -//} +// if (!Engine::get_singleton()->has_singleton("ImGuiRoot")) +// { +// String resPath = "res://addons/imgui-godot-native/ImGuiGodot.tscn"; +// if (ResourceLoader::get_singleton()->exists(resPath)) +// { +// Ref scene = ResourceLoader::get_singleton()->load(resPath); +// if (scene.is_valid()) +// parent->add_child(scene->instantiate()); +// } +// } +// #endif +// } void ImGuiGD::ToolInit() { @@ -95,31 +95,35 @@ void ImGuiGD::AddFontDefault() void ImGuiGD::RebuildFontAtlas(float scale) { - ImGui::Godot::RebuildFontAtlas(scale); + ImGui::Godot::RebuildFontAtlas(); } void ImGuiGD::_SetVisible(bool visible) { - ImGui::Godot::SetVisible(visible); + CanvasLayer* igl = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiLayer")); + ERR_FAIL_COND(!igl); + igl->set_visible(visible); } bool ImGuiGD::_GetVisible() { - return true; + CanvasLayer* igl = Object::cast_to(Engine::get_singleton()->get_singleton("ImGuiLayer")); + ERR_FAIL_COND_V(!igl, false); + return igl->is_visible(); } void ImGuiGD::_SetJoyAxisDeadZone(float zone) { + ImGui::Godot::GetContext()->input->SetJoyAxisDeadZone(zone); } float ImGuiGD::_GetJoyAxisDeadZone() { - return 0.15f; + return ImGui::Godot::GetContext()->input->GetJoyAxisDeadZone(); } void ImGuiGD::_SetScale(float scale) { - } float ImGuiGD::_GetScale() diff --git a/gdext/src/ImGuiLayer.cpp b/gdext/src/ImGuiLayer.cpp index 8c807d6..d6fe83a 100644 --- a/gdext/src/ImGuiLayer.cpp +++ b/gdext/src/ImGuiLayer.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -23,11 +24,37 @@ struct ImGuiLayer::Impl { ImGuiLayerHelper* helper = nullptr; Window* window = nullptr; + RID subViewportRid; + Vector2i subViewportSize{0, 0}; RID canvasItem; - bool wantHide = false; + Transform2D finalTransform{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}; // identity + bool visible = true; Ref cfg; + + static RID AddLayerSubViewport(Node* parent); + void CheckContentScale(); }; +RID ImGuiLayer::Impl::AddLayerSubViewport(Node* parent) +{ + RenderingServer* RS = RenderingServer::get_singleton(); + RID svp = RS->viewport_create(); + RS->viewport_set_transparent_background(svp, true); + RS->viewport_set_update_mode(svp, RenderingServer::VIEWPORT_UPDATE_ALWAYS); + RS->viewport_set_clear_mode(svp, RenderingServer::VIEWPORT_CLEAR_ALWAYS); + RS->viewport_set_active(svp, true); + RS->viewport_set_parent_viewport(svp, parent->get_window()->get_viewport_rid()); + return svp; +} + +void ImGuiLayer::Impl::CheckContentScale() +{ + if (window->get_content_scale_mode() == Window::CONTENT_SCALE_MODE_VIEWPORT) + { + UtilityFunctions::printerr("imgui-godot: scale mode `viewport` is unsupported"); + } +} + ImGuiLayer::ImGuiLayer() : impl(std::make_unique()) { } @@ -48,8 +75,8 @@ void ImGuiLayer::_enter_tree() Node* parent = get_parent(); if (!parent) return; - //if (parent->get_class() != "ImGuiRoot") - // return; + // if (parent->get_class() != "ImGuiRoot") + // return; if (Engine::get_singleton()->has_singleton("ImGuiLayer")) return; @@ -57,7 +84,19 @@ void ImGuiLayer::_enter_tree() Engine::get_singleton()->register_singleton("ImGuiLayer", this); impl->window = get_window(); - Ref cfg = parent->get("Config"); + impl->CheckContentScale(); + + RenderingServer* RS = RenderingServer::get_singleton(); + + impl->subViewportRid = Impl::AddLayerSubViewport(this); + impl->canvasItem = RS->canvas_item_create(); + RS->canvas_item_set_parent(impl->canvasItem, get_canvas()); + + Ref cfgPackedScene = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/Config.tscn"); + Node* cfgScene = cfgPackedScene->instantiate(); + Ref cfg = cfgScene->get("Config"); + memdelete(cfgScene); + if (cfg.is_null()) { Ref script = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/scripts/ImGuiConfig.gd"); @@ -67,10 +106,6 @@ void ImGuiLayer::_enter_tree() set_layer(cfg->get("Layer")); - RenderingServer* RS = RenderingServer::get_singleton(); - impl->canvasItem = RS->canvas_item_create(); - RS->canvas_item_set_parent(impl->canvasItem, get_canvas()); - impl->helper = memnew(ImGuiLayerHelper); add_child(impl->helper); @@ -78,7 +113,7 @@ void ImGuiLayer::_enter_tree() if (Engine::get_singleton()->is_editor_hint()) return; #endif - ImGui::Godot::Init(get_window(), impl->canvasItem, cfg); + ImGui::Godot::Init(get_window(), impl->subViewportRid, cfg); } void ImGuiLayer::_ready() @@ -104,6 +139,7 @@ void ImGuiLayer::_exit_tree() Engine::get_singleton()->unregister_singleton("ImGuiLayer"); ImGui::Godot::Shutdown(); RenderingServer::get_singleton()->free_rid(impl->canvasItem); + RenderingServer::get_singleton()->free_rid(impl->subViewportRid); } void ImGuiLayer::_process(double delta) @@ -122,39 +158,32 @@ void ImGuiLayer::_process(double delta) } } #endif - emit_signal("imgui_layout"); - ImGui::Godot::Render(); - if (impl->wantHide) + if (impl->visible) { - impl->wantHide = false; - set_process(false); - set_physics_process(true); - impl->helper->set_process(false); - set_process_input(false); - RenderingServer::get_singleton()->canvas_item_clear(impl->canvasItem); + Vector2i winSize = impl->window->get_size(); + Transform2D ft = impl->window->get_final_transform(); - // hide viewport windows immediately - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + if (impl->subViewportSize != winSize || impl->finalTransform != ft) { - ImGui::NewFrame(); - ImGui::EndFrame(); - ImGui::UpdatePlatformWindows(); + // this is more or less how SubViewportContainer works + impl->subViewportSize = winSize; + impl->finalTransform = ft; + + RenderingServer* RS = RenderingServer::get_singleton(); + RS->viewport_set_size(impl->subViewportRid, impl->subViewportSize.x, impl->subViewportSize.y); + RID vptex = RS->viewport_get_texture(impl->subViewportRid); + RS->canvas_item_clear(impl->canvasItem); + RS->canvas_item_set_transform(impl->canvasItem, ft.affine_inverse()); + RS->canvas_item_add_texture_rect(impl->canvasItem, + Rect2(0, 0, impl->subViewportSize.x, impl->subViewportSize.y), + vptex); } - ImGui::NewFrame(); - } -} -void ImGuiLayer::_physics_process(double delta) -{ - static int count = 0; - if (++count > 60) - { - count = 0; - ImGui::EndFrame(); - ImGui::UpdatePlatformWindows(); - ImGui::NewFrame(); + emit_signal("imgui_layout"); } + + ImGui::Godot::Render(); } void ImGuiLayer::_input(const Ref& event) @@ -176,16 +205,17 @@ void ImGuiLayer::_notification(int p_what) void ImGuiLayer::on_visibility_changed() { - if (is_visible()) + impl->visible = is_visible(); + if (impl->visible) { - set_process(true); - set_physics_process(false); - impl->helper->set_process(true); set_process_input(true); } else { - impl->wantHide = true; + set_process_input(false); + ImGui::Godot::GetContext()->renderer->OnHide(); + impl->subViewportSize = {0, 0}; + RenderingServer::get_singleton()->canvas_item_clear(impl->canvasItem); } } @@ -194,16 +224,6 @@ void ImGuiLayer::on_frame_pre_draw() ImGui::Godot::OnFramePreDraw(); } -void ImGuiLayer::NewFrame(double delta) -{ - if (ImGui::GetCurrentContext()->WithinFrameScope) - { - ImGui::EndFrame(); - ImGui::UpdatePlatformWindows(); - } - ImGui::Godot::Update(delta); -} - void ImGuiLayer::ToolInit() { if (!is_visible()) diff --git a/gdext/src/ImGuiLayer.h b/gdext/src/ImGuiLayer.h index 19dffad..d5577cd 100644 --- a/gdext/src/ImGuiLayer.h +++ b/gdext/src/ImGuiLayer.h @@ -26,13 +26,11 @@ class ImGuiLayer : public CanvasLayer void _enter_tree() override; void _exit_tree() override; void _process(double delta) override; - void _physics_process(double delta) override; void _input(const Ref& event) override; void _notification(int p_what); void on_visibility_changed(); void on_frame_pre_draw(); - void NewFrame(double delta); void ToolInit(); private: diff --git a/gdext/src/ImGuiLayerHelper.cpp b/gdext/src/ImGuiLayerHelper.cpp index ff4c816..34d4437 100644 --- a/gdext/src/ImGuiLayerHelper.cpp +++ b/gdext/src/ImGuiLayerHelper.cpp @@ -1,5 +1,5 @@ #include "ImGuiLayerHelper.h" -#include "ImGuiLayer.h" +#include "Context.h" #include "imgui-godot.h" #pragma warning(push, 0) @@ -15,7 +15,7 @@ namespace ImGui::Godot { struct ImGuiLayerHelper::Impl { - ImGuiLayer* igl = nullptr; + Window* window; }; ImGuiLayerHelper::ImGuiLayerHelper() : impl(std::make_unique()) @@ -32,12 +32,14 @@ void ImGuiLayerHelper::_bind_methods() void ImGuiLayerHelper::_enter_tree() { - impl->igl = Object::cast_to(get_parent()); } void ImGuiLayerHelper::_ready() { + set_name("ImGuiLayerHelper"); set_process_priority(std::numeric_limits::min()); + set_process_mode(Node::PROCESS_MODE_ALWAYS); + impl->window = get_window(); } void ImGuiLayerHelper::_exit_tree() @@ -46,7 +48,7 @@ void ImGuiLayerHelper::_exit_tree() void ImGuiLayerHelper::_process(double delta) { - impl->igl->NewFrame(delta); + ImGui::Godot::Update(delta, impl->window->get_size()); } } // namespace ImGui::Godot diff --git a/gdext/src/Input.cpp b/gdext/src/Input.cpp index 84e79b7..1639117 100644 --- a/gdext/src/Input.cpp +++ b/gdext/src/Input.cpp @@ -27,11 +27,13 @@ namespace ImGui::Godot { struct Input::Impl { Window* mainWindow = nullptr; + godot::SubViewport* previousSubViewport = nullptr; godot::SubViewport* currentSubViewport = nullptr; Vector2 currentSubViewportPos; Vector2 mouseWheel; ImGuiMouseCursor currentCursor = ImGuiMouseCursor_None; bool hasMouse; + float deadZone = 0.15f; void UpdateMouse(); }; @@ -152,6 +154,7 @@ void Input::Update() if (impl->hasMouse) impl->UpdateMouse(); + impl->previousSubViewport = impl->currentSubViewport; impl->currentSubViewport = nullptr; } @@ -163,10 +166,12 @@ bool Input::ProcessInput(const Ref& evt, Window* window) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) windowPos = window->get_position(); - if (impl->currentSubViewport != nullptr) + if (impl->currentSubViewport) { - Ref vpevt = evt->duplicate(); + if (impl->currentSubViewport != impl->previousSubViewport) + impl->currentSubViewport->notification(Node::NOTIFICATION_VP_MOUSE_ENTER); + Ref vpevt = evt->duplicate(); if (Ref me = vpevt; me.is_valid()) { Vector2 gpos = me->get_global_position(); @@ -176,6 +181,10 @@ bool Input::ProcessInput(const Ref& evt, Window* window) } impl->currentSubViewport->push_input(vpevt, true); } + else if (impl->previousSubViewport) + { + impl->previousSubViewport->notification(Node::NOTIFICATION_VP_MOUSE_EXIT); + } bool consumed = false; @@ -202,6 +211,7 @@ bool Input::ProcessInput(const Ref& evt, Window* window) break; case MOUSE_BUTTON_XBUTTON2: io.AddMouseButtonEvent(ImGuiMouseButton_Middle + 2, pressed); + break; case MOUSE_BUTTON_WHEEL_UP: impl->mouseWheel.y = mb->get_factor(); break; @@ -252,7 +262,7 @@ bool Input::ProcessInput(const Ref& evt, Window* window) { bool pressed = true; float v = jm->get_axis_value(); - if (std::abs(v) < 0.15f) // TODO: set dead zone + if (std::abs(v) < impl->deadZone) { v = 0; pressed = false; @@ -306,4 +316,14 @@ void Input::SetActiveSubViewport(godot::SubViewport* svp, Vector2 pos) impl->currentSubViewportPos = pos; } +void Input::SetJoyAxisDeadZone(float val) +{ + impl->deadZone = val; +} + +float Input::GetJoyAxisDeadZone() +{ + return impl->deadZone; +} + } // namespace ImGui::Godot diff --git a/gdext/src/Input.h b/gdext/src/Input.h index 880b113..b513f31 100644 --- a/gdext/src/Input.h +++ b/gdext/src/Input.h @@ -20,6 +20,9 @@ class Input void ProcessNotification(int what); void SetActiveSubViewport(godot::SubViewport* svp, Vector2 pos); + void SetJoyAxisDeadZone(float val); + float GetJoyAxisDeadZone(); + private: struct Impl; std::unique_ptr impl; diff --git a/gdext/src/RdRenderer.cpp b/gdext/src/RdRenderer.cpp index d43d1ea..7d24d04 100644 --- a/gdext/src/RdRenderer.cpp +++ b/gdext/src/RdRenderer.cpp @@ -172,13 +172,25 @@ void RdRenderer::Impl::SetupBuffers(ImDrawData* drawData) } RdRenderer::RdRenderer() : impl(std::make_unique()) +{ +} + +bool RdRenderer::Init() { RenderingDevice* RD = RenderingServer::get_singleton()->get_rendering_device(); + if (!RD) + return false; + + // set up everything to match the official Vulkan backend as closely as possible + Ref shaderFile = ResourceLoader::get_singleton()->load("res://addons/imgui-godot/data/ImGuiShader.glsl"); impl->shader = RD->shader_create_from_spirv(shaderFile->get_spirv()); + if (!impl->shader.is_valid()) + return false; + TypedArray vattrs; RDVertexAttribute attr_points; @@ -244,6 +256,7 @@ RdRenderer::RdRenderer() : impl(std::make_unique()) impl->srcBuffers.resize(3); impl->uniforms.resize(1); + return true; } void RdRenderer::Render(RID fb, ImDrawData* drawData) @@ -326,6 +339,11 @@ RdRenderer::~RdRenderer() RD->free_rid(impl->vtxBuffer); } +void RdRenderer::InitViewport(RID vprid) +{ + RenderingServer::get_singleton()->viewport_set_clear_mode(vprid, RenderingServer::VIEWPORT_CLEAR_NEVER); +} + void RdRenderer::Render() { auto& pio = ImGui::GetPlatformIO(); diff --git a/gdext/src/RdRenderer.h b/gdext/src/RdRenderer.h index d03e2a9..592c24a 100644 --- a/gdext/src/RdRenderer.h +++ b/gdext/src/RdRenderer.h @@ -22,9 +22,8 @@ class RdRenderer : public Renderer return "godot4_rd"; } - void InitViewport(RID vprid) override - { - } + bool Init() override; + void InitViewport(RID vprid) override; void CloseViewport(RID vprid) override { } diff --git a/gdext/src/Renderer.h b/gdext/src/Renderer.h index 17cdccb..11cc5c8 100644 --- a/gdext/src/Renderer.h +++ b/gdext/src/Renderer.h @@ -23,6 +23,7 @@ class Renderer virtual const char* Name() = 0; + virtual bool Init() = 0; virtual void InitViewport(RID vprid) = 0; virtual void CloseViewport(RID vprid) = 0; virtual void Render() = 0; diff --git a/gdext/src/Viewports.cpp b/gdext/src/Viewports.cpp index 2411af7..ebb622d 100644 --- a/gdext/src/Viewports.cpp +++ b/gdext/src/Viewports.cpp @@ -63,7 +63,7 @@ static void Godot_CreateWindow(ImGuiViewport* vp) vp->RendererUserData = (void*)vprid.get_id(); RenderingServer* RS = RenderingServer::get_singleton(); - RS->viewport_set_clear_mode(vprid, RenderingServer::VIEWPORT_CLEAR_NEVER); + ImGui::Godot::GetContext()->renderer->InitViewport(vprid); RS->viewport_set_transparent_background(vprid, true); } @@ -192,7 +192,7 @@ Viewports::~Viewports() { } - void ImGuiWindow::_input(const Ref& evt) +void ImGuiWindow::_input(const Ref& evt) { ImGui::Godot::ProcessInput(evt, this); } diff --git a/gdext/src/main.cpp b/gdext/src/main.cpp index 9603b7b..0c4530c 100644 --- a/gdext/src/main.cpp +++ b/gdext/src/main.cpp @@ -63,7 +63,6 @@ void initialize_ign_module(ModuleInitializationLevel p_level) ImGui::CreateContext(); - // ClassDB::register_internal_class(); ClassDB::register_internal_class(); ClassDB::register_internal_class(); ClassDB::register_class();