1This document describes how FlightGear searches and loads scenery, how to
2add static objects to the scenery as well as the syntax of *.stg files.
3
4
5
6
7Contents ----------------------------------------------------------------------
8
91  scenery path
102  terrasync
11
123  stg files
13   3.1  OBJECT_BASE
14   3.2  OBJECT
15   3.4  OBJECT_SHARED / OBJECT_SHARED_AGL
16   3.3  OBJECT_STATIC / OBJECT_STATIC_AGL
17   3.5  OBJECT_SIGN /OBJECT_SIGN_AGL
18   3.6  BUILDING_ROUGH / BUILDING_DETAILED
19   3.7  ROAD_ROUGH / ROAD_DETAILED
20   3.8  RAILWAY_ROUGH / RAILWAY_DETAILED
21   3.9  BUILDING_LIST
22
234  model manager ("/models/model")
24   4.1  static objects
25   4.2  dynamic objects
26   4.3  loading/unloading at runtime
27
285  tools for object placing
29   5.1  calc-tile.pl
30   5.2  ufo scenery object editor
31
326  embedded Nasal
33   6.1  static models
34   6.2  AI models
35
36
37
38
391 scenery path ----------------------------------------------------------------
40
41FlightGear loads scenery by default from the Scenery/ subdirectory of its
42data directory. The path to this data directory can be set via environment
43variable FG_ROOT or the --fg-root option. The scenery path can be set
44independently via environment variable FG_SCENERY or option --fg-scenery.
45The order of precedence is as follows:
46
47  --fg-scenery=/some/dir         ... highest priority
48  $FG_SCENERY
49  $FG_ROOT/Scenery/              ... lowest priority
50
51
52A scenery specification may be a list of paths, separated by the OS-specific
53path separator (colon on Unix/OSX, semicolon on MS Windows). The paths are
54searched in the order from left to right:
55
56  FG_SCENERY=/first/dir:/second/dir:/third/dir
57                                     (likewise with --fg-scenery option)
58
59Each of the scenery paths normally contains a set of directories, each containing
60a particular scenery type :
61
62 Terrain/ containing the terrain mesh and airport data
63 Objects/ containing placed scenery objects such as large landmarks, bridges
64 Pylons/  containing electricity pylon Models
65 Roads/   containing detailed road and railways
66 Buildings/ containing osm2city-generated buildings
67
68The user can control at runtime which of these directories is actually loaded
69via the property tree:
70/sim/rendering/scenery-path-suffix[n]/name      defines the name of the sub-directory
71/sim/rendering/scenery-path-suffix[n]/enabled   indicates whether it is loaded.
72
73hese properties are typically set in the Rendering Options menu.
74
75In turn each of these contains a tree of directories breaking down the world into
7610x10 degree and then 1x1 degree chunks.  E.g. Terrain/w130n30/w123n37/
77
78For backwards compatibility reasons, the top level can alternatively just
79contain 10x10 degree directories.  E.g. w130n30/w123n37/
80
81Note that as soon as any of the scenery-path-suffix directories is found, any
8210x10 directory on the same hierarchy level will be ignored!
83
84This example shows which directories are used to search for scenery:
85
86  $ ls /first/dir
87  w130n30/                   searched
88
89  $ ls /second/dir
90  Objects/                   searched
91  Terrain/                   searched
92  w130n30/                   *not* searched
93
94  $ ls /third/dir
95  Terrain/                   searched
96  w130n30/                   *not* searched
97
98
99Within the 1x1 degree chunks, the ground is further broken up into "tiles" of
100approx 20x20km. If FlightGear searches for a particular "tile" file, let's say for
101"w130n30/w123n37/942050.stg", then (using the above examples) it looks
102into
103
104  /first/dir/w130n30/w123n37/942050.stg             (A)
105
106  /second/dir/Terrain/w130n30/w123n37/942050.stg      (B)\__ same path element
107  /second/dir/Objects/w130n30/w123n37/942050.stg      (C)/      /second/dir
108  /second/dir/Pylons/w130n30/w123n37/942050.stg       (D)/
109  /second/dir/Roads/w130n30/w123n37/942050.stg        (E)/
110  /second/dir/Buildings/w130n30/w123n37/942050.stg    (F)/
111
112  /third/dir/Terrain/w130n30/w123n37/942050.stg       (G)
113
114As soon as it finds an OBJECT_BASE entry it only finishes this
115path element and then stops scanning. So, if (B) contains an entry
116"OBJECT_BASE 942050.btg, then the other directories in /second/dir (C, D, E, F)
117will be read.  But (G) will *not*!
118
119This searching behavior is usually used to collect user-downloaded scenery first,
120then to read automatically downloaded scenery (see terrasync
121below), then standard scenery and objects that came with the distribution.
122So a typical scenery path specification could look like this:
123
124  FG_SCENERY=$HOME/.fgfs/Scenery:$HOME/.fgfs/TerraSync:$FG_ROOT/Scenery
125
126The first path would then be populated by the user with unpacked scenery
127archives downloaded various sources.
128
129Using a private directory for downloaded add-on scenery and adding
130that path to FG_SCENERY is the preferred way. This separates default
131data from locally added data, and makes administration and later updates
132easier.
133
134HINT: if you want to see where FlightGear is searching and finding
135terrain/objects, start it with the --log-level=info option.
136
1372 terrasync -------------------------------------------------------------------
138
139FlightGear can download scenery on-the-fly.  Simply select "Download scenery
140automatically" from the launcher, or use --enable-terrasync from the commandline.
141By default this will add $HOME/.fgfs/TerraSync to the scenery path.
142
143Note, however, that if it downloads scenery for the area around your
144starting location, then you'll only see that after the next start, or
145after you flew or teleported to a distant location and then back.
146
1472.1 .dirindex -----------------------------------------------------------------
148
149To save having to download scenery every time, while allowing updates to
150propagate, terrasync checks for a .dirindex file each time it downloads a
151directory from the server.  This lists files, directories and tarballs that
152should be present in the local directory, along with their size and a sha1sum
153of their contents.  This sha1sum is checked against the local files to
154determine which files are to be downloaded.
155
156The format of the .dirindex is as follows:
157
158version:<version>
159path:<scenery_path>
160<type>:<filename>:<sha1sum>:<size>
161<type>:<filename>:<sha1sum>:<size>
162<type>:<filename>:<sha1sum>:<size>
163.
164.
165.
166
167Where:
168
169<version> is the .dirindex version number
170<scenery_path>  is the path of the current file, e.g. Objects/w010n50/w009n57
171<type>  is the file type - "f" for file, "d" for directory, "t" for tarball
172<filename> is the file name  (e.g. 2811121.stg)
173<sha1sum>  is the sha1sum of the file named (e.g. 9da6ebc1695ed1c3b151cd34263e9c931ee309ea)
174<size>     is the size of the file
175
176Tarballs (labelled with a "t" in the .dirindex) must be <name>.tgz files which
177unpack into a <name>/ sub-directory.  This enables terrasync to clean up
178out of date files by simply deleting the <name>/ subdirectory before unpacking
179the updated file.
180
1813 stg files -------------------------------------------------------------------
182
183stg files ("static terragear") define the static elements of a scenery
184"tile", including the terrain elevation data, airport geometry, and all
185static objects placed on this tile. (See section 5 for how to find out which
186geo coordinates belong to which tile.) Four of the available key words
187are followed by a string and four numbers. The meaning of these numbers
188is always the same and described in section 3.3.
189
1903.1  OBJECT_BASE
191----------------
192
193specifies the terrain elevation data file. These files are generated with
194the TerraGear tools (http://www.terragear.org/) and have file extension
195".btg" ("binary terragear"; there used to be an "*.atg" file, too, where
196the 'a' stood for ASCII).
197
198Example:
199
200  OBJECT_BASE 942050.btg
201
202The entry may be anywhere in the 942050.stg file, on a separate line.
203
2043.2  OBJECT
205-----------
206
207specifies an airport geometry 'drop-in' file. The scenery elevation file
208has cut out holes for airports, that are filled with such objects. They
209are usually called after the airport ICAO id:
210
211Example:
212
213  OBJECT KSFO.btg
214
215These files are, again, created by TerraGear tools and are usually gzipped,
216so you'll find that file stored as KSFO.btg.gz.
217
218
219
220
2213.3  OBJECT_SHARED / OBJECT_SHARED_AGL
222--------------------------------------
223
224add static object to the tile.
225
226Example:
227
228  OBJECT_SHARED Models/Airport/tower.xml -122.501090 37.514830 15.5 0.00 0.00 0.00
229
230Syntax:
231
232  OBJECT_SHARED <object-path> <lon> <lat> <elev-m> <hdg-deg> <pitch-deg> <roll-deg>
233
234The <object-path> is relative to the data directory (FG_ROOT).
235<elev-m> is in meter and relative to mean sea-level (in the fgfs world).
236<hdg-deg> is in degree, counter-clockwise with North being 0.0. Note
237that this differs from about every other place in FlightGear, most notably
238the /orientation/heading-deg entry in the property system, which is clockwise.
239<pitch-deg> and <roll-deg> are in degree and optional.
240OBJECT_SHARED models are cached and reused. They are only once in memory
241and never freed. (See also the next section.)
242
243OBJECT_SHARED_AGL places the object relative to the ground elevation.  Note that
244this is an expensive operation and is strongly discouraged.
245
2463.4  OBJECT_STATIC / OBJECT_STATIC_AGL
247--------------------------------------
248
249add static objects to the tile, just like OBJECT_SHARED. There are three
250differences to OBJECT_SHARED (apart from the name):
251
252(A) the path is relative to the tile directory where the *.stg file with
253    this entry is located. For example, relative to 130n30/w123n37/. This
254    usually means that all 3D object files, textures, and XML animation
255    files are in this tile directory, too.
256
257(B) these objects are *not* cached and kept loaded, but rather freed with
258    the tile (that is, when you leave that area).
259
260(C) the animation XML files may contain Nasal blocks <nasal><load> and
261    <nasal><unload> which are executed on loading/unloading.
262
263Example:
264
265  OBJECT_STATIC ggb-fb.xml -122.4760494 37.81876042 0 105 0.00 0.00
266
267
268OBJECT_STATIC_AGL places the object relative to the ground elevation.  Note that
269this is an expensive operation and is strongly discouraged.
270
271
2723.5 OBJECT_SIGN / OBJECT_SIGN_AGL
273---------------------------------
274
275defines taxiway or runway sign. The syntax is much like that of OBJECT_SHARED
276entries, except that the path is replaced with a sign contents specification
277and that there is an additional size value at the end of the line.
278
279Example:
280
281  OBJECT_SIGN {@R}10L-28R{@L}C -122.35797457 37.61276290 -0.5398 74.0 2
282
283The sign specification defines the sign contents. We try to resemble the
284apt.dat 850 specifications in our implementation.
285In the simplest form it contains just 'normal' text, for example:
286
287  EXIT
288
289This will create a black panel of 1m height with "EXIT" written on it
290in white versal letters. Actually, each of those characters are
291single-letter glyph names that are looked up in the <glyph> map of a
292texture font <material> entry in $FG_ROOT/materials.xml. It just
293happens that the <glyph> entry for <name> 'E' maps to a drawn 'E' in
294the font texture. This isn't true for all ASCII characters. Many aren't
295mapped at all (and thus not available), others are mapped to non-standard
296drawings. The '_', for example, is mapped to an empty black area and can
297therefore be used as a space. (The sign specification must not contain
298real spaces.) The '*' is mapped to a raised period.
299
300Some glyph names consist of more than one character, and can't, thus, be
301used directly. They have to be put in a pair of curly braces:
302
303  {^rd}
304
305This creates an arrow that points to the right and down. Braces can really
306contain a list of glyph names, separated by commas (no space!).
307Single-letter glyph names can be used that way, too, or in any mixture
308of both methods:
309
310  EXIT
311  {E,X,I,T}
312  {E}{X}{I}{T}
313  EX{I,T}
314  E{X,I}T{^lu,^rd}
315  {^u}EXIT{^u}
316
317
318Multi-letter glyph names are usually used for symbols. Arrow symbol names
319always start with a caret ("arrow head") and the left or right direction
320always comes first (like the x in a Cartesian coordinate system). Here's
321a list of some of the available names (see $FG_ROOT/materials.xml for
322more):
323
324
325  ^l       left arrow
326  ^r       right arrow
327  ^u       up arrow
328  ^d       down arrow
329  ^lu      left-up arrow
330  ^ld      left-down arrow
331  ^ru      right-up arrow
332  ^rd      right-down arrow
333  no-entry "no entry" symbol
334  critical runway critical area
335  safety   ils safety area
336  hazard   end of taxiway
337
338
339
340There are commands for pre-defined sign types according to the FAA
341specification (5345-44; see http://www.google.com/search?q=5345-44g).
342
343  @Y   "Direction, Destination, Boundary" sign (black on yellow)
344  @R   "Mandatory Instruction" sign (white on red with black outline)
345  @L   "Location" sign  (yellow text and frame on black)
346  @B   "Runway Distance Remaining" sign  (white on black)
347
348
349Examples:
350
351  {@R}10L-28R{@L}C
352  {@Y,^l}P|{^lu}N{@L}F{@Y}F{^ru}
353  {@Y,^ld}C   ...  same as any of {@Y}{@ld}C  {@Y,@ld,C}
354  {@B}17
355
356
357Syntax errors are reported in --log-level=debug, in the SG_TERRAIN
358group. You can use this command line to filter out such messages:
359
360  $ fgfs --log-level=debug 2>&1|grep OBJECT_SIGN
361
362OBJECT_SIGN_AGL places the sign relative to the ground elevation.  Note that
363this is an expensive operation and is strongly discouraged.
364
3653.6  BUILDING_ROUGH / BUILDING_DETAILED
366---------------------------------------
367
368defines building meshes, typically based on OSM data.
369
370Example:
371
372  BUILDING_ROUGH buildings.ac -122.501090 37.514830 15.5 0.00 0.00 0.00
373
374Syntax:
375
376  BUILDING_ROUGH <object-path> <lon> <lat> <elev-m> <hdg-deg> <pitch-deg> <roll-deg>
377
378Note that only bare .ac files should be referenced. The material definition for
379"OSM_Building" will be used to determine the texture and Effects.
380
381BUILDING_ROUGH uses the "rough" LOD range, while BUILDING_DETAILED uses the
382"detailed" LOD range. Some randomness is applied so that building meshes
383gradually fade in
384
3853.7  ROAD_ROUGH / ROAD_DETAILED
386-------------------------------
387
388Identical to BUILDING_ROUGH / BIULDING_DETAILED above, except used for roads.
389the material definition "OSM_Road" is applied.
390
3913.8  RAILWAY_ROUGH / RAILWAY_DETAILED
392-------------------------------
393
394Identical to BUILDING_ROUGH / BIULDING_DETAILED above, except used for roads.
395the material definition "OSM_Railway" is applied.
396
3973.9  BUILDING_LIST
398------------------
399
400Defines a file containing building coordinates that should be rendered using
401the building shader (aka Random Buildings).
402
403Example:
404
405  BUILDING_LIST buildings.txt OSM_Building -2.72943543 56.00080606 36.1
406
407Syntax
408
409  BUILDING_LIST <filename> <material name> <lon> <lat> <elev>
410
411Where:
412- <filename> is the name of a file containing building positions
413- <material name> is the name of the material that will be referenced to find
414  random building parameters.
415- <lat>, <lon>, <elev> defines the center of the set of buildings, and also
416  the point at which the material definition will be evaluated (for regional
417  materials).
418
419See README.materials for details on configuring the random building parameters.
420
421The referenced <filename> (in the example buildings.txt) contains lines of the form
422
423X Y Z R B W D H P S O F WT RT
424
425Where:
426- X,Y,Z are the cartesian coordinates of the center of the front face. +X is East, +Y is North
427- R is the building rotation in degrees centered on the middle of the front face.
428- B is the building type [0, 1, 2] for SMALL, MEDIUM, LARGE
429- W is the building width in meters
430- D is the building depth in meters
431- H is the building height in meters, excluding any pitched roof
432- P is the pitch height in meters. 0 for a flat roof
433- S is the roof shape (only 0, 2, 4, 6 are implemented, others are approximated to those) :
434    0=flat 1=skillion 2=gabled 3=half-hipped 4=hipped 5=pyramidal 6=gambrel
435    7=mansard 8=dome 9=onion 10=round 11=saltbox
436- O is the roof ridge orientation :
437    0 = parallel to the front face of the building
438    1 = orthogonal to the front face of the building
439- F is the number of floors (integer)
440- WT is the texture index to use for walls (integer). Buildings with the same WT value will have the same wall texture assigned.  There are 6 small, 6 medium and 4 large textures.
441- RT is the texture index to use for roofs (integer). Buildings with the same RT value will have the same roof texture assigned.  There are 6 small, 6 medium and 4 large textures.
442
443<x> <y> <z> <rot> <type>
444
445where :
446- (<x>,<y>,<z>) define the bottom left corner of the building in cartesian space (+X is North, +Y is East, +Z is up), with (0,0,0) being the position referenced above
447- <rot> is the clockwise rotation around the Z-axis in degrees, rotating around the bottom left (SW) corner of the building
448- <type> is {0,1,2} which map to small, medium and large buildings respectively, as per random buildings.
449
450For example, the following entries generates 3 small, 2 medium and 2 large buildings in an easterly line:
451
4520 0 0 0 0
4530 100 0 0 0
4540 200 0 0 0
4550 300 0 0 1
4560 400 0 0 1
4570 500 0 0 2
458
4594 model manager ("/models/model") --------------------------------------------
460
461
4624.1  static objects
463-------------------
464
465Another way to add objects to the scenery is via the "model manager".
466It reads all /models/model entries at startup and places these objects
467in the scenery. Just load a definition like the following into the
468property tree, for example by putting it into $FG_ROOT/preferences.xml, or
469better: an XML file that you load with e.g. --config=$HOME/.fgfs/stuff.xml:
470
471  <models>
472      <model n="0">
473          <name>pony</name>
474          <path>Local/pony.ac</path>
475          <longitude-deg>-115.8352869</longitude-deg>
476          <latitude-deg>37.24302849</latitude-deg>
477          <elevation-ft>4534.691321</elevation-ft>
478          <heading-deg>0</heading-deg>
479          <pitch-deg>0</pitch-deg>
480          <roll-deg>0</roll-deg>
481      </model>
482  </models>
483
484The <path> is relative to $FG_ROOT, the <name> is optional. One can leave the
485heading/pitch/roll entries away, in which case they are set to zero. The values
486are fixed and unchangeable at runtime.
487
488
489
490
4914.2  dynamic objects
492--------------------
493
494Any of the model properties can be made changeable at runtime by appending
495"-prop" and using a property path name instead of the fixed value:
496
497  <local>
498      <pony>
499          <longitude-deg>-115.8352869/<longitude-deg>
500          <latitude-deg>37.24302849</latitude-deg>
501          <elevation-ft>4534.691321</elevation-ft>
502          <heading-deg>0</heading-deg>
503      </pony>
504  </local>
505
506  <models>
507      <model n="1">
508          <name>pony</name>
509          <path>Local/pony.ac</path>
510          <longitude-deg-prop>/local/pony/longitude-deg</longitude-deg-prop>
511          <latitude-deg-prop>/local/pony/latitude-deg</latitude-deg-prop>
512          <elevation-ft-prop>/local/pony/elevation-ft</elevation-ft-prop>
513          <heading-deg-prop>/local/pony/heading-deg</heading-deg-prop>
514          <pitch-deg>1.234</pitch-deg>  <!-- static, just for fun -->
515      </model>
516  </models>
517
518Then one can move the pony around by changing the values in /local/pony/ in
519the property system. One can, of course, use other animals, too.
520
521
522
523
5244.3 loading/unloading at runtime
525--------------------------------
526
527Both dynamic and static model-manager-models can be loaded and unloaded
528at runtime. For loading you first create a new <model> entry under <models>,
529initialize all properties there (<longitude-deg> or <longitude-deg-prop>,
530etc.), and finally you create a child <load> of any type in this group.
531This is the signal for the model manager to load the object. You can
532remove the <load> property after that. It has no further meaning.
533
534To remove a model-manager model at runtime, you simply delete the whole
535<model> group.
536
537
538
539
540
5415 tools for object placing ----------------------------------------------------
542
543
5445.1 calc-tile.pl
545----------------
546
547For finding out the tile number for a given geo coordinate pair there's
548a script "scripts/perl/scenery/calc-tile.pl" in the FlightGear sources.
549You feed longitude and latitude to it and it returns the path to the
550*.stg file where you have to add the object entry.
551
552  $ perl calc-tile.pl 16.1234 48.5678
553  Longitude: 16.1234
554  Latitude:  48.5678
555  Tile:      3220128
556  Path:      "e010n40/e016n48/3220128.stg"
557
558
559
560
5615.2 ufo scenery object editor
562-----------------------------
563
564The ufo has a scenery object editor built-in. It uses the model manager
565described in section 4. To place objects with it, start fgfs, optionally
566with specifying an initial model type ("cursor") and a list of subdirectories
567of $FG_ROOT where the ufo should search for available 3D models ("source"):
568
569  $ fgfs --aircraft=ufo --prop:cursor=Models/Airport/radar.xml  \
570                        --prop:source=Models,Scenery/Objects
571
572Then click anywhere on the terrain to add a model (left mouse button).
573You can open the adjustment dialog (Tab-key) to make adjustments to
574position and orientation. Click as often as you like, choose further
575models from the space-key dialog. You can select an already placed object
576by Ctrl-clicking at its base (not at the object itself, but the surface
577point where it's located!). By also holding the Shift key down, you
578can select several objects or add them to a selection. You can remove
579the selected object(s) with the Backspace-key. (See the ?-key dialog
580for futher available keys.) After clicking on the input field right
581over the status line (invisible if there's no text in it) you can enter
582a comment/legend for the selected object.
583
584And finally, you dump the object data to the terminal (d-key) or export
585them to a file $HOME/.fgfs/ufo-model-export.xml (Unix) or
586%APPDATA%\flightgear.org\ufo-model-export.xml (MS Windows).
587
588You can now put the generated object entries into the specified *.stg
589file to make them permanent. Or load the whole exported *.xml file
590via --config option:
591
592  $ fgfs --config=$HOME/.fgfs/ufo-model-export.xml
593
594If you choose the sign placeholder object from the m-key dialog (first
595entry; "Aircraft/ufo/Models/sign.ac"), then an OBJEC_SIGN *.stg line
596will be generated with the legend used as sign contents. If you didn't
597insert any legend, then the sign text will be: NO CONTENTS and a 4 digits
598random number for later identification in the *.stg file.
599
600Unfortunately, objects added with this method are kept in memory, no
601matter where you are actually flying, so the *.stg method is preferable.
602
603
604
605
606
6076 embedded Nasal in XML files (static objects and AI) -------------------------
608
609
6106.1 static models
611-----------------
612
613Objects loaded via OBJECT_STATIC in *.stg files as well as AI models loaded
614via scenarios may contain embedded Nasal code. This can be used to drive
615more advanced animations. An example is a lighthouse with specific light
616signals, or hangar doors that open when the "player"'s aircraft is nearby.
617The Nasal code is added to the object's XML wrapper/animation file, anywhere
618on the top level, for example:
619
620
621   <PropertyList>
622       <path>lighthouse.ac</path>
623
624       <nasal>
625           <load>
626               var loop_id = 0;
627               var light = aircraft.light.new("
628                       "/models/static/w120n30/w118n35/lighthouse/light",
629                       [2, 1, 2, 1, 2, 1, 2, 5]);
630
631               var loop = func(id) {
632                   id == loop_id or return;
633                   light.switch(getprop("/sim/time/sun-angle-rad") > 1.37);
634                   settimer(func { loop(id) }, 30);
635               }
636               loop(loop_id += 1);
637           </load>
638
639           <unload>loop_id += 1</unload>
640       </nasal>
641
642       <animation>
643           <type>select</type>
644           <object-name>light-halo</object-name>
645           <property>/models/static/w120n30/w118n35/lighthouse/light/state</property>
646       </animation>
647
648       ...
649   </PropertyList>
650
651
652The <load> part is executed when the scenery tile on which the model is placed
653is loaded into memory. It can start timers or listeners that modify properties,
654which are then queried by the <animation>. As a convention developers are requested
655to use "/models/static/" + <tile-path> + <file-basename>. So, in the above example
656file "$FG_ROOT/Scenery/Objects/w120n30/w118n35/lighthouse.xml" all properties
657are stored under "/models/static/w120n30/w118n35/lighthouse/". That way collisions
658with other models are quite unlikely.
659
660An optional <unload> part is executed when the tile and model is removed from
661memory. Note that this is only when the "player" is already far away! To
662cause minimal impact on the framerate it is recommended to do as few
663calculations as possible, to use as large timer intervals as possible, and to
664stop all timers and listeners in the <unload> part, as shown in the example.
665
666All Nasal variables/functions are in a separate namespace, which is named
667after the file name. It's recommended not to access this namespace from
668outside for other than development purposes.
669
670What the above code does: as soon as the model is loaded, an aircraft.light
671is created with a specific light sequence. Then, in half-minute intervals,
672the light is turned on or off depending on the sun angle. On <unload> the
673loop identifier is increased, which makes the loop terminate itself. For
674more info about this technique, see the Nasal wiki.
675
676
677
678
6796.2 AI models
680-------------
681
682Here the syntax is the same like for static models. The only two differences
683are:
684
685- these models are currently only removed at program end, so it's more
686  important to consider effects on performance.
687
688- AI models don't need to store their properties in /models/static/...,
689  but get a separate node under /ai/models/, for example /ai/models/carrier[1].
690  The embedded Nasal code can access this dynamically assigned property
691  via cmdarg() function, which returns a props.Node hash. Example:
692
693     <nasal>
694         <load>print("my data are under ", cmdarg().getPath())</load>
695         <unload>print("Currently I'm only called at fgfs exit!")</unload>
696     </nasal>
697