Integrating Nuclex Framework GUI with Xen

Jan 20, 2011 at 4:53 PM
Edited Jan 20, 2011 at 4:56 PM

I'm trying to integrate the GUI system from Nuclex Framework (http://nuclexframework.codeplex.com) but I'm not sure how to get it to render on top of Xen, or how to pipe input from Xen's system to Nuclex's. I tried calling its guimanager.draw() method which draws using a spritebatch, but it doesn't seem to do anything. If anyone knows how to render a spritebatch using Xen that could be helpful. I'm thinking of maybe making it generate a series of SpriteElement and rendering those.

Jan 22, 2011 at 9:37 PM

A quick guess is that you might just be drawing over the GUI, check that the GUI draw call is performed last.

I use http://www.tomshane.cz/neoforce/ for drawing GUI's with Xen and it works great.  I wrote my own UI manager to integrate it and it works like a charm.  If your only using Nuclex for its GUI features, I would dump it and use NeoForce instead.  It'll probably use fewer resources especially since your not using much of what Nuclex expects you too.  I can probably scrounge up some sample code to integrate it with Xen if you want.  If you do give this a try, make sure you initialize it in your SetupGraphicsDeviceManager call or you'll get weird flickering results.

I've found that the trick to integrating other libraries with Xen is to just cast the Xen Application object to a Microsoft.Xna.Framework.Game, and then manually add the game components as you would with any normal XNA project.  Trying to modify them to integrate directly with Xen isn't worth the effort imo.  Use Xen for what it does well.

Jan 22, 2011 at 10:28 PM
Edited Jan 23, 2011 at 5:56 PM

Yeah, it sounds like neoforce would be easier to integrate. Your code would be much appreciated :D Here is what I'm working on incase anyone is interested: http://github.com/apeape/Xen-Game-Client

 

edit: I just found out about XPF, sounds like it might work out a bit nicer than neoforce since its still being actively developed. I still want to check out your code for integrating neoforce, though, since its probably applicable to XPF as well.

http://red-badger.com/product

Jan 24, 2011 at 7:04 PM

There is a lot of code that I wrote to make building complex controls and UI Animations easy so instead of posting a project, heres my UIManager class which basically integrates NeoForce with Xen.  You'll have to take the time to figure out whats important and whats not for you, but the whole system I've built works really well.

 

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using TomShane.Neoforce.Controls;
using Xen;
using Xen.Graphics;
using System.IO;
using Zephyr.UI.Controls;

namespace Zephyr.UI
{
    public class UIManager : ZephyrComponent, IUpdate
    {
        private const string CONSOLE = "Console";
        private const string POSTPROCESSOR = "Post Processor";
		private const string EDITORUI = "Editor UI";

        public TomShane.Neoforce.Controls.Manager manager;
        private SpriteBatch sprite;
        GraphicsDeviceManager graphics;
        MainTabHost mainTabHost;
        public static List<Control> ControlHosts = new List<Control>();
        
#if WINDOWS
		EditableListViewer editableListViewer; public EditableListViewer EditableListViewer { get { return this.editableListViewer; } }
		ParameterEditor paramEditor; public ParameterEditor ParamEditor { get { return this.paramEditor; } }
#endif
		EditorUI editorUI;

        public UIManager(Game game, GraphicsDeviceManager graphics, DrawTarget drawTo, UpdateManager updateManager, ContentRegister contentRegister)
            : base(drawTo, updateManager, contentRegister)
        {
            this.graphics = graphics;

            manager = new TomShane.Neoforce.Controls.Manager(game, graphics, "Default", false);
            manager.RenderTargetUsage = RenderTargetUsage.PlatformContents;
            
            graphics.SynchronizeWithVerticalRetrace = false;

            manager.SkinDirectory = @"..\..\..\..\Skins\";
            manager.AutoUnfocus = false;

#if WINDOWS
			game.IsMouseVisible = Settings.AlwaysDisplayMouse;
#endif
        }

        public override void Initialise(DrawTarget drawTo, UpdateManager updateManager, ContentRegister contentRegister)
        {
            // Create sprite batch for ploting texture with rendered UI.
            sprite = new SpriteBatch(graphics.GraphicsDevice);

            // Initialize manager.
            manager.Initialize();

            // Create and assign render target for UI rendering.
            // manager.RenderTarget = new RenderTarget2D(this.graphics.GraphicsDevice, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight, 1, SurfaceFormat.Color, manager.RenderTargetUsage);
            // Maximum of frames we want to render per second (applies only for Neoforce, not Game itself)
            manager.TargetFrames = 60;

            mainTabHost = new MainTabHost(manager);
            InitializeConsoleInterfaceTab();
            InitializePostProcessorInterfaceTab();

            //manager.Add(new ControlHost(manager));
            //manager.Add(new UIAnimateTest(manager));
			//new MenuTest(manager);

#if WINDOWS
			if (Settings.Editing)
			{
				editableListViewer = new EditableListViewer(manager);
				editableListViewer.IsClosed = true;
				manager.Add(editableListViewer);

				paramEditor = new ParameterEditor(manager);
				paramEditor.IsClosed = true;
				manager.Add(paramEditor);

				InitializeEditorUITab();
			}
#endif

			manager.Add(mainTabHost.Control);


            updateManager.Add(this);
        }


		private void InitializeEditorUITab()
		{
			mainTabHost.AddPage(EDITORUI);
			editorUI = new EditorUI(manager, mainTabHost.GetPage(EDITORUI));

			editorUI.editableListButton.Click += new TomShane.Neoforce.Controls.EventHandler(editableListButton_Click);
			editorUI.paramEditorButton.Click += new TomShane.Neoforce.Controls.EventHandler(paramEditorButton_Click);
		}

		/// <summary>
		/// Toggles the Parameter Editor UI. Handles the Click event of the paramEditorButton control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="TomShane.Neoforce.Controls.EventArgs"/> instance containing the event data.</param>
		void paramEditorButton_Click(object sender, TomShane.Neoforce.Controls.EventArgs e)
		{
			if (Settings.Editing)
			{
				ParamEditor.IsClosed = !ParamEditor.IsClosed;
			}
		}

		/// <summary>
		/// Toggles the List Viewer UI for Editables. Handles the Click event of the editableListButton control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">The <see cref="TomShane.Neoforce.Controls.EventArgs"/> instance containing the event data.</param>
		void editableListButton_Click(object sender, TomShane.Neoforce.Controls.EventArgs e)
		{
			if (Settings.Editing)
			{
				EditableListViewer.IsClosed = !EditableListViewer.IsClosed;
			}
		}



        /// <summary>
        /// Initializes the console interface tab.
        /// </summary>
        private void InitializeConsoleInterfaceTab()
        {
            mainTabHost.AddPage(CONSOLE);
            Controls.Console console = new Zephyr.UI.Controls.Console(manager);
            Global.Console = console;
            console.Initialize(mainTabHost.GetPage(CONSOLE));
            this.ConsoleEnabled += new ConsoleEvent(console.ConsoleEnabled);
            this.ConsoleDisabled += new ConsoleEvent(console.ConsoleDisabled);
        }

        private void InitializePostProcessorInterfaceTab()
        {
            mainTabHost.AddPage(POSTPROCESSOR);
            new PostProcessorUI(manager, mainTabHost.GetPage(POSTPROCESSOR));
        }

        public UpdateFrequency Update(UpdateState state)
        {
			if (!Enabled || !Global.AcceptInput)
				return UpdateFrequency.OncePerFrame;

            GameTime gametime = new GameTime(new TimeSpan(state.TotalTimeTicks), new TimeSpan(state.TotalRealTimeTicks), new TimeSpan(state.DeltaTimeTicks), new TimeSpan(state.DeltaTimeTicks), false);

            manager.Update(gametime); // UI Updates

			if (state.KeyboardState.KeyState.OemTilde.OnPressed)
			{
				SetConsoleEnabled(!mainTabHost.IsClosed, false);
			}
            Zephyr.Paths.Animator.UpdateAllAnimators(state.DeltaTimeSeconds);

            return UpdateFrequency.OncePerFrame;
        }
		
		public void SetConsoleEnabled(bool state, bool displayConsolePage)
		{
			mainTabHost.IsClosed = state;

            if (mainTabHost.Control.Enabled && ConsoleEnabled != null)
                ConsoleEnabled(this, new System.EventArgs());
            else if (!mainTabHost.Control.Enabled && ConsoleDisabled != null)
                ConsoleDisabled(this, new System.EventArgs());

            if(Camera.BaseCamera.LockCursor)
                Camera.BaseCamera.CenterCursor();

			if (displayConsolePage)
				mainTabHost.Control.SelectedPage = mainTabHost.GetPage(CONSOLE);
		}


