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