Howto:Add a 2D canvas instrument to your aircraft

From FlightGear wiki
Jump to: navigation, search

A canvas is just a conventional texture but unlike a static texture it is not loaded from your file system, a canvas texture is dynamically created and updated using the property tree and Nasal scripting, therefore we need an object to place it onto. This object can either be directly contained in the aircraft model (see also AC file basics) or included through a model xml-file (see Howto:3D_Aircraft_Models). The canvas system is not able to add new textures to objects, therefore it is important that the targeted object has assigned a valid texture. It's however ok to have for example just a black texture.

We also need to be able to uniquely identify the object, which can be done either by the name of the used texture, the name of the object itself, the name of a parent object or also by a combination of the just mentioned identifiers.

Creating a canvas with Nasal

The canvas can be fully controlled by just using the property tree, but as this can get quite cumbersome we will use the Nasal API which provides an easy to use, object oriented interface and hides the interaction with the property tree.

The latest information on the canvas subsystem can be found in README.canvas: https://sourceforge.net/p/flightgear/flightgear/ci/next/tree/docs-mini/README.canvas

Adding a Nasal file to the aircraft

First we need to create a new Nasal file and add it to our aircraft (see Howto:Write_simple_scripts_in_Nasal):

  1. Open your aircraft-set.xml file
  2. Go to the <nasal></nasal> section, add a new Nasal module (the example uses the name "canvas_demo". Don't use "canvas" because it's reserved for the canvas API itself).
  3. Add a <file></file> tag specifying the Nasal module that implements your Canvas instrument (Aircraft/Instruments-3d/canvas-test/canvas-test.nas in this case).
<nasal>
 <canvas_demo>
  <file>Aircraft/Instruments-3d/canvas-test/canvas-test.nas</file>
 </canvas_demo>
</nasal>

Initialize a Canvas

The first thing we need to do is creating a new canvas object. This can be done by calling the canvas.new with a hash containing some basic settings:

var my_canvas = canvas.new({
  "name": "PFD-Test",   # The name is optional but allow for easier identification
  "size": [1024, 1024], # Size of the underlying texture (should be a power of 2, required) [Resolution]
  "view": [768, 1024],  # Virtual resolution (Defines the coordinate system of the canvas [Dimensions]
                        # which will be stretched the size of the texture, required)
  "mipmapping": 1       # Enable mipmapping (optional)
});

Place it somewhere on your aircraft

The next step is to place the Canvas somewhere in the scene. For this we use the addPlacement method of the Canvas object. A placement is something the canvas will be placed onto. In FlightGear 2.8 this can only be an object (AC3D object) in the aircraft model. It is important that the targeted object is texture mapped and has a valid texture assigned (event if its just a black 1x1 sized texture). With addPlacement you tell the Canvas system to replace the texture of your object with the Canvas texture. To identify the object you can pass multiple of the following values in a hash:

  • node: Match the name of the object
  • texture: Match the name of the assigned texture (only basename, no directory)
  • parent: Match every object which has a parent object with the given name (Normally combined with second matching criterion if one would not be unique)
# Place it on all objects called PFD-Screen
my_canvas.addPlacement({"node": "PFD-Screen"});

Drawing on the Canvas

After we have created a canvas and placed it into the scene we can actually start drawing onto it. The API is inspired by OpenVG and SVG and uses many concepts from them.

Basically we build up a tree of elements, where each leaf of the tree represents some graphical item. Every element can have different properties which control how it will finally be drawn. By using a stack of transformations for each element they can also be transformed in any possible way (translation, rotation, scale, etc.)

The basic element is of type Group which is the only element that can have other elements as children. So the first step is to create a new group:

var group = my_canvas.createGroup();

Inside this group you can create every type of supported element by calling Group::createChild(type [, name]). Afterwards you can set various values to influence the appearance of the element (all methods should be chainable):

# Create a text element and set some values
var text = group.createChild("text", "optional-id-for element")
                .setTranslation(10, 20)      # The origin is in the top left corner
                .setAlignment("left-center") # All values from osgText are supported (see $FG_ROOT/Docs/README.osgtext)
                .setFont("LiberationFonts/LiberationSans-Regular.ttf") # Fonts are loaded either from $AIRCRAFT_DIR/Fonts or $FG_ROOT/Fonts
                .setFontSize(14, 1.2)        # Set fontsize and optionally character aspect ratio
                .setColor(1,0,0)             # Text color
                .setText("This is a text element");
# ...
text.hide();
# ...
text.setText("Hello!").show();

Supported alignment options can be seen in api.nas line 415.

For a list of supported OpenVG path directives, please see api.nas line 479 and api.nas line 519.

Demo (c172p-canvas)

Use the release/3.4.0 branch of fgdata , launch FlightGear with --aircraft=c172p-canvas and rotate the view down a bit to see the Canvas in action: