C3301Games Development 2101Adding a GUI to a DirectX Application
In this worksheet we look at the steps to put a high performance graphics app into a Windows form, and allow control of the graphics settings using Windows controls. The exercise is related to the Maths and Technologies for Games GPU particle system lab
Getting Started / Overview
· Go to the module page (CO3301). Download the files for this week from the materials page. Extract the contents of the zip file to a work folder and open the Visual Studio project ‘ParticleTool.sln’.
· If necessary first switch the project to the x86 platform (from x64). Then compile the project before editing it. The forms are not editable until they are compiled. So go to Build->Clean Solution, then Build->Rebuild Solution
· Run the project. You will see a GPU-based particle system running in a simple form. Close the program.
The source code is split into two sections in Solution Explorer:
- The Windows UI folder: This contains the code to create the form and respond to form events. It uses standard managed C++ .NET programming.
- The Graphics Code folder: This contains the DirectX application to render the particle system. The code is almost unchanged from the standalone version.
Make a New Panel
· Go into the Windows UI->Header Files folder, and double click ParticleForm.h. The form should appear in the form designer
· View the toolbox. If its not visible, choose View->Toolbox
· Open the Containers section in the Toolbox:
o Drag a Panel somewhere into the right blank area of the form
o In the Properties panel (if not visible, select View->Properties), change:
§ BorderStyle to “Fixed3D”
§ Design->(name) to "newPanel"
§ Design->Modifiers to "Public".
· Now go to Source Files->ParticleTool.cpp
· Spend some time looking at the three "| INFO |" comments, which explain how the standard forms application has been adjusted to initialise, update and shutdown the 3D application in the panel.
· Now make a simple change in the file to have the particle system render into the new panel you created.
Adding Buttons
We can use buttons to trigger functions in our graphics code:
· Change back to using the original panel and delete your new panel
· Again double click ParticleForm.h to go to the form editor.
o Double click the Exit button. See how it takes you to the event code in the associated .h file
· Go back to the form editor and add a "Reset" button next to the "Exit" button:
o Change the Text and (name) properties. The Text property is what the user sees, the (name) property is the variable name for the button in the code
· Double click the button to create an "OnClick" event for it. Make this event function call the ResetParticles function from the DX10Particles.cpp file
o Access the function using an extern in the same way as you should have seen earlier in ParticleTool.cpp
o This is not an ideal way to do things, but we are aiming for simplicity here
· When done correctly, you should be able to reset all the particles to their original position with the new button
Further Controls
We can control variables in our app with edit boxes, track bars and similar components:
· In the toolbox, open "All Windows Forms" and scroll down to "TrackBar". Drag a track bar onto the form below the two buttons. Widen it somewhat
· In its properties carefully set:
TickFrequency = 10000
LargeChange = 1000
Maximum = 200000
Minimum = 100
SmallChange = 100
Value = 100000
· On the Properties panel click the lightning bolt – showing possible trackbar events
· Double-click in the blank to the right of ValueChanged, near the top of the list.
· This will create an event function. Use it to set NumParticles in DX10Particles.cpp (use an extern) to the trackbar's value (lookup/set the trackbar's name and use experience/common sense - it's easy). Also reset the particles as you did earlier
This gives a flavour of running DirectX in a form, and accessing functions and data. It would be nicer to use an OO approach - maybe create a GraphicsManager class that acts as an interface between form and DirectX code.
· Add further controls as you wish. An edit box for the number of particles is good practice, as is adding labels for clarity
Note the remainder of this worksheet is not to be done in the lab. It is guidance for those who want to build their own Windows forms based DirectX applications from scratch. This might be useful for projects (e.g. creating an editors).
Adding GUIs to other Applications (information, not lab work)
1. Create Project
· Create new empty C++ CLR project called say, "TestProject". Ensure you first uncheck "Create directory for solution"
· Right-click the project and choose Properties.
· Select All Configurations
· In Linker->System, choose Windows (/SUBSYSTEM:WINDOWS)
· In Linker->Advanced, type Main in "Entry Point"
· Press OK
· Right-click project and "Add..." a new form (UI->Windows Form), called say "TestForm"
· An error window will show up. You must close this window.
· Edit TestForm.cpp and change it to these lines, then build/run the project. Nothing will happen yet (this code assumes you used the names given above, change the code if necessary).
#include "TestForm.h"
using namespace System;
using namespace System::Windows::Forms;
[STAThread]
void Main(array<String^>^ args) {}
2. Add a Render Panel to the Form
· Next double-click the form (TestForm.h), or right-click and choose "View Designer" (if an error window is showing, close it and try again)
· Open the Toolbox on the left, or in View->Toolbox
· Open Containers, and drag-drop a "Panel" onto the form
· Select the Panel and look at the properties pane (or press F4)
· Change the name of the panel to renderPanel, and change Modifiers to "Public"
3. Add Main Entry Point Code
· Edit TestProject.cpp again and replace it with something like this. You might want different externs but this is the general idea.
#include <windows.h>
#include "TestForm.h"
using namespace System;
using namespace System::Windows::Forms;
// Add any externs you need here. Change these as necessary
extern bool D3DSetup(HWND hWnd);
extern void D3DShutdown();
extern bool SceneSetup();
extern void SceneShutdown();
extern void RenderScene();
extern void UpdateScene();
// Declaration of idle event for the render/update loop (see below)
void OnApplicationIdle(Object^ sender, EventArgs^ e);
[STAThread]
void Main(array<String^>^ args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
TestProject::TestForm form;
if (D3DSetup((HWND)(form.renderPanel->Handle.ToPointer()))) {
if (SceneSetup()) {
// Call OnApplicationIdle henever the form is idle
Application::Idle += gcnew EventHandler(OnApplicationIdle);
// Execute the form
Application::Run(%form);
SceneShutdown();
}
D3DShutdown();
}
}
void OnApplicationIdle(Object^ sender, EventArgs^ e) {
MSG msg;
while (!PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
RenderScene();
UpdateScene();
}
}
4. Create Folders on Harddrive and Filters (folders) in Visual Studio
· Right click project and add new filter (basically a folder), rename it to "Windows UI"
· Drag all project contents except "References" and "External Dependencies" into that folder
· Now you copy all your DirectX graphics code into your project folder (the actual Windows folder, not just a filter in Visual Studio)
· Put the code in a sub-folder if you want to make it tidy. If you do, be careful on the next steps as your code will now be in a different location relative to the project
· Create another filter in the project called "Graphics Code" and add all your DirectX based code into that (note filters in Visual Studio don't need to match actual folders)
5. Copy Include and Library Settings
· In project properties Linker->Input->Additional Dependencies, add user32.lib and any other libraries that your graphics project uses (e.g. DirectX libs)
· In project properties C++->General->Additional Include Directories add any additional include folders from your original project
- If you created a code sub-folder, then you need to add that sub-folder to each directory you add here
· In the same way add any Linker->General->Additional Library Directories that are required (maybe none)
6. Final Tweaks
· Windows UI projects default to Unicode strings, if your project uses multi-byte strings you will get many string related errors. Change the setting in the new project properties under "General".
· If you used a subfolder for your graphics code then the program won't find things like textures or shaders when you run and it will fail. You can solve this in several ways. You can update the folders used in the code for loading. You can copy all the relevant files into the main project folder. Or you can make the project target (the executable) appear in the graphics code sub-folder and make sure it runs from there (do this in the project settings).
- Choice is up to you - the lab code has the media in the main project folder, but changes the path for shader loading so they can stay in the sub-folder.
Causes of Crashes:
· Forms applications seem to crash if your project uses atlbase.h and CA2CT calls to get round unicode / multi-byte string conversion. You may have inherited such code. Remove all of these and instead change the project default character set under the project properties->General
· Global variables (or class static member variables) that require construction cause problems if the constructor uses Windows functions. Make the variable a non-static member or a local variable or a singleton or something. Global pointers are OK as they cause no construction until you allocate them. The CTimer class is an example a class that can cause these problems - the lab project works around it.
C3301 - Games Development 2, Week 10 / Lab Worksheet 1-4