Export (0) Print
Expand All

Drawing Gauges Using GDI+

Microsoft ESP Technical Articles:

Drawing Gauges Using GDI+

Jessica Zorich

Microsoft Corporation

October 2008

Significance of the Sample

The Drawing Gauges Using GDI+ code sample demonstrates one method of creating a custom C++ gauge in Microsoft ESP without using the XML gauge system. This sample lays the groundwork for many practical applications of C++ gauges and the Panels and Gauges SDK, some examples of which include:

  • Access to the complete suite of GDI+ drawing facilities for gauge display
  • Drawing bitmaps to a gauge from shared memory for:
    • Compatibility with a variety of graphics APIs
    • Use with any external source of bitmap images

Read more about the extensibility of this sample below, in the Do More with Drawing Gauges Using GDI+ section.

Functionality of the Sample

The gauges provided in the Drawing Gauges Using GDI+ code sample do the following:

  • Retrieve real-time data from a running simulation
  • Interpret that data, and
  • Represent the data graphically in several ways

There is an opaque and a transparent version of the gauge that both do the following:

  • Display lat/long/alt and heading data as strings
  • Represent the throttle percentage as a string and as a scalable rectangle

The opaque version of the gauge represents heading graphically using an arrow image to display a heading indicator. The transparent version does the same, with the addition of utilizing the heading arrow’s alpha channel. This allows the arrow to appear without a rectangular bounding box. Thus, the simulated world view shows through, creating a clean transparency appearance. For a screenshot example of the running sample, see Figure 1.

Applies To

The guidance in this article applies to the following Microsoft products:

  • Microsoft ESP 1.0
  • Microsoft Flight Simulator X

Requirements

To build the C++ sample solution provided, one of the following is required:

Three Methods for Developing Custom Gauges

The method of gauge development used in this sample, pure C++, is only one of three available methods.

The XML gauge development method is recommended for most gauge needs since it does not involve compiled code or the use of DLL files. Additionally, the ACE resource tool (included with the Panels and Gauges SDK) provides a GUI for creating and modifying XML gauges. If the flexibility of XML gauges meets the needs of the situation, we suggest using this method.

An intermediate method between pure C++ and XML is to create a Mixed Mode gauge, one that uses XML for the front end and C++ for the back end. This alternative provides the convenience of the ACE tool for building the gauge, as well as the power of C++ for control of the back end processing. The Cabin Comfort sample demonstrates the Mixed Mode method.

For developers, the pure C++ approach provides maximum control and flexibility of gauge rendering. One advantage that pure C++ gauges have over XML gauges is the ability to alpha blend gauge elements with the simulation world view. A final note on choosing to develop gauges with C++ is that there may be performance considerations associated with this method.

Token Variables vs. SimConnect

All of the simulation variables that are accessed by Drawing Gauges Using GDI+ are in fact accessible through the token variable system (by deriving from the IGaugeCCallback class provided in gauges.h). The Cabin Comfort sample accesses simulation data in this way. The Drawing Gauges Using GDI+ sample shows how SimConnect can be an alternative to token variables in the gauge development process.

Installing and Running the Sample