        public void Draw(DrawState state)
        {
            if (!this.Enabled)
                return;

            GameTime gametime = new GameTime(new TimeSpan(state.TotalTimeTicks), new TimeSpan(state.TotalRealTimeTicks), new TimeSpan(state.DeltaTimeTicks), new TimeSpan(state.DeltaTimeTicks), false);
            state.BeginGetGraphicsDevice(Xen.Graphics.State.StateFlag.None);

            manager.BeginDraw(gametime);

            manager.EndDraw();
            state.EndGetGraphicsDevice(Xen.Graphics.State.StateFlag.None);
        }

        public static bool OverControlHost(Xen.Input.State.MouseInputState mouseState)
        {
            foreach (TomShane.Neoforce.Controls.Control c in ControlHosts)
            {
                if (c.Enabled && c.Visible && c.IsWithin(mouseState))
                    return true;
            }
            return false;
        }
#region Events
        // Instances of delegate event.
        public delegate void ConsoleEvent(object sender, System.EventArgs e);

		public ConsoleEvent ConsoleEnabled;
		public ConsoleEvent ConsoleDisabled;
#endregion


    }
}

 

 

Heres how to integrate it with your Xen Application Class

Add a line to the draw call, I want it to be unaffected by any post processing effects, so I draw it last.

protected override void Draw(DrawState state)
{

        state.SetShaderGlobal("fTimer", state.TotalTimeSeconds);

        drawToTexture.Draw(state);

        Global.PostProcessor.Draw(state);

        drawToScreen.Draw(state);

        Global.UIManager.Draw(state); // <---------------------
 }

 

And then I initialize it in my LoadContent method for my Application class.

protected override void LoadContent(DrawState state, Microsoft.Xna.Framework.Content.ContentManager contentManager)
        {
			//... Lots of other crap

			Global.UIManager.Initialise(drawToTexture, this.UpdateManager, this.Content);

			//... Lots of other crap
	}

Its also important to Create the UIManager in the SetupGraphicsDevice method or youll get some strange effects.

public void SetupGraphicsDeviceManager_Zephyr(GraphicsDeviceManager graphics)
        {
            if (graphics == null)
                return;
            ZephyrApplication.graphicsDeviceManager = graphics;

            Global.UIManager = new UI.UIManager(Global.Game, graphics, null, null, null);
            
            graphics.PreferredDepthStencilFormat = Settings.PreferredDepthStencilFormat;
            graphics.PreferredBackBufferWidth = Settings.ResolutionWidth;
            graphics.PreferredBackBufferHeight = Settings.ResolutionHeight;
            graphics.PreferMultiSampling = Settings.MultiSampling;
            graphics.IsFullScreen = Settings.Instance.FullScreen;
            
            graphics.ApplyChanges();

            
        }

And finally, heres a sample control.

using System;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using TomShane.Neoforce.Controls;
using Xen;
using System.IO;
using System.Collections;
using Zephyr.PostProcessing;
using Zephyr.Timing;
using Zephyr.Paths;

namespace Zephyr.UI.Controls
{
	class MenuTest
	{
		private AnimatedControlGroup controls;
		private Manager manager;
        
		public MenuTest(Manager manager)
		{
			this.manager = manager;
			controls = new AnimatedControlGroup();
            

			Initialize();
		}

		private void Initialize()
		{
			Button button = new Button(manager);
			button.Left = 500;
			button.Top = 200;
			button.Width = 120; //Helper.Text.MeasureConsoleString(manager, change.Name) * 2;
			button.Height = 30;
			button.Text = "Continue Game";
			button.Movable = true;
			button.Init();
			manager.Add(button);
			controls.Add(button);

			button = new Button(manager);
			button.Left = 500;
			button.Top = 250;
			button.Width = 120; //Helper.Text.MeasureConsoleString(manager, change.Name) * 2;
			button.Height = 30;
			button.Text = "New Game";
			button.Movable = true;
			button.Init();
			manager.Add(button);
			controls.Add(button);

			button = new Button(manager);
			button.Left = 500;
			button.Top = 300;
			button.Width = 120; //Helper.Text.MeasureConsoleString(manager, change.Name) * 2;
			button.Height = 30;
			button.Text = "Options";
			button.Movable = true;
			button.Init();
			manager.Add(button);
			controls.Add(button);
		}

	}
}

Hope that helps

Jan 25, 2011 at 12:26 AM

Thanks a lot, I'll try it out