Monday, September 11, 2006

Ebichu

So I'm still hacking away at the old map editor, DirectX is now playing nicely with windows forms. In fact this is such a useful thing to do that I may as well explain how (briefly).

Using DirectX, C#, Windows Forms, to create a non-flickering interactive form application that also has a render loop for DirectX.



Now there's a smooth, smooth title.



using System;


using System.Collections.Generic;


using System.Text;


using System.Windows.Forms;


using System.Runtime.InteropServices;


 


namespace MapEditor


{


    [StructLayout(LayoutKind.Sequential)]


    public struct Message


    {


        public IntPtr hWnd;


        public Int32 msg;


        public IntPtr wParam;


        public IntPtr lParam;


        public uint time;


        public System.Drawing.Point p;


    }


 


    delegate void LoopHandler();


 


    /// <summary>


    /// Sets up a render loop so DirectX bits are updated as often as possible.


    /// </summary>


    class RenderLoop


    {


        [System.Security.SuppressUnmanagedCodeSecurity]


        [DllImport("User32.dll", CharSet = CharSet.Auto)]


        public static extern bool PeekMessage(


            out Message msg,


            IntPtr hWnd,


            uint messageFilterMin,


            uint messageFilterMax,


            uint flags);


 


        public event LoopHandler Loop;


 


        private bool AppStillIdle


        {


            get


            {


                Message msg;


                return !PeekMessage(out msg,


                                    IntPtr.Zero,


                                    0, 0, 0);


            }


        }


 


        public RenderLoop(LoopHandler loopHandler)


        {


            Loop += loopHandler;


            Application.Idle += new EventHandler(OnApplicationIdle);


        }


 


        private void OnApplicationIdle(object sender, EventArgs e)


        {


            while (AppStillIdle)


            {


                Loop();


            }


        }


    }


 


 


}





Ok, so this is a slightly modified version of my render loop. The thing to notice is there's an event called Loop, this is where you want to attach a function that'll handle your DirectX rendering. Loop's called as often as possible, while the application's free. Yes, I know for a map editor I really should try and save cycles because it should play friendly with other programs but it's far easier doing this way. ( I haven't tested it after making modifications but I'm pretty sure it should work :D)

Anyway, we've got this wonderful looping class let's see how we'd use it in an application.



/// <summary>


/// The main entry point for the application.


/// </summary>


[STAThread]


static void Main()


{


    Application.EnableVisualStyles();


    Application.SetCompatibleTextRenderingDefault(false);


 


 


    Form form1 = new Form();


 


 


    RenderLoop renderLoop = new RenderLoop(delegate()


    {


        form1.Render();


    });


    Application.Run(form1);


}




So we create a new form and that form has a render method. This is where DirectX does it's stuff. DirectX might be updating a panel or something, or maybe you want rotating 3D cubes on your buttons - whatever, this is where that code goes.

We hook the form's Render method up the RenderLoop's Loop event. So the game loop now will call form1.Render each iteration. This is often called loose coupling, it's cool because I can use the class RenderLoop again and again in different projects, in different ways.

I hooked the loop to the Render method using an anonymous delegate. If you program C# and don't know what this is - learn it! Right now! Go! Here I use one so I can make use of the form1 variable, instead of having to make it static. I'll finish with an example render loop. *warning this is far more complicated than it has to be because it's ripped straight from my code and it's using swap chains)



internal void Render()


{


    if (swapChain.Device == null)


    {


        return;


    }


 


    Device device = swapChain.Device;


 


    device.SetRenderTarget(0, renderTarget);


    device.Clear(ClearFlags.Target, Color.Aquamarine, 0.0f, 1);


    device.BeginScene();


    device.EndScene();


    swapChain.Present(this);


}




Wow an actual post with some substance, it's been a while. Using the above code you could have a windows form which is partly directX. For instance a panel might show a £D model and by the panel are a number of buttons that make the model play various animations. This stuff is very useful for writing test programs and editors, or borrowing the Windows GUI if you don't fancy rolling your own.

Happy hacking.

No comments: