Last active
March 26, 2017 07:08
-
-
Save mellinoe/488bfde9139da90d3d9a8914fcc657c8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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