Welcome to TechNet Blogs Sign in | Join | Help
Jagged edges in your needles?

One issue that we've seen internally is that some of our needles sometimes appear jagged on our analog gauges.  If it's been a while since encountering this issue, we may not remember that gauge bitmaps need to be at least 4 pixels wide in order for the bilinear filtering (basically, antialiasing) to be applied properly.

Hopefully we'll address this issue at a core level in a future version of Flight Simulator, but in the meantime, you should be able to simply widen your needles with a line of transparent pixels on each edge and the jagged edges will disappear in-game.

I hope this helps some of you out there!

Too many generators?

I just noticed today that our EH-101 has 3 generators (one for each engine), but we only have switches for turning two of them on and off... thus you can never turn off the third one, which means that as long as that third engine is running, the electrical systems in the cockpit keep running!

 To change the number of generators for an aircraft, you can use the engine_generator_map parameter in the [electrical] section of your aircraft.cfg.  For example, in the EH-101, this should be:

engine_generator_map=1,1,0 

This indicates that there should be a generator driven by engine one, a generator driven by engine 2, but no generator driven by engine 3.  Likewise, in a 4-engine aircraft, "0, 1, 0, 1" would indicate 2 generators attached to engines 2 and 4.

Just thought I'd share in case it helps someone else!  This is documented in the SDK, but it's easy to miss and I thought the explanation was a little too brief.  :)

Walking around in FSX

I like this video because it demonstrates what I expected to see in FS when I started using it years ago: the ability to walk around and into an airplane.  :)

http://www.youtube.com/watch?v=ly0eo2cuT6g

Funny video

At least, I thought it was pretty good.  ; )

http://www.youtube.com/watch?v=8r2CIE-0rWk

XML Gauge Text, Part III: Mind your %'s and ('s

It looks like a common mistake made by people creating XML gauges is to use too many percent signs (%).  For example, I saw this in a 3rd-party gauge recently:

%{if}%(On)%{else}%(Off)%{end}

The underlined percent signs are unnecessary, and the parser will try to translate the "On" and "Off" as an expression that can be calculated.  Recall that "%( )" is used to calculate expressions; for example:

%((A:Eng1 Oil Pressure, PSF) 20 <)

would equate to TRUE or FALSE and could be used with the %{if} conditional above to conditionally display text.

Thus, the 3rd-party gauge should have used:

%{if}(On)%{else}(Off)%{end}

This above information is also covered in the SDK, an excerpt of which is pasted below:

Conditional Gauge Strings

The format of conditions (if, then, else, and case statements) in gauge strings is different from that in other scripts. In gauge strings use the %{if}, %{else}, and %{end} constructs to choose which text to display.  Note that these keywords are case-sensitive and must be typed in lowercase. Also, there must not be a space between the ‘%’ and the ‘{‘. An if statement can be used without a corresponding else, in which case nothing is displayed if the result of the condition is false. The syntax for usage is one of the following:

  • %(CONDITIONAL)%{if}TEXT TO DISPLAY IF TRUE%{else}TEXT TO DISPLAY IF FALSE%{end}
  • %(CONDITIONAL)%{if}TEXT TO DISPLAY IF TRUE%{end}
GPS detail level enumerations

In the XPack SDK we added more documentation about the enumerations relating to the GPS, but it looks like we left out some, too.  Some people were investigating what each meant based on trial and error, but I think I can help out a bit here.

For example, Geoff_D was wondering what these numbers meant from the gps_500.xml:

<CustomDrawParam id="ObjectDetailLayerAirports" Name="ObjectDetailLayerAirports"> 
<Value>0 0 0 0 0 0 0x5 0x5 0x7 0x7 0x1F 0x1F 0x1F 0x1F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 24 (@g:map_ZoomStep) case</Value>  

So here's a quick runthrough that I just put together of what the numbers that you're scratching your head about mean:

·         Detail level enumerations

o        Airport

§         Dot = 1

§         Circle = 2

§         Circle and runways = 3

§         Block runways = 4

§         Airport runways = 5

o        Vor

§         Dot = 1

§         Symbol  = 2

o        NDB

§         Dot = 1

§         Symbol = 2

o        Airway

§         Thin lines = 1

§         Thick lines = 2

o        Terrain

§         Water only = 1

§         Color = 2

o        Approach

§         Approach = 1

§         Approach missed = 2

§         Approach arrow head = 4

o        Flight plan

§         Solid line = 1

§         Dashed line = 2

o        Vehicles

§         ATC symbol = 1

§         TCAS symbol = 2

§         Realistic symbol = 3

 

·         Text detail level

o        Airport

§         ICAO = 1

§         Name = 2

§         Name and elevation = 3

§         Name, elevation, and frequencies = 4

§         Runways = 5

o        Vor

§         ID = 1

§         ID and frequency = 2

§         ID and morse code = 3

o        ILS

§         ID = 1

§         ID and frequency = 2

§         ID and morse code = 3

o        NDB

§         ID = 1

§         ID and frequency = 2

§         ID and morse = 3

o        Vehicles

§         Realistic = 1

§         Detailed = 2

·         Object detail level (flags and can be combined)

o        Airport

§         Towered = 0x1

§         Not towered = 0x2

§         Hard runway = 0x4

§         Soft runway = 0x8

§         Water = 0x10

§         Heliports = 0x20

§         Private = 0x40

o        VOR

§         VOR = 0x1

§         VOT = 0x2

o        Marker

§         Outer marker = 0x1

§         Middle = 0x2

§         Inner = 0x4

§         Backcourse = 0x8

o        Geopolitical

§         Coastline = 0x1

§         Boundary = 0x2

o        Airway

§         Victor = 0x1

§         Jet = 0x2

o        Flight plan

§         Enroute = 0x1

§         Approach = 0x2

§         Missed approach = 0x4

§         Waypoints = 0x8

o        Vehicles

§         Track line = 0x1

§         Ground vehicles = 0x2

§         Airborne vehicles = 0x4

§         Racing vehicles = 0x8

o        ILS

§         Cone = 0x1

§         Line = 0x2

 

So, to go back to Geoff_D's question:

<CustomDrawParam id="ObjectDetailLayerAirports" Name="ObjectDetailLayerAirports"> 
<Value>0 0 0 0 0 0 0x5 0x5 0x7 0x7 0x1F 0x1F 0x1F 0x1F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 0x4F 24 (@g:map_ZoomStep) case</Value>  

0x5 = 0x1 | 0x4 =  Towered and hard-runwayed airports
0x7 = 0x1 | 0x2 | 0x4 = Towered, untowered, and hard-runwayed airports.
0x1F = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 = Towered, untowered, hard-runwayed, soft-runwayed, and water-based airports

and so forth...

In other words, the map is showing more and more airports as the user zooms in on the map (based on the value of @g:map_ZoomStep).

Thanks to Geoff_D for the question.

How do I share data between C++ gauges in a shared cockpit scenario?

Based on the lack of references to "process_shared_event_out()" and "GAUGE_HEADER_FS1000" in a popular search engine and the fact that I've gotten a few questions on the subject, I'm guessing that the understanding of how to share data between two instances of the same C++ gauge in a shared cockpit scenario is quite limited.

First let me describe a possible scenario:

Let's say you're flying along in a multiplayer session in the C172 and your friend Mary (who is also in the session, even though she lives on the other side of the country) joins your aircraft.  If you click on the "SELECT" button on the clock to switch between time-of-day and stopwatch mode, Mary sees the clock change modes on her machine.  Likewise, if she clicks on the "SELECT" button, you see the clock change mode on your end.  How is this magic happening?

First let's think about how most gauges work, and then we'll look into how the clock is a special case.

Most gauges simply reflect gauge simulation state.  For example, an airspeed gauge doesn't have to do any work to stay in sync with the other user's machine; the airspeed is automatically synced by the multiplayer system.  Similarly, a gear lever automatically stays in sync because the GEAR_TOGGLE event (like most key events) are automatically sent to the other machine in the shared cockpit by the multiplayer system.

However, in some cases with default aircraft (and with many super-detailed 3rd-party aircraft), gauges maintain some of their own state data.  The clock in the C172 is an example.  The simulation engine doesn't know anything about the clock at all; hence it must not only maintain its own state data, but also send data to the other machine in the shared cockpit scenario in order to keep both machines in sync.

So how do we send this data?  Let's take a look basic premise behind the synchronization system in FSX and beyond. 

When two users' aircraft are first joined to a shared cockpit session, the state of the host's gauges is sent to the other user's machine.  This is called "serialization".  A few milliseconds later (or many, depending on network latency), the non-host receives this data, which must then be "deserialized".  Once the two machines are initially synchronized, gauge state only needs to be updated when something changes; for example, when a user on either side clicks on the "SELECT" button.  However, not all state needs to be synchronized; we can simply send an event to signal that the "SELECT" button was pressed.  Although in this situation we don't need to send any additional data, in some other situations it is both necessary and possible.

So, if we look at the new-fangled GAUGE_HEADER_FS1000 macro, we see that there are parameters that we can use to specify the serialization function, deserialization function, and event processing function.  In addition to this, we also see that we can specify the size of the buffers that will be allocated to pass data both for serialization and for events.  Lastly, the macro also requires a GUID so that each gauge can be uniquely identified on each machine.

#define GAUGE_HEADER_FS1000( \
gaugehdr_var_name, \
default_size_mm, \
gauge_name, \
element_list, \
pmouse_rect, \
pgauge_callback, \
user_data, \
usage, \
guid, \
serialize_size_callback,\
serialize_callback, \
deserialize_callback, \
event_size_callback, \
process_event_callback) \