This section describes the setup tasks required to get Drawing Gauges Using GDI+ running within a simulation.

  1. Download and extract the complete source code and resource files
  2. Include gauges.h and SimConnect.h, and link to SimConnect.lib
    • In Visual Studio, go to Tools->Options->Projects and Solutions->VC++ Directories
      • From the “Show directories for:” drop-down list, choose “Include files”
        • Add the path to the ESP\1.0\SDK\SimObject Creation Kit\Panels and Gauges SDK\inc directory on your system
        • Add the path to the ESP\1.0\SDK\Core Utilities Kit\SimConnect SDK\inc directory on your system
      • From the “Show directories for:” drop-down list, choose “Library files”
        • Add the path to the ESP\1.0\SDK\Core Utilities Kit\SimConnect SDK\lib directory on your system
  3. Build the provided solution and copy the resulting GDIPlus_Gauge.dll and the following image resources to the local ESP\1.0\Gauges directory
    • HeadingBackground.jpg
    • HeadingArrow.png
    • gauge_background.bmp
  4. Backup the existing panel.cfg file and copy the provided panel.cfg file into the local ESP\1.0\SimObjects\Airplanes\C172\panel.G1000 directory
  5. Run ESP and go to Aircraft->Select Aircraft; choose the Cessna C172SP Skyhawk G1000 Glass Cockpit
  6. Choose Run on the security dialog box that appears
  7. Choose “Yes” to automatically load the gauge DLL each time a panel.cfg specifies it, or choose “No” to be prompted each time
  8. Toggle the Opaque and Transparent Gauges on
    • Choose Views->Instrument Panel->”Opaque Sample Gauge”
    • Choose Views->Instrument Panel->”Transparent Sample Gauge”

Note that an instance of the Opaque Sample Gauge automatically appears where the Glass Cockpit gauge would normally be. This is specified by the provided panel.cfg file. The next section describes this and other changes to panel.cfg.

Drawing Gauges Using GDI Pllus Screenshot

Figure 1. Screenshot of the running Drawing Gauges Using GDI+ code sample. The Transparent Sample Gauge is shown in the upper right. The Opaque Sample Gauge is shown embedded in the Virtual Cockpit.

Modifying the panel.cfg File

If you choose to use the provided panel.cfg as-is with the Cessna C172SP Skyhawk G1000 Glass Cockpit aircraft, you may skip this section.

The gauges provided in this sample can be added to any aircraft installed on your system. The following is a list of the changes made to the panel.cfg for the Cessna C172SP Skyhawk G1000 Glass Cockpit. Using this as a guide, any aircraft’s panel.cfg file can be modified to accommodate the Transparent Sample Gauge and Opaque Sample Gauge.

[Window Titles]

Add the two new gauges as new Instrument Panel views to the [Window Titles] section:

  • Window09=Opaque Sample Gauge
  • Window10=Transparent Sample Gauge

[Window00]

Since the 2D panel of the Cessna C172SP Skyhawk G1000 Glass Cockpit has existing gauges, we can replace one of these gauges with an instance of the Opaque Sample Gauge. This is accomplished by changing the [Window00] section as follows:

  • [Window 00] file_1024=c172_g1000_background.bmpfile_1024_night=c172_g1000_background_night.bmp size_mm=1024 position=7 visible=1 no_luminous=1 ident=MAIN_PANEL zorder=0 //gauge00=G1000!G1000_PFD, 9, 276,729,488 gauge00=GDIPlus_Gauge!OpaqueGauge ,9, 276,729,488 gauge01=G1000!audio_panel, 744, 276, 95,488 gauge02=G1000!MFD_C172_LeftSide, 846, 276,191,489 gauge03=SimIcons!Kneeboard Icon, 16, 540, 20, 20 gauge04=SimIcons!ATC Icon, 36, 540, 20, 20 gauge05=SimIcons!Map Icon, 56, 540, 20, 20 gauge06=SimIcons!GPS Icon, 16, 560, 20, 20 gauge07=SimIcons1024!Landing Icon, 36, 560, 20, 20 gauge08=n_number_plaque!n_number_plaque, 892,221 windowsize_ratio=1.000 window_pos=0.0,0.0 window_size=1.000,1.000

[WindowXX]

Add new [WindowXX] entries for each of the two gauges:

  • [Window09] size_mm=440,440 window_size=0.4 position=9 Background_color=16,16,16 VISIBLE=1 ident=MISC_POPUP_1 zorder=4 gauge00=GDIPlus_Gauge!OpaqueGauge,0,0,440,440
  • [Window10] size_mm=456,378 window_size=0.5 position=10 BACKGROUND_COLOR=0,0,0 alpha_blend=0.95 VISIBLE=1 ident=MISC_POPUP_2 zorder=4 gauge00=GDIPlus_Gauge!TransparentGauge, 0, 0, 456, 378

[VCockpitXX]

Since the virtual cockpit of the Cessna C172SP Skyhawk G1000 Glass Cockpit has existing gauges, we can replace one of these gauges with an instance of the Opaque Sample Gauge. This is accomplished by changing a [VCockpitXX] section as follows:

  • [VCockpit01] size_mm=1024,1024 pixel_size=1024,1024 texture=$C172_G1000 background_color=42,42,40 gauge00=G1000!MFD_c172, 0,0,765,500 gauge01=G1000!audio_panel, 779,513,97,511 //gauge02=G1000!G1000_PFD, 0,514,765,500 gauge02=GDIPlus_Gauge!OpaqueGauge ,100,550,555,410

For information about panel.cfg parameters, please see the SDK document Panel Configuration Files.

Analysis of the Sample Code

Overview

The four aspects of the sample discussed in this article are:

  • DLL Initialization
  • Gauge Exports and Declarations
  • Gauge Callbacks–Gauge Class Instantiation
  • Gauge Callbacks–Processing and Drawing

Touching briefly on the above topics, the following is an overview of how the in-process DLL interacts with a running simulation. Refer to the complete code and the Figure 2 diagram as you follow along. Note that the numbers in the following list correspond to those in Figure 2.

1) DLL Initialization

The simulation Panel Service initializes the DLL when an aircraft whose panel.cfg has been modified to reference the DLL gauges is loaded. The sample instantiates a singleton class for SimConnect data storage and retrieval.

2) Gauge Exports and Declarations

Gauge headers are declared, which includes pointers to the appropriate callback functions. These headers are exported to the simulation Panel Service.

3) Gauge Callbacks—Class Instantiation

Each time the simulation is prompted to show a DLL gauge for the first time, Panel Service calls back the DLL with the event to create an instance of the appropriate gauge.

4) Gauge Callbacks—Processing and Drawing

By this point, the ongoing streams of calls to the gauge callback functions have begun. Until the user switches aircraft or exits the simulation, the DLL callback functions will retrieve data from the SimConnect singleton, process it, and draw to the gauges using GDI+ API calls.

Figure 2. High-Level Diagram: Drawing Gauges Using GDI+

High Level Diagram: Drawing Gauges Using GDI Pllus

DLL Initialization

In the sample code, the body of the DLL initialization function (module_init) consists of a series of SimConnect setup steps. Since (a) SimConnect is not the focus of this sample, and (b) the use of SimConnect to retrieve simulation data is discussed in-depth by other samples (DLL SimConnect sample) and by the SimConnect SDK documentation, we will only briefly discuss SimConnect in this article.

One thing to note about the use of SimConnect in this sample is that the SimConnect class instance is a singleton. The intent of the singleton pattern, as defined by Design Patterns: Elements of Reusable Software, is to "ensure a class has only one instance, and provide a global point of access to it." This is exactly what we want in the case of our SimConnect class. We are retrieving a set of simulation variables on an interval, storing them momentarily for access by the gauge callback functions for processing and drawing, and then overwriting the same SimConnect class instance at each interval.

Gauge Exports and Declarations

GaugeDeclarations.cpp contains the declarations for both the OpaqueGauge and the TransparentGauge as well as the exports for the DLL. For the purpose of this discussion, we’ll consider the OpaqueGauge. Looking at the first of the defines at the start of the declaration,

#define GAUGE_NAME "OpaqueGauge"

The GAUGE_NAME define specifies the string that must match the name specified in all panel.cfg files that reference this gauge. The corresponding line in the provided panel.cfg is:

gauge00=GDIPlus_Gauge!OpaqueGauge,0,0,440,440

Next, the GAUGE_HEADER_FS1000 macro is used to define the interface used by the Panel System to control the gauge DLL. Note that the sixth parameter, GDIPlus_Gauge::Callback<OpaqueGauge>, is a pointer to the callback function for OpaqueGauge class instances. More on this callback will be explained in the Gauge Callbacks–Class Instantiation section.

The MAKE_STATIC macro defines the background image for the gauge. Both the GAUGE_HEADER_FS1000 and the MAKE_STATIC macros are documented in the Programming C++ Gauges section of the SDK reference.

To expose the gauge interface to the Panel System, the gaugehdr_OpaqueGauge (generated by the GAUGE_HEADER_FS1000 macro) must be exported. To do this, we define the GAUGESLINKAGE Linkage structure.

Gauge Callbacks—Class Instantiation

In the sample, when the simulation Panel Service determines that a DLL gauge is to be displayed for the first time, Panel Service calls either GDIPlus_Gauge::Callback<OpaqueGauge> or GDIPlus_Gauge::Callback<TransparentGauge> (specified by the gauge declaration and export). Consider why the callback function declared in the GDIPlus_Gauge base class must be templatized:

     template <class T>
    inline void FSAPI GDIPlus_Gauge::Callback(GAUGEHDR* gau, int serviceId, unsigned)
    { 
        // Body of the function here
    }
    

This callback function is initiated not from an instance of either an OpaqueGauge or TransparentGauge, but directly from the function pointer provided in the gauge header macro (see Gauge Exports and Declarations). For the body to reference the appropriate OpaqueGauge or TransparentGauge class type, that class type must be templatized for the function.

GDIPlus_Gauge::Callback handles the PANEL_SERVICE_CONNECT_TO_WINDOW event, which instantiates the appropriate gauge class as the gauge is requested for the first time.

Gauge Callbacks—Processing and Drawing

The core of the gauge logic is implemented in the following callback function:

     void GDIPlus_Gauge::DoCallback(GAUGEHDR* gau, const int serviceId)
    {
        // Body of the function here
    }
    

Note that DoCallback is not templatized around which derived class gauge type is being operated on. Instead, the mHasAlpha flag is checked when it matters which gauge type is being drawn. DoCallback handles two events:

PANEL_SERVICE_POST_INSTALL

This event occurs before the first draw event of a gauge and again after each time the gauge is resized. Here, we clear the GDI+ mCanvas and determine its new dimensions.

PANEL_SERVICE_PRE_DRAW

This event happens at each simulation draw frame. The sample performs three types of actions in this event: it gathers data from the SimConnect singleton instance, processes that data (i.e., converting radians to degrees), and uses GDI+ facilities to draw to the mCanvas of the gauge class instance. For documentation on GDI+, refer to the complete MSDN Library GDI+ reference.

Do More with Drawing Gauges Using GDI+

The functionality of Drawing Gauges Using GDI+ can be extended to achieve a variety of drawing results with custom gauges. One immediate extension of the sample would be to use an expanded set of the available GDI+ facilities, as they are all compatible with custom C++ gauge rendering. For instance, if you have a data source and a pre-existing way to draw it, this functionality can be reused. By creating a C++ custom gauge and using GDI+ rendering, bitmap output from your program can be drawn to the gauge via shared memory, thus streamlining the process of getting your gauge up and running in the simulation.

Conclusion

The Drawing Gauges Using GDI+ sample demonstrates the Panels and Gauges SDK facilities for creating pure C++ custom gauges. This sample successfully achieves the following:

  • Declares and instantiates both an opaque and a transparent custom C++ gauge
  • Gathers data about a running simulation via SimConnect
  • Processes and displays this data via the GDI+ graphics API

To take the next step, refer to the Panels and Gauges SDK documentation and learn about the many other functionalities of the powerful Panels and Gauges SDK.

Acknowledgements

Special thanks to Peter Turcan for information on the three methods of gauge development, to Susan Ashlock for lending her gauges expertise to the sample code, and to everyone who reviewed the code and this article.

Show:
© 2014 Microsoft