Skip to content

Instantly share code, notes, and snippets.

@mellinoe
Last active March 26, 2017 07:08
Show Gist options
  • Save mellinoe/488bfde9139da90d3d9a8914fcc657c8 to your computer and use it in GitHub Desktop.
Save mellinoe/488bfde9139da90d3d9a8914fcc657c8 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using ImGuiNET;
namespace Imgui_FNA
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
IO io;
private Matrix worldMatrix, viewMatrix, projectionMatrix;
private BasicEffect basicEffect;
private VertexBuffer vbuff;
private IndexBuffer ibuff;
private Texture2D defaultWhiteTexture, fontTexture;
private int scrollWheel = 0;
private char[] input = new char[1024];
private float renderTime = 0f;
private VertexDeclaration vertexDeclaration = new VertexDeclaration(
new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0),
new VertexElement(8, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0),
new VertexElement(16, VertexElementFormat.Color, VertexElementUsage.Color, 0)
);
private int _counter;
public Game()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "../../Content";
}
unsafe protected override void Initialize()
{
SetupBasicShader();
// Application init
io = ImGui.GetIO();
io.DisplaySize = new System.Numerics.Vector2(GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
SetupFontTextures();
SetupBuffers();
//not sure if we really need pointclamp since we add a 1px padding around each glyph
GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
GraphicsDevice.BlendState = new BlendState()
{
ColorSourceBlend = Blend.SourceAlpha,
ColorDestinationBlend = Blend.InverseSourceAlpha,
ColorBlendFunction = BlendFunction.Add,
AlphaSourceBlend = Blend.InverseSourceAlpha,
AlphaDestinationBlend = Blend.Zero,
AlphaBlendFunction = BlendFunction.Add,
BlendFactor = Color.White,
};
GraphicsDevice.BlendFactor = Color.White;
GraphicsDevice.RasterizerState = new RasterizerState() { CullMode = CullMode.None, ScissorTestEnable = true };
SetupKeyMappings();
base.Initialize();
}
bool SetupFontTextures()
{
var io = ImGui.GetIO();
//font texture
var data = io.FontAtlas.GetTexDataAsRGBA32();
fontTexture = new Texture2D(GraphicsDevice, data.Width, data.Height);
unsafe
{
fontTexture.SetData(GetBytes((IntPtr)data.Pixels, data.Height * data.Width * data.BytesPerPixel));
}
io.FontAtlas.SetTexID(fontTexture.GetHashCode());
basicEffect.Texture = fontTexture;
//default white texture
defaultWhiteTexture = new Texture2D(GraphicsDevice, 1, 1);
defaultWhiteTexture.SetData(new Color[] { Color.White });
return true;
}
void SetupBasicShader()
{
viewMatrix = Matrix.Identity;
projectionMatrix = Matrix.CreateOrthographicOffCenter(0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, -1);
worldMatrix = Matrix.Identity;
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.World = worldMatrix;
basicEffect.View = viewMatrix;
basicEffect.Projection = projectionMatrix;
basicEffect.VertexColorEnabled = true;
basicEffect.TextureEnabled = true;
}
void SetupBuffers()
{
vbuff = new VertexBuffer(GraphicsDevice, vertexDeclaration, 4096, BufferUsage.WriteOnly);
ibuff = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, 4096 * 4, BufferUsage.WriteOnly);
}
void SetupKeyMappings()
{
io.KeyMap[GuiKey.Tab] = (int)Keys.Tab;
io.KeyMap[GuiKey.LeftArrow] = (int)Keys.Left;
io.KeyMap[GuiKey.RightArrow] = (int)Keys.Right;
io.KeyMap[GuiKey.UpArrow] = (int)Keys.Up;
io.KeyMap[GuiKey.DownArrow] = (int)Keys.Down;
io.KeyMap[GuiKey.PageUp] = (int)Keys.PageUp;
io.KeyMap[GuiKey.PageDown] = (int)Keys.PageDown;
io.KeyMap[GuiKey.Home] = (int)Keys.Home;
io.KeyMap[GuiKey.End] = (int)Keys.End;
io.KeyMap[GuiKey.Delete] = (int)Keys.Delete;
io.KeyMap[GuiKey.Backspace] = (int)Keys.Back;
io.KeyMap[GuiKey.Enter] = (int)Keys.Enter;
io.KeyMap[GuiKey.Escape] = (int)Keys.Escape;
io.KeyMap[GuiKey.A] = (int)Keys.A;
io.KeyMap[GuiKey.C] = (int)Keys.C;
io.KeyMap[GuiKey.V] = (int)Keys.V;
io.KeyMap[GuiKey.X] = (int)Keys.X;
io.KeyMap[GuiKey.Y] = (int)Keys.Y;
io.KeyMap[GuiKey.Z] = (int)Keys.Z;
}
//// An umanaged function that retrieves the states of each key
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
public static extern short GetKeyState(int keyCode);
#region Convert Key
public static char ConvertKeyboardInput(Keys key, bool alt, bool shift, bool ctrl, bool caps)
{
bool upperCase = caps ^ shift;
switch (key)
{
//Alphabet keys
case Keys.A: if (upperCase) { return 'A'; } else { return 'a'; }
case Keys.B: if (upperCase) { return 'B'; } else { return 'b'; }
case Keys.C: if (upperCase) { return 'C'; } else { return 'c'; }
case Keys.D: if (upperCase) { return 'D'; } else { return 'd'; }
case Keys.E: if (upperCase) { return 'E'; } else { return 'e'; }
case Keys.F: if (upperCase) { return 'F'; } else { return 'f'; }
case Keys.G: if (upperCase) { return 'G'; } else { return 'g'; }
case Keys.H: if (upperCase) { return 'H'; } else { return 'h'; }
case Keys.I: if (upperCase) { return 'I'; } else { return 'i'; }
case Keys.J: if (upperCase) { return 'J'; } else { return 'j'; }
case Keys.K: if (upperCase) { return 'K'; } else { return 'k'; }
case Keys.L: if (upperCase) { return 'L'; } else { return 'l'; }
case Keys.M: if (upperCase) { return 'M'; } else { return 'm'; }
case Keys.N: if (upperCase) { return 'N'; } else { return 'n'; }
case Keys.O: if (upperCase) { return 'O'; } else { return 'o'; }
case Keys.P: if (upperCase) { return 'P'; } else { return 'p'; }
case Keys.Q: if (upperCase) { return 'Q'; } else { return 'q'; }
case Keys.R: if (upperCase) { return 'R'; } else { return 'r'; }
case Keys.S: if (upperCase) { return 'S'; } else { return 's'; }
case Keys.T: if (upperCase) { return 'T'; } else { return 't'; }
case Keys.U: if (upperCase) { return 'U'; } else { return 'u'; }
case Keys.V: if (upperCase) { return 'V'; } else { return 'v'; }
case Keys.W: if (upperCase) { return 'W'; } else { return 'w'; }
case Keys.X: if (upperCase) { return 'X'; } else { return 'x'; }
case Keys.Y: if (upperCase) { return 'Y'; } else { return 'y'; }
case Keys.Z: if (upperCase) { return 'Z'; } else { return 'z'; }
//Decimal keys
case Keys.D0: if (shift) { return ')'; } else { return '0'; }
case Keys.D1: if (shift) { return '!'; } else { return '1'; }
case Keys.D2: if (shift) { return '@'; } else { return '2'; }
case Keys.D3: if (shift) { return '#'; } else { return '3'; }
case Keys.D4: if (shift) { return '$'; } else { return '4'; }
case Keys.D5: if (shift) { return '%'; } else { return '5'; }
case Keys.D6: if (shift) { return '^'; } else { return '6'; }
case Keys.D7: if (shift) { return '&'; } else { return '7'; }
case Keys.D8: if (shift) { return '*'; } else { return '8'; }
case Keys.D9: if (shift) { return '('; } else { return '9'; }
//Decimal numpad keys
case Keys.NumPad0: return '0';
case Keys.NumPad1: return '1';
case Keys.NumPad2: return '2';
case Keys.NumPad3: return '3';
case Keys.NumPad4: return '4';
case Keys.NumPad5: return '5';
case Keys.NumPad6: return '6';
case Keys.NumPad7: return '7';
case Keys.NumPad8: return '8';
case Keys.NumPad9: return '9';
//Special keys
case Keys.OemTilde: if (shift) { return '~'; } else { return '`'; }
case Keys.OemSemicolon: if (shift) { return ':'; } else { return ';'; }
case Keys.OemQuotes: if (shift) { return '"'; } else { return '\''; }
case Keys.OemQuestion: if (shift) { return '?'; } else { return '/'; }
case Keys.OemPlus: if (shift) { return '+'; } else { return '='; }
case Keys.OemPipe: if (shift) { return '|'; } else { return '\\'; }
case Keys.OemPeriod: if (shift) { return '>'; } else { return '.'; }
case Keys.OemOpenBrackets: if (shift) { return '{'; } else { return '['; }
case Keys.OemCloseBrackets: if (shift) { return '}'; } else { return ']'; }
case Keys.OemMinus: if (shift) { return '_'; } else { return '-'; }
case Keys.OemComma: if (shift) { return '<'; } else { return ','; }
case Keys.Space: return ' ';
}
return '\0';
}
#endregion
/// <summary>
///
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
protected override void Update(GameTime gameTime)
{
io.DeltaTime = gameTime.ElapsedGameTime.Milliseconds / 1000;
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == (ButtonState.Pressed) || Keyboard.GetState().IsKeyDown(Keys.Escape))
{
Exit();
}
if (Keyboard.GetState().IsKeyDown(Keys.F))
{
graphics.ToggleFullScreen();
}
int w = GraphicsDevice.Viewport.Width, h = GraphicsDevice.Viewport.Height;
int display_w = GraphicsDevice.Viewport.Width, display_h = GraphicsDevice.Viewport.Height;
//int display_w = GraphicsDevice.PresentationParameters.BackBufferWidth, display_h = GraphicsDevice.PresentationParameters.BackBufferHeight;
KeyboardState keyboard = Keyboard.GetState();
MouseState mouse = Mouse.GetState();
io.DisplaySize = new System.Numerics.Vector2(w, h);
//io.DisplayFramebufferScale = new ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0);
//// 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.)
//// TODO: fill all fields of IO structure and call NewFrame
io.DeltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
if (IsActive)
{
io.MousePosition = new System.Numerics.Vector2(mouse.X, mouse.Y);
}
else
{
io.MousePosition = new System.Numerics.Vector2(-1, -1);
}
io.MouseDown[0] = mouse.LeftButton == ButtonState.Pressed;
io.MouseDown[1] = mouse.RightButton == ButtonState.Pressed;
io.MouseDown[2] = mouse.MiddleButton == ButtonState.Pressed;
io.MouseWheel = mouse.ScrollWheelValue > scrollWheel ? 1 : mouse.ScrollWheelValue < scrollWheel ? -1 : 0;
scrollWheel = mouse.ScrollWheelValue;
io.CtrlPressed = keyboard.IsKeyDown(Keys.LeftControl) || keyboard.IsKeyDown(Keys.RightControl);
io.AltPressed = keyboard.IsKeyDown(Keys.LeftAlt) || keyboard.IsKeyDown(Keys.RightAlt);
io.ShiftPressed = keyboard.IsKeyDown(Keys.LeftShift) || keyboard.IsKeyDown(Keys.RightShift);
bool capslock = ((((ushort)GetKeyState(0x14)) & 0xffff) != 0);
Keys[] keys = keyboard.GetPressedKeys();
for (int i = 0; i < 256; i++)
io.KeysDown[i] = false;
for (int i = 0; i < keys.Length; i++)
{
Keys key = keys[i];
char ch = ConvertKeyboardInput(key, io.AltPressed, io.ShiftPressed, io.CtrlPressed, capslock);
if (!io.AltPressed && !io.CtrlPressed && (int)key < 0x80 && ch != 0)
{
io.KeysDown[ch] = true;
ImGui.AddInputCharacter(ch);
}
else
io.KeysDown[(int)key + 0xff] = true;
}
// TODO: Add your update logic here
ImGui.NewFrame();
ImGui.Text("Hello MonoGame!");
if (ImGui.Button("Press me!"))
{
_counter++;
}
ImGui.SameLine();
ImGui.Text("I've been pressed " + _counter + " times.");
base.Update(gameTime);
}
private void EnsureBuffers(int vertexCount, int indexCount)
{
if (vertexCount >= vbuff.VertexCount)
{
vbuff.Dispose();
vbuff = new VertexBuffer(this.GraphicsDevice, vertexDeclaration, vertexCount * 3 / 2, BufferUsage.WriteOnly);
}
if (indexCount >= ibuff.IndexCount)
{
ibuff.Dispose();
ibuff = new IndexBuffer(this.GraphicsDevice, IndexElementSize.SixteenBits, indexCount * 3 / 2, BufferUsage.WriteOnly);
}
}
private byte[] GetBytes(IntPtr m_pBuffer, int size)
{
byte[] bytes = new byte[size];
Marshal.Copy(m_pBuffer, bytes, 0, size);
return bytes;
}
private unsafe DrawVert[] GetVertArray(IntPtr buffer, int numVerts)
{
DrawVert[] verts = new DrawVert[numVerts];
fixed (DrawVert* destPtr = verts)
{
DrawVert* srcPtr = (DrawVert*)buffer;
for (int i = 0; i < numVerts; i++)
{
destPtr[i] = srcPtr[i];
}
}
return verts;
}
private unsafe ushort[] GetUshortArray(IntPtr buffer, int numUshorts)
{
ushort[] indices = new ushort[numUshorts];
fixed (ushort* destPtr = indices)
{
ushort* srcPtr = (ushort*)buffer;
for (int i = 0; i < numUshorts; i++)
{
destPtr[i] = srcPtr[i];
}
}
return indices;
}
unsafe protected override void Draw(GameTime gameTime)
{
ImGui.Text("AVG Render: " + renderTime + "");
Stopwatch timer = Stopwatch.StartNew();
ImGui.Render();
DrawData* data = ImGui.GetDrawData();
int vertexOffsetInVertices = 0;
int indexOffsetInElements = 0;
for (int i = 0; i < data->CmdListsCount; i++)
{
NativeDrawList* cmd_list = data->CmdLists[i];
EnsureBuffers(cmd_list->VtxBuffer.Size, cmd_list->IdxBuffer.Size);
DrawVert[] vbData = GetVertArray((IntPtr)cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size);
ushort[] ibData = GetUshortArray((IntPtr)cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size);
vbuff.SetData(vertexOffsetInVertices / sizeof(DrawVert), vbData, 0, cmd_list->VtxBuffer.Size, 0);
ibuff.SetData(indexOffsetInElements / sizeof(ushort), ibData, 0, ibData.Length);
vertexOffsetInVertices += cmd_list->VtxBuffer.Size;
indexOffsetInElements += cmd_list->IdxBuffer.Size;
}
GraphicsDevice.Clear(Color.CornflowerBlue);
for (int k = 0; k < data->CmdListsCount; k++)
{
NativeDrawList* drawlist = data->CmdLists[k];
GraphicsDevice.SetVertexBuffer(vbuff);
GraphicsDevice.Indices = ibuff;
GraphicsDevice.DepthStencilState = DepthStencilState.None;
// Render command lists
int idx_offset = 0;
for (int j = 0; j < drawlist->CmdBuffer.Size; j++)
{
DrawCmd* pcmd_ = &(((DrawCmd*)drawlist->CmdBuffer.Data)[j]);
DrawCmd pcmd = *pcmd_;
if (pcmd.UserCallback != IntPtr.Zero)
{
throw new NotImplementedException();
}
else
{
GraphicsDevice.ScissorRectangle = new Rectangle((int)pcmd.ClipRect.X, (int)pcmd.ClipRect.Y, (int)(pcmd.ClipRect.Z - pcmd.ClipRect.X), (int)(pcmd.ClipRect.W - pcmd.ClipRect.Y));
if (pcmd.TextureId.ToInt32() == fontTexture.GetHashCode())
{
basicEffect.Texture.Equals(fontTexture);
}
else
{
throw new NotImplementedException();
}
for (int i = 0; i < basicEffect.CurrentTechnique.Passes.Count; i++)
{
EffectPass pass = basicEffect.CurrentTechnique.Passes[i];
pass.Apply();
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, idx_offset, (int)(pcmd.ElemCount));
}
}
idx_offset += (int)pcmd.ElemCount;
}
}
base.Draw(gameTime);
timer.Stop();
renderTime = renderTime * 0.8f + (float)timer.Elapsed.TotalSeconds * 0.2f;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment