CREATING WASM GAUGES
WASM (C/C++) gauges can be developed for Microsoft Flight Simulator 2024 with the Microsoft Flight Simulator Platform Toolset. Those gauges will draw in a bitmap which will replace a texture inside the aircraft.
MSFS 2024 vs MSFS 2020
The way WASM gauges are implemented has changed between MSFS 2020 and MSFS 2024, and even though the legacy methods still work, we strongly advise you to use the new MSFS 2024 way of working. Thanks to these changes, WASM gauges in Microsoft Flight Simulator 2024 benefit from better optimisations and a much clearer way to define them. These changes have been made to remove the burden of legacy code from Microsoft Flight Simulator X that no longer makes sense in the new engine architecture.
In Microsoft Flight Simulator 2020, all gauge functions - update and draw - were executed during the draw time. However, because the update function doesn't draw anything (and must not), it could be moved in Microsoft Flight Simulator 2024 to the actual update time. Thanks to that change, the new gauge system uses the entire frame while the old one used only a small part of it which has led to improved performance.
In Microsoft Flight Simulator 2020 and previously, gauge functions could be called before and after a specific timing: pre-update, post-update, pre-draw, post-draw, etc... This made some sense in FSX, but less-so in MSFS 2020 where some pre/post function were called sequentially, and now in MSFS 2024 it makes no sense at all. That is why new callbacks have been created to make the gauge system clearer.
- New system : Creating A New Gauge For Your Project
- Old system (deprecated) : Creating A New Gauge For Your Project - MSFS 2020
Creating A New Gauge For Your Project
As explained in the Creating A WASM Project page, you first need to create a MSFS 2024 WASM Module. By doing that, you will get a new project with only the module_init
and module_deinit
functions.
To create a new gauge, right click on your project in the solution explorer and add a new file. In the selection page, choose "MSFS" and pick "Gauge".
After selecting the MSFS WASM Module template, you will be presented with the following basic project, which we'll be editing:
MSFS_CALLBACK bool [MODULE_NAME]_gauge_init(FsContext ctx, sGaugeInstallData* pInstallData)
{
// This is called during initialization of the gauge
// Width given in p_install_data->iSizeX
// Height given in p_install_data->iSizeY
NVGparams params;
params.userPtr = ctx;
params.edgeAntiAlias = true;
NVGcontext* nvgCtx = nvgCreateInternal(¶ms);
g_[MODULE_NAME]Vars.m_nvgCtx = nvgCtx;
// do initialization
// ...
// return false if there is any error
return true; // if successfully intialized
}
- The
[MODULE_NAME]_gauge_init
is the initialisation function - the first function to be called in the gauge cycle - that allows you to initialise everything related to the gauge.- The
pInstallData
parameter points to asGaugeInstallData
structure:- The
iSizeX
member gives the width of the gauge bitmap. - The
iSizeY
member gives the height of the gauge bitmap. - The
strParameters
member gives the optional parameter string.
- The
- The
MSFS_CALLBACK bool [MODULE_NAME]_gauge_update(FsContext ctx, float dTime)
{
// This is called each frame
// dTime is the time between the previous frame and this one
// return false if there is any error
return true;
}
- The
[MODULE_NAME]_gauge_update
is the update function dedicated to this gauge. It will be called once per frame. This function must not use any draw functions because it will be called at a moment in which draw system in not ready.- The
dTime
parameter represents the time during the previous update and this one.
- The
MSFS_CALLBACK bool [MODULE_NAME]_gauge_draw(FsContext ctx, sGaugeDrawData* pDrawData)
{
// This is called each frame, this is the only callback in which it is possible to draw
// update visual layout
float pxRatio = (float)p_draw_data->fbWidth / (float)pDrawData->winWidth;
NVGcontext* nvgCtx = g_[MODULE_NAME]Vars.m_nvgCtx;
nvgBeginFrame(nvgCtx, pDrawData->winWidth, pDrawData->winHeight, pxRatio);
{
// render
// ...
}
nvgEndFrame(nvgCtx);
// return false if there is any error
return true;
}
- The
[MODULE_NAME]_gauge_draw
is the draw function dedicated to this gauge. It will be called once per frame. All draw function calls must be made in the context of this function. - The
pDrawData
parameter points to asGaugeDrawData
structure:- The
mx
member gives the X-coordinate of the mouse in texture space. - The
my
member gives the Y-coordinate of the mouse in texture space. - The
t
member gives the absolute simulation time. - The
dt
member gives the time elapsed since last frame. - The
winWidth
member gives the width of the gauge bitmap. - The
winHeight
member gives the height of the gauge bitmap. - The
fbWidth
member gives the width of the gauge bitmap. - The
fbHeight
member gives the height of the gauge bitmap.
- The
MSFS_CALLBACK bool [MODULE_NAME]_gauge_kill(FsContext ctx)
{
// This is called before the gauge is destroyed
NVGcontext* nvgCtx = g_[MODULE_NAME]Vars.m_nvgCtx;
nvgDeleteInternal(nvgCtx);
// return false if there is any error
return true;
}
- The
[MODULE_NAME]_gauge_kill
is the de-initialisation function, last function to be called in the gauge cycle, that allow you to de-initialise everything related to the gauge.
MSFS_CALLBACK void [MODULE_NAME]_gauge_mouse_handler(FsContext ctx, float fX, float fY, int iFlags)
{
// This is called when the user click on the gauge, enum for iFlags are in "MSFS/Types/MSFS_EventsEnum.h"
}
- The
[MODULE_NAME]_gauge_mouse_handler
is a mouse callback function. See Handling Mouse Events, below.
Handling Mouse Events
It may be that you also need your gauge to deal with the input from users, and in that case you will need to add in mouse events. Mouse events are forwarded to your gauge through a mouse callback that is defined as follows:
extern "C" {
MSFS_CALLBACK void <GAUGENAME>_mouse_callback(float fX, float fY, unsigned int iFlags)
{
...
}
}
The parameters in this callback are:
- The
fX
parameter gives the X-coordinate of the mouse in texture space. - The
fY
parameter gives the Y-coordinate of the mouse in texture space. - The
iFlags
parameter is a combination of the variousMOUSE_*
flags defined ingauges.h
. Supported flags are:MOUSE_MOVE
: the mouse cursor has moved.MOUSE_LEFTDRAG
: left button down.MOUSE_RIGHTDRAG
: right button down.MOUSE_MIDDLEDRAG
: middle button down.MOUSE_LEFTRELEASE
: left button up.MOUSE_RIGHTRELEASE
: right button up.MOUSE_MIDDLERELEASE
: middle button up.MOUSE_LEFTSINGLE
: left button click (sent after a down/up sequence).MOUSE_RIGHTSINGLE
: right button click (sent after a down/up sequence).MOUSE_MIDDLESINGLE
: middle button click (sent after a down/up sequence).MOUSE_LEFTDOUBLE
: left button click (sent after two consecutive clicks).MOUSE_MIDDLEDOUBLE
: middle button click (sent after two consecutive clicks).MOUSE_WHEEL_UP
: wheel moved up.MOUSE_WHEEL_DOWN
: wheel moved down.
Editing The WASM Module Project
Now you've initialised the new WASM project and setup Visual Studio, you'll need to edit the source code to include the minimum header files - supplied by the SDK - to create the module. You can find all the available files that can be included from the following SDK location:
<SDK Root>\WASM\include\MSFS
These different API's are as follows:
- GDI+
- NanoVG API
- Gauge API
- SimConnect SDK
- MapView API
- Network API
- Communication API
- Event API
- Vars API
- VFX API
Adding The Gauge To Your Aircraft
Once you have created the gauge, you need to be able to add it to your aircraft and to test it. This means that you should already have created an aircraft package (as explained here: Creating The Project), and after building the WASM module from Visual Studio, you will want to save it beside the panel.cfg
file in the PackageSources directory, as shown in the following image taken from the Gauge Aircraft Sample:
Since Microsoft Flight Simulator 2024, it is also possible to put your WASM file in a wasm
folder (instead of the panel one). Thanks to that, the same WASM file can be use to handle WASM gauges and WASM systems at the same time. If a WASM system is implemented but the WASM file is located in the panel
folder, it will not be accessible.
Once you have built the module and saved it to the appropriate location, you need to tell the aircraft to reference it. This is done in the panel.cfg
, under the [VCockpitN]
header, something like this:
[VCockpit01]
size_mm=1024,768
pixel_size=1024,768
texture=TEXTURE
background_color=0,0,255
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=<MODULENAME>.wasm&wasm_gauge=<GAUGENAME>, 0, 0, 1024, 768, <OPTIONAL PARAMETER STRING>
The important thing to note here is that all WASM modules will need a [VCockpitN]
definition, and the path (defined by the htmlgauge00
parameter) will always take the same prefixed format:
WasmInstrument/WasmInstrument.html?wasm_module=
When you have the file in the correct location, you can go ahead and build the package (as explained here: Building A Package). Once built, you will need to restart the flight to see the module in the simulation. Note that this is the case for every edition you make to the WASM code - afterwards it will need exported, built, and the flight restarted.
Creating A New Gauge For Your Project - MSFS 2020
The old system (which is now deprecated) works with a unique callback which will receive an enum telling the user when this callback is called and a pointer that must be cast accordingly.
extern "C" {
MSFS_CALLBACK bool [MODULE_NAME]_gauge_callback(FsContext ctx, int service_id, void* pData)
{
switch (service_id)
{
// further code here ...
}
return false;
}
}
Check Events Received By The Gauge Callback for more information.
Events Received By The Gauge Callback
Once the gauge has been loaded by Microsoft Flight Simulator 2020, the following events are sent to the callback function so they can be intercepted and acted on (if required):
PANEL_SERVICE_PRE_INSTALL
: sent before the gauge is installed. ThepData
parameter points to asGaugeInstallData
structure:- The
iSizeX
member gives the width of the gauge bitmap. - The
iSizeY
member gives the height of the gauge bitmap. - The
strParameters
member gives the optional parameter string.
- The
PANEL_SERVICE_POST_INSTALL
: sent after the gauge has been installed. ThepData
parameter points to asGaugePostInstallData
structure:- The
ctx
member is a pointer to theFsContex
t used by the gauge (it is the same as thectx
parameter provided to the callback function).
- The
PANEL_SERVICE_PRE_INITIALIZE
: sent before the gauge is initialized. ThepData
parameter is null.PANEL_SERVICE_POST_INITIALIZE
: sent after the gauge has been initialized. ThepData
parameter is null.PANEL_SERVICE_PRE_UPDATE
: sent before the gauge is updated. ThepData
parameter is null.PANEL_SERVICE_POST_UPDATE
: sent after the gauge has been updated. ThepData
parameter is null.PANEL_SERVICE_PRE_DRAW
: sent before the gauge is drawn. ThepData
parameter points to asGaugeDrawData
structure:- The
mx
member gives the X-coordinate of the mouse in texture space. - The
my
member gives the Y-coordinate of the mouse in texture space. - The
t
member gives the absolute simulation time. - The
dt
member gives the time elapsed since last frame. - The
winWidth
member gives the width of the gauge bitmap. - The
winHeight
member gives the height of the gauge bitmap. - The
fbWidth
member gives the width of the gauge bitmap. - The
fbHeight
member gives the height of the gauge bitmap.
- The
PANEL_SERVICE_POST_DRAW
: sent after the gauge has been drawn. ThepData
parameter points to asGaugeDrawData
structure (see above).PANEL_SERVICE_PRE_KILL
: sent before the gauge is deleted. ThepData
parameter is null.PANEL_SERVICE_POST_KILL
: sent after the gauge has been deleted. ThepData
parameter is null.