Yesterday in part 1 we assembled a list of elements we might want for a new avionics page to display a mini version of the heads up display.After a review and an early morning email from the accuracy police stationed in friendly places of the world I have since updated them.
Confidence is now high that the list is accurate and can form the basis of a simple specification for the new FLT flight data page. The next process is deciding which of these elements we want to add to the game. Are they necessary, sensitive, overly complicated for our design goal?
As it happens we can accommodate all of them. There's nothing ambitious and many elements have already been created in the heads-up display. To reach our goal of replicating this avionics page we'll need to copy the appearance, position and size of each element from our reference images. Then we'll add the functionality.
Let's remind ourselves of what we're looking to make...
|FLT page from a procedural desktop simulator|
Translating co-ordinates from source images to game
We want some positions for each element in the source material. First I'll touch on the co-ordinate system we use in game. For HUD elements and MPDs it's resolution independent. The graphics buffer is set such that the origin (0,0) is positioned in the centre using glOrtho(-1,1,1,-1,-5,5), the top left corner becomes (-1.0, 1.0). Bottom right becomes (1.0, -1.0). You can now express positions as percentages or offsets from the middle.
Note: If I had to do this all over again, I'd use actual percentages +-100.0 instead of +-1.0 as fractional accuracy in drivers vary, very small values negatively impact on quality.
To faciliate this, you can print out an image to a fized size, then overlay a sheet of acetate with a grid printed on it. I prefer to use Photoshop, crop the image to a square, size to 512x512. Turn on the ruler display and drag from the top-left corner of the ruler down to the middle of the image, position 255,255 this now adjust the ruler so that the origin is in the middle.
|resize to 512x512 and place ruler in the centre|
You can use this guide to pick off co-ordinates easily. To make things even better, you can change the ruler units to "percent". This gives you a -50% to +50% scale remembering to double the co-ordinates.
|same as above but ruler units set to percent|
Common to all MPD pages are the function key labels arranged around the outside of the instrument. The position aligns them with physical button locations on the device.
Different display devices may be used in aircraft across their lifetime as they are upgraded. As such these button positions may change. In Combat-Helo we're currently working with slightly different version of the display device in which the button locations won't match up to the above reference image. Howerver this is not a problem.
Compare the knobs the buttons of the first image with our Combat-Helo cockpit MPD device. I've annotated the internal buttons numbers.
Our MPD class references buttons by ID, an internal table stores physical button locations for the device so they can match up on a per device basis. If we want the "ENG" button label displayed for button #1 we simply issue a call to MFD.DrawButton(<string>,<id>,....flags) and the label will appear where it's supposed to for that device correctly aligned with any additional flourishes (e.g boxed, masked, arrowed).
This might be a good time to discuss messaging, it's a part of Leadwerks engine, and indeed many different engines today.
Communication (didn't) let me down
A key design element in Combat-Helo are objects that communicate via messaging. The Apache is a vehicle entity that acts as a a 'hub' for any objects it contains, these objects send data to the parent vehicle which in turn decides what to pass on.
Using messaging in games gained favour many years ago. As multi-player games grew popular, the number of possible messages rocketed, and with it the need to include lots of CONSTS and message IDs in #include files. Every time you added a new message you needed to recompile the whole project. Which for large games could take a long time. Using messaging potentially reduced the need to recompile everything. However there are no hard fast rules in game development, there are instances where we pass a data structure for sake of speed.
A message system cleans up communication between different kinds of entities. They just need to care about themselves and event handling. This is why we can walk up to an Apache and request to arm it while on foot, or request to board it, or start the APU when I click on the APU start switch.
Technically I could be on foot and somehow send a message to the Apache that I had hit the APU switch via the console interface. For the most part you can't do this as you don't have direct line of sight access to the switch unless you're in the rear cockpit, plus someone needs to be inside the Apache for an instance of the powerplant entity which controls the APU to run. Hey, it all works out.
So you know it's got something to do with messages, a cockpit has buttons, you click on them and some magic beans happen. Well we want a FLT page to appear when we click on some MPD buttons. How does the cockpit work?
Yup. Pretty good work. The smart part is in the layout, and this is why technical artists should perhaps be paid more than programmers.
Cockpit objects adopt a naming convention that allow for convenient name filtering for 3D pick testing. Object names are constructed as a "combined key" borrowed from old database techniques, their unique names locate them in a hierarchy, e.g PILOT_MPD_LEFT_SWITCH_11
This "combined key" is assigned to the cockpit element at the final build stage by the 3D modeller. As you might imagine with a complex object some errors did creep in early on. It's handy to have a cockpit test program an artist can use to do a model check, see the names of things.
A cockpit database contains these key names along with information on what type of control it is (knob, toggle, pushbutton, lamp etc.) and if it is a switch, how many steps, rotation limits, axis of rotation. Additionally a message string can be assigned, sent whenever a switch is operated without the need to write any logic for that switch, messages are sent to the parent vehicle. Only controls with a cockpit database entry trigger events.
FLT page - the easy parts
We'll start by opening up our MPD class source, locating the APACHEMPD class and adding a new method named DrawFLT(), this will contain all the OpenGL needed. In our class the buffer is ready for drawing by the time we get this far. The superclass Draw() method sets up everything needed to do OpenGL rendering with Leadwerks 3D engine. The only care we need to take is restoring the OpenGL() state at the end. Failure to do so may result in odd 3D engine artefacts or a GL_ERROR message.
We'll need a page mode for draw updates and message handling.
int MPDMODE_FLT = 12; // new FLT page
After adding suitable additions to SWTICH/CASE constructs in the Draw() ButtonHandler() and InputMenu() functions. We create our DrawFLT() method and add the following two lines...
DrawButton("ENG", 1, BUT_ARROW + BUT_SOLID);
DrawButton("FLT", 18, BUT_BOXED + BUT_SOLID) ;
DrawButton("FLT", 18, BUT_BOXED + BUT_SOLID) ;
Compile and run to test...
|FLT page in progress|
So far so good. The VID button to toggle the underlay and menu key are working out of the box. We'll add logic for page specific buttons after we're done adding the rest of the page elements. We'll finish this up by adding the rest of the menu buttons. Then we'll get to work on the header tape tomorrow.
Parting shot - Data driven vs. Compiled code
If there's a requirement for user modifcation there's no question that a data driven method of making cockpit instruments would be the way forward. There is one package that will compile instruments as a DLL called GLStudio used in some advanced training environments. Time and speed are the driving force here, since I'm just as fast programming as using visual packages I go with compiled code.
It would be great if we had a little API to make new instruments from a script and we could just add a datafile and voilla, new instrument. Plans for such a system in a later project exist. We already have a kind of API in the form of OpenGL, this is how we currently build instrumentation. It's fast, convenient and we even pre-compile some geometry on the display card. The vector font for example contains many HUD elements such as arrows, boxes, flight path markers stored as an OpenGL display list. Display lists are supported on small mobile devices like the iPhone and iPad so it's not too much of a stretch to port these instruments to external devices. I avoid using 3D engine specific code in the avionics as much as I can. There are some exceptions, for example using co-ordinate transforms to virtualise some symbols. Buffer and shader management are device/engine specific too.