So, in the case of the clock, the setup and declaration of the gauge would look something like this:

#define GAUGEHDR_VAR_NAME       gaugehdr_clock

#define GAUGE_ITEM              clock

#define GAUGE_NAME              "Clock"

#define GAUGE_W                 54

 

char gauge_name_clock[] = GAUGE_NAME;

 

extern PELEMENT_HEADER list_clock;

extern MOUSERECT mouse_rect_clock[];

 

extern GAUGE_CALLBACK       gcb_clock;

 

SERIALIZE_SIZE_CALLBACK     sscb_clock;

SERIALIZE_CALLBACK          scb_clock;

DESERIALIZE_CALLBACK        dcb_clock;

EVENT_SIZE_CALLBACK         escb_clock;

PROCESS_EVENT_CALLBACK      pecb_clock;

 

BOOL  clock_control_button( PGAUGEHDR gauge_header);

BOOL  clock_select_button( PGAUGEHDR gauge_header);

BOOL  clock_toggle_temp_volts_button( PGAUGEHDR gauge_header);

 

// {7B293842-E2C2-4800-A190-33A7F20385AB}

GUID clock_guid = { 0x7b293842, 0xe2c2, 0x4800, { 0xa1, 0x90, 0x33, 0xa7, 0xf2, 0x3, 0x85, 0xab } };

 

GAUGE_HEADER_FS1000(GAUGEHDR_VAR_NAME, GAUGE_W, gauge_name_clock, &list_clock, mouse_rect_clock, gcb_clock, 0L, 0L, clock_guid, sscb_clock, scb_clock, dcb_clock, escb_clock, pecb_clock);

 

 

So, what do these callback functions do?  In the case of the clock, the process is fairly simple.  First, be aware that the clock has some of its own enums and a struct:

 

typedef enum {

  CLOCK_MODE_CLOCK,

  CLOCK_MODE_TIMER,

} CLOCK_MODE;

