1
0
Fork 0

Added Docs directory and start populating it.

Added Files:
 	README.Joystick README.xmlpanel
This commit is contained in:
j4strngs 2001-07-02 21:58:43 +00:00
parent e7ef251807
commit 3c0a328e46
2 changed files with 1186 additions and 0 deletions

550
Docs/README.Joystick Normal file
View file

@ -0,0 +1,550 @@
Users Guide to FGInput - Joystick And Keyboard Bindings For FlightGear
Or
"The document formerly know as The Users Guide to Joystick Usage Under
FlightGear Flight Simulator"
version 0.7.7.2 07/02/2001
Author John Check <j4strngs@rockfish.net>
This document is written with versions of FlightGear 0.7.7 and greater
in mind. It assumes a working joystick present on your system. It
is written from the perspective a Linux user, but the information presented
is valid on other platforms. The most current version can be found at:
http://rockfish.net/shell/aboutFGInput.txt
Thanks to David Megginson, who aside from actually implementing FGFS XML
features, lets me rip off his descriptions of how stuff works so I can look
smart.
Some History:
------------
Earlier versions of FGFS had assignments of joystick axis/buttons
and key bindings hard coded. If you had a joystick that did not use
the default channel assignments, or wanted different key bindings
you had to edit the source code and recompile.
Fortunately, around about v0.7.5 a "property manager" was implemented,
which facilitated being able to set the parameters for the joystick at runtime.
Version 0.7.7 saw an expanded role for the property manager and the
addition of a "command manager" that allows for binding of events to commands.
The code that does this is known as FGInput and is used to configure
keyboard command bindings as well as joysticks.
Storing alternate keyboard or joystick bindings can be done in a variety of ways.
The order of precedence for options is thus:
Source Location Format Scope
------ -------- ------ -----
command line STDIN see examples session
.fgfsrc Users home directory. command line options single user
system.fgfsrc $FG_ROOT command line options system wide
joystick.xml $FG_ROOT XML property list system wide
keyboard.xml $FG_ROOT XML property list system wide
First Things First
------------------
I will cover joysticks first and save the keyboard stuff for later.
FGInput treats things in a generic enough way that the line between
joystick buttons and keyboard events starts to blur.
About XML
---------
In case you were wondering, XML stands for eXtensible Markup Language.
It looks a lot like HTML, except you get to define your own tags. Well,
in the case of FGFS we defined the tags you need to configure things.
It is well suited for describing hierarchically organized structures, such as
the property tree FGFS uses to expose the it's innards to external applications.
Your XML configuration files for FGFS must start and end with the following
pair of tags.
<PropertyList>
<!-- this is a comment, See I told you it was like HTML -->
</PropertyList>
Determining your joystick output:
--------------------------------
FlightGear ships with a utility called js_demo. It will report
the number of joysticks attached to a system and their capabilites.
By observing the output of js_demo while working your joystick you can
determine what controls are where.
It should be noted that, at least on UNIX, numbering generally starts with
zero. In the following example the system has 1 joystick (js0) connected.
The output shown is from an analog Gravis BlackHawk with four buttons and
a throttle.
Typical output of js_demo:
Joystick test program.
~~~~~~~~~~~~~~~~~~~~~~
Joystick 1 not detected <!-- remember we start at 0 -->
Joystick 2 not detected
+---------------JS.0-----------------+
| Btns Ax:0 Ax:1 Ax:2 |
+------------------------------------+
| 0000 +0.0 +0.0 -1.0 . . . |
The buttons are handled internally as a binary number in which bit 0 (the
least significant bit) represents button 0, bit 1 represents button 1, etc.,
but this number is displayed on the screen in hexadecimal notation, so:
0001 => button 0 pressed
0002 => button 1 pressed
0004 => button 2 pressed
0008 => button 3 pressed
0010 => button 4 pressed
0020 => button 5 pressed
0040 => button 6 pressed
... etc. up to ...
8000 => button 15 pressed
... and ...
0014 => buttons 2 and 4 pressed simultaneously
... etc.
Default Joystick properties:
----------------------------
Axis 0 = Aileron
Axis 1 = Elevator
Axis 2 = Rudder
Axis 3 = Throttle
Button 0 = All brakes
Button 1 = Elevator trim (up)
Button 2 = Elevator trim (down)
Okay, Now what?
---------------
Now that you know what the output of the devices connected to the joystick
port (or USB port joystick devices) is, you probably want to dive straight
in and to start connecting to FGInput. If you are familiar with configuring
the joystick on versions of FGFS prior to 0.7.7 you can skip down to the
section "The Command Manager".
If you are a new FGFS user, you should at least skim the next bit since it
explains some concepts you may or may not know. It also covers some legacy
joystick options which have not been implemented yet in the context of the
command manager.
Modifiers For Raw Joystick Values
---------------------------------
These concepts are expressed by supplying arguments to the joystick bindings.
The raw values coming from the joystick axes may not be suitable to use directly.
For that matter not all joysticks are created equal so understanding the basic
concepts should save you some time when experimenting to get the best performance.
The full order of precedence for axis properties is
1. The raw axis value ...
2. is adjusted to dead-band, ... deadband is implemented outside the command manager
3. then adjusted to offset, ...
4. then multiplied by factor, which ...
5. is assigned to the FlightGear control property.
Put another way....
cooked_value = (( raw_value > dead-band ) + offset) * factor
Axis properties:
dead-band
-1 0 1
.......................
-1 | | 1
^
dead-band
This is an area where signals are ignored. It is used to compensate
for noise or potentiometers of dubious quality by creating a threshold
below which any signal is ignored.
The default of 0.1 for elevators and ailerons is very forgiving. A lower
number results in a tighter feel. In some cases such as throttle you may
wish to not set a deadband. Use a value of 0.0 in this case.
offset
-1 0 1
.......................
-1 ^ 1
offset
Used to maximize a controls use of it's axis, as in the case of a
throttle where zero would be a minimum and not a center point like in the case
of a rudder. Typical value -1.0.
factor
Controls sensitivity of an axis. If the factor is too low it results in
control not reaching its maximum possible limit. Negating the number will result in
the control moving counter to the default. The default value is 1.0, think unity gain.
In my case, throttle behavior was inverted from what I preferred.
I set this value to -1.0 and everything was groovy.
The Command Manager
-------------------
Previous versions of FGFS allowed joystick output to be bound directly
to the property manager. This has changed for FGFS v0.7.7 and now events
are bound to commands. Commands *must* be specified for a binding to have
an effect. The current list of commands is broken down here into two
categories, mainly for my convenience.
Visual And File Related:
command options used for
------- ------- --------
null none useful for clearing a previous binding
exit none Exiting FGFS
load file name* Load a saved flight
save file name* Save current flight
load-panel path ** Change/reload panel
load-preferences path ** Load preferences ***
screen-capture none Save a screenshot to ./fgfs-screen.ppm
view-cycle none Change the direction of the pilots view
*Saved/loaded relative to current working directory.
**The path includes the filename you wish to load and is relative to $FG_ROOT,
which is the location of the installed base package. The default for load-panel
is the value of /sim/panel/path (from preferences.xml) or if that is unset
Panel/Default/default.xml. The default for load preferences is preferences.xml.
***This might make a good first binding to experiment with, since it's not currently
bound to anything. Reloading preferences will allow you to test settings
without having to sit through a restart of FGFS for every edit. You can
always (re)move it later.
Flight Control:
command options effect
------- ------- ------
property-toggle property toggle the property full on
property-assign "" "", value targets a property for action
property-adjust "" "", step Increment size for changes
property-swap "" ""[0], "" ""[1] Set values in a switch
property-scale "" "", offset, factor Processes the raw joystick value
Bindings
--------
A command may have more than one binding. By default, the examples below
use just /binding or <binding>, but /binding[0] or <binding n="0"> is implied.
When bindings are specified in XML the indices are created automagically. If
you wish to avoid XML you must supply the index number for multiple bindings
in your command line formatted options.
Joystick Axes
-------------
Here's a sample Joystick axis declaration in XML:
<axis n="0"> <!-- target an axis -->
<desc>Aileron</desc> <!-- descriptive name (optional) -->
<binding> <!-- open a container for the binding -->
<command>property-scale</command> <!-- pick a command -->
<property>/controls/aileron</property> <!-- target a property -->
</binding> <!-- closing tag for binding -->
</axis> <!-- closing tag for axis -->
Remember how I said the property tree was a hierarchy? Thoughtful readers
will notice how the nested tags keep things organized.
This binding appears in the context /input/joysticks/js/, so the
command-line option equivalent of this declaration (leaving out the
'desc', which isn't strictly necessary), is
--prop:/input/joysticks/js[0]/axis[0]/binding/command=property-scale
--prop:/input/joysticks/js[0]/axis[0]/binding/property=/controls/aileron
Do you see how the command line versions uses a path to represent the hierarchy?
Cool! You should read README.xmlpanel next, working with FGFS XML configuration
system is easy and it's fun for the whole family! ( not sold in stores. excludes
tax and title. void where prohibited by law.)
Ok, back to business. The 'property-scale' command also recognizes 'offset' and
'factor' arguments. The "type" arguments shown in the following example are
specifying a double precision floating point number. A double has more discrete
steps and gives a smoother action than a plain float.
<axis n="2">
<desc>Throttle</desc>
<!-- See important note about dead-band below -->
<binding>
<command>property-scale</command>
<property>/controls/throttle</property>
<offset type="double">-1.0</offset>
<factor type="double">-0.5</factor>
</binding>
</axis>
or
--prop:/input/joysticks/js[0]/axis[2]/binding/command=property-scale
--prop:/input/joysticks/js[0]/axis[2]/binding/property=/controls/throttle
--prop:/input/joysticks/js[0]/axis[2]/binding/offset=-1.0
--prop:/input/joysticks/js[0]/axis[2]/binding/factor=-0.5
*Important Note About dead-band*
--------------------------------
You may recall from the section about raw axis value modifiers that dead-band
is implemented outside the command manager. This means that if you want to apply
a dead-band, the tag *must* precede the binding tag. If you are using the command
line format you must omit the 'binding' part like so:
--prop:/input/joysticks/js[0]/axis[2]/dead-band=0.005
Joystick Button Properties
--------------------------
Buttons, being boolean by nature, can use a little help. By this I mean that there
are times when you need momentary action, times where you need a repeating action
and sometimes you just want a plain old toggle. In order to facilitate this need
we have some tags that modify the actions of buttons.
<repeatable>
<!-- Will be either true or false. If it is true, holding down a button (or key) will
cause repeated events, say, for moving the trim; if false (the default), pressing
a key will cause only a single event. -->
<step>
<!-- The property-adjust command takes a 'step' argument specifying the
amount of the adjustment for each event. In the following example, the elevator
trim moves 0.1% for each event (without automatic repetition, you'd
have a pretty sore finger). -->
<value>
<!-- Use value on non-repeatables to supply the value for each consecutive press-->
<mod-up>
<!-- This stands for "modifier up", my favorite I think. This is used to set up a binding
for when you *release* a key. As you'll see, it comes in handy -->
Here's a sample joystick button declaration in XML:
<button n="1"> <!-- target a button -->
<desc>Elevator trim up</desc> <!-- optional description -->
<repeatable>true</repeatable> <!-- Ok, repeatable is outside the command manager too -->
<binding> <!-- Open the "binding" node of the tree-->
<command>property-adjust</command> <!-- pick a command type to bind -->
<property>/controls/elevator-trim</property> <!-- target a property -->
<step type="double">0.001</step>
</binding>
</button>
In command-line option syntax, this would appear as
--prop:/input/joysticks/js[0]/button[1]/repeatable=true <!-- See? no 'binding' -->
--prop:/input/joysticks/js[0]/button[1]/binding/command=property-adjust
--prop:/input/joysticks/js[0]/button[1]/binding/property=/controls/elevator-trim
--prop:/input/joysticks/js[0]/button[1]/binding//step=0.001
Here's a slightly fancier declaration, that applies the left (differential) brakes
when button 4 is pressed, and releases them automatically when the user releases the
button:
<button n="4">
<desc>Left brake</desc>
<binding>
<command>property-assign</command>
<property>/controls/brakes[0]</property>
<value type="double">1.0</value> <!-- brakes are a toggle so 1.0 represents on -->
</binding>
<mod-up> <!-- it's not a parking brake so we need to release it -->
<binding>
<command>property-assign</command>
<property>/controls/brakes[0]</property>
<value type="double">0.0</value> <!-- 1.0 is on so 0.0 is off, right? -->
</binding>
</mod-up>
</button>
The first binding is straight-forward: when the button is pressed, the
'property-assign' command assigns the value 1.0 (i.e. full) to the left brake
property. The second binding, however, is nested inside a 'mod-up' element,
it will be fired when the user *releases* the button, and will use the
'property-assign' command to assign the value 0.0 (i.e. none) to the left brake
property. Repetition is left at the default value of false, so that the same
value will not be assigned over and over again.
Here's the command-line equivalent:
--prop:/input/joysticks/js[0]/button[4]/binding/command=property-assign
--prop:/input/joysticks/js[0]/button[4]/binding/property=/controls/brakes[0]
--prop:/input/joysticks/js[0]/button[4]/binding/value=1.0
--prop:/input/joysticks/js[0]/button[4]/mod-up/binding/command=property-assign
--prop:/input/joysticks/js[0]/button[4]/mod-up/binding/property=/controls/brakes[0]
--prop:/input/joysticks/js[0]/button[4]/mod-up/binding/value=0.0
Remember that more than one binding can be included in each context. Here's a
very hairy example from the default bindings that fires all three brakes when
button 0 is pressed, and releases all three when button 0 is released:
<button n="0">
<desc>Brakes</desc>
<binding>
<command>property-assign</command>
<property>/controls/brakes[0]</property>
<value type="double">1.0</value>
</binding>
<binding>
<command>property-assign</command>
<property>/controls/brakes[1]</property>
<value type="double">1.0</value>
</binding>
<binding>
<command>property-assign</command>
<property>/controls/brakes[2]</property>
<value type="double">1.0</value>
</binding>
<mod-up>
<binding>
<command>property-assign</command>
<property>/controls/brakes[0]</property>
<value type="double">0.0</value>
</binding>
<binding>
<command>property-assign</command>
<property>/controls/brakes[1]</property>
<value type="double">0.0</value>
</binding>
<binding>
<command>property-assign</command>
<property>/controls/brakes[2]</property>
<value type="double">0.0</value>
</binding>
</mod-up>
</button>
For people who take pleasure in avoiding XML, here's the command-line
equivalent (note the subscripts to distinguish multiple bindings; the
XML will handle this automatically):
--prop:/input/joysticks/button[0]/binding[0]/command=property-assign
--prop:/input/joysticks/button[0]/binding[0]/property=/controls/brakes[0]
--prop:/input/joysticks/button[0]/binding[0]/value=1.0
--prop:/input/joysticks/button[0]/binding[1]/command=property-assign
--prop:/input/joysticks/button[0]/binding[1]/property=/controls/brakes[1]
--prop:/input/joysticks/button[0]/binding[1]/value=1.0
--prop:/input/joysticks/button[0]/binding[2]/command=property-assign
--prop:/input/joysticks/button[0]/binding[2]/property=/controls/brakes[2]
--prop:/input/joysticks/button[0]/binding[2]/value=1.0
--prop:/input/joysticks/button[0]/mod-up/binding[0]/command=property-assign
--prop:/input/joysticks/button[0]/mod-up/binding[0]/property=brakes[0]
--prop:/input/joysticks/button[0]/mod-up/binding[0]/value=0.0
--prop:/input/joysticks/button[0]/mod-up/binding[1]/command=property-assign
--prop:/input/joysticks/button[0]/mod-up/binding[1]/property=brakes[1]
--prop:/input/joysticks/button[0]/mod-up/binding[1]/value=0.0
--prop:/input/joysticks/button[0]/mod-up/binding[2]/command=property-assign
--prop:/input/joysticks/button[0]/mod-up/binding[2]/property=brakes[2]
--prop:/input/joysticks/button[0]/mod-up/binding[2]/value=0.0
This is a good time to remind you that command line formatted options are used
in your .fgfsrc file. You can put them in $FG_ROOT/system.fgfsrc to make them
global.
Keyboard Bindings
-----------------
Keyboard bindings are almost exactly identical to joystick-button
bindings, as in the following example (the context is
/input/keyboard):
<key n="1">
<name>Ctrl-A</name>
<desc>Toggle autopilot altitude lock.</desc>
<binding>
<command>property-toggle</command>
<property>/autopilot/locks/altitude</property>
</binding>
</key>
There are a few gotcha's though. First, the index of the key
specifies the key code that you're binding; in the above example, '1'
corresponds to Ctrl-A (as mentioned in the friendly comment). Regular
key codes go up to 255; codes from 256 up represent special keys like
function and arrow keys (you can get the value from include/GL/glut.h,
but adding 256 to the special key code). Here is the command-line
equivalent of the above, leaving out the documentation properties:
--prop:/input/keyboard/key[1]/binding/command=property-toggle
--prop:/input/keyboard/key[1]/binding/property=/autopilot/locks/altitude
(The 'property-toggle' command switches a boolean value between true and
false, so it needs no arguments except for the property name.) DON'T
LEAVE OUT THE INDEX!!!
The second gotcha is that keys can take more modifiers than joystick
buttons. In addition to mod-up (representing the key release), a key
may use any combination of nested 'mod-alt', 'mod-ctrl', and
'mod-shift' modifiers, as in the following example:
<key n="49">
<name>1</name>
<mod-shift>
<desc>Look back left</desc>
<binding>
<command>property-assign</command>
<property>/sim/view/goal-offset</property>
<value type="double">135</value>
</binding>
</mod-shift>
</key>
In this example, the '1' key combined with shift causes the view to
switch to back left. Note that this will work only with the keypad 1,
since pressing shift+1 on the regular keyboard will give a '!'
character instead.
The input module tries to be smart about supplying control and shift
modifiers automatically where they make sense -- note that it wasn't
necessary to use a nested 'mod-ctrl' element for the ctrl-A binding
earlier, since a keycode less that 32 implies a control character
anyway.
With the new input module, the key-up events can also be detected for
the keyboard, so it's possible to have touch-sensitive brakes (etc.)
just as with the joystick:
<key n="44">
<name>,</name>
<desc>Left brake</desc>
<binding>
<command>property-assign</command>
<property>/controls/brakes[0]</property>
<value type="double">1.0</value>
</binding>
<mod-up>
<binding>
<command>property-assign</command>
<property>/controls/brakes[0]</property>
<value type="double">0.0</value>
</binding>
</mod-up>
</key>
Now here is a different way to bind a brake. In this example, there is
no <mod-up> tag, so it *does* work like a parking brake.
<key n="66">
<name>B</name>
<desc>Toggle parking brake on or off</desc>
<binding>
<command>property-toggle</command>
<property>/controls/brakes[0]</property>
</binding>
<binding>
<command>property-toggle</command>
<property>/controls/brakes[1]</property>
</binding>
<binding>
<command>property-toggle</command>
<property>/controls/brakes[2]</property>
</binding>
</key>

