From ef9814253ce19e9da26a3f7523983d0566aa89e9 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 17 Feb 2019 01:13:43 -0800 Subject: [PATCH] switch to new ImGuiManager --- .vscode/tasks.json | 5 +- project_name/Game1.cs | 8 +- .../ImGui/ImGuiFinalRenderDelegate.cs | 95 -------- project_name/ImGui/ImGuiManager.cs | 221 ++++++++++++++++++ project_name/ImGui/ImGuiRenderer.cs | 16 +- 5 files changed, 235 insertions(+), 110 deletions(-) delete mode 100644 project_name/ImGui/ImGuiFinalRenderDelegate.cs create mode 100644 project_name/ImGui/ImGuiManager.cs diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d609b37..3d8b600 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -97,10 +97,7 @@ "label": "Build Content", "type": "shell", "group": "build", - "command": "mono /Applications/Pipeline.app/Contents/MonoBundle/MGCB.exe /@:Content.mgcb", - "options": { - "cwd": "${workspaceFolder}/project_name/CompiledContent" - }, + "command": "msbuild /t:BuildContent", "problemMatcher": "$msCompile" }, diff --git a/project_name/Game1.cs b/project_name/Game1.cs index 45d22e3..e474f16 100644 --- a/project_name/Game1.cs +++ b/project_name/Game1.cs @@ -18,9 +18,6 @@ namespace project_name // setup a Scene so we have something to show var newScene = new Scene(); newScene.addRenderer(new DefaultRenderer()); - - // optionally render Nez in an ImGui window - newScene.finalRenderDelegate = new ImGuiFinalRenderDelegate(); var logo = newScene.content.Load("nez-logo-black"); newScene.createEntity("logo") @@ -28,6 +25,11 @@ namespace project_name .addComponent(new Nez.Sprites.Sprite(logo)); scene = newScene; + + // optionally render Nez in an ImGui window + var imGuiManager = new ImGuiManager(); + Core.registerGlobalManager( imGuiManager ); + imGuiManager.setEnabled( true ); } } } \ No newline at end of file diff --git a/project_name/ImGui/ImGuiFinalRenderDelegate.cs b/project_name/ImGui/ImGuiFinalRenderDelegate.cs deleted file mode 100644 index 7853005..0000000 --- a/project_name/ImGui/ImGuiFinalRenderDelegate.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Reflection; -using ImGuiNET; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace Nez -{ - public class ImGuiFinalRenderDelegate : IFinalRenderDelegate - { - public Scene scene { get; set; } - - ImGuiRenderer _imGuiRenderer; - RenderTarget2D _lastRenderTarget; - IntPtr _renderTargetId; - - public ImGuiFinalRenderDelegate() - { - _imGuiRenderer = new ImGuiRenderer( Core.instance ); - _imGuiRenderer.rebuildFontAtlas(); - ImGui.GetIO().ConfigWindowsMoveFromTitleBarOnly = true; - } - - [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; - } - - void layoutGui() - { - ImGui.ShowDemoWindow(); - - 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" ); - - 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/ImGuiManager.cs b/project_name/ImGui/ImGuiManager.cs new file mode 100644 index 0000000..d7a4b8f --- /dev/null +++ b/project_name/ImGui/ImGuiManager.cs @@ -0,0 +1,221 @@ +using Nez; +using ImGuiNET; +using Microsoft.Xna.Framework; +using System; +using Microsoft.Xna.Framework.Graphics; +using System.Collections.Generic; + +namespace Nez +{ + public class ImGuiManager : GlobalManager, IFinalRenderDelegate, IDisposable + { + public Scene scene { get; set; } + public List drawCommands = new List(); + public ImGuiRenderer renderer { get; private set; } + + RenderTarget2D _lastRenderTarget; + IntPtr _renderTargetId; + bool _isGameWindowFocused; + + public ImGuiManager( string pathToFontFile, float fontSizePixels ) + { + renderer = new ImGuiRenderer( Core.instance ); + + if( pathToFontFile != null ) + ImGui.GetIO().Fonts.AddFontFromFileTTF( pathToFontFile, fontSizePixels ); + + renderer.rebuildFontAtlas(); + + Core.emitter.addObserver( CoreEvents.SceneChanged, onSceneChanged ); + } + + public ImGuiManager() : this( null, -1 ) + {} + + void onSceneChanged() + { + // when the Scene changes we need to rewire ourselves up as the IFinalRenderDelegate in the new Scene + // if we were previously enabled + drawCommands.Clear(); + if( enabled ) + onEnabled(); + } + + /// + /// this is where we issue any and all ImGui commands to be drawn + /// + void layoutGui() + { + for( var i = drawCommands.Count - 1; i >= 0; i-- ) + drawCommands[i](); + + ImGui.ShowDemoWindow(); + + if( _lastRenderTarget == null ) + return; + + 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" ); + _isGameWindowFocused = ImGui.IsWindowFocused(); + + //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 Public API + + /// + /// registers an Action that will be called and any ImGui drawing can be done in it + /// + /// + public void registerDrawCommand( Action drawCommand ) + { + drawCommands.Add( drawCommand ); + } + + /// + /// removes the Action from the draw commands + /// + /// + public void unregisterDrawCommand( Action drawCommand ) + { + drawCommands.Remove( drawCommand ); + } + + /// + /// adds the font to the atlas and regenerates it + /// + /// + /// + /// + public ImFontPtr addFontAndRegenerateAtlas( string pathToFontFile, float fontSizePixels ) + { + var fontPtr = ImGui.GetIO().Fonts.AddFontFromFileTTF( pathToFontFile, fontSizePixels ); + renderer.rebuildFontAtlas(); + return fontPtr; + } + + #endregion + + #region GlobalManager Lifecycle + + public override void onEnabled() + { + Core.scene.finalRenderDelegate = this; + } + + public override void onDisabled() + { + Core.scene.finalRenderDelegate = null; + } + + public override void update() + { + renderer.beforeLayout( Time.deltaTime ); + layoutGui(); + } + + #endregion + + #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 ) + renderer.unbindTexture( _renderTargetId ); + + // bind the new texture + _lastRenderTarget = source; + _renderTargetId = renderer.bindTexture( source ); + } + + Core.graphicsDevice.setRenderTarget( finalRenderTarget ); + Core.graphicsDevice.Clear( letterboxColor ); + + renderer.afterLayout(); + } + + public void onAddedToScene() + { } + + public void onSceneBackBufferSizeChanged( int newWidth, int newHeight ) + { } + + public void unload() + { } + + #endregion + + #region IDisposable Support + + bool _isDisposed = false; // To detect redundant calls + + protected virtual void Dispose( bool disposing ) + { + if( !_isDisposed ) + { + if( disposing ) + { + Core.emitter.removeObserver( CoreEvents.SceneChanged, onSceneChanged ); + } + + _isDisposed = true; + } + } + + void IDisposable.Dispose() + { + Dispose( true ); + } + + #endregion + + [Console.Command( "toggle-imgui", "Toggles the Dear ImGui renderer" )] + static void toggleImGui() + { + // install the service if it isnt already there + var service = Core.getGlobalManager(); + if( service == null ) + { + service = new ImGuiManager(); + Core.registerGlobalManager( service ); + } + + service.setEnabled( !service.enabled ); + } + + } +} \ No newline at end of file diff --git a/project_name/ImGui/ImGuiRenderer.cs b/project_name/ImGui/ImGuiRenderer.cs index 41c7523..0905145 100644 --- a/project_name/ImGui/ImGuiRenderer.cs +++ b/project_name/ImGui/ImGuiRenderer.cs @@ -9,10 +9,12 @@ using System.Runtime.InteropServices; namespace ImGuiNET { /// - /// ImGui renderer for use with XNA-likes (FNA and MonoGame) + /// ImGui renderer for use with XNA-likes (FNA & MonoGame) /// public class ImGuiRenderer { + public ImFontPtr defaultFontPtr { get; private set; } + // Graphics BasicEffect _effect; RasterizerState _rasterizerState; @@ -29,7 +31,7 @@ namespace ImGuiNET int _indexBufferSize; // Textures - Dictionary _loadedTextures; + Dictionary _loadedTextures = new Dictionary(); int _textureId; IntPtr? _fontTextureId; @@ -52,10 +54,7 @@ namespace ImGuiNET new VertexElement( 16, VertexElementFormat.Color, VertexElementUsage.Color, 0 ) ); - var context = ImGui.CreateContext(); - ImGui.SetCurrentContext( context ); - - _loadedTextures = new Dictionary(); + ImGui.SetCurrentContext( ImGui.CreateContext() ); _rasterizerState = new RasterizerState() { @@ -79,6 +78,9 @@ namespace ImGuiNET { // Get font texture from ImGui var io = ImGui.GetIO(); + + defaultFontPtr = ImGui.GetIO().Fonts.AddFontDefault(); + io.Fonts.GetTexDataAsRGBA32( out byte* pixelData, out int width, out int height, out int bytesPerPixel ); // Copy the data to a managed array @@ -187,8 +189,6 @@ namespace ImGuiNET ImGui.GetIO().AddInputCharacter( c ); }; /////////////////////////////////////////// - - ImGui.GetIO().Fonts.AddFontDefault(); } ///