typedef enum {

  TEMP_VOLTS_MODE_TEMP,

  TEMP_VOLTS_MODE_VOLTS,

} TEMP_VOLTS_MODE;

typedef enum {

  CLOCK_FORMAT_12,

  CLOCK_FORMAT_24,

} CLOCK_FORMAT;

typedef enum {

  CLOCK_EVENT_CONTROL,

  CLOCK_EVENT_SELECT,

  CLOCK_EVENT_TOGGLE_TEMP_VOLTS,

} CLOCK_EVENT;

 

typedef struct {

  CLOCK_MODE        clock_mode;

  TEMP_VOLTS_MODE   temp_volts_mode;

  CLOCK_FORMAT      format;

  FLOAT64           clock_timer_start;

  FLOAT64           timer_accumulated;

  BOOL              timer_running;

  BOOL              timer_stopped;

} CLOCK_DATA;

 

And now let's look at the callback function definitions.  Note that the amount of data sent for serialization and deserialization is significantly greater than the amount of data (one 32-bit number) that's sent with an event.

 

// serialization size (return length of buffer)

void sscb_clock(PGAUGEHDR gauge_header, UINT32* nSize)

{

    *nSize = sizeof(CLOCK_DATA) + sizeof(FLOAT64);

}

 

// serialize

void scb_clock(PGAUGEHDR gauge_header, BYTE* pBuf)

{

    CLOCK_DATA* data = (CLOCK_DATA*)gauge_header->user_data;

       

    memcpy(pBuf, data, sizeof(CLOCK_DATA));      

}

 

// deserialize

bool dcb_clock(PGAUGEHDR gauge_header, BYTE* pBuf )

{

    CLOCK_DATA* data = (CLOCK_DATA*)gauge_header->user_data;

    memcpy(data, pBuf, sizeof(CLOCK_DATA));       

       

    return true;

}

 

// event size

void escb_clock(PGAUGEHDR gauge_header, UINT32* nSize)

{

    if (!nSize)

        return;

    *nSize = sizeof(CLOCK_EVENT);

}

 

// process event - return true if successful

bool pecb_clock(PGAUGEHDR gauge_header, BYTE* pBuf)

{

    CLOCK_EVENT event;

    memcpy(&event, pBuf, sizeof(CLOCK_EVENT));

 

    switch(event)

    {

        // to pass to the mouse callback, we cast to a PPIXPOINT.

        case CLOCK_EVENT_CONTROL:

            return clock_control_button(gauge_header) ? true : false;

            break;

        case CLOCK_EVENT_SELECT:

            return clock_select_button(gauge_header) ? true : false;

            break;

        case CLOCK_EVENT_TOGGLE_TEMP_VOLTS:

            return clock_toggle_temp_volts_button(gauge_header) ? true : false;

            break;

        default:

            return false;

            break; // should NOT hit this.               

    }

 

    return false; // should also NOT hit this.

}

 

And finally, the function that sends an event from one machine to the other.

 

BOOL  clock_select_button_mouse( PPIXPOINT relative_point, FLAGS32 /* mouse_flags */ )

{

  PGAUGEHDR gauge_header = GAUGEHDR_FOR_MOUSE_CALLBACK(relative_point);

 

  CLOCK_EVENT event = CLOCK_EVENT_SELECT;

 

  process_shared_event_out(gauge_header, (BYTE*)&event, sizeof(CLOCK_EVENT));

 

  return TRUE;

}

 

So, if you have a gauge that you want to be shared in a shared cockpit scenario but doesn't work currently because the gauge maintains some of its own state, give this a try!  Bear in mind that the above example doesn't contain all of the code for the C172 clock, but it should be enough to get across the concepts of serialization and event sharing.

 

Next time I'll talk about data sharing in XML gauges, so stay tuned!

 

A word to the wise

Let's say you're posting to a forum and not getting responses from the person or people who you think should be responding.

