diff --git a/project_name/ImGui/DrawVertDeclaration.cs b/project_name/ImGui/DrawVertDeclaration.cs
deleted file mode 100644
index 276e5df..0000000
--- a/project_name/ImGui/DrawVertDeclaration.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Microsoft.Xna.Framework.Graphics;
-
-namespace ImGuiNET.SampleProgram.XNA
-{
- public static class DrawVertDeclaration
- {
- public static readonly VertexDeclaration Declaration;
- public static readonly int Size;
-
- static DrawVertDeclaration()
- {
- unsafe { Size = sizeof(ImDrawVert); }
-
- Declaration = new VertexDeclaration(
- Size,
-
- // Position
- new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0),
-
- // UV
- new VertexElement(8, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
-
- // Color
- new VertexElement(16, VertexElementFormat.Color, VertexElementUsage.Color, 0)
- );
- }
- }
-}
\ No newline at end of file
diff --git a/project_name/ImGui/ImGuiFinalRenderDelegate.cs b/project_name/ImGui/ImGuiFinalRenderDelegate.cs
index 349dbf1..4d70719 100644
--- a/project_name/ImGui/ImGuiFinalRenderDelegate.cs
+++ b/project_name/ImGui/ImGuiFinalRenderDelegate.cs
@@ -1,7 +1,6 @@
using System;
using System.Reflection;
using ImGuiNET;
-using ImGuiNET.SampleProgram.XNA;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
@@ -11,68 +10,99 @@ namespace Nez
{
public Scene scene { get; set; }
- ImGuiRenderer _imGuiRenderer;
- RenderTarget2D _lastRenderTarget;
- IntPtr _renderTargetId;
+ ImGuiRenderer _imGuiRenderer;
+ RenderTarget2D _lastRenderTarget;
+ IntPtr _renderTargetId;
- public ImGuiFinalRenderDelegate()
- {
- _imGuiRenderer = new ImGuiRenderer(Core.instance);
- _imGuiRenderer.RebuildFontAtlas();
- ImGui.GetIO().ConfigWindowsMoveFromTitleBarOnly = true;
- }
-
- public void handleFinalRender( Color letterboxColor, RenderTarget2D source, Rectangle finalRenderDestinationRect, SamplerState samplerState )
+ public ImGuiFinalRenderDelegate()
{
- if(_lastRenderTarget != source)
- {
- // unbind the old texture if we had one
- if(_lastRenderTarget != null)
- _imGuiRenderer.UnbindTexture(_renderTargetId);
-
- // bind the new texture
- _lastRenderTarget = source;
- _renderTargetId = _imGuiRenderer.BindTexture(source);
- }
-
- Core.graphicsDevice.setRenderTarget( null );
- Core.graphicsDevice.Clear( letterboxColor );
-
-
- _imGuiRenderer.BeforeLayout(Time.time);
- layoutGui();
- _imGuiRenderer.AfterLayout();
+ _imGuiRenderer = new ImGuiRenderer( Core.instance );
+ _imGuiRenderer.rebuildFontAtlas();
}
- void layoutGui()
- {
- ImGui.ShowDemoWindow();
+ [Console.Command( "toggle-imgui", "Toggles the Dear ImGui renderer" )]
+ static void toggleImGui()
+ {
+ if( Core.scene.finalRenderDelegate == null )
+ Core.scene.finalRenderDelegate = new ImGuiFinalRenderDelegate();
+ else
+ Core.scene.finalRenderDelegate = null;
+ }
- var maxSize = new System.Numerics.Vector2(_lastRenderTarget.Width, _lastRenderTarget.Height);
- var minSize = maxSize / 4;
- unsafe
- {
- ImGui.SetNextWindowSizeConstraints(minSize, maxSize, data =>
- {
- var size = (*data).CurrentSize;
- var ratio = size.X / _lastRenderTarget.Width;
- (*data).DesiredSize.Y = ratio * _lastRenderTarget.Height;
- });
- }
+ void layoutGui()
+ {
+ ImGui.ShowDemoWindow();
- ImGui.SetNextWindowPos(new System.Numerics.Vector2(0, 0), ImGuiCond.FirstUseEver);
- ImGui.Begin("Game Window");
- ImGui.Image(_renderTargetId, ImGui.GetContentRegionAvail());
- ImGui.End();
- }
+ var maxSize = new System.Numerics.Vector2( _lastRenderTarget.Width, _lastRenderTarget.Height );
+ var minSize = maxSize / 4;
+ maxSize *= 4;
+ unsafe
+ {
+ ImGui.SetNextWindowSizeConstraints( minSize, maxSize, data =>
+ {
+ var size = ( *data ).CurrentSize;
+ var ratio = size.X / _lastRenderTarget.Width;
+ ( *data ).DesiredSize.Y = ratio * _lastRenderTarget.Height;
+ } );
+ }
+
+ ImGui.SetNextWindowPos( new System.Numerics.Vector2( 0, 0 ), ImGuiCond.FirstUseEver );
+ ImGui.PushStyleVar( ImGuiStyleVar.WindowPadding, new System.Numerics.Vector2( 0, 0 ) );
+ ImGui.Begin( "Game Window" );
+
+ Nugget.InputDisplay.cursorScreenPos = new Vector2( ImGui.GetCursorScreenPos().X, ImGui.GetCursorScreenPos().Y );
+ Nugget.InputDisplay.scaleX = ImGui.GetContentRegionAvail().X / _lastRenderTarget.Width;
+ Nugget.InputDisplay.scaleY = ImGui.GetContentRegionAvail().Y / _lastRenderTarget.Height;
+
+ //Debug.log( $"window pos: {ImGui.GetWindowPos()}" );
+ //Debug.log( $"avail size: {ImGui.GetContentRegionAvail()}" );
+ //Debug.log( $"rt {_lastRenderTarget.Width} x {_lastRenderTarget.Height}" );
+ //Debug.log( $"scaleX: {ImGui.GetContentRegionAvail().X / _lastRenderTarget.Width}" );
+ //Debug.log( $"scaleY: {ImGui.GetContentRegionAvail().Y / _lastRenderTarget.Height}" );
+ //Debug.log( ImGui.GetWindowSize() - ImGui.GetContentRegionAvail() );
+ //Debug.log( $"titleHeight: {titleHeight}" );
+ //Debug.log( $"screenPos: {ImGui.GetCursorScreenPos()}" );
+
+
+ ImGui.Image( _renderTargetId, ImGui.GetContentRegionAvail() );
+ ImGui.End();
+
+ ImGui.PopStyleVar();
+ }
+
+ #region IFinalRenderDelegate
+
+ public void handleFinalRender( RenderTarget2D finalRenderTarget, Color letterboxColor, RenderTarget2D source, Rectangle finalRenderDestinationRect, SamplerState samplerState )
+ {
+ if( _lastRenderTarget != source )
+ {
+ // unbind the old texture if we had one
+ if( _lastRenderTarget != null )
+ _imGuiRenderer.unbindTexture( _renderTargetId );
+
+ // bind the new texture
+ _lastRenderTarget = source;
+ _renderTargetId = _imGuiRenderer.bindTexture( source );
+ }
+
+ Core.graphicsDevice.setRenderTarget( finalRenderTarget );
+ Core.graphicsDevice.Clear( letterboxColor );
+
+
+ _imGuiRenderer.beforeLayout( Time.deltaTime );
+ layoutGui();
+ _imGuiRenderer.afterLayout();
+ }
public void onAddedToScene()
- {}
+ { }
public void onSceneBackBufferSizeChanged( int newWidth, int newHeight )
- {}
+ { }
public void unload()
- {}
+ { }
+
+ #endregion
}
}
diff --git a/project_name/ImGui/ImGuiRenderer.cs b/project_name/ImGui/ImGuiRenderer.cs
index 0c6886a..41c7523 100644
--- a/project_name/ImGui/ImGuiRenderer.cs
+++ b/project_name/ImGui/ImGuiRenderer.cs
@@ -1,387 +1,389 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
+using Nez;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
-namespace ImGuiNET.SampleProgram.XNA
+namespace ImGuiNET
{
- ///
- /// ImGui renderer for use with XNA-likes (FNA & MonoGame)
- ///
- public class ImGuiRenderer
- {
- private Game _game;
-
- // Graphics
- private GraphicsDevice _graphicsDevice;
-
- private BasicEffect _effect;
- private RasterizerState _rasterizerState;
-
- private byte[] _vertexData;
- private VertexBuffer _vertexBuffer;
- private int _vertexBufferSize;
-
- private byte[] _indexData;
- private IndexBuffer _indexBuffer;
- private int _indexBufferSize;
-
- // Textures
- private Dictionary _loadedTextures;
-
- private int _textureId;
- private IntPtr? _fontTextureId;
-
- // Input
- private int _scrollWheelValue;
-
- private List _keys = new List();
-
- public ImGuiRenderer(Game game)
- {
- var context = ImGui.CreateContext();
- ImGui.SetCurrentContext(context);
-
- _game = game ?? throw new ArgumentNullException(nameof(game));
- _graphicsDevice = game.GraphicsDevice;
-
- _loadedTextures = new Dictionary();
-
- _rasterizerState = new RasterizerState()
- {
- CullMode = CullMode.None,
- DepthBias = 0,
- FillMode = FillMode.Solid,
- MultiSampleAntiAlias = false,
- ScissorTestEnable = true,
- SlopeScaleDepthBias = 0
- };
-
- SetupInput();
- }
-
- #region ImGuiRenderer
-
- ///
- /// Creates a texture and loads the font data from ImGui. Should be called when the is initialized but before any rendering is done
- ///
- public virtual unsafe void RebuildFontAtlas()
- {
- // Get font texture from ImGui
- var io = ImGui.GetIO();
- io.Fonts.GetTexDataAsRGBA32(out byte* pixelData, out int width, out int height, out int bytesPerPixel);
-
- // Copy the data to a managed array
- var pixels = new byte[width * height * bytesPerPixel];
- unsafe { Marshal.Copy(new IntPtr(pixelData), pixels, 0, pixels.Length); }
-
- // Create and register the texture as an XNA texture
- var tex2d = new Texture2D(_graphicsDevice, width, height, false, SurfaceFormat.Color);
- tex2d.SetData(pixels);
-
- // Should a texture already have been build previously, unbind it first so it can be deallocated
- if (_fontTextureId.HasValue) UnbindTexture(_fontTextureId.Value);
-
- // Bind the new texture to an ImGui-friendly id
- _fontTextureId = BindTexture(tex2d);
-
- // Let ImGui know where to find the texture
- io.Fonts.SetTexID(_fontTextureId.Value);
- io.Fonts.ClearTexData(); // Clears CPU side texture data
- }
-
- ///
- /// Creates a pointer to a texture, which can be passed through ImGui calls such as . That pointer is then used by ImGui to let us know what texture to draw
- ///
- public virtual IntPtr BindTexture(Texture2D texture)
- {
- var id = new IntPtr(_textureId++);
-
- _loadedTextures.Add(id, texture);
-
- return id;
- }
-
- ///
- /// Removes a previously created texture pointer, releasing its reference and allowing it to be deallocated
- ///
- public virtual void UnbindTexture(IntPtr textureId)
- {
- _loadedTextures.Remove(textureId);
- }
-
- ///
- /// Sets up ImGui for a new frame, should be called at frame start
- ///
- public virtual void BeforeLayout(float deltaTime)
- {
- ImGui.GetIO().DeltaTime = deltaTime;
- UpdateInput();
- ImGui.NewFrame();
- }
-
- ///
- /// Asks ImGui for the generated geometry data and sends it to the graphics pipeline, should be called after the UI is drawn using ImGui.** calls
- ///
- public virtual void AfterLayout()
- {
- ImGui.Render();
-
- unsafe { RenderDrawData(ImGui.GetDrawData()); }
- }
-
- #endregion ImGuiRenderer
-
- #region Setup & Update
-
- ///
- /// Maps ImGui keys to XNA keys. We use this later on to tell ImGui what keys were pressed
- ///
- protected virtual void SetupInput()
- {
- var io = ImGui.GetIO();
-
- _keys.Add(io.KeyMap[(int)ImGuiKey.Tab] = (int)Keys.Tab);
- _keys.Add(io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Keys.Left);
- _keys.Add(io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Keys.Right);
- _keys.Add(io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Keys.Up);
- _keys.Add(io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Keys.Down);
- _keys.Add(io.KeyMap[(int)ImGuiKey.PageUp] = (int)Keys.PageUp);
- _keys.Add(io.KeyMap[(int)ImGuiKey.PageDown] = (int)Keys.PageDown);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Home] = (int)Keys.Home);
- _keys.Add(io.KeyMap[(int)ImGuiKey.End] = (int)Keys.End);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Delete] = (int)Keys.Delete);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Backspace] = (int)Keys.Back);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Enter] = (int)Keys.Enter);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Escape] = (int)Keys.Escape);
- _keys.Add(io.KeyMap[(int)ImGuiKey.A] = (int)Keys.A);
- _keys.Add(io.KeyMap[(int)ImGuiKey.C] = (int)Keys.C);
- _keys.Add(io.KeyMap[(int)ImGuiKey.V] = (int)Keys.V);
- _keys.Add(io.KeyMap[(int)ImGuiKey.X] = (int)Keys.X);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Y] = (int)Keys.Y);
- _keys.Add(io.KeyMap[(int)ImGuiKey.Z] = (int)Keys.Z);
-
-
- // MonoGame-specific //////////////////////
- // _game.Window.TextInput += (s, a) =>
- // {
- // if (a.Character == '\t') return;
-
- // io.AddInputCharacter(a.Character);
- // };
- ///////////////////////////////////////////
-
- // FNA-specific ///////////////////////////
- TextInputEXT.TextInput += c =>
- {
- if (c == '\t') return;
-
- ImGui.GetIO().AddInputCharacter(c);
- };
- ///////////////////////////////////////////
-
- ImGui.GetIO().Fonts.AddFontDefault();
- }
-
- ///
- /// Updates the to the current matrices and texture
- ///
- protected virtual Effect UpdateEffect(Texture2D texture)
- {
- _effect = _effect ?? new BasicEffect(_graphicsDevice);
-
- var io = ImGui.GetIO();
-
- // MonoGame-specific //////////////////////
- //var offset = .5f;
- ///////////////////////////////////////////
-
- // FNA-specific ///////////////////////////
- var offset = 0f;
- ///////////////////////////////////////////
-
- _effect.World = Matrix.Identity;
- _effect.View = Matrix.Identity;
- _effect.Projection = Matrix.CreateOrthographicOffCenter(offset, io.DisplaySize.X + offset, io.DisplaySize.Y + offset, offset, -1f, 1f);
- _effect.TextureEnabled = true;
- _effect.Texture = texture;
- _effect.VertexColorEnabled = true;
-
- return _effect;
- }
-
- ///
- /// Sends XNA input state to ImGui
- ///
- protected virtual void UpdateInput()
- {
- var io = ImGui.GetIO();
-
- var mouse = Mouse.GetState();
- var keyboard = Keyboard.GetState();
-
- for (int i = 0; i < _keys.Count; i++)
- {
- io.KeysDown[_keys[i]] = keyboard.IsKeyDown((Keys)_keys[i]);
- }
-
- io.KeyShift = keyboard.IsKeyDown(Keys.LeftShift) || keyboard.IsKeyDown(Keys.RightShift);
- io.KeyCtrl = keyboard.IsKeyDown(Keys.LeftControl) || keyboard.IsKeyDown(Keys.RightControl);
- io.KeyAlt = keyboard.IsKeyDown(Keys.LeftAlt) || keyboard.IsKeyDown(Keys.RightAlt);
- io.KeySuper = keyboard.IsKeyDown(Keys.LeftWindows) || keyboard.IsKeyDown(Keys.RightWindows);
-
- io.DisplaySize = new System.Numerics.Vector2(_graphicsDevice.PresentationParameters.BackBufferWidth, _graphicsDevice.PresentationParameters.BackBufferHeight);
- io.DisplayFramebufferScale = new System.Numerics.Vector2(1f, 1f);
-
- io.MousePos = new System.Numerics.Vector2(mouse.X, mouse.Y);
-
- io.MouseDown[0] = mouse.LeftButton == ButtonState.Pressed;
- io.MouseDown[1] = mouse.RightButton == ButtonState.Pressed;
- io.MouseDown[2] = mouse.MiddleButton == ButtonState.Pressed;
-
- var scrollDelta = mouse.ScrollWheelValue - _scrollWheelValue;
- io.MouseWheel = scrollDelta > 0 ? 1 : scrollDelta < 0 ? -1 : 0;
- _scrollWheelValue = mouse.ScrollWheelValue;
- }
-
- #endregion Setup & Update
-
- #region Internals
-
- ///
- /// Gets the geometry as set up by ImGui and sends it to the graphics device
- ///
- private void RenderDrawData(ImDrawDataPtr drawData)
- {
- // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers
- var lastViewport = _graphicsDevice.Viewport;
- var lastScissorBox = _graphicsDevice.ScissorRectangle;
-
- _graphicsDevice.BlendFactor = Color.White;
- _graphicsDevice.BlendState = BlendState.NonPremultiplied;
- _graphicsDevice.RasterizerState = _rasterizerState;
- _graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
-
- // Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays)
- drawData.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
-
- // Setup projection
- _graphicsDevice.Viewport = new Viewport(0, 0, _graphicsDevice.PresentationParameters.BackBufferWidth, _graphicsDevice.PresentationParameters.BackBufferHeight);
-
- UpdateBuffers(drawData);
-
- RenderCommandLists(drawData);
-
- // Restore modified state
- _graphicsDevice.Viewport = lastViewport;
- _graphicsDevice.ScissorRectangle = lastScissorBox;
- }
-
- private unsafe void UpdateBuffers(ImDrawDataPtr drawData)
- {
- if (drawData.TotalVtxCount == 0)
- {
- return;
- }
-
- // Expand buffers if we need more room
- if (drawData.TotalVtxCount > _vertexBufferSize)
- {
- _vertexBuffer?.Dispose();
-
- _vertexBufferSize = (int)(drawData.TotalVtxCount * 1.5f);
- _vertexBuffer = new VertexBuffer(_graphicsDevice, DrawVertDeclaration.Declaration, _vertexBufferSize, BufferUsage.None);
- _vertexData = new byte[_vertexBufferSize * DrawVertDeclaration.Size];
- }
-
- if (drawData.TotalIdxCount > _indexBufferSize)
- {
- _indexBuffer?.Dispose();
-
- _indexBufferSize = (int)(drawData.TotalIdxCount * 1.5f);
- _indexBuffer = new IndexBuffer(_graphicsDevice, IndexElementSize.SixteenBits, _indexBufferSize, BufferUsage.None);
- _indexData = new byte[_indexBufferSize * sizeof(ushort)];
- }
-
- // Copy ImGui's vertices and indices to a set of managed byte arrays
- int vtxOffset = 0;
- int idxOffset = 0;
-
- for (int n = 0; n < drawData.CmdListsCount; n++)
- {
- ImDrawListPtr cmdList = drawData.CmdListsRange[n];
-
- fixed (void* vtxDstPtr = &_vertexData[vtxOffset * DrawVertDeclaration.Size])
- fixed (void* idxDstPtr = &_indexData[idxOffset * sizeof(ushort)])
- {
- Buffer.MemoryCopy((void*)cmdList.VtxBuffer.Data, vtxDstPtr, _vertexData.Length, cmdList.VtxBuffer.Size * DrawVertDeclaration.Size);
- Buffer.MemoryCopy((void*)cmdList.IdxBuffer.Data, idxDstPtr, _indexData.Length, cmdList.IdxBuffer.Size * sizeof(ushort));
- }
-
- vtxOffset += cmdList.VtxBuffer.Size;
- idxOffset += cmdList.IdxBuffer.Size;
- }
-
- // Copy the managed byte arrays to the gpu vertex- and index buffers
- _vertexBuffer.SetData(_vertexData, 0, drawData.TotalVtxCount * DrawVertDeclaration.Size);
- _indexBuffer.SetData(_indexData, 0, drawData.TotalIdxCount * sizeof(ushort));
- }
-
- private unsafe void RenderCommandLists(ImDrawDataPtr drawData)
- {
- _graphicsDevice.SetVertexBuffer(_vertexBuffer);
- _graphicsDevice.Indices = _indexBuffer;
-
- int vtxOffset = 0;
- int idxOffset = 0;
-
- for (int n = 0; n < drawData.CmdListsCount; n++)
- {
- ImDrawListPtr cmdList = drawData.CmdListsRange[n];
-
- for (int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++)
- {
- ImDrawCmdPtr drawCmd = cmdList.CmdBuffer[cmdi];
-
- if (!_loadedTextures.ContainsKey(drawCmd.TextureId))
- {
- throw new InvalidOperationException($"Could not find a texture with id '{drawCmd.TextureId}', please check your bindings");
- }
-
- _graphicsDevice.ScissorRectangle = new Rectangle(
- (int)drawCmd.ClipRect.X,
- (int)drawCmd.ClipRect.Y,
- (int)(drawCmd.ClipRect.Z - drawCmd.ClipRect.X),
- (int)(drawCmd.ClipRect.W - drawCmd.ClipRect.Y)
- );
-
- var effect = UpdateEffect(_loadedTextures[drawCmd.TextureId]);
-
- foreach (var pass in effect.CurrentTechnique.Passes)
- {
- pass.Apply();
+ ///
+ /// ImGui renderer for use with XNA-likes (FNA and MonoGame)
+ ///
+ public class ImGuiRenderer
+ {
+ // Graphics
+ BasicEffect _effect;
+ RasterizerState _rasterizerState;
+
+ readonly VertexDeclaration _vertexDeclaration;
+ readonly int _vertexDeclarationSize;
+
+ byte[] _vertexData;
+ VertexBuffer _vertexBuffer;
+ int _vertexBufferSize;
+
+ byte[] _indexData;
+ IndexBuffer _indexBuffer;
+ int _indexBufferSize;
+
+ // Textures
+ Dictionary _loadedTextures;
+
+ int _textureId;
+ IntPtr? _fontTextureId;
+
+ // Input
+ int _scrollWheelValue;
+
+ List _keys = new List();
+
+ public ImGuiRenderer( Game game )
+ {
+ unsafe { _vertexDeclarationSize = sizeof( ImDrawVert ); }
+ _vertexDeclaration = new VertexDeclaration(
+ _vertexDeclarationSize,
+ // Position
+ new VertexElement( 0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0 ),
+ // UV
+ new VertexElement( 8, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0 ),
+ // Color
+ new VertexElement( 16, VertexElementFormat.Color, VertexElementUsage.Color, 0 )
+ );
+
+ var context = ImGui.CreateContext();
+ ImGui.SetCurrentContext( context );
+
+ _loadedTextures = new Dictionary();
+
+ _rasterizerState = new RasterizerState()
+ {
+ CullMode = CullMode.None,
+ DepthBias = 0,
+ FillMode = FillMode.Solid,
+ MultiSampleAntiAlias = false,
+ ScissorTestEnable = true,
+ SlopeScaleDepthBias = 0
+ };
+
+ setupInput();
+ }
+
+ #region ImGuiRenderer
+
+ ///
+ /// Creates a texture and loads the font data from ImGui. Should be called when the is initialized but before any rendering is done
+ ///
+ public unsafe void rebuildFontAtlas()
+ {
+ // Get font texture from ImGui
+ var io = ImGui.GetIO();
+ io.Fonts.GetTexDataAsRGBA32( out byte* pixelData, out int width, out int height, out int bytesPerPixel );
+
+ // Copy the data to a managed array
+ var pixels = new byte[width * height * bytesPerPixel];
+ Marshal.Copy( new IntPtr( pixelData ), pixels, 0, pixels.Length );
+
+ // Create and register the texture as an XNA texture
+ var tex2d = new Texture2D( Core.graphicsDevice, width, height, false, SurfaceFormat.Color );
+ tex2d.SetData( pixels );
+
+ // Should a texture already have been built previously, unbind it first so it can be deallocated
+ if( _fontTextureId.HasValue )
+ unbindTexture( _fontTextureId.Value );
+
+ // Bind the new texture to an ImGui-friendly id
+ _fontTextureId = bindTexture( tex2d );
+
+ // Let ImGui know where to find the texture
+ io.Fonts.SetTexID( _fontTextureId.Value );
+ io.Fonts.ClearTexData(); // Clears CPU side texture data
+ }
+
+ ///
+ /// Creates a pointer to a texture, which can be passed through ImGui calls such as . That pointer is then used by ImGui to let us know what texture to draw
+ ///
+ public IntPtr bindTexture( Texture2D texture )
+ {
+ var id = new IntPtr( _textureId++ );
+ _loadedTextures.Add( id, texture );
+ return id;
+ }
+
+ ///
+ /// Removes a previously created texture pointer, releasing its reference and allowing it to be deallocated
+ ///
+ public void unbindTexture( IntPtr textureId )
+ {
+ _loadedTextures.Remove( textureId );
+ }
+
+ ///
+ /// Sets up ImGui for a new frame, should be called at frame start
+ ///
+ public void beforeLayout( float deltaTime )
+ {
+ ImGui.GetIO().DeltaTime = deltaTime;
+ updateInput();
+ ImGui.NewFrame();
+ }
+
+ ///
+ /// Asks ImGui for the generated geometry data and sends it to the graphics pipeline, should be called after the UI is drawn using ImGui.** calls
+ ///
+ public void afterLayout()
+ {
+ ImGui.Render();
+ unsafe { renderDrawData( ImGui.GetDrawData() ); }
+ }
+
+ #endregion ImGuiRenderer
+
+ #region Setup & Update
+
+ ///
+ /// Maps ImGui keys to XNA keys. We use this later on to tell ImGui what keys were pressed
+ ///
+ void setupInput()
+ {
+ var io = ImGui.GetIO();
+
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Tab] = (int)Keys.Tab );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Keys.Left );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Keys.Right );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Keys.Up );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Keys.Down );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.PageUp] = (int)Keys.PageUp );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.PageDown] = (int)Keys.PageDown );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Home] = (int)Keys.Home );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.End] = (int)Keys.End );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Delete] = (int)Keys.Delete );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Backspace] = (int)Keys.Back );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Enter] = (int)Keys.Enter );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Escape] = (int)Keys.Escape );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.A] = (int)Keys.A );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.C] = (int)Keys.C );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.V] = (int)Keys.V );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.X] = (int)Keys.X );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Y] = (int)Keys.Y );
+ _keys.Add( io.KeyMap[(int)ImGuiKey.Z] = (int)Keys.Z );
+
+
+ // MonoGame-specific //////////////////////
+ // _game.Window.TextInput += (s, a) =>
+ // {
+ // if (a.Character == '\t') return;
+
+ // io.AddInputCharacter(a.Character);
+ // };
+ ///////////////////////////////////////////
+
+ // FNA-specific ///////////////////////////
+ TextInputEXT.TextInput += c =>
+ {
+ if( c == '\t' ) return;
+
+ ImGui.GetIO().AddInputCharacter( c );
+ };
+ ///////////////////////////////////////////
+
+ ImGui.GetIO().Fonts.AddFontDefault();
+ }
+
+ ///
+ /// Updates the to the current matrices and texture
+ ///
+ Effect updateEffect( Texture2D texture )
+ {
+ _effect = _effect ?? new BasicEffect( Core.graphicsDevice );
+
+ var io = ImGui.GetIO();
+
+ // MonoGame-specific //////////////////////
+ //var offset = .5f;
+ ///////////////////////////////////////////
+
+ // FNA-specific ///////////////////////////
+ var offset = 0f;
+ ///////////////////////////////////////////
+
+ _effect.World = Matrix.Identity;
+ _effect.View = Matrix.Identity;
+ _effect.Projection = Matrix.CreateOrthographicOffCenter( offset, io.DisplaySize.X + offset, io.DisplaySize.Y + offset, offset, -1f, 1f );
+ _effect.TextureEnabled = true;
+ _effect.Texture = texture;
+ _effect.VertexColorEnabled = true;
+
+ return _effect;
+ }
+
+ ///
+ /// Sends XNA input state to ImGui
+ ///
+ void updateInput()
+ {
+ var io = ImGui.GetIO();
+
+ var mouse = Mouse.GetState();
+ var keyboard = Keyboard.GetState();
+
+ for( int i = 0; i < _keys.Count; i++ )
+ {
+ io.KeysDown[_keys[i]] = keyboard.IsKeyDown( (Keys)_keys[i] );
+ }
+
+ io.KeyShift = keyboard.IsKeyDown( Keys.LeftShift ) || keyboard.IsKeyDown( Keys.RightShift );
+ io.KeyCtrl = keyboard.IsKeyDown( Keys.LeftControl ) || keyboard.IsKeyDown( Keys.RightControl );
+ io.KeyAlt = keyboard.IsKeyDown( Keys.LeftAlt ) || keyboard.IsKeyDown( Keys.RightAlt );
+ io.KeySuper = keyboard.IsKeyDown( Keys.LeftWindows ) || keyboard.IsKeyDown( Keys.RightWindows );
+
+ io.DisplaySize = new System.Numerics.Vector2( Core.graphicsDevice.PresentationParameters.BackBufferWidth, Core.graphicsDevice.PresentationParameters.BackBufferHeight );
+ io.DisplayFramebufferScale = new System.Numerics.Vector2( 1f, 1f );
+
+ io.MousePos = new System.Numerics.Vector2( mouse.X, mouse.Y );
+
+ io.MouseDown[0] = mouse.LeftButton == ButtonState.Pressed;
+ io.MouseDown[1] = mouse.RightButton == ButtonState.Pressed;
+ io.MouseDown[2] = mouse.MiddleButton == ButtonState.Pressed;
+
+ var scrollDelta = mouse.ScrollWheelValue - _scrollWheelValue;
+ io.MouseWheel = scrollDelta > 0 ? 1 : scrollDelta < 0 ? -1 : 0;
+ _scrollWheelValue = mouse.ScrollWheelValue;
+ }
+
+ #endregion Setup & Update
+
+ #region Internals
+
+ ///
+ /// Gets the geometry as set up by ImGui and sends it to the graphics device
+ ///
+ void renderDrawData( ImDrawDataPtr drawData )
+ {
+ // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers
+ var lastViewport = Core.graphicsDevice.Viewport;
+ var lastScissorBox = Core.graphicsDevice.ScissorRectangle;
+
+ Core.graphicsDevice.BlendFactor = Color.White;
+ Core.graphicsDevice.BlendState = BlendState.NonPremultiplied;
+ Core.graphicsDevice.RasterizerState = _rasterizerState;
+ Core.graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
+
+ // Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays)
+ drawData.ScaleClipRects( ImGui.GetIO().DisplayFramebufferScale );
+
+ // Setup projection
+ Core.graphicsDevice.Viewport = new Viewport( 0, 0, Core.graphicsDevice.PresentationParameters.BackBufferWidth, Core.graphicsDevice.PresentationParameters.BackBufferHeight );
+
+ updateBuffers( drawData );
+ renderCommandLists( drawData );
+
+ // Restore modified state
+ Core.graphicsDevice.Viewport = lastViewport;
+ Core.graphicsDevice.ScissorRectangle = lastScissorBox;
+ }
+
+ unsafe void updateBuffers( ImDrawDataPtr drawData )
+ {
+ if( drawData.TotalVtxCount == 0 )
+ {
+ return;
+ }
+
+ // Expand buffers if we need more room
+ if( drawData.TotalVtxCount > _vertexBufferSize )
+ {
+ _vertexBuffer?.Dispose();
+
+ _vertexBufferSize = (int)( drawData.TotalVtxCount * 1.5f );
+ _vertexBuffer = new VertexBuffer( Core.graphicsDevice, _vertexDeclaration, _vertexBufferSize, BufferUsage.None );
+ _vertexData = new byte[_vertexBufferSize * _vertexDeclarationSize];
+ }
+
+ if( drawData.TotalIdxCount > _indexBufferSize )
+ {
+ _indexBuffer?.Dispose();
+
+ _indexBufferSize = (int)( drawData.TotalIdxCount * 1.5f );
+ _indexBuffer = new IndexBuffer( Core.graphicsDevice, IndexElementSize.SixteenBits, _indexBufferSize, BufferUsage.None );
+ _indexData = new byte[_indexBufferSize * sizeof( ushort )];
+ }
+
+ // Copy ImGui's vertices and indices to a set of managed byte arrays
+ int vtxOffset = 0;
+ int idxOffset = 0;
+
+ for( var n = 0; n < drawData.CmdListsCount; n++ )
+ {
+ var cmdList = drawData.CmdListsRange[n];
+
+ fixed ( void* vtxDstPtr = &_vertexData[vtxOffset * _vertexDeclarationSize] )
+ fixed ( void* idxDstPtr = &_indexData[idxOffset * sizeof( ushort )] )
+ {
+ Buffer.MemoryCopy( (void*)cmdList.VtxBuffer.Data, vtxDstPtr, _vertexData.Length, cmdList.VtxBuffer.Size * _vertexDeclarationSize );
+ Buffer.MemoryCopy( (void*)cmdList.IdxBuffer.Data, idxDstPtr, _indexData.Length, cmdList.IdxBuffer.Size * sizeof( ushort ) );
+ }
+
+ vtxOffset += cmdList.VtxBuffer.Size;
+ idxOffset += cmdList.IdxBuffer.Size;
+ }
+
+ // Copy the managed byte arrays to the gpu vertex- and index buffers
+ _vertexBuffer.SetData( _vertexData, 0, drawData.TotalVtxCount * _vertexDeclarationSize );
+ _indexBuffer.SetData( _indexData, 0, drawData.TotalIdxCount * sizeof( ushort ) );
+ }
+
+ unsafe void renderCommandLists( ImDrawDataPtr drawData )
+ {
+ Core.graphicsDevice.SetVertexBuffer( _vertexBuffer );
+ Core.graphicsDevice.Indices = _indexBuffer;
+
+ int vtxOffset = 0;
+ int idxOffset = 0;
+
+ for( int n = 0; n < drawData.CmdListsCount; n++ )
+ {
+ var cmdList = drawData.CmdListsRange[n];
+ for( int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++ )
+ {
+ var drawCmd = cmdList.CmdBuffer[cmdi];
+ if( !_loadedTextures.ContainsKey( drawCmd.TextureId ) )
+ {
+ throw new InvalidOperationException( $"Could not find a texture with id '{drawCmd.TextureId}', please check your bindings" );
+ }
+
+ Core.graphicsDevice.ScissorRectangle = new Rectangle(
+ (int)drawCmd.ClipRect.X,
+ (int)drawCmd.ClipRect.Y,
+ (int)( drawCmd.ClipRect.Z - drawCmd.ClipRect.X ),
+ (int)( drawCmd.ClipRect.W - drawCmd.ClipRect.Y )
+ );
+
+ var effect = updateEffect( _loadedTextures[drawCmd.TextureId] );
+ foreach( var pass in effect.CurrentTechnique.Passes )
+ {
+ pass.Apply();
#pragma warning disable CS0618 // // FNA does not expose an alternative method.
- _graphicsDevice.DrawIndexedPrimitives(
- primitiveType: PrimitiveType.TriangleList,
- baseVertex: vtxOffset,
- minVertexIndex: 0,
- numVertices: cmdList.VtxBuffer.Size,
- startIndex: idxOffset,
- primitiveCount: (int)drawCmd.ElemCount / 3
- );
+ Core.graphicsDevice.DrawIndexedPrimitives(
+ primitiveType: PrimitiveType.TriangleList,
+ baseVertex: vtxOffset,
+ minVertexIndex: 0,
+ numVertices: cmdList.VtxBuffer.Size,
+ startIndex: idxOffset,
+ primitiveCount: (int)drawCmd.ElemCount / 3
+ );
#pragma warning restore CS0618
- }
+ }
- idxOffset += (int)drawCmd.ElemCount;
- }
+ idxOffset += (int)drawCmd.ElemCount;
+ }
- vtxOffset += cmdList.VtxBuffer.Size;
- }
- }
+ vtxOffset += cmdList.VtxBuffer.Size;
+ }
+ }
- #endregion Internals
- }
+ #endregion Internals
+ }
}
\ No newline at end of file