diff --git a/getFNA.sh b/getFNA.sh
index c0a480b..ba85897 100755
--- a/getFNA.sh
+++ b/getFNA.sh
@@ -68,7 +68,9 @@ function downloadImGui()
{
checkGit
echo "Downloading ImGui..."
- git -C $MY_DIR clone https://github.com/mellinoe/ImGui.NET.git --recursive
+ echo "Temporarily using ImGui.NET branch until ImGui.NET master is updated"
+ #git -C $MY_DIR clone https://github.com/mellinoe/ImGui.NET.git --recursive
+ git -C $MY_DIR clone -b fix-MonoGame-FNA https://github.com/prime31/ImGui.NET.git --recursive
if [ $? -eq 0 ]; then
echo "Finished downloading!\n"
else
@@ -168,12 +170,7 @@ if [ ! -d "$MY_DIR/project_name" ]; then
exit 1
fi
-# copy over ImGui files before renaming the project
-echo "Copying ImGui renderer to project..."
-cp "$MY_DIR/ImGui.NET/src/ImGui.NET.SampleProgram.XNA/DrawVertDeclaration.cs" "$MY_DIR/project_name/ImGui"
-cp "$MY_DIR/ImGui.NET/src/ImGui.NET.SampleProgram.XNA/ImGuiRenderer.cs" "$MY_DIR/project_name/ImGui"
-sed -i '' "s/cimgui/cimgui.dylib/g" ImGui.NET/src/ImGui.NET/Generated/ImGuiNative.gen.cs
-
+
read -p "Enter the project name to use for your folder and csproj file or 'exit' to quit: " newProjectName
if [[ $newProjectName = 'exit' || -z "$newProjectName" ]]; then
exit 1
@@ -202,4 +199,10 @@ cd Nez.FNA
git submodule init
git submodule update
-printf "\n\nManually run the following command:\n\nnuget restore Nez.FNA/Nez/Nez.sln && msbuild Nez.FNA/Nez/Nez.sln && msbuild /t:restore $newProjectName\n\n"
+command -v pbcopy > /dev/null 2>&1
+if [ ! $? -eq 0 ]; then
+ printf "\n\nManually run the following command:\n\nnuget restore Nez.FNA/Nez/Nez.sln && msbuild Nez.FNA/Nez/Nez.sln && msbuild /t:restore $newProjectName\n\n"
+else
+ echo "nuget restore Nez.FNA/Nez/Nez.sln && msbuild Nez.FNA/Nez/Nez.sln && msbuild /t:restore $newProjectName" | pbcopy
+ echo "command copied to your clipboard\n"
+fi
diff --git a/project_name.sln b/project_name.sln
index 7d9eda8..bf0682f 100644
--- a/project_name.sln
+++ b/project_name.sln
@@ -39,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNA", "FNA\FNA.csproj", "{3
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nez.FNA", "Nez.FNA\Nez.FNA\Nez.FNA.csproj", "{11A5855C-B12C-4F8D-B935-56F3D0B671C3}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET", "ImGui.NET\src\ImGui.NET\ImGui.NET.csproj", "{AD548A1D-01B2-410E-B3ED-ADAC05C3C4A3}"
+EndProject
Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8C576ECC-147D-4B4A-8EC1-56533D26A178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -47,6 +49,8 @@ Global
{35253CE1-C864-4CD3-8249-4D1319748E8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11A5855C-B12C-4F8D-B935-56F3D0B671C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11A5855C-B12C-4F8D-B935-56F3D0B671C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AD548A1D-01B2-410E-B3ED-ADAC05C3C4A3}.Debug|Any CPU.ActiveCfg = Release|Any CPU
+ {AD548A1D-01B2-410E-B3ED-ADAC05C3C4A3}.Debug|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/project_name/ImGui/DrawVertDeclaration.cs b/project_name/ImGui/DrawVertDeclaration.cs
new file mode 100644
index 0000000..276e5df
--- /dev/null
+++ b/project_name/ImGui/DrawVertDeclaration.cs
@@ -0,0 +1,28 @@
+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/ImGuiRenderer.cs b/project_name/ImGui/ImGuiRenderer.cs
new file mode 100644
index 0000000..0c6886a
--- /dev/null
+++ b/project_name/ImGui/ImGuiRenderer.cs
@@ -0,0 +1,387 @@
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Input;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace ImGuiNET.SampleProgram.XNA
+{
+ ///
+ /// 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();
+
+#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
+ );
+#pragma warning restore CS0618
+ }
+
+ idxOffset += (int)drawCmd.ElemCount;
+ }
+
+ vtxOffset += cmdList.VtxBuffer.Size;
+ }
+ }
+
+ #endregion Internals
+ }
+}
\ No newline at end of file
diff --git a/project_name/project_name.csproj b/project_name/project_name.csproj
index fc687f1..0cc174c 100644
--- a/project_name/project_name.csproj
+++ b/project_name/project_name.csproj
@@ -72,19 +72,19 @@
- osx\%(RecursiveDir)%(Filename)%(Extension)
+ %(RecursiveDir)%(Filename)%(Extension)
PreserveNewest
- osx\%(RecursiveDir)%(Filename)%(Extension)
+ %(RecursiveDir)%(Filename)%(Extension)
PreserveNewest
- osx\%(RecursiveDir)%(Filename)%(Extension)
+ %(RecursiveDir)%(Filename)%(Extension)
PreserveNewest
- osx\%(RecursiveDir)%(Filename)%(Extension)
+ %(RecursiveDir)%(Filename)%(Extension)
PreserveNewest