If you think that voicing your concerns in an aggressive manner to that person in front of a hundred or so of their colleagues and 3rd-party partners (in person) is a good solution to your problem, please think twice about it.  Having one's commitment to their customers and job questioned in such a manner can be quite aggravating and doesn't help to build a good working relationship between anyone.

If someone (such as myself) isn't responsive in a particular forum, try another way of asking.  Ask someone else who might work with them if they can pass the question along.  Perhaps consider visiting their blog site and posting a comment there.

We're all busy, and don't always have time to check forums, much less every forum that's out there.  We definitely have some dedicated team members here who spend a good deal of time reading forum posts (heck, we have an entire Community Team that spends a lot of time doing just that.).  So if there are really questions out there that a large number of people need answered and no one can find the answer, keep asking.  Just try not to take it personally.  And we'll try not to take your feedback personally either.  ; )

Keep in mind that posting answers on blogs/forums can take MUCH more time than what is required to simply type a hundred or so words.  It can take hours or days to investigate an issue.  And technically speaking, the SDK is a feature that's unsupported by Microsoft.  And every hour that a developer spends investigating an issue for a product that has already shipped is an hour less time spent expanding capabilities for future versions.  So be aware that we are always walking along the tightrope, balancing our customer's needs with add-on developers' needs.  There are only so many hours in the day, and many of us have spouses, children, and other interests - just like you.  I promise that we're doing our best.

And yes, I am the one to answer gauge- and panel- related questions.  I'm a medium and I like chocolate.  ; )

How did we get a transparent 2D HUD in the FA-18? I thought that wasn't possible?

Someone was asking about this earlier today, and now that we're not busy getting XPack out the door, I finally have time to answer it. 

We made some changes to the XPack code base to allow this.

And we added a flag to gauges.h: IMAGE_ERASE_ALWAYS.

Note that your gauge update rate will suffer a bit, since we erase the entire image before redrawing it again.

The gauge that we use for the 2D panel is 99% the same as the one we map into the 3D cockpit, but differs in that the 2D gauge has the new IMAGE_ERASE_ALWAYS flag applied and that its background bitmap is transparent (all black) instead of white.

What is "Shared Cockpit"?

I've gotten some questions about Shared Cockpit over the past months, and I believe that a response is well overdue at this point.  I plan to cover the topic in several posts, focussing on the aspects of shared cockpit that apply to 3rd party add-on developers.

First, let's describe what we (on the Flight Sim team) mean by "shared cockpit", also known as "shared aircraft" (this is the term that Bruce Williams uses in his book Microsoft Fligth Simulator as a Training Aid (2006).

The shared cockpit feature is one way of using the multiplayer system.  Typically, each user in a multiplayer session flies in his or her own aircraft; in "Shared Cockpit" two (and not more than two) users can fly the same aircraft simultaneously.  Imagine that one might act as a pilot and the other as a passenger.  Or perhaps the two are acting as pilot and copilot.

At all times one of the users is the "pilot flying", who may, at their discretion, turn over control to the other user by hitting "Shift+T" (for Transfer control).  (The other user may not 'steal' control).  The "pilot flying' controls basic aircraft controls such as the yoke, throttle, and rudder pedals; either pilot can control avionics systems, gear, and other secondary control systems.

The users must have separate machines running separate installations of FSX (or FSX+Acceleration Expansion Pack), but as long as they are connected by a network, they can be anywhere in the world.  Want to get some training from your experienced Uncle Bob but he lives on the other side of the world?  No problem.

For more information on how to actually use the feature, check out the Learning Center in FSX.  Go to the Index and click on "Shared aircraft" under the "Multiplayer" section.

That's all the time I have for now, but stay tuned for information and sample code for keeping the gauges you develop in sync in the shared cockpit scenario!  (We'll start with an overview of the model that we use to keep the two users' aircraft in sync; from then we'll move onto the callback functions that were added to the GAUGE_HEADER_FS1000 macro, and the two API functions "process_shared_event_out()" and "is_master()" and when you should use them).