636
Docs/README.xmlpanel Normal file
View file

@ -0,0 +1,636 @@
Users Guide to FlightGear panel configuration
Version 0.7.7.1, June 23 2001
Author: John Check <j4strngs@rockfish.net>
This document describes the configuration of
FlightGear flight simulator's aircraft panel display via XML.
The information was culled from the fgfs-devel@flightgear.org
mailing list and my experiences making alternate panels.
Corrections and additions are encouraged.
Some History:
------------
Older versions of FGFS had a hard coded display of instruments.
This was a less than ideal state of affairs due to FGFS ability
to use different aircraft models. Being primarily developed on
UNIX type systems, a modular approach is taken towards the
simulation. To date, most alternatives to the default
Cessna 172 aircraft are the product of research institutions
interested in the flight characteristics and not cosmetics.
The result of this was that one could fly the X-15 or a Boeing 747
but be limited to C172 instrumentation.
A rewrite of the panel display code was done around v0.7.5 by
developer David Megginson allowing for configuration of the panel
via XML to address this limitation. Some major changes and additions
were made during the course of version 0.7.7 necessitating a rewrite
and expansion of this document.
About The Property Manager:
--------------------------
While intimate knowledge of the property manager is unnecessary to create
aircraft panels, some familiarity with the concept is required.
FlightGear provides a hierarchical representation of all aspects of
the state of the running simulation that is known as the property tree.
Some properties, such as velocities, are read only. Others such as the frequencies
to which the navcom radios are tuned or the position of control surfaces
can be set by various means.
FlightGear can optionally provide an interface to these properties for external
applications such as Atlas, the moving map program, or even lowly telnet via
a network socket. Data can even be routed to a serial port and connected to
say, a GPS receiver.
Aside from its usefulness in a flight training context, being able to manipulate
the property tree on a running copy of FG allows for switching components on the fly,
a positive boon for panel authors.
To see the property tree start FG with the following command line:
fgfs --props=socket,bi,5,localhost,5500,tcp
Then use telnet to connect to localhost on port 5500. You can browse the
tree as you would a filesystem.
XML and the Property Manager:
----------------------------
Panel instruments interface with the property tree to get/set values as
appropriate. Properties for which FG doesn't yet provide a value can be
created by simply making them up. Values can be adjusted using the telnet
interface allowing for creation and testing of instruments while code to
drive them is being developed.
If fact, the XML configuration system allows a user to combine
components such as flight data model, aircraft exterior model,
heads up display, and of course control panel. Furthermore,
such a preconfigured aircraft.xml can be included into a scenario
with specific flight conditions. These can be manually specified or
a FG session can be saved and/or edited and reloaded later. Options specified
in these files can be overridden on the command line. For example:
--prop:/sim/panel/path=Aircraft/c172/Panels/c172-panel.xml
passed as an option, would override a panel specified elsewhere.
Property tree options all have the same format, specify the node
and supply it a value.
The order of precedence for options is thus:
Source Location Format
------ -------- ------
command line
.fgfsrc Users home directory. command line options
system.fgfsrc $FG_ROOT "" ""
preferences.xml $FG_ROOT XML property list
Loading Panels on the fly:
-------------------------
When editing a panel configuration, pressing Shift +F3 will reload the
panel. If your changes don't seem to be taking effect, check the console output.
It will report the success or failure of the panel reload*. Editing textures requires
restarting FGFS so the new textures can be loaded. Panels can be switched on the fly
by setting the /sim/panel/path property value and reloading.
Regarding Window Geometry:
-------------------------
For the sake of simplicity the FGFS window is always considered to be 1024x768
so all x/y values for instrument placement should relative to these dimensions.
Since FG uses OpenGL 0,0 represents the lower left hand corner of the
screen. Panels may have a virtual size larger than 1024x768. Vertical scrolling is accomplished
with Shift+F5/F6. Horizontal scrolling is via Shift+F7/F8. An offset should be supplied
to set the default visible area. It is possible to place items to overlap the 3D viewport.
Panel Architecture:
-------------------
All of the panel configuration files are XML-encoded* property lists.
The root element of each file is always named <PropertyList>. Tags are
almost always found in pairs, with the closing tag having a slash prefixing
the tag name, i.e </PropertyList>. The exception is the tag representing an aliased
property. In this case a slash is prepended to the closing angle bracket.
(see section Aliasing)
The top level panel configuration file is composed of a <name>, a <background>
texture and zero or more <instruments>.
[ Paths are relative to $FG_ROOT ( the installed location of FGFS data files ). ]
[ Absolute paths may be used. Comments are bracketed with <!-- -->. ]
Example Top Level Panel Config:
----------------------------------------
<PropertyList>
<name>Example Panel</name>
<background>Aircraft/c172/Panels/Textures/panel-bg.rgb</background>
<w>1024</w> <!-- virtual width -->
<h>768</h> <!-- virtual height -->
<y-offset>-305</y-offset> <!-- hides the bottom part -->
<view-height>172</view-height> <!-- amount of overlap between 2D panel and 3D viewport -->
<instruments>
<instrument include="../Instruments/clock.xml">
<name>Chronometer</name> <!-- currently optional but strongly recommended -->
<x>150</x> <!-- required horizontal placement -->
<y>645</y> <!-- required vertical placement -->
<w>72</w> <!-- optional width specification -->
<h>72</h> <!-- optional height specification -->
</instrument>
</instruments>
</PropertyList>
Indexed Properties
------------------
The property manager assigns incremental indices to repeated
properties with the same parent node, so that
<PropertyList>
<x>1</x>
<x>2</x>
<x>3</x>
</PropertyList>
shows up as
/x[0] = 1
/x[1] = 2
/x[2] = 3
In fact, the panel I/O code insists that every instrument have the XML element
name "instrument", every layer have the name "layer", every text chunk have the
name "chunk", every action have the name "action", and every transformation
have the name "transformation" -- this makes the XML more regular (so
that it can be created in a DTD-driven tool) and also allows us to
include other kinds of information (such as doc strings) in the lists
without causing confusion.
Inclusion:
----------
Inclusion means that a node can include another property list as if it
were a part of the current file.
To clarify how inclusion works, consider the following examples:
If bar.xml contains
<PropertyList>
<a>1</a>
<b>
<c>2</c>
</b>
</PropertyList>
then the declaration
<foo include="../bar.xml">
</foo>
is exactly equivalent to
<foo>
<a>1</a>
<b>
<c>2</c>
</b>
</foo>
However, it is also possible to selectively override properties in the
included file. For example, if the declaration were
<foo include="../bar.xml">
<a>3</a>
</foo>
then the property manager would see
<foo>
<a>3</a>
<b>
<c>2</c>
</b>
</foo>
with the original 'a' property's value replaced with 3.
Inclusion allows property files to be broken up and reused
arbitrarily -- for example, there might be separate texture cropping
property lists for commonly-used textures or layers, to avoid
repeating the information in each instrument file.
Aliasing
--------
Properties can alias other properties, similar to a symbolic link
in Unix. When the target property changes value, the new value will
show up in the aliased property as well. For example,
<PropertyList>
<foo>3</foo>
<bar alias="/foo"/>
</PropertyList>
will look the same to the application as
<PropertyList>
<foo>3</foo>
<bar>3</bar>
</PropertyList>
except that when foo changes value, bar will change too.
*IMPORTANT*
-----------
The combination of inclusions and aliases is very powerful, because it
allows for parameterized property files. However, you must keep in
mind that when an instrument is included by reference, its root is
*not* the root of the property tree, therefore aliases must be relative.
The relative location of the alias' root in the property hierarchy depends
on whether the alias is used in a layer, a switch or an action.
In lieu of snappy mnemonic, please use the following table.
when alias
is used in go up
--------- -----
layer 5 ( ../../../../../params/foo )
switch 3 ( ../../../params/foo )
action 3 ( ../../../params/foo )
As an example of inclusion and aliasing, consider the XML file
for the NAVCOM radio, which includes a parameter subtree at the start,
like this:
<PropertyList>
<params>
<comm-freq-prop>/radios/comm1/frequencies/selected</comm-freq-prop>
<nav-freq-prop>/radios/nav1/frequencies/selected</nav-freq-prop>
</params>
...
<chunk>
<type>number-value</type>
<property alias="../../../../../params/nav-freq-prop"/>
</chunk>
...
</PropertyList>
The same instrument file is used for navcomm1 and navcomm2 simply by
overriding the parameters at inclusion in the top level panel property list.
<instrument include="../Instruments/navcomm.xml">
<name>NAVCOM 1 radio</name>
<params>
<comm-freq-prop>/radios/comm1/frequencies/selected</comm-freq-prop>
<nav-freq-prop>/radios/nav1/frequencies/selected</nav-freq-prop>
</params>
.....
</instrument>
<instrument include="../Instruments/navcomm.xml">
<name>NAVCOM 2 radio</name>
<params>
<comm-freq-prop>/radios/comm2/frequencies/selected</comm-freq-prop>
<nav-freq-prop>/radios/nav2/frequencies/selected</nav-freq-prop>
</params>
.....
</instrument>
Instrument Architecture:
-----------------------
Instruments are defined in separate configuration files. An instrument
consists of a base width and height, one or more stacked layers,
and zero or more actions. Base dimensions are specified as follows:
<PropertyList> <!-- remember, all xml files start like this -->
<name>Airspeed Indicator</name> <!-- names are good -->
<w-base>128</w-base> <!-- required width spec-->
<h-base>128</h-base> <!-- required height spec-->
<layers> <!-- begins layers section -->
Height and width can be overriden in the top level panel.xml by
specifying <w> and <h>. Transformations are caculated against the base size
regardless of the display size. This ensures that instruments remain calibrated.
Textures:
--------
FG uses red/green/blue/alpha .rgba files for textures. Dimensions for
texture files should be power of 2 with a maximum 8:1 aspect ratio.
The lowest common denominator for maximum texture size is 256 pixels.
This is due to the limitations of certain video accelerators, most notably
those with 3Dfx chipset such as the Voodoo2.
Instrument Layers**:
-------------------
The simplest layer is a <texture>. These can be combined in <switch> layers
<texture>
A texture layer looks like this:
<layer> <!-- creates a layer -->
<name>face</name>
<texture> <!-- defines it as a texture layer -->
<path>Aircraft/c172/Instruments/Textures/faces-2.rgb</path>
<x1>0</x1> <!-- lower boundary for texture cropping-->
<y1>0.51</y1> <!-- left boundary for texture cropping-->
<x2>0.49</x2> <!-- upper boundary for texture cropping-->
<y2>1.0</y2> <!-- right boundary for texture cropping-->
</texture> <!-- closing texure tag -->
</layer> <!-- closing layer tag -->
The texture cropping specification is represented as a decimal. There is a table
at the end of this document for converting from pixel coordinates to percentages.
This particular layer, being a gauge face has no transformations applied to it.
Layers with that aren't static *must* include <w> and <h> parameters to be visible.
<type> May be either text or switch..
<type>switch</type>
A switch layer is composed of two or more nested layers and will display
one of the nested layers based on a boolean property. For a simple example
of a switch see $FG_ROOT/Aircraft/c172/Instruments/brake.xml.
<layer>
<name>Brake light</name>
<type>switch</type> <!-- define layer as a switch -->
<property>/controls/brakes</property> <!-- tie it to a property -->
<layer1> <!-- layer for true state -->
<name>on</name> <!-- label to make life easy -->
<texture> <!-- layer1 of switch is a texture layer -->
<path>Aircraft/c172/Instruments/Textures/brake.rgb</path>
<x1>0.25</x1>
<y1>0.0</y1>
<x2>0.5</x2>
<y2>0.095</y2>
</texture>
<w>64</w> <!-- required width - layer isn't static -->
<h>24</h> <!-- required height - layer isn't static -->
</layer1> <!-- close layer1 of switch -->
<layer2> <!-- layer for false state -->
<name>off</name>
<texture>
<path>Aircraft/c172/Instruments/Textures/brake.rgb</path>
<x1>0.0</x1>
<y1>0.0</y1>
<x2>0.25</x2>
<y2>0.095</y2>
</texture>
<w>64</w>
<h>24</h>
</layer2>
</layer>
Switches can have more than 2 states. This requires nesting one switch inside another.
One could make, for example, a 3 color LED by nesting switch layers.
<type>text</type>
A text layer may be static, as in a label, generated from a property or a combination of both.
This example is a switch that contains both static and dynamic text:
<layer1> <!-- switch layer -->
<name>display</name>
<type>text</type> <!-- type == text -->
<point-size>12</point-size> <!-- font size -->
<color> <!-- specify rgb values to color text -->
<red>1.0</red>
<green>0.5</green>
<blue>0.0</blue>
</color> <!-- close color section -->
<chunks> <!-- sections of text are referred to as chunks -->
<chunk> <!-- first chunk of text -->
<type>number-value</type> <!-- value defines it as dynamic -->
<property>/radios/nav1/dme/distance</property> <!-- ties it to a property -->
<scale>0.00053995680</scale> <!-- convert between statute and nautical miles? -->
<format>%5.1f</format> <!-- define format -->
</chunk>
</chunks>
</layer1>
<layer2> <!-- switch layer -->
<name>display</name>
<type>text</type> <!-- type == text -->
<point-size>10</point-size> <!-- font size -->
<color> <!-- specify rgb values to color text -->
<red>1.0</red>
<green>0.5</green>
<blue>0.0</blue>
</color> <!-- close color section -->
<chunks> <!-- sections of text are referred to as chunks -->
<chunk> <!-- first chunk of text -->
<type>literal</type> <!-- static text -->
<text>---.--</text> <!-- fixed value -->
</chunk>
</chunks>
</layer2>
Transformations:
---------------
A transformation is a rotation, an x-shift, or a y-shift. Transformations
can be static or they can be based on properties. Static rotations are
useful for flipping textures horizontally or vertically. Transformations
based on properties are useful for driving instrument needles. I.E. rotate the
number of degrees equal to the airspeed. X and y shifts are relative to the
center of the instrument. Each specified transformation type takes an <offset>.
Offsets are relative to the center of the instrument. A shift without an offset
has no effect. For example, let's say we have a texure that is a circle. If we
use this texture in two layers, one defined as having a size of 128x128 and
the second layer is defined as 64x64 and neither is supplied a shift and offset
the net result appears as 2 concentric circles.
About Transformations and Needle Placement:
------------------------------------------
When describing placement of instrument needles, a transformation offset must
be applied to shift the needles fulcrum or else the needle will rotate around it's
middle. The offset will be of <type> x-shift or y-shift depending on the orientation of
the needle section in the cropped texture.
This example comes from the altimeter.xml
<layer>
<name>long needle (hundreds)</name> <!-- the altimeter has more than one needle -->
<texture>
<path>Aircraft/c172/Instruments/Textures/misc-1.rgb</path>
<x1>0.8</x1>
<y1>0.78125</y1>
<x2>0.8375</x2>
<y2>1.0</y2>
</texture>
<w>8</w>
<h>56</h>
<transformations> <!-- begin defining transformations -->
<transformation> <!-- start definition of transformation that drives the needle -->
<type>rotation</type>
<property>/steam/altitude</property> <!-- bind it to a property -->
<max>100000.0</max> <!-- upper limit of instrument -->
<scale>0.36</scale> <!-- once around == 1000 ft -->
</transformation> <!-- close this transformation -->
<transformation> <!-- this one shifts the fulcrum of the needle -->
<type>y-shift</type> <!-- y-shift relative to needle -->
<offset>24.0</offset> <!-- amount of shift -->
</transformation>
</transformations>
</layer>
This needles has its origin in the center of the instrument. If the needles fulcrum was
towards the edge of the instrument, the transformations to place the pivot point must
precede those which drive the needle,
Interpolation
-------------
Non linear transformations are now possible via the use of interpolation tables.
<transformation>
...
<interpolation>
<entry>
<ind>0.0</ind> <!-- raw value -->
<dep>0.0</dep> <!-- displayed value -->
</entry>
<entry>
<ind>10.0</ind>
<dep>100.0</dep>
</entry>
<entry>
<ind>20.0</ind>
<dep>-5.0</dep>
</entry>
<entry>
<ind>30.0</ind>
<dep>1000.0</dep>
</entry>
</interpolation>
</transformation>
Of course, interpolation tables are useful for non-linear stuff, as in
the above example, but I kind-of like the idea of using them for
pretty much everything, including non-trivial linear movement -- many
instrument markings aren't evenly spaced, and the interpolation tables
are much nicer than the older min/max/scale/offset stuff and should
allow for a more realistic panel without adding a full equation parser
to the property manager.
If you want to try this out, look at the airspeed.xml file in the base
package, and uncomment the interpolation table in it for a
very funky, non-linear and totally unreliable airspeed indicator.
Actions:
-------
An action is a hotspot on an instrument where something will happen
when the user clicks the left or center mouse button. Actions are
always tied to properties: they can toggle a boolean property, adjust
the value of a numeric property, or swap the values of two properties.
The x/y placement for actions specifies the origin of the lower left corner.
In the following example the first action sets up a hotspot 32 pixels wide
and 16 pixels high. It lower left corner is placed 96 pixels (relative to the
defined base size of the instrument) to the right of the center of the
instrument. It is also 32 pixels below the centerline of the instrument.
The actual knob texture over which the action is superimposed is 32x32.
Omitted here is a second action, bound to the same property, with a positive
increment value. This second action is placed to cover the other half of the
knob. The result is that clicking on the left half of the knob texture decreases
the value and clicking the right half increases the value. Also omitted here
is a second pair of actions with the same coordinates but a larger increment
value. This second pair is bound to a different mouse button. The net result
is that we have both fine and coarse adjustments in the same hotspot, each
bound to a different mouse button.
These examples come from the radio stack:
<actions> <!-- open the actions section -->
<action> <!- first action -->
<name>small nav frequency decrease</name>
<type>adjust</type>
<button>0</button> <!-- bind it to a mouse button -->
<x>96</x> <!-- placement relative to instrument center -->
<y>-32</y>
<w>16</w> <!-- size of hotspot -->
<h>32</h>
<property>/radios/nav1/frequencies/standby</property> <!-- bind to a property -->
<increment>-0.05</increment> <!-- amount of adjustment per mouse click -->
<min>108.0</min> <!-- lower range -->
<max>117.95</max> <!-- upper range -->
<wrap>1</wrap> <!-- boolean value -- value wraps around when it hits bounds -->
</action>
<action>
<name>swap nav frequencies</name>
<type>swap</type> <!-- define type of action -->
<button>0</button>
<x>48</x>
<y>-32</y>
<w>32</w>
<h>32</h>
<property1>/radios/nav1/frequencies/selected</property1> <!-- properties to toggle between -->
<property2>/radios/nav1/frequencies/standby</property2>
</action>
<action>
<name>ident volume on/off</name>
<type>adjust</type>
<button>1</button>
<x>40</x>
<y>-24</y>
<w>16</w>
<h>16</h>
<property>/radios/nav1/ident</property> <!-- this property is for Morse code identification of nav beacons -->
<increment>1.0</increment> <!-- the increment equals the max value so this toggles on/off -->
<min>0</min>
<max>1</max>
<wrap>1</wrap> <!-- a shortcut to avoid having separate actions for on/off -->
</action>
</actions>
More About Textures:
-------------------
As previously stated, the usual size instrument texture files in FGFS are 256x256
pixels, red/green/blue/alpha format. However the mechanism for specifying
texture cropping coordinates is decimal in nature. When calling a section
of a texture file the 0,0 lower left convention is used.
There is a pair of x/y coordinates defining which section of the texture
to use.
The following table can be used to calculate texture cropping specifications.
# of divisions | width in pixels | decimal specification
per axis
1 = 256 pixels 1
2 = 128 pixels, 0.5
4 = 64 pixels, 0.25
8 = 32 pixels, 0.125
16 = 16 pixels, 0.0625
32 = 8 pixels, 0.03125
64 = 4 pixels, 0.015625
128 = 2 pixels, 0.0078125
A common procedure for generating gauge faces is to use a
vector graphics package such as xfig, exporting the result as a
postscript file. 3D modeling tools may also be used and I prefer them
for pretty items such as levers, switches, bezels and so forth.
Ideally, the size of the item in the final render
should be of proportions that fit into the recommended pixel widths.
The resulting files can be imported into a graphics manipulation
package such as GIMP, et al for final processing.
How do I get my panels/instruments into the base package?
-------------------------------------------------------
Cash bribes always help ;) Seriously though, there are two main considerations.
Firstly, original artwork is a major plus since you as the creator can dictate the terms
of distribution. All Artwork must have a license compatible with the GPL.
Artwork of unverifiable origin is not acceptable.
Secondly, texture sizes must meet the lowest common denominator of 256e2 pixels.
Artwork from third parties may be acceptable if it meets these criteria.
* If there are *any* XML parsing errors, the panel will fail to load,
so it's worth downloading a parser like Expat (http://www.jclark.com/xml/)
for checking your XML. FlightGear will print the location of errors, but
the messages are a little cryptic right now.
** NOTE: There is one built-in layer -- for the mag compass ribbon --
and all other layers are defined in the XML files. In the future,
there may also be built-in layers for special things like a
weather-radar display or a GPS (though the GPS could be handled with
text properties).