home *** CD-ROM | disk | FTP | other *** search
/ Virtual Reality Homebrewer's Handbook / vr.iso / avril / avriltut.txt < prev    next >
Text File  |  1996-03-19  |  86KB  |  1,771 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.                               Using AVRIL -- A Tutorial
  11.                                      Version 2.0
  12.                                     March 28, 1995
  13.  
  14.                                      Bernie Roehl
  15.  
  16.  
  17.  
  18.           Note: This is the AVRIL tutorial.  The detailed technical
  19.           reference is a separate document.
  20.  
  21.           What is AVRIL?
  22.  
  23.                AVRIL is A Virtual Reality Interface Library, a software
  24.           package that allows you to create and interact with virtual
  25.           worlds.  It consists of a fast polygon-based rendering engine and
  26.           a set of support routines that make virtual world creation a
  27.           reasonably straightforward process.
  28.  
  29.                AVRIL is designed to be fast, portable and easy to use. 
  30.           It's written entirely in ANSI C; the PC version also has a few
  31.           short assembly-language routines to handle some of the fixed
  32.           point math.  The API (Applications Programming Interface) is
  33.           simple and well-documented, which should make applications easy
  34.           to develop.
  35.  
  36.                Most important, AVRIL is free for non-commercial use.  The
  37.           problem with most current VR libraries is that they're very, very
  38.           expensive; many of them cost more than the computers they run on! 
  39.           AVRIL is intended to give everyone with an interest in VR an
  40.           opportunity to develop some simple applications without having to
  41.           invest huge sums of money.
  42.  
  43.           What does "free for non-commercial use" mean?
  44.  
  45.                It means that if you're only writing programs for your own
  46.           use, or to give away free to others, you can use AVRIL without
  47.           paying anything.
  48.  
  49.           Who developed AVRIL?
  50.  
  51.                AVRIL was developed by Bernie Roehl, between November of
  52.           1993 and April of 1995.  It's designed to be somewhat backward-
  53.           compatible with an earlier rendering engine called REND386 that
  54.           was developed by Bernie Roehl and Dave Stampe.
  55.  
  56.  
  57.  
  58.  
  59.  
  60.                                     AVRIL Tutorial                        1
  61.  
  62.  
  63.  
  64.  
  65.  
  66.           So what makes AVRIL different from REND386?
  67.  
  68.                From the beginning, we knew that REND386 would never run on
  69.           anything but computers in the 386 family; that's why we called it
  70.           "REND386" in the first place.  REND386 was fast, but it achieved
  71.           its speed at the price of portability; large parts of the code
  72.           were hand-translated to 386 assembly language.  This obviously
  73.           reduced the portability of the software, as well as making it
  74.           more difficult to maintain.
  75.  
  76.                AVRIL, by contrast, is written entirely in C.  It's fast
  77.           because the algorithms are well-chosen and carefully written. 
  78.           While it's not as fast overall as REND386, there are actually
  79.           some situations where it's faster; once it's been optimized a
  80.           bit, the speed should be comparable.  Since it's written in C,
  81.           AVRIL is also much easier to maintain than REND386 was.
  82.  
  83.           Using AVRIL
  84.  
  85.                AVRIL is very easy to use.  Rather than spend a lot of time
  86.           discussing the details of how it works, let's start by creating a
  87.           simple AVRIL program:
  88.  
  89.           /* EXAMPLE1 -- a cube */
  90.  
  91.                  /* Written by Bernie Roehl, April 1994 */
  92.  
  93.                  #include "avril.h"
  94.  
  95.                  void main()
  96.                           {
  97.                           vrl_Object *cube;
  98.                           vrl_Light *light;
  99.                           vrl_Camera *camera;
  100.  
  101.                           vrl_SystemStartup();
  102.  
  103.                           cube = vrl_ObjectCreate(vrl_PrimitiveBox(100, 100, 100, NULL));
  104.                           vrl_ObjectRotY(cube, float2angle(45));
  105.  
  106.                           light = vrl_LightCreate();
  107.                           vrl_LightRotY(light, float2angle(45));
  108.                           vrl_LightRotX(light, float2angle(45));
  109.  
  110.                           camera = vrl_CameraCreate();
  111.                           vrl_CameraRotX(camera, float2angle(45));
  112.                           vrl_CameraMove(camera, 0, 500, -500);
  113.  
  114.                           vrl_SystemRun();
  115.                           }
  116.  
  117.  
  118.  
  119.                                                              AVRIL Tutorial                        2
  120.  
  121.  
  122.  
  123.  
  124.  
  125.           Notice that the only file we had to #include was "avril.h"; that
  126.           file contains prototypes for all the AVRIL functions, along with
  127.           a number of useful macros.  The avril.h file #includes <stdio.h>
  128.           (since it references the FILE * type) so there's no need for you
  129.           to do so yourself.  Since some of the macros in avril.h use the
  130.           memcpy() function, the avril.h file will automatically #include
  131.           whatever header file is needed to define memcpy(); this is
  132.           <string.h> on most platforms.
  133.  
  134.                The program shown above simply creates a cube, a light
  135.           source and a virtual camera.  All the AVRIL routines and data
  136.           types have names beginning with "vrl_"; this ensures that they
  137.           won't conflict with any routines you write.  The
  138.           vrl_SystemStartup() routine does all the system initialization;
  139.           the source code for all the vrl_System functions is found in
  140.           system.c, in case you're curious as to how they work.  We'll be
  141.           looking at them in detail later.
  142.  
  143.                Once the initialization is done, the program creates the
  144.           cube by calling a routine that generates a primitive box shape;
  145.           the sides are all 100 units in length.  After it's been created,
  146.           the cube is rotated 45 degrees around the vertical (Y) axis.  The
  147.           float2angle() routine converts a floating-point number into an
  148.           internal format used for storing angles.
  149.  
  150.                A directional light source is then created, and rotated 45
  151.           degrees in each of X and Y.  Next, a virtual camera is created,
  152.           rotated and moved into position.  Finally, vrl_SystemRun() is
  153.           called; vrl_SystemRun() sits in a loop, checking for keyboard or
  154.           mouse activity and doing the rendering as needed.
  155.  
  156.                To compile and link the program using Borland C++, you would
  157.           give the following command:
  158.  
  159.                bcc -ml example1.c input.c avril.lib
  160.  
  161.           This compiles example1.c and input.c and links them with the
  162.           AVRIL library.  The routines in input.c are discussed in a later
  163.           section.
  164.  
  165.           Sharing Shapes
  166.  
  167.                Our first example was pretty straightforward; let's try
  168.           something more complex.
  169.  
  170.           /* EXAMPLE2 -- several asteroids, sharing the same geometry */
  171.  
  172.                  /* Written by Bernie Roehl, April 1994 */
  173.  
  174.                  #include "avril.h"
  175.                  #include <stdlib.h>  /* needed for rand() */
  176.  
  177.  
  178.                                                              AVRIL Tutorial                        3
  179.  
  180.  
  181.  
  182.  
  183.  
  184.                  void main()
  185.                           {
  186.                           FILE *infile;
  187.                           vrl_Light *light;
  188.                           vrl_Camera *camera;
  189.                           vrl_Shape *asteroidshape = NULL;
  190.                           int i;
  191.  
  192.                           vrl_SystemStartup();
  193.  
  194.                           vrl_WorldSetHorizon(0);    /* turn off horizon */
  195.                           vrl_WorldSetSkyColor(0);   /* black sky */
  196.  
  197.                           infile = fopen("asteroid.plg", "r");
  198.                           if (infile)
  199.                                   {
  200.                                   asteroidshape = vrl_ReadPLG(infile);
  201.                                   fclose(infile);
  202.                                   }
  203.  
  204.                           light = vrl_LightCreate();
  205.                           vrl_LightRotY(light, float2angle(45));
  206.                           vrl_LightRotX(light, float2angle(45));
  207.                           vrl_LightSetIntensity(light, float2factor(0.9));
  208.  
  209.                           camera = vrl_CameraCreate();
  210.                           vrl_CameraMove(camera, 0, 100, -50);
  211.  
  212.                           for (i = 0; i < 5; ++i)
  213.                                   {
  214.                                   vrl_Object *obj = vrl_ObjectCreate(asteroidshape);
  215.                                   vrl_ObjectMove(obj, rand() % 1000, rand() % 1000, rand() % 1000);
  216.                                   }
  217.  
  218.                           vrl_SystemRun();
  219.                           }
  220.  
  221.                  When you run this program, look around using the arrow keys to
  222.           spot the (stationary) asteroids.  This program illustrates a
  223.           useful memory-saving feature of AVRIL.  The shape of an object
  224.           (i.e., its geometric description) is separate from the
  225.           information about its location and orientation.  Any number of
  226.           objects can share the same geometric description, saving
  227.           substantial amounts of memory.  A geometric description is called
  228.           a vrl_Shape, and consists of a set of vertices, facets and other
  229.           information.
  230.  
  231.                The program shown above begins by turning off the horizon
  232.           (it's on by default) and setting the sky color to 0 (black).  The
  233.           sky color is used as the screen clear color if there's no
  234.           horizon.  Next, the file "asteroid.plg" is loaded; AVRIL supports
  235.           the PLG file format, described in Appendix C.  The vrl_ReadPLG()
  236.  
  237.                                     AVRIL Tutorial                        4
  238.  
  239.  
  240.  
  241.  
  242.  
  243.           function returns a pointer to a vrl_Shape (the same data type
  244.           that was returned by the vrl_PrimitiveBox() function in our first
  245.           example).
  246.  
  247.                A light source and camera are again set up, and five virtual
  248.           objects are created using the shape that was loaded by
  249.           vrl_ReadPLG().  Notice that the file only had to be read once,
  250.           and that the vertices and facets making up an asteroid are only
  251.           stored once in memory.  Each of the asteroids is moved to a
  252.           random location in an imaginary box 1000 units on a side.
  253.  
  254.                As you move around, you'll notice that the appearance of an
  255.           asteroid changes depending on how far away you are from it; if
  256.           you get close enough, it's a rough, craggy surface.  The
  257.           "asteroid.plg" file stores multiple representations of the
  258.           object, and AVRIL automatically selects one of those
  259.           representations based on distance.  This can speed up the
  260.           rendering process by allowing fewer vertices and facets to be
  261.           used when an object is far away.
  262.  
  263.           Making Maps
  264.  
  265.                AVRIL not only separates geometry from location/orientation
  266.           information, it also stores surface descriptions separately. 
  267.           Each object has a "surface map" associated with it, which stores
  268.           pointers to actual vrl_Surface specifiers.  Each surface has a
  269.           type, a hue and a brightness; in our examples, the surface type
  270.           is always SURFACE_FLAT (meaning that flat shading is used).  The
  271.           hue is what most people think of as the "color", and the
  272.           brightness is how much light the surface reflects back to the
  273.           eye.  The higher the brightness value and the more directly that
  274.           light is striking the surface, the more intense the color.
  275.  
  276.                You can assign surface maps to objects, and change them
  277.           whenever you like.  Our third example program uses two different
  278.           surface maps, called map1 and map2:
  279.  
  280.           /* EXAMPLE3 -- surface maps */
  281.  
  282.                  /* Written by Bernie Roehl, April 1994 */
  283.  
  284.                  #include "avril.h"
  285.                  #include <stdlib.h>  /* needed for rand() */
  286.  
  287.                  void main()
  288.                           {
  289.                           FILE *infile;
  290.                           vrl_Light *light;
  291.                           vrl_Camera *camera;
  292.                           vrl_Shape *colorthing = NULL;
  293.                           vrl_Surfacemap *map1, *map2;
  294.                           int i;
  295.  
  296.                                                              AVRIL Tutorial                        5
  297.  
  298.  
  299.  
  300.  
  301.  
  302.                           vrl_SystemStartup();
  303.  
  304.                           map1 = vrl_SurfacemapCreate(6);
  305.                           map2 = vrl_SurfacemapCreate(6);
  306.                           for (i = 0; i < 6; ++i)
  307.                                   {
  308.                                   vrl_SurfacemapSetSurface(map1, i, vrl_SurfaceCreate(i + 1));
  309.                                   vrl_SurfacemapSetSurface(map2, i, vrl_SurfaceCreate(7 + i));
  310.                                   }
  311.  
  312.                           infile = fopen("colorful.plg", "r");
  313.                           if (infile)
  314.                                   {
  315.                                   colorthing = vrl_ReadPLG(infile);
  316.                                   fclose(infile);
  317.                                   }
  318.  
  319.                           light = vrl_LightCreate();
  320.                           vrl_LightRotY(light, float2angle(45));
  321.                           vrl_LightRotX(light, float2angle(45));
  322.  
  323.                           camera = vrl_CameraCreate();
  324.                           vrl_CameraMove(camera, 0, 100, -50);
  325.  
  326.                           for (i = 0; i < 10; ++i)
  327.                                   {
  328.                                   vrl_Object *obj = vrl_ObjectCreate(colorthing);
  329.                                   if (i & 1)
  330.                                            vrl_ObjectSetSurfacemap(obj, map1);
  331.                                   else
  332.                                            vrl_ObjectSetSurfacemap(obj, map2);
  333.                                   vrl_ObjectMove(obj, rand() % 1000, rand() % 1000, rand() % 1000);
  334.                                   }
  335.  
  336.                           vrl_SystemRun();
  337.                           }
  338.  
  339.                  The program creates the two maps using the vrl_SurfacemapCreate()
  340.           function; the parameter is the number of entries the map should
  341.           have.  Six entries are then created in each map by calling
  342.           vrl_SurfaceCreate(); the parameter to that function is the hue. 
  343.           The first map will use hues 1 through 6 inclusive, the second
  344.           will use hues 7 through 12.  A shape is then loaded from the file
  345.           "colorful.plg"; that file uses indexed surface descriptors
  346.           (0x8000, 0x8001 etc) that refer to entries in the surface map. 
  347.           Refer to Appendix C for more details about surface descriptors.
  348.  
  349.                The light source and camera are again set up, and ten
  350.           objects are created.  Half of them (the odd-numbered ones) are
  351.           assigned map1 and the others are assigned map2.  The objects are
  352.           again positioned randomly.
  353.  
  354.  
  355.                                     AVRIL Tutorial                        6
  356.  
  357.  
  358.  
  359.  
  360.  
  361.                Notice how half the cubes are a different color from the
  362.           other half.  Each set of surface descriptions is only stored
  363.           once, and each surface map is shared by five of the ten cubes. 
  364.           All the cubes share the same vrl_Shape information, which is only
  365.           stored once.
  366.  
  367.           A Real Taskmaster
  368.  
  369.                AVRIL has a pseudo-tasking facility, which allows you to add
  370.           routines to a list that gets processed continuously while the
  371.           system runs.  Each task has a function and possibly some data, as
  372.           well as an indication of how often it should be run.
  373.  
  374.                Our fourth example is more complex that the first three; it
  375.           creates several primitive shapes, sets up surface maps, and
  376.           creates tasks to make the objects move by themselves.  We'll have
  377.           spinning cubes, bouncing spheres and pulsating cylinders.
  378.  
  379.           /* EXAMPLE4 -- simple object behaviours */
  380.  
  381.                  /* Written by Bernie Roehl, April 1994 */
  382.  
  383.                  #include "avril.h"
  384.                  #include <stdlib.h>  /* needed for rand() */
  385.  
  386.                  static vrl_Angle spinrate;
  387.                  static vrl_Time bounce_period;
  388.                  static vrl_Scalar maxheight;
  389.                  static vrl_Time pulse_period;
  390.  
  391.                  static void spin(void)
  392.                           {
  393.                           vrl_ObjectRotY(vrl_TaskGetData(), vrl_TaskGetElapsed() * spinrate);
  394.                           vrl_SystemRequestRefresh();
  395.                           }
  396.  
  397.                  static void bounce(void)
  398.                           {
  399.                           vrl_Object *obj = vrl_TaskGetData();
  400.                           unsigned long off;
  401.                           vrl_Scalar height;
  402.                           off = (360 * (vrl_TaskGetTimeNow() % bounce_period)) / bounce_period;
  403.                           height = vrl_FactorMultiply(vrl_Sine(float2angle(off)), maxheight);
  404.                           vrl_ObjectMove(obj, vrl_ObjectGetWorldX(obj), height, vrl_ObjectGetWorldZ(obj));
  405.                           vrl_SystemRequestRefresh();
  406.                           }
  407.  
  408.                  static void pulsate(void)
  409.                           {
  410.                           vrl_Surface *surf = vrl_SurfacemapGetSurface((vrl_Surfacemap *) vrl_TaskGetData(), 0);
  411.                           unsigned long off;
  412.                           int brightness;
  413.  
  414.                                                              AVRIL Tutorial                        7
  415.  
  416.  
  417.  
  418.  
  419.  
  420.                           off = (360 * (vrl_TaskGetTimeNow() % pulse_period)) / pulse_period;
  421.                           brightness = abs(vrl_FactorMultiply(vrl_Sine(float2angle(off)), 255));
  422.                           vrl_SurfaceSetBrightness(surf, brightness);
  423.                           vrl_SystemRequestRefresh();
  424.                           }
  425.  
  426.                  void main()
  427.                           {
  428.                           vrl_Light *light;
  429.                           vrl_Camera *camera;
  430.                           vrl_Shape *cube, *sphere, *cylinder;
  431.                           vrl_Surfacemap *cubemap, *pulsemap;
  432.                           int i;
  433.  
  434.                           vrl_SystemStartup();
  435.  
  436.                           cube = vrl_PrimitiveBox(100, 100, 100, NULL);
  437.                           sphere = vrl_PrimitiveSphere(100, 6, 6, NULL);
  438.                           cylinder = vrl_PrimitiveCylinder(100, 50, 100, 8, NULL);
  439.  
  440.                           cubemap = vrl_SurfacemapCreate(1);
  441.                           vrl_SurfacemapSetSurface(cubemap, 0, vrl_SurfaceCreate(5));
  442.                           pulsemap = vrl_SurfacemapCreate(1);
  443.                           vrl_SurfacemapSetSurface(pulsemap, 0, vrl_SurfaceCreate(14));
  444.  
  445.                           spinrate = float2angle(72.0 / vrl_TimerGetTickRate());  /* deg per tick */
  446.                           bounce_period = 4 * vrl_TimerGetTickRate();  /* four-second period */
  447.                           maxheight = float2scalar(400);    /* maximum height in units */
  448.                           pulse_period =  2 * vrl_TimerGetTickRate();  /* two-second period */
  449.  
  450.                           light = vrl_LightCreate();
  451.                           vrl_LightRotY(light, float2angle(45));
  452.                           vrl_LightRotX(light, float2angle(45));
  453.  
  454.                           camera = vrl_CameraCreate();
  455.                           vrl_CameraRotY(camera, float2angle(5));
  456.                           vrl_CameraMove(camera, 0, 200, -4400);
  457.  
  458.                           for (i = 0; i < 10; ++i)
  459.                                   {
  460.                                   vrl_Object *obj = vrl_ObjectCreate(NULL);
  461.                                   vrl_ObjectMove(obj, rand() % 1000, rand() % 1000, rand() % 1000);
  462.                                   switch (i & 3)
  463.                                            {
  464.                                            case 0:
  465.                                                    vrl_ObjectSetShape(obj, cube);
  466.                                                    break;
  467.                                            case 1:
  468.                                                    vrl_ObjectSetShape(obj, cube);
  469.                                                    vrl_ObjectSetSurfacemap(obj, cubemap);
  470.                                                    vrl_TaskCreate(spin, obj, 10);
  471.                                                    break;
  472.  
  473.                                                              AVRIL Tutorial                        8
  474.  
  475.  
  476.  
  477.  
  478.  
  479.                                            case 2:
  480.                                                    vrl_ObjectSetShape(obj, sphere);
  481.                                                    vrl_TaskCreate(bounce, obj, 10);
  482.                                                    break;
  483.                                            case 3:
  484.                                                    vrl_ObjectSetShape(obj, cylinder);
  485.                                                    vrl_ObjectSetSurfacemap(obj, pulsemap);
  486.                                                    break;
  487.                                            }
  488.                                   vrl_TaskCreate(pulsate, pulsemap, 10);
  489.                                   }
  490.  
  491.                           vrl_SystemRun();
  492.                           }
  493.  
  494.                  Let's start by looking at main().  Three primitive shapes are
  495.           created -- a box (100 units on a side), a sphere (100 units in
  496.           radius, with 6 facets around its "latitude" and 6 slices around
  497.           its "longitude") and a tapered cylinder (base radius 100, top
  498.           radius 50, height 100 units with 8 sides).  Two surface maps are
  499.           created, each with a single surface; one called cubemap using hue
  500.           5 and one called pulsemap using hue 14.
  501.  
  502.                Some global variables are then set; spinrate is the rate
  503.           that the cubes should spin, in degrees per "tick".  A tick is a
  504.           small unit of time; the timer runs at 1000 ticks per second, so
  505.           each tick is one millisecond.  In case this changes, you should
  506.           use the routine vrl_TimerGetTickRate() to found out how many
  507.           ticks per second the timer is running at.
  508.  
  509.                We do the float2angle() conversion here rather than in the
  510.           spin() task itself; by storing the vrl_Angle value, we avoid
  511.           having to do the conversion each time through the simulation
  512.           loop.  Also notice that we divide by the rate at which the system
  513.           timer runs, in ticks per second; the rotation rate is 72 degrees
  514.           per second, so we divide by ticks per second to get the rotation
  515.           rate in degrees per tick.
  516.  
  517.                The bounce_period is 4 seconds, converted to ticks; this is
  518.           the time it takes a bouncing ball to go through one complete up-
  519.           down cycle.  The maximum height a ball will rise to is maxheight,
  520.           arbitrarily set to be 400 units.  Note the conversion from
  521.           floating-point to the internal "vrl_Scalar" format.  The
  522.           pulse_period is set to two seconds.
  523.  
  524.                Again, a light and camera are set up so we can view the
  525.           scene, and ten objects are created and randomly positioned.  Some
  526.           of them are simple cubes (using the default color assigned by
  527.           vrl_PrimitiveBox()).  Some of them are spinning cubes, with a
  528.           single-entry surfacemap.
  529.  
  530.  
  531.  
  532.                                     AVRIL Tutorial                        9
  533.  
  534.  
  535.  
  536.  
  537.  
  538.                A task is created to make each cube spin.  Each task has a
  539.           function, some data, and a "period" which indicates how often the
  540.           task should be run.  In this case, the function is spin(), the
  541.           data is a pointer to the object to be spun, and the period is 10
  542.           ticks.  The period doesn't affect the speed at which the cube
  543.           will spin; it only determines how often the spin() function
  544.           should be called.  The smaller the number, the more often the
  545.           routine will run and the "smoother" the motion will be; of
  546.           course, running the tasks more often takes CPU cycles away from
  547.           rendering.
  548.  
  549.                The bouncing balls are handled the same way as the spinning
  550.           cubes.  The cylinders don't have a task associated with them;
  551.           instead a separate task is set up that will cause the pulsing to
  552.           happen.  The data for that task is not an object pointer, but
  553.           rather a pointer to a surface map.
  554.  
  555.                The tasks themselves are quite straightforward.  The
  556.           simplest is the spin() task, which is only two lines long:
  557.  
  558.           static void spin(void)
  559.                           {
  560.                           vrl_ObjectRotY(vrl_TaskGetData(), vrl_TaskGetElapsed() * spinrate);
  561.                           vrl_SystemRequestRefresh();
  562.                           }
  563.  
  564.           This task gets a pointer to its data using vrl_TaskGetData();
  565.           this is a pointer to the object associated with this task.  The
  566.           task also gets the elapsed time (in ticks) since it last ran,
  567.           multiplies that value by spinrate, and rotates the object by that
  568.           amount around the vertical (Y) axis.  The spin() function then
  569.           calls vrl_SystemRequestRefresh(), which tells the system that the
  570.           screen should be refreshed (since an object has moved).
  571.  
  572.                The bounce() task is only slightly more complex; it uses the
  573.           sine function to determine the height at which the object should
  574.           be positioned:
  575.  
  576.           static void bounce(void)
  577.                           {
  578.                           vrl_Object *obj = vrl_TaskGetData();
  579.                           unsigned long off;
  580.                           vrl_Scalar height;
  581.                           off = (360 * (vrl_TaskGetTimeNow() % bounce_period)) / bounce_period;
  582.                           height = vrl_FactorMultiply(vrl_Sine(float2angle(off)), maxheight);
  583.                           vrl_ObjectMove(obj, vrl_ObjectGetWorldX(obj), height, vrl_ObjectGetWorldZ(obj));
  584.                           vrl_SystemRequestRefresh();
  585.                           }
  586.  
  587.           The current time is obtained from vrl_TaskGetTimeNow(), and the %
  588.           operator is used to find the modulus (remainder) of the current
  589.           time relative to the bounce period.  That value, divided by the
  590.  
  591.                                     AVRIL Tutorial                       10
  592.  
  593.  
  594.  
  595.  
  596.  
  597.           bounce period, is the fraction of the bounce period that has
  598.           elapsed.  We multiply that by 360 (the number of degrees in a
  599.           circle) to get an offset value; we take the sine of that value
  600.           (using the fast vrl_Sine() routine) and multiply by the maximum
  601.           height value.  The vrl_FactorMultiply() routine takes a
  602.           fractional number (of the type returned by vrl_Sine()) and
  603.           multiplies it by a vrl_Scalar value to get a (smaller) vrl_Scalar
  604.           value.
  605.  
  606.                We use vrl_ObjectMove() to actually position the object. 
  607.           Notice the use of vrl_ObjectGetWorldX() and vrl_ObjectGetWorldZ()
  608.           to find the current X and Z values of the object's location; we
  609.           don't want to alter those values, only the height.  A call to the
  610.           function vrl_SystemRequestRefresh() ensures that the screen will
  611.           be redrawn with the object at its new height.
  612.  
  613.                The pulsate() task is similar to the bounce() task, but
  614.           instead of computing a height it computes a brightness and sets
  615.           it as the new brightness value of the surface.  Brightness values
  616.           are in the range of 0 to 255.
  617.  
  618.           Left to Our Own Devices
  619.  
  620.                AVRIL supports the use of a variety of input devices for
  621.           manipulating your viewpoint and the objects in your virtual
  622.           world.  Our next example shows one way to use them.
  623.  
  624.           /* EXAMPLE5 -- manipulating a cube with the Logitech Cyberman */
  625.  
  626.                  /* Written by Bernie Roehl, August 1994 */
  627.  
  628.                  #include "avril.h"
  629.                  #include "avrildrv.h"
  630.  
  631.                  vrl_Object *cube = NULL;
  632.  
  633.                  static void cube_mover(void)
  634.                           {
  635.                           vrl_Device *dev = vrl_TaskGetData();
  636.                           vrl_Object *viewer = vrl_CameraGetObject(vrl_WorldGetCamera());
  637.                           vrl_Vector v;
  638.                           vrl_ObjectRotate(cube, vrl_DeviceGetValue(dev, YROT), Y, VRL_COORD_OBJREL, viewer);
  639.                           vrl_ObjectRotate(cube, vrl_DeviceGetValue(dev, XROT), X, VRL_COORD_OBJREL, viewer);
  640.                           vrl_ObjectRotate(cube, vrl_DeviceGetValue(dev, ZROT), Z, VRL_COORD_OBJREL, viewer);
  641.                           vrl_VectorCreate(v, vrl_DeviceGetValue(dev, X), vrl_DeviceGetValue(dev, Y),
  642.                                   vrl_DeviceGetValue(dev, Z));
  643.                           vrl_ObjectTranslate(cube, v, VRL_COORD_OBJREL, viewer);
  644.                           vrl_SystemRequestRefresh();
  645.                           }
  646.  
  647.                  void main()
  648.                           {
  649.  
  650.                                                              AVRIL Tutorial                       11
  651.  
  652.  
  653.  
  654.  
  655.  
  656.                           vrl_Light *light;
  657.                           vrl_Camera *camera;
  658.                           vrl_Device *dev;
  659.  
  660.                           vrl_SystemStartup();
  661.  
  662.                           cube = vrl_ObjectCreate(vrl_PrimitiveBox(100, 100, 100, NULL));
  663.                           vrl_ObjectRotY(cube, float2angle(45));
  664.  
  665.                           light = vrl_LightCreate();
  666.                           vrl_LightRotY(light, float2angle(45));
  667.                           vrl_LightRotX(light, float2angle(45));
  668.  
  669.                           camera = vrl_CameraCreate();
  670.                           vrl_CameraRotX(camera, float2angle(45));
  671.                           vrl_CameraMove(camera, 0, 500, -500);
  672.  
  673.                           dev = vrl_DeviceOpen(vrl_CybermanDevice, vrl_SerialOpen(0x2F8, 3, 2000));
  674.                           if (dev)
  675.                                   {
  676.                                   vrl_DeviceSetScale(dev, X, float2scalar(50));
  677.                                   vrl_DeviceSetScale(dev, Y, float2scalar(50));
  678.                                   vrl_DeviceSetScale(dev, Z, float2scalar(50));
  679.                                   vrl_TaskCreate(cube_mover, dev, 0);
  680.                                   }
  681.  
  682.                           vrl_SystemRun();
  683.                           }
  684.  
  685.  
  686.           As you can see, there's not much to it.  Most of the code is
  687.           exactly the same as our first example;  The only difference is
  688.           that just before we start running the main loop, we open up a
  689.           device.  The first parameter to the vrl_DeviceOpen() routine is
  690.           the address of a function that is responsible for operating the
  691.           device; in this case, it's called vrl_CybermanDevice, and it
  692.           reads the Logitech Cyberman.  Notice that we #included the
  693.           avrildrv.h file; it has declarations for all the device
  694.           functions.  When you create a new device driver (as described in
  695.           Appendices F of the technical reference manual) you should put an
  696.           entry into the avrildrv.h file for it.
  697.  
  698.                The second parameter to vrl_DeviceOpen() is a pointer to a
  699.           serial port; we could have opened the serial port, assigned it to
  700.           a variable, and passed that variable to the vrl_DeviceOpen()
  701.           function, but there was no need to in this case.
  702.  
  703.                The values 0x2F8 and 3 are the hardware address and IRQ
  704.           number of the COM2 port on a PC-compatible; this example is very
  705.           platform-specific, but we'll see shortly how to get around that. 
  706.           The value 2000 is the size of the input buffer the serial port
  707.           should use.
  708.  
  709.                                     AVRIL Tutorial                       12
  710.  
  711.  
  712.  
  713.  
  714.  
  715.                Assuming the device was successfully opened, we scale the X,
  716.           Y and Z translation values read by the device to be 50 units;
  717.           that will be the maximum number of world-space units per second
  718.           that we can move objects using this device.  Finally, we create a
  719.           task whose data parameter is a pointer to our newly-opened
  720.           device.
  721.  
  722.                The task that does the work of moving the object is called
  723.           cube_mover().  You'll notice that unlike our first example
  724.           program, we've declared the cube object as a global variable
  725.           instead of a local one; this so that cube_mover() can access it.
  726.  
  727.                The cube_mover() task starts by getting the device pointer,
  728.           and a pointer to the object corresponding to our viewpoint.
  729.  
  730.                vrl_Device *dev = vrl_TaskGetData();
  731.                           vrl_Object *viewer = vrl_CameraGetObject(vrl_WorldGetCamera());
  732.  
  733.           Next, cube_mover() rotates the cube.  First it does the Y axis,
  734.           then the X axis, and finally the Z axis.  In each case, it
  735.           rotates the cube relative to the viewer object by an amount that
  736.           is read from the device.
  737.  
  738.                vrl_ObjectRotate(cube, vrl_DeviceGetValue(dev, YROT),
  739.                                   Y, VRL_COORD_OBJREL, viewer);
  740.                           vrl_ObjectRotate(cube, vrl_DeviceGetValue(dev, XROT),
  741.                                   X, VRL_COORD_OBJREL, viewer);
  742.                           vrl_ObjectRotate(cube, vrl_DeviceGetValue(dev, ZROT),
  743.                                   Z, VRL_COORD_OBJREL, viewer);
  744.  
  745.           The final step is to read the X, Y and Z translation values from
  746.           the device, store them in a vector, and translate (move) the
  747.           object along that vector relative to the viewer.
  748.  
  749.                vrl_VectorCreate(v, vrl_DeviceGetValue(dev, X),
  750.                                   vrl_DeviceGetValue(dev, Y), vrl_DeviceGetValue(dev, Z));
  751.                           vrl_ObjectTranslate(cube, v, VRL_COORD_OBJREL, viewer);
  752.                           vrl_SystemRequestRefresh();
  753.  
  754.           That's it.
  755.  
  756.           An Independence Movement
  757.  
  758.                The example program above works fine.  If you have a
  759.           Cyberman.  And if it's on COM2.  And if all you want to do is
  760.           move a cube.  Wouldn't it be nice to have a little more
  761.           flexibility?
  762.  
  763.                As it turns out, you can.  AVRIL supports the use of
  764.           "configuration files" that store information about a user's
  765.           preferences and hardware configuration.  Our next example uses
  766.           that configuration information to make our life simpler.
  767.  
  768.                                     AVRIL Tutorial                       13
  769.  
  770.  
  771.  
  772.  
  773.  
  774.                  /* EXAMPLE6 -- using the configuration file to simplify setup */
  775.  
  776.                  /* Written by Bernie Roehl, August 1994 */
  777.  
  778.                  #include "avril.h"
  779.  
  780.                  static void object_manipulator(void)
  781.                           {
  782.                           extern vrl_Object *active_object;  /* defined in input.c */
  783.                           vrl_Device *dev = vrl_TaskGetData();
  784.                           vrl_Object *viewer = vrl_CameraGetObject(vrl_WorldGetCamera());
  785.                           vrl_Vector v;
  786.                           vrl_ObjectRotate(active_object, vrl_DeviceGetValue(dev, YROT),
  787.                                   Y, VRL_COORD_OBJREL, viewer);
  788.                           vrl_ObjectRotate(active_object, vrl_DeviceGetValue(dev, XROT),
  789.                                   X, VRL_COORD_OBJREL, viewer);
  790.                           vrl_ObjectRotate(active_object, vrl_DeviceGetValue(dev, ZROT),
  791.                                   Z, VRL_COORD_OBJREL, viewer);
  792.                           vrl_VectorCreate(v, vrl_DeviceGetValue(dev, X),
  793.                                   vrl_DeviceGetValue(dev, Y), vrl_DeviceGetValue(dev, Z));
  794.                           vrl_ObjectTranslate(active_object, v, VRL_COORD_OBJREL, viewer);
  795.                           vrl_SystemRequestRefresh();
  796.                           }
  797.  
  798.                  void main(int argc, char *argv[])
  799.                           {
  800.                           vrl_Device *dev;
  801.                           vrl_SystemStartup();
  802.                           vrl_ReadCFGfile("example6.cfg");
  803.                           vrl_SystemCommandLine(argc, argv);
  804.                           dev = vrl_DeviceFind("manipulator");
  805.                           if (dev)
  806.                                   vrl_TaskCreate(object_manipulator, dev, 0);
  807.                           vrl_SystemRun();
  808.                           }
  809.  
  810.           Our main() is shorter, and simpler.  You'll notice that we've
  811.           added a call to vrl_ReadCFGfile(); it reads the configuration
  812.           file we specify (in this case it's "example6.cfg"), and
  813.           configures and initializes all the devices (even opening the
  814.           serial ports) as specified in the configuration file.  The format
  815.           of the configuration file is described in Appendix B of the
  816.           technical reference manual.
  817.  
  818.                The vrl_SystemCommandLine() function reads the command line,
  819.           and loads whatever PLG files, FIG files and WLD files we specify
  820.           there.  The vrl_DeviceFind() function looks for a device that was
  821.           given the name "manipulator" in the configuration file, and if it
  822.           finds one it creates a task to move an object using the
  823.           manipulation device.
  824.  
  825.  
  826.  
  827.                                     AVRIL Tutorial                       14
  828.  
  829.  
  830.  
  831.  
  832.  
  833.                The object_manipulator() function is almost the same as
  834.           cube_mover(), but it uses an external variable called
  835.           active_object.  As we'll see later, this variable is found in
  836.           input.c (where it gets set to the object most recently selected
  837.           by the mouse).
  838.  
  839.                Using this program, we can explore a virtual world, click on
  840.           objects to select them, and use the manipulator device we specify
  841.           in our configuration file to manipulate the selected object.  All
  842.           with just a few lines of code.  Note that if you're using the
  843.           mouse to manipulate objects, you should hit the spacebar to
  844.           toggle between selecting objects and moving them.
  845.  
  846.           A Smooth Operator
  847.  
  848.                So far all the objects we've been looking at have been flat
  849.           shaded; that gives them the distinctive "faceted" appearance that
  850.           you often see in VR systems.  However, AVRIL is capable of smooth
  851.           shading as well, as shown in the following example:
  852.  
  853.           /* EXAMPLE7 -- Gouraud shading */
  854.  
  855.                  /* Written by Bernie Roehl, April 1995 */
  856.  
  857.                  #include "avril.h"
  858.  
  859.                  static void load_palette(char *filename)
  860.                           {
  861.                           FILE *infile = fopen(filename, "rb");
  862.                           if (infile)
  863.                                   {
  864.                                   vrl_PaletteRead(infile, vrl_WorldGetPalette());
  865.                                   fclose(infile);
  866.                                   }
  867.                           }
  868.  
  869.                  static vrl_Angle tumblerate;
  870.  
  871.                  void tumbler(void)
  872.                           {
  873.                           vrl_Object *obj = vrl_TaskGetData();
  874.                           vrl_Angle amount = vrl_TaskGetElapsed() * tumblerate;
  875.                           vrl_ObjectRotY(obj, amount);
  876.                           vrl_ObjectRotX(obj, amount);
  877.                           vrl_SystemRequestRefresh();
  878.                           }
  879.  
  880.                  void main()
  881.                           {
  882.                           vrl_Shape *smooth_shape;
  883.                           vrl_Object *thing;
  884.                           vrl_Light *light;
  885.  
  886.                                                              AVRIL Tutorial                       15
  887.  
  888.  
  889.  
  890.  
  891.  
  892.                           vrl_Camera *camera;
  893.                           vrl_Surface *surf;
  894.  
  895.                           vrl_SystemStartup();
  896.  
  897.                           load_palette("shade32.pal");
  898.  
  899.                           smooth_shape = vrl_PrimitiveCylinder(100, 25, 200, 16, NULL);
  900.                           vrl_ShapeComputeVertexNormals(smooth_shape);
  901.  
  902.                           surf = vrl_SurfacemapGetSurface(vrl_ShapeGetSurfacemap(smooth_shape), 0);
  903.                           vrl_SurfaceSetType(surf, VRL_SURF_GOURAUD);
  904.                           vrl_SurfaceSetHue(surf, 4);
  905.                           vrl_SurfaceSetBrightness(surf, 243);
  906.  
  907.                           thing = vrl_ObjectCreate(smooth_shape);
  908.                           vrl_ObjectRelMove(thing, 0, -100, 0);
  909.  
  910.                           vrl_WorldSetAmbient(0);
  911.                           light = vrl_LightCreate();
  912.                           vrl_LightRotY(light, float2angle(45));
  913.  
  914.                           camera = vrl_CameraCreate();
  915.                           vrl_CameraMove(camera, 0, 0, -1400);
  916.  
  917.                           tumblerate = float2angle(72.0 / vrl_TimerGetTickRate());
  918.                           vrl_TaskCreate(tumbler, thing, 0);
  919.  
  920.                           vrl_SystemRun();
  921.                           }
  922.  
  923.           Almost everything in Example 7 has been used in earlier examples,
  924.           with three exceptions.  The first is the loading of a palette and
  925.           hue map from a disk file, the second is the setting of the
  926.           VRL_SURF_GOURAUD shading type on the surface used by the cone,
  927.           and the third is the call to vrl_ShapeComputeVertexNormals().  In
  928.           order for smooth (i.e., Gouraud) shading to work, the renderer
  929.           needs to know the normal vectors at each vertex; the
  930.           vrl_ShapeComputeVertexNormals() routine computes them by
  931.           averaging the facet normals.
  932.  
  933.                Technically, you don't need to call
  934.           vrl_ShapeComputeVertexNormals() on spheres, cones and cylinders
  935.           created by the vrl_Primitive family of functions; the creation
  936.           routines do this automatically.  Shapes created by the
  937.           vrl_PrimitiveBox() and vrl_PrimitivePrism() are flat-shaded by
  938.           default.
  939.  
  940.                The shade32.pal file contains a palette and a hue map, set
  941.           up to give fewer colors but more shades (in this case, 32 shades
  942.           of each color instead of the standard 16).  This makes the
  943.           Gouraud shading look a lot better.
  944.  
  945.                                     AVRIL Tutorial                       16
  946.  
  947.  
  948.  
  949.  
  950.  
  951.                You may notice a number of glitches in the shading,
  952.           especially little white flecks; that's because the Gouraud
  953.           shading routine was the very last thing I added to this release,
  954.           and it isn't fully debugged yet!  I should have that fixed up in
  955.           version 2.1, but I didn't want to leave out Gouraud shading
  956.           altogether for this release.  I also didn't want to keep people
  957.           waiting any longer for this release than I already have.
  958.  
  959.           A Tiny Program
  960.  
  961.                AVRIL provides a number of useful utility routines that
  962.           reduce the amount of actual programming you have to do in order
  963.           to create a virtual world.  A minimal AVRIL program looks like
  964.           this:
  965.  
  966.           /* A very simple demo of AVRIL */
  967.  
  968.                  /* Written by Bernie Roehl, April 1994 */
  969.  
  970.                  #include "avril.h"
  971.  
  972.                  void main(int argc, char *argv[])
  973.                           {
  974.                           vrl_SystemStartup();
  975.                           vrl_ReadCFGfile(NULL);
  976.                           vrl_SystemCommandLine(argc, argv);
  977.                           vrl_SystemRun();
  978.                           }
  979.  
  980.           The NULL parameter to vrl_ReadCFGfile() causes it to use its
  981.           built-in default of "avril.cfg".  This example shows just how
  982.           little it takes to create a VR program using AVRIL.
  983.  
  984.           Of Mice and Menus
  985.  
  986.                By now, you've probably noticed that something is missing;
  987.           how have our programs been able to respond to our keystrokes and
  988.           mouse presses?  Well, AVRIL does some of this for you
  989.           automatically.  When you call vrl_SystemRun(), you're essentially
  990.           turning control of the application over to the system.  From time
  991.           to time, the system will make calls back to your application to
  992.           give you control if you need it.  (If you don't like this
  993.           approach, you're not stuck with it; the source for the vrl_System
  994.           functions is provided, so you can do things however you like).
  995.  
  996.                There are currently five places that the system calls your
  997.           application.  Just before starting its main internal loop for the
  998.           first time, it calls vrl_ApplicationInit().  Just after it clears
  999.           the screen (or draws the horizon, as the case may be) but before
  1000.           it does the actual rendering of the scene, it calls
  1001.           vrl_ApplicationDrawUnder().  You can use that routine to
  1002.           "underlay" information on the screen that appears behind any
  1003.  
  1004.                                     AVRIL Tutorial                       17
  1005.  
  1006.  
  1007.  
  1008.  
  1009.  
  1010.           objects that are drawn.  If you want to use your own background,
  1011.           just turn off screen clearing using vrl_WorldSetScreenClear(0)
  1012.           and do your background drawing in vrl_ApplicationDrawUnder().
  1013.  
  1014.                After the system has rendered the entire scene, it calls
  1015.           vrl_ApplicationDrawOver(); this allows you to "overlay"
  1016.           information on the screen.  The vrl_ApplicationDrawOver() routine
  1017.           is where you would put any "heads-up display" type information,
  1018.           such as frame rate or orientation information.
  1019.  
  1020.                Whenever a keystroke is detected, it's passed to the
  1021.           vrl_ApplicationKey() routine.  Similarly, mouse-up events are
  1022.           passed to the application using vrl_ApplicationMouseUp().
  1023.  
  1024.                All of these routines have default versions in the AVRIL
  1025.           library, so you don't have to write all of them.  The default
  1026.           versions of the functions vrl_ApplicationDrawUnder(),
  1027.           vrl_ApplicationDrawOver() and vrl_ApplicationMouseUp() are empty
  1028.           (i.e., they don't do anything).  The default version of
  1029.           vrl_ApplicationKey() just checks to see if the user has pressed
  1030.           the ESC key; if they have, vrl_SystemStopRunning() is called.
  1031.  
  1032.                In addition to all this, there's a simple menu system built
  1033.           into this version of AVRIL; it will be described later.
  1034.  
  1035.           A Moving Experience
  1036.  
  1037.                Objects can have functions and data associated with them. 
  1038.           When the system walks through the hierarchy of objects, it calls
  1039.           each object's function; those functions can make use of the data
  1040.           associated with the object.
  1041.  
  1042.                The default vrl_ApplicationInit() routine sets up an object
  1043.           function to let an input device (the keypad by default) move the
  1044.           user around.  You can look at the code in input.c for all the
  1045.           details, but essentially here's what it does:
  1046.  
  1047.                vrl_Object *head = vrl_CameraGetObject(vrl_WorldGetCamera());
  1048.                           vrl_Device *headdev = vrl_DeviceFind("head");
  1049.                           if (headdev == NULL)
  1050.                                   headdev = vrl_DeviceOpen(vrl_KeypadDevice, 0);
  1051.                           vrl_ObjectSetApplicationData(head, headdev);
  1052.                           vrl_ObjectSetFunction(head, head_mover);
  1053.  
  1054.           If no head device was specified in the configuration file, the
  1055.           keypad is used.  The head is found (the head being the object to
  1056.           which the camera is attached), and the head object's application-
  1057.           specific data field is set to point to the headdev.
  1058.  
  1059.                The functions that are set on objects get called whenever
  1060.           the world is updated by the vrl_ObjectUpdate() or
  1061.           vrl_WorldUpdate() routines.  When object_move_locally() gets
  1062.  
  1063.                                     AVRIL Tutorial                       18
  1064.  
  1065.  
  1066.  
  1067.  
  1068.  
  1069.           called, it just calls object_mover() on the object, passing the
  1070.           device pointer which is stored in the object's application data.
  1071.  
  1072.                The object_mover() routine is basically the same as the
  1073.           movement tasks that were described earlier (the ones in example
  1074.           6) but slightly more general.
  1075.  
  1076.           Lots of Input
  1077.  
  1078.                The file input.c contains simple versions of
  1079.           vrl_ApplicationDrawOver(), vrl_ApplicationMouseUp(),
  1080.           vrl_ApplicationKey() and vrl_ApplicationInit() that are shared by
  1081.           all our example programs.  The vrl_ApplicationMouseUp() routine
  1082.           looks like this:
  1083.  
  1084.                  vrl_Object *active_object = NULL;  /* points to the currently-selected object, if any */
  1085.  
  1086.                  void vrl_ApplicationMouseUp(int x, int y, unsigned int buttons)
  1087.                           {
  1088.                           vrl_Object *old_active = active_object;
  1089.                           if ((buttons & 1) == 0)
  1090.                                   return;
  1091.                           vrl_RenderMonitorInit(x, y);
  1092.                           vrl_SystemRender(NULL);  /* redraw screen */
  1093.                           if (vrl_RenderMonitorRead(&active_object, NULL, NULL))
  1094.                                   {
  1095.                                   if (active_object == old_active)
  1096.                                            active_object = NULL;
  1097.                                   else
  1098.                                            vrl_ObjectSetHighlight(active_object, 1);
  1099.                                   }
  1100.                           if (old_active)
  1101.                                   vrl_ObjectSetHighlight(old_active, 0);
  1102.                           vrl_SystemRequestRefresh();
  1103.                           }
  1104.  
  1105.           This routine uses the "Monitor" facility of AVRIL to allow the
  1106.           user to select objects.  The mouse location is passed to
  1107.           vrl_RenderMonitorInit(); this tells the system to keep an eye on
  1108.           that point on the screen.  The screen is then re-drawn using
  1109.           vrl_SystemRender(), and the monitor is read using
  1110.           vrl_RenderMonitorRead().  If that function returns a non-zero
  1111.           value, then the mouse cursor was on top of an object; since we
  1112.           passed &active_object to the vrl_RenderMonitorRead() function,
  1113.           active_object now points to the object that the mouse cursor was
  1114.           on top of.  This is the object that got moved around by the
  1115.           manipulation device in example 6.  If the user clicks again on
  1116.           the previously-selected object, then the active_object is set to
  1117.           NULL; otherwise, the newly-activated object gets its highlighting
  1118.           turned on.  In any case, we un-highlight the previously active
  1119.           object, and tell the system the screen needs to be refreshed
  1120.           (since the highlighting of an object has changed).
  1121.  
  1122.                                     AVRIL Tutorial                       19
  1123.  
  1124.  
  1125.  
  1126.  
  1127.  
  1128.                The vrl_ApplicationKey() routine is very simple; the only
  1129.           complicated part is that it handles auto-repeat of keystrokes:
  1130.  
  1131.           void vrl_ApplicationKey(unsigned int c)
  1132.                           {
  1133.                           static int lastkey = 0;
  1134.                           if (c == INS)
  1135.                                   {
  1136.                                   int i;
  1137.                                   for (i = 0; i < 100; ++i)
  1138.                                            {
  1139.                                            process_key(lastkey);
  1140.                                            vrl_SystemRender(vrl_WorldUpdate());
  1141.                                            }
  1142.                                   }
  1143.                           else
  1144.                                   process_key(lastkey = c);
  1145.                           }
  1146.  
  1147.                  If the key is INS (defined in avrilkey.h), the last key is re-
  1148.           processed 100 times; all other keys are processed once, and the
  1149.           lastkey variable is updated.  Notice the call to
  1150.           vrl_SystemRender(); it looks pretty complicated, but after you
  1151.           read some of the later sections it will make more sense.  We need
  1152.           to update the world and re-render the scene after every
  1153.           keystroke, so the user will see the ongoing changes.
  1154.  
  1155.                The process_key() function is fairly long, and will probably
  1156.           change from version to version of AVRIL.  Most of it should be
  1157.           pretty easy to understand, so you may want to take a few minutes
  1158.           to look through the source code in input.c (where you'll also
  1159.           find the source for the vrl_ApplicationMouseUp() and
  1160.           vrl_ApplicationDrawOver() routines).
  1161.  
  1162.                The vrl_ApplicationDrawOver() routine provides the position,
  1163.           frame rate, compass and "heads-up display" support for the AVRIL
  1164.           demos.  It looks like this:
  1165.  
  1166.           void vrl_ApplicationDrawOver(vrl_RenderStatus *stat)
  1167.                           {
  1168.                           vrl_Camera *cam = vrl_WorldGetCamera();
  1169.                           char buff[100];
  1170.                           if (vrl_ConfigGetPositionDisplay())
  1171.                                   {
  1172.                                   sprintf(buff, "Position: %ld,%ld", vrl_CameraGetWorldX(cam),
  1173.                                            vrl_CameraGetWorldZ(cam));
  1174.                                   vrl_UserInterfaceDropText(10, 10, 15, buff);
  1175.                                   }
  1176.                           if (vrl_ConfigGetFramerateDisplay())
  1177.                                   {
  1178.                                   sprintf(buff, "Frames/sec: %ld", vrl_SystemGetFrameRate());
  1179.                                   vrl_UserInterfaceDropText(5, 170, 15, buff);
  1180.  
  1181.                                                              AVRIL Tutorial                       20
  1182.  
  1183.  
  1184.  
  1185.  
  1186.  
  1187.                                   }
  1188.                           if (vrl_ConfigGetCompassDisplay())
  1189.                                   vrl_UserInterfaceDrawCompass(cam, 250, 40, 35);
  1190.                           if (showhud)
  1191.                                   {
  1192.                                   sprintf(buff, "%c%c%c",
  1193.                                            stat->memory ?  'M' : ' ',
  1194.                                            stat->objects ? 'O' : ' ',
  1195.                                            stat->facets ?  'F' : ' ');
  1196.                                   vrl_UserInterfaceDropText(10, 20, 15, buff);
  1197.                                   }
  1198.                           if (vrl_MouseGetUsage())
  1199.                                   {
  1200.                                   vrl_Device *dev = vrl_MouseGetPointer();
  1201.                                   if (dev)
  1202.                                            {
  1203.                                            int x = vrl_DeviceGetCenter(dev, X);
  1204.                                            int y = vrl_DeviceGetCenter(dev, Y);
  1205.                                            int deadx = vrl_DeviceGetDeadzone(dev, X);
  1206.                                            int deady = vrl_DeviceGetDeadzone(dev, Y);
  1207.                                            /* white inner box */
  1208.                                            vrl_DisplayLine(x - deadx, y - deady, x + deadx, y - deady, 15);
  1209.                                            vrl_DisplayLine(x - deadx, y + deady, x + deadx, y + deady, 15);
  1210.                                            vrl_DisplayLine(x - deadx, y - deady, x - deadx, y + deady, 15);
  1211.                                            vrl_DisplayLine(x + deadx, y - deady, x + deadx, y + deady, 15);
  1212.                                            /* black outer box */
  1213.                                            vrl_DisplayLine(x-deadx-1, y-deady-1, x+deadx+1, y-deady-1, 0);
  1214.                                            vrl_DisplayLine(x-deadx-1, y+deady+1, x+deadx+1, y+deady+1, 0);
  1215.                                            vrl_DisplayLine(x-deadx-1, y-deady-1, x-deadx-1, y+deady+1, 0);
  1216.                                            vrl_DisplayLine(x+deadx+1, y-deady-1, x+deadx+1, y+deady+1, 0);
  1217.                                            }
  1218.                                   }
  1219.                           }
  1220.  
  1221.           There are several "configuration" settings that get accessed to
  1222.           determine what information should be overlaid on the display; the
  1223.           state of those configuration variables is toggled by code in
  1224.           process_key().  This configuration information will be explained
  1225.           in more detail later, in the section about configuration files.
  1226.  
  1227.                The call to vrl_WorldGetCamera() returns a pointer to the
  1228.           currently-active virtual camera.  The buffer buff[] will be used
  1229.           to construct strings that we want to display on the screen.
  1230.  
  1231.                If the user wants their location displayed, a text string
  1232.           containing the camera's current X and Z values is constructed and
  1233.           displayed at location (10, 10) on the screen.  The first value is
  1234.           the horizontal distance in pixels from the left of the screen,
  1235.           and the second value is the vertical distance in pixels from the
  1236.           top of the screen.  The color used is 15, which is white.  The
  1237.           vrl_UserInterfaceDropText() function automatically produces a
  1238.  
  1239.  
  1240.                                     AVRIL Tutorial                       21
  1241.  
  1242.  
  1243.  
  1244.  
  1245.  
  1246.           "drop shadow" behind the text, ensuring it's visible even if it's
  1247.           overlaid on top of a white background.
  1248.  
  1249.                If the user wants a compass to be shown, the
  1250.           vrl_UserInterfaceDrawCompass() routine is called.  The compass is
  1251.           displayed at location (250, 40) on the screen, and each "arm" of
  1252.           the compass is 35 pixels long.
  1253.  
  1254.                If the showhud variable is set, a variety of debugging
  1255.           information is displayed.  When the renderer draws a scene, it
  1256.           may run out of internal memory, or it may find there are too many
  1257.           objects or facets for it to process.  If this happens, it sets
  1258.           bits in a special structure; a pointer to this structure is
  1259.           passed to vrl_ApplicationDrawOver(), so that it can alert the
  1260.           user to the problem.  In this case, an 'M' is displayed if the
  1261.           renderer ran out of memory, an 'O' is displayed if there were too
  1262.           many objects, and an 'F' is displayed if there were too many
  1263.           facets.
  1264.  
  1265.                If the mouse is in 6D input device mode, a small square is
  1266.           drawn on the screen; if the mouse cursor is inside this box,
  1267.           there'll be no movement.  It's sort of a visual "dead zone", if
  1268.           you will.  The idea for this box came from a demo of the
  1269.           Superscape VR system; it was a clever enough idea that I adopted
  1270.           it for this example.
  1271.  
  1272.           Into the System
  1273.  
  1274.                We've talked a lot so far about the vrl_System routines; now
  1275.           let's take a closer look at how they work.
  1276.  
  1277.           vrl_Boolean vrl_SystemStartup(void)
  1278.                           {
  1279.                           vrl_MathInit();
  1280.                           vrl_WorldInit(vrl_WorldGetCurrent());
  1281.                           if (vrl_VideoSetup(0))
  1282.                                   {
  1283.                                   printf("Could not enter graphics mode!\n");
  1284.                                   return -1;
  1285.                                   }
  1286.                           atexit(vrl_VideoShutdown);
  1287.                           if (vrl_DisplayInit(NULL))
  1288.                                   return -1;
  1289.                           atexit(vrl_DisplayQuit);
  1290.                           vrl_MouseInit();
  1291.                           atexit(vrl_MouseQuit);
  1292.                           if (vrl_TimerInit())
  1293.                                   return -2;
  1294.                           atexit(vrl_TimerQuit);
  1295.                           if (vrl_RenderInit(800, 800, 500, 5, 65000))
  1296.                                   return -3;
  1297.                           atexit(vrl_RenderQuit);
  1298.  
  1299.                                                              AVRIL Tutorial                       22
  1300.  
  1301.  
  1302.  
  1303.  
  1304.  
  1305.                           atexit(vrl_DeviceCloseAll);
  1306.                           atexit(vrl_SerialCloseAll);
  1307.                           /* make sure that exit() [and therefore the atexit() functions] get
  1308.                              called if there are any fatal errors */
  1309.                           signal(SIGABRT, exit);
  1310.                           signal(SIGFPE, exit);
  1311.                           signal(SIGILL, exit);
  1312.                           signal(SIGINT, exit);
  1313.                           signal(SIGSEGV, exit);
  1314.                           signal(SIGTERM, exit);
  1315.                           vrl_SystemStartRunning();
  1316.                           vrl_SystemRequestRefresh();
  1317.                           vrl_SystemRender(NULL);
  1318.                           return 0;
  1319.                           }
  1320.  
  1321.           The vrl_SystemStartup() routine does the initialization of all
  1322.           the various AVRIL subsystems.  It starts by calling
  1323.           vrl_MathInit(), which sets up the trig tables used internally by
  1324.           AVRIL (for example, a table of sines that's used by the
  1325.           vrl_Sine() function described earlier).
  1326.  
  1327.                Next, the world is initialized and the video subsystem is
  1328.           started up; from this point on, the system is running in graphics
  1329.           mode.  The display subsystem is then initialized, followed by the
  1330.           mouse and the timer.
  1331.  
  1332.                After that, the rendering engine itself is initialized; the
  1333.           parameters to the vrl_RenderInit() function may change with a
  1334.           future release of the software, but for now just use the values
  1335.           that are shown above.  The value 65000 is the amount of memory
  1336.           the renderer should allocate for its internal use; if the
  1337.           renderer needs more than this amount of memory when rendering a
  1338.           scene, it will set the "memory" value in the status struct
  1339.           described earlier (which is passed to vrl_ApplicationDrawOver()). 
  1340.           If the renderer is unable to initialize itself (for example, if
  1341.           it couldn't allocate the specified amount of memory) then
  1342.           vrl_RenderInit() returns a non-zero value.
  1343.  
  1344.                Notice the use of atexit() to ensure that everything is shut
  1345.           down properly when the program exits.  The signal() calls ensure
  1346.           that the exit() routine will be called in case of any errors;
  1347.           exit() will in turn call the various atexit() functions, cleanly
  1348.           closing down the system.
  1349.  
  1350.                Finally, vrl_SystemStartRunning() is called and an initial
  1351.           display refresh is requested.  The vrl_SystemStartRunning(),
  1352.           vrl_SystemStopRunning(), and vrl_SystemIsRunning() routines are
  1353.           used to control whether the system is currently "running" or not. 
  1354.           They just set and check the value of the variable
  1355.           system_is_running; however, using the routines makes your code a
  1356.  
  1357.  
  1358.                                     AVRIL Tutorial                       23
  1359.  
  1360.  
  1361.  
  1362.  
  1363.  
  1364.           bit more readable.  It's also possible to redefine those routines
  1365.           to do something in addition to just setting or clearing a flag.
  1366.  
  1367.                The vrl_SystemRun() routine is the main loop of every AVRIL
  1368.           application.  It looks like this:
  1369.  
  1370.           void vrl_SystemRun(void)
  1371.                           {
  1372.                           vrl_ApplicationInit();
  1373.                           if (vrl_WorldGetStereoConfiguration())
  1374.                                   vrl_StereoConfigure(vrl_WorldGetStereoConfiguration());
  1375.                           while (vrl_SystemIsRunning())
  1376.                                   {
  1377.                                   vrl_Object *list;
  1378.                                   if (vrl_KeyboardCheck())
  1379.                                            vrl_ApplicationKey(vrl_KeyboardRead());
  1380.                                   check_mouse();
  1381.                                   vrl_TaskRun();
  1382.                                   vrl_DevicePollAll();
  1383.                                   list = vrl_WorldUpdate();
  1384.                                   if (vrl_SystemQueryRefresh())
  1385.                                            vrl_SystemRender(list);
  1386.                                   }
  1387.                           }
  1388.  
  1389.           It shouldn't come as any surprise that this looks like an event
  1390.           loop in a GUI application; on some systems, that's exactly how
  1391.           vrl_SystemRun() will be implemented.  However, on a DOS platform
  1392.           it's necessary to explicitly check the mouse and keyboard for
  1393.           activity.
  1394.  
  1395.                If a key has been pressed, the keyboard is read and the
  1396.           value of the key is passed to vrl_ApplicationKey().  The function
  1397.           check_mouse() is used to interrogate the mouse for updates:
  1398.  
  1399.           static void check_mouse(void)
  1400.                           {
  1401.                           unsigned int mouse_buttons;
  1402.                           if (vrl_MouseGetUsage())  /* being used as 6D pointing device */
  1403.                                   return;
  1404.                           if (!vrl_MouseRead(NULL, NULL, NULL))  /* mouse hasn't changed */
  1405.                                   return;
  1406.                           vrl_MouseRead(NULL, NULL, &mouse_buttons);
  1407.                           if (mouse_buttons)  /* button down */
  1408.                                   {
  1409.                                   int mouse_x, mouse_y;
  1410.                                   vrl_ScreenPos win_x, win_y;
  1411.                                   unsigned int down_buttons = mouse_buttons;
  1412.                                   vrl_DisplayGetWindow(&win_x, &win_y, NULL, NULL);
  1413.                                   while (mouse_buttons)  /* wait for button release */
  1414.                                            vrl_MouseRead(&mouse_x, &mouse_y, &mouse_buttons);
  1415.                                   if (down_buttons & 0x07)
  1416.  
  1417.                                                              AVRIL Tutorial                       24
  1418.  
  1419.  
  1420.  
  1421.  
  1422.  
  1423.                                            vrl_ApplicationMouseUp(mouse_x - win_x, mouse_y - win_y, down_buttons);
  1424.                                   }
  1425.                           }
  1426.  
  1427.           The vrl_MouseGetUsage() call is necessary because the mouse can
  1428.           be used in either of two completely different ways: as a pointing
  1429.           device for selecting objects on the screen, or as a 6 Degree-Of-
  1430.           Freedom (6D) input device; the 6D mode is described later, in the
  1431.           section on input devices.  If vrl_MouseGetUsage() returns a non-
  1432.           zero value, then the mouse is being used as a 6D input device,
  1433.           and input from it shouldn't be processed any further at this
  1434.           point.
  1435.  
  1436.                If the mouse hasn't changed location or button status, the
  1437.           call to vrl_MouseRead() will return zero, in which case no
  1438.           further processing is done.  If the mouse buttons are down, the
  1439.           routine tracks the mouse input until the buttons are released. 
  1440.           The button status is saved in the variable down_buttons, and then
  1441.           passed to the routine vrl_ApplicationMouseUp() along with the
  1442.           mouse cursor location in the current window.
  1443.  
  1444.                Back in vrl_SystemRun(), the vrl_TaskRun() function is
  1445.           called to run all the tasks that have been created (like the
  1446.           spin(), bounce() and pulsate() tasks we used in example 4).  The
  1447.           input devices are polled, and vrl_WorldUpdate() is called; it
  1448.           walks the hierarchical tree of objects in the world, updating
  1449.           their location and orientation information and threading them
  1450.           onto a linked list which is returned as the value of the
  1451.           vrl_WorldUpdate() function.  Walking the tree also causes the
  1452.           function associated with each object to be called.
  1453.  
  1454.                If the display needs to be redrawn (i.e. the
  1455.           vrl_SystemRequestRefresh() routine that we mentioned earlier has
  1456.           been called at least once since we last re-drew the screen) then
  1457.           the vrl_SystemRender() routine is called, and is given the linked
  1458.           list of objects to render.
  1459.  
  1460.                The vrl_SystemRender() routine does the actual updating of
  1461.           the screen.  Even though source is provided, you should use this
  1462.           routine as-is; it's likely to change in future releases of AVRIL,
  1463.           and several additional features will be added.  The code
  1464.           currently looks like this:
  1465.  
  1466.           vrl_RenderStatus *vrl_SystemRender(vrl_Object *list)
  1467.                           {
  1468.                           static vrl_Object *lastlist = NULL;
  1469.                           vrl_Palette *pal;
  1470.                           vrl_StereoConfiguration *conf;
  1471.                           vrl_RenderStatus *stat;
  1472.                           int pagenum;
  1473.                           int two_eyes = 0;
  1474.                           vrl_Time render_start = vrl_TimerRead();
  1475.  
  1476.                                                              AVRIL Tutorial                       25
  1477.  
  1478.  
  1479.  
  1480.  
  1481.  
  1482.                           if (list == NULL)
  1483.                                   list = lastlist;
  1484.                           else
  1485.                                   lastlist = list;
  1486.                           pal = vrl_WorldGetPalette();
  1487.                           if (vrl_PaletteHasChanged(pal))
  1488.                                   {
  1489.                                   vrl_VideoSetPalette(0, 255, pal);
  1490.                                   vrl_PaletteSetChanged(pal, 0);
  1491.                                   }
  1492.                           pagenum = vrl_VideoGetDrawPage();
  1493.                           if (++pagenum >= vrl_VideoGetNpages())
  1494.                                   pagenum = 0;
  1495.                           vrl_VideoSetDrawPage(pagenum);
  1496.                           vrl_RenderSetAmbient(vrl_WorldGetAmbient());
  1497.                           vrl_DisplayStereoSetDrawEye(VRL_STEREOEYE_BOTH);
  1498.                           if (vrl_WorldGetScreenClear())
  1499.                                   {
  1500.                                   vrl_DisplayBeginFrame();
  1501.                                   if (vrl_WorldGetHorizon() && !vrl_RenderGetDrawMode())
  1502.                                            vrl_RenderHorizon();
  1503.                                   else
  1504.                                            vrl_DisplayClear(vrl_WorldGetSkyColor());
  1505.                                   vrl_DisplayEndFrame();
  1506.                                   }
  1507.                           vrl_ApplicationDrawUnder();
  1508.                           conf = vrl_WorldGetStereoConfiguration();
  1509.                           if (conf)
  1510.                                   two_eyes = vrl_StereoGetNeyes(conf);
  1511.                           if (vrl_WorldGetStereo() && vrl_WorldGetLeftCamera()
  1512.                                   && vrl_WorldGetRightCamera() && two_eyes)
  1513.                                   {
  1514.                                   /* draw left-eye image */
  1515.                                   vrl_DisplayStereoSetDrawEye(VRL_STEREOEYE_LEFT);
  1516.                                   vrl_RenderSetHorizontalShift(vrl_StereoGetTotalLeftShift(conf));
  1517.                                   vrl_DisplayBeginFrame();
  1518.                                   vrl_RenderBegin(vrl_WorldGetLeftCamera(), vrl_WorldGetLights());
  1519.                                   stat = vrl_RenderObjlist(list);
  1520.                                   vrl_DisplayEndFrame();
  1521.  
  1522.                                   /* draw right-eye image */
  1523.                                   vrl_DisplayStereoSetDrawEye(VRL_STEREOEYE_RIGHT);
  1524.                                   vrl_RenderSetHorizontalShift(vrl_StereoGetTotalRightShift(conf));
  1525.                                   vrl_RenderBegin(vrl_WorldGetRightCamera(), vrl_WorldGetLights());
  1526.                                   vrl_DisplayBeginFrame();
  1527.                                   stat = vrl_RenderObjlist(list);
  1528.                                   vrl_DisplayEndFrame();
  1529.                                   }
  1530.                           else  /* not two-eye stereo */
  1531.                                   {
  1532.                                   vrl_DisplayStereoSetDrawEye(VRL_STEREOEYE_BOTH);
  1533.                                   vrl_RenderSetHorizontalShift(0);
  1534.  
  1535.                                                              AVRIL Tutorial                       26
  1536.  
  1537.  
  1538.  
  1539.  
  1540.  
  1541.                                   vrl_DisplayBeginFrame();
  1542.                                   vrl_RenderBegin(vrl_WorldGetCamera(), vrl_WorldGetLights());
  1543.                                   stat = vrl_RenderObjlist(list);
  1544.                                   vrl_DisplayEndFrame();
  1545.                                   }
  1546.                           vrl_DisplayStereoSetDrawEye(VRL_STEREOEYE_BOTH);
  1547.                           vrl_RenderSetHorizontalShift(0);
  1548.                           vrl_ApplicationDrawOver(stat);
  1549.                           vrl_VideoCursorHide();
  1550.                           vrl_DisplayUpdate();
  1551.                           vrl_VideoSetViewPage(pagenum);
  1552.                           vrl_VideoCursorShow();
  1553.                           last_render_ticks = vrl_TimerRead() - render_start;
  1554.                           need_to_redraw = 0;
  1555.                           return stat;
  1556.                           }
  1557.  
  1558.           First, the current time is stored in the variable render_start;
  1559.           this is later used to compute the frame rate.
  1560.  
  1561.                If the list that the vrl_SystemRender() routine is given is
  1562.           NULL (as was the case in our example vrl_ApplicationMouseUp()
  1563.           routine in input.c) then the last list of objects rendered is
  1564.           used.  If the palette has changed since the last frame, it gets
  1565.           copied to the hardware palette and the "changed" flag is cleared.
  1566.  
  1567.                The system uses the concept of a "draw" page (on which
  1568.           drawing takes place) and a "view" page (which is the one the user
  1569.           is currently viewing).  The vrl_SystemRender() routine gets the
  1570.           current drawing page number, and increments it (so we start
  1571.           drawing on the next page).  It wraps back to page zero after it's
  1572.           drawn the last available display page.
  1573.                The ambient light level is set according to that of the
  1574.           current world.  If it's necessary to clear the screen, the system
  1575.           does so (or draws a horizon, as appropriate).  Then the
  1576.           vrl_ApplicationDrawUnder() routine we looked at earlier is
  1577.           called.
  1578.  
  1579.                A check is made to see if we're configured for stereoscopic
  1580.           viewing.  If we are, and if both the left and right eye cameras
  1581.           exist, and if it's a "two-eye" system (i.e., not Chromadepth or
  1582.           SIRDS) then the left eye image is drawn followed by the right eye
  1583.           image.  If we're not doing two-eye stereoscopic rendering, a
  1584.           single image is drawn.
  1585.  
  1586.                To draw an image, we start by selecting an eye and setting a
  1587.           corresponding horizontal offset.  Next, we tell the display
  1588.           subsystem to get ready for a new frame, tell the rendering engine
  1589.           about our camera and lights (using vrl_RenderBegin()), and call
  1590.           vrl_RenderObjlist() to actually draw the objects.  Finally, we
  1591.           tell the display subsystem that the frame is complete.
  1592.  
  1593.  
  1594.                                     AVRIL Tutorial                       27
  1595.  
  1596.  
  1597.  
  1598.  
  1599.  
  1600.                The vrl_ApplicationDrawOver() routine is then called to put
  1601.           any additional information on the display.  The cursor is hidden,
  1602.           and vrl_DisplayUpdate() is called; this is necessary, since some
  1603.           display systems don't have multiple pages and instead use an off-
  1604.           screen buffer which the vrl_DisplayUpdate() routine copies to the
  1605.           screen.  For systems that have multiple-page displays, the
  1606.           current view page is set to the (now finished) drawing page.  The
  1607.           mouse cursor is then revealed again, the time it took to do all
  1608.           this is noted, and the need_to_redraw variable (which was set by
  1609.           vrl_SystemRequestRefresh()) is cleared.
  1610.  
  1611.                The final vrl_System routine is vrl_SystemCommandLine().  It
  1612.           just goes through the command-line parameters and calls the
  1613.           appropriate routines to read the various types of files:
  1614.  
  1615.           void vrl_SystemCommandLine(int argc, char *argv[])
  1616.                           {
  1617.                           int i;
  1618.                           vrl_Camera *cam;
  1619.                           for (i = 1; i < argc; ++i)  /* i = 1 to skip argv[0] */
  1620.                                   {
  1621.                                   FILE *in = fopen(argv[i], "r");
  1622.                                   if (in == NULL) continue;
  1623.                                   if (strstr(argv[i], ".wld"))
  1624.                                            vrl_ReadWLD(in);
  1625.                                   else if (strstr(argv[i], ".fig"))
  1626.                                            vrl_ReadFIG(in, NULL, NULL);
  1627.                                   else if (strstr(argv[i], ".plg"))
  1628.                                            vrl_ReadObjectPLG(in);
  1629.                                   /* ignore anything else */
  1630.                                   fclose(in);
  1631.                                   }
  1632.                           if (!vrl_WorldGetCamera())   /* need to have a camera */
  1633.                                   vrl_CameraCreate();
  1634.                           vrl_WorldUpdate();
  1635.                           }
  1636.  
  1637.           After all the files on the command line have been processed, the
  1638.           vrl_SystemCommandLine() routine checks to see if a current camera
  1639.           has been set.  If not, a new camera is created.
  1640.  
  1641.           Configuration Files
  1642.  
  1643.           AVRIL supports the loading of configuration files; the format of
  1644.           these files is given in Appendix B of the technical reference
  1645.           manual.  The functions that support loading configuration
  1646.           information are
  1647.  
  1648.                int vrl_ReadCFG(FILE *in);
  1649.                           int vrl_ReadCFGfile(char *filename);
  1650.  
  1651.  
  1652.  
  1653.                                     AVRIL Tutorial                       28
  1654.  
  1655.  
  1656.  
  1657.  
  1658.  
  1659.           The vrl_ReadCFG() function reads a configuration file and stores
  1660.           the information from it in a set of internal data structures. 
  1661.           Any devices specified in the file are opened, and the serial
  1662.           ports they use are opened as well.  If a display driver is
  1663.           specified, the display is initialized using that driver.  The
  1664.           vrl_ReadCFGfile() routine does the same thing, but uses the name
  1665.           of a file rather than a pointer to an already-opened file.  The
  1666.           filename is processed to prepend the current loadpath (unless the
  1667.           filename begins with a slash).  If the filename is NULL, then
  1668.           "avril.cfg" is used.
  1669.  
  1670.                There are routines for reading, setting and toggling the
  1671.           various flags that the user can specify in the configuration
  1672.           file:
  1673.  
  1674.                void vrl_ConfigSetCompassDisplay(vrl_Boolean flag);
  1675.                           vrl_Boolean vrl_ConfigGetCompassDisplay(void);
  1676.                           void vrl_ConfigToggleCompassDisplay(void);
  1677.                           void vrl_ConfigSetPositionDisplay(vrl_Boolean flag);
  1678.                           vrl_Boolean vrl_ConfigGetPositionDisplay(void);
  1679.                           void vrl_ConfigTogglePositionDisplay(void);
  1680.                           void vrl_ConfigSetFramerateDisplay(vrl_Boolean flag);
  1681.                           vrl_Boolean vrl_ConfigGetFramerateDisplay(void);
  1682.                           void vrl_ConfigToggleFramerateDisplay(void);
  1683.  
  1684.           If you wanted to let the user specify a configuration file to
  1685.           load by setting the AVRIL environment variable, you would make
  1686.           the following call:
  1687.  
  1688.                vrl_ReadCFGfile(getenv("AVRIL"));
  1689.  
  1690.           If no AVRIL environment variable is found, getenv() will return
  1691.           NULL and avril.cfg will be used.
  1692.  
  1693.           That's All, Folks!
  1694.  
  1695.                For more detailed information about AVRIL, check out the
  1696.           AVRIL reference manual.  It contains an in-depth description of
  1697.           all the AVRIL functions and data types.
  1698.  
  1699.                If you have problems using AVRIL, drop me a line via email. 
  1700.           My address is broehl@sunee.uwaterloo.ca; be sure to put AVRIL in
  1701.           the subject line so I know what it's about, otherwise it might
  1702.           take me days to get back to you.  (It might anyway...)
  1703.  
  1704.           Support for AVRIL
  1705.  
  1706.                There are two electronic mailing lists for discussing AVRIL. 
  1707.           The first list is called avril-announce, and is used for
  1708.           announcements of new releases, utilities, applications and so on. 
  1709.           The second list is called avril-developers, and is used as a way
  1710.  
  1711.  
  1712.                                     AVRIL Tutorial                       29
  1713.  
  1714.  
  1715.  
  1716.  
  1717.  
  1718.           for people who are developing applications using AVRIL to
  1719.           communicate and exchange ideas.
  1720.  
  1721.                To subscribe to either or both lists, send mail to
  1722.           majordomo@sunee.uwaterloo.ca with the following line(s) in the
  1723.           body of the message:
  1724.  
  1725.                subscribe avril-announce YourName
  1726.                           subscribe avril-developers YourName
  1727.  
  1728.           To unsubscribe from either or both lists, do the exact same thing
  1729.           but with the word "unsubscribe" instead of the word "subscribe".
  1730.  
  1731.           Future Features
  1732.  
  1733.                Features that will be added in future releases include
  1734.           routines for sound, networking, and an application language of
  1735.           some sort.  I also hope to add Z-buffering and texture mapping.
  1736.  
  1737.                The latest release of AVRIL can always be found on
  1738.           sunee.uwaterloo.ca, in the pub/avril directory.  I will also try
  1739.           to put the latest version on major sites such as ftp.wustl.edu,
  1740.           oak.oakland.edu, x2ftp.oulu.fi and possibly others.  Please feel
  1741.           free to make it available to everyone; the only restrictions are
  1742.           that you can't sell it (since it's free!) and you can't develop
  1743.           commercial applications without properly licensing it.
  1744.  
  1745.                There should be a new release of AVRIL every few months;
  1746.           starting with version 2.5, AVRIL should be ported to several
  1747.           other platforms.
  1748.  
  1749.                One sad note: after many years of cheerfully using Borland
  1750.           C, I've decided to move on.  All the new features I want to add
  1751.           (Z-buffering, texture mapping, higher resolutions, support for 16-
  1752.            and 24-bit color) all require lots of memory.  The old 640k
  1753.           barrier is just too much of a limitation, so I'm going to
  1754.           protected mode.  The best protected mode compiler is Watcom C,
  1755.           and I've already started the conversion.
  1756.  
  1757.                What does this mean for users of AVRIL?  Well, it might mean
  1758.           buying a new compiler.  However, I may try to port AVRIL to
  1759.           DJGPP, a freeware C compiler based on GNU C.  No promises, but if
  1760.           I have time I'll give it a shot.
  1761.  
  1762.                In the meantime, I hope you enjoy using AVRIL.
  1763.  
  1764.  
  1765.  
  1766.  
  1767.  
  1768.  
  1769.  
  1770.  
  1771.                                     AVRIL Tutorial                       30