[Extract from AVRIL.doc]



          Appendix E - WLD FILE FORMAT



          WLD files were designed to store information about the layout of

          objects in a virtual world.



                    This format will soon be considered obsolete.



          There will soon be a file format for the interchange of virtual

          objects and virtual worlds between VR systems; at that point,

          support for the WLD file format will diminish.  Conversion

          programs will be made available to convert WLD files to the new

          format.



          A WLD file is entirely ascii.  Each statement is one line;

          anything after the first '#' is treated as a comment and ignored. 

          Blank lines are also ignored.  The format is intended to be

          highly extensible; any line which cannot be recognized should

          simply be ignored.  Each statement contains some information

          about the scene; the possible types of statements are listed

          below.  Everything is case-insensitive; keywords are shown below

          in uppercase, but are generally entered in lowercase.



          LOADPATH path

               Specifies a path prefix for loading files.  Any files

               (whether specified in the world file itself, subsequent

               world files, or in referenced FIG files) will be loaded from

               the specified directory.  However, if a filename begins with

               the '\' or '/' characters, it is used verbatim (i.e. the

               LOADPATH setting is ignored).



          PALETTE filename

               Loads a 256-entry binary palette file (3 bytes (R,G,B) for

               each entry).  Note that alternate palettes may not handle

               shading as well as the default one does.



          SKYCOLOR index

               Specifies which of the 256 available colors should be used

               for the "sky".



          GROUNDCOLOR index

               Specifies which of the 256 available colors should be used

               for the "ground". If the sky and ground color are identical,

               a solid screen clear is used; this is a bit faster.



          SCREENCLEAR value

               If the specified value is non-zero, then the screen will be

               cleared before each frame; if it's zero, the screen clearing

               is not done (this is useful if you know that the entire

               window will be covered by the image, and that no background

               will show through; in such a situation, specifying this

               option will improve performance).



                                                                         72













          AMBIENT value

               Specifies the level of the ambient light; 76 is the default,

               and a good value to use.



          LIGHT x,y,z

               Specifies the location of a light source in world

               coordinates.



          CAMERA x,y,z tilt,pan,roll zoom

               Specifies your starting location, viewing direction and zoom

               factor.  The x,y,z values are floating-point numbers giving

               coordinates, the tilt,pan,roll values are floating-point

               angles, and the zoom is a floating-point number giving the

               zoom factor.



          HITHER value

               Specifies the near clipping distance in world coordinates. 

               The value should typically be 10 or more.



          YON value

               Specifies the far clipping distance in world coordinates. 

               The value should typically be 1000000 or more.



          OBJECT [objname=]filename sx,sy,sz rx,ry,rz tx,ty,tz depthtype

          mappings parent

               Loads an object from a .plg file with the given filename. 

               If the objname= is present, it assigns the newly-loaded

               object that name for future reference.  The sx,sy,sz values

               are floating-point scale factors to increase or decrease the

               size of the object as it's loaded.  The rx,ry,rz values are

               the angles to rotate the object around in each of the three

               axes; ry is done first, then rx and finally rz.  The

               tx,ty,tz values translate (move) the object to a new

               location; this is done after the scaling and rotation.  The

               depthtype field is not currently used.  The mappings feature

               is explained below.  The parent field is the name of the

               object that this object is a child of; if omitted, the child

               moves independently.  If parent is the word "fixed", then

               the object is fixed in space and cannot be moved.  All

               fields are optional, but you must include all the fields

               prior to the last one you wish to use (i.e. you can only

               leave things off the end, not off the beginning or out of

               the middle).



          FIGURE [figname=]filename sx,sy,sz rx,ry,rz tx,ty,tz parent

               Loads a segmented figure from a FIG file with the given

               filename.  All the parameters have the same meaning as for

               the OBJECT statement described above.



          POLYOBJ npts surface x1,y1,z1 x2,y2,z2 [...] x8,y8,z8

               Directly specifies a facet to be placed in the scene.  The

               value npts is the number of points (maximum 8), the surface



                                                                         73













               is a surface name (see below on surfaces) and the vertices

               are given in world coordinates.



          POLYOBJ2 npts surface1,surface2 x1,y1,z1 x2,y2,z2 [...] x8,y8,z8

               Directly specifies a double-sided facet to be placed in the

               scene.  The value npts is the number of points (maximum 8),

               surface1 and surface2 are surface names (see below on

               surfaces) and the vertices are given in world coordinates.



          INCLUDE filename

               Includes the specified file as if its contents appeared at

               the current point in the current file.



          POSITION objname x,y,z

               Moves (i.e. translates) the specified object to the given

               x,y,z location.



          ROTATE objname rx,ry,rz

               Rotates the specified object to the given angles about each

               of the axes.  The angles are specified in floating point,

               and are measured in degrees.  The rotation order is Y then X

               then Z.



          VERSION number

               Allows you to define a version number.  Not currently used

               for anything; can be omitted.



          TITLE text

               Allows you to define a title for your world.



          About Mapping



          A PLG file can contain indexed color values (such as 0x8002)

          which are used to index a surface map.  Entries in surface maps

          refer to surfaces.  Surfaces are created using the SURFACEDEF

          statement, surface maps are created with the SURFACEMAP

          statement, and entries are placed in them with the SURFACE

          statement.  The statement formats are as follows:



          SURFACEDEF name value

               Defines a new surface; maps a surface name (such as "wood")

               to a numeric surface descriptor (value) of the type

               described in Appendix C.



          SURFACEMAP name maxentries

               Marks the start of a new surface map.  All subsequent

               SURFACE entries will be placed in this map.  The maxentries

               field gives the maximum number of entries this surface map

               will have; if omitted, it defaults to 10.



          SURFACE index name





                                                                         74













               Defines an entry in the current surface map, which takes an

               index value (the bottom 15 bits of the value in the .plg

               file) and maps it into a surface name (which is in turn

               mapped to a 16-bit color value).



          USEMAP mapname

               Causes all subsequently loaded objects that don't have a

               mapname on their OBJECT statements to use the specified

               mapname.

























































































                                                                         75















          Appendix F - WRITING DEVICE DRIVERS



          Writing device drivers for AVRIL is easy.  You basically create a

          single function with a unique name; for example, if you want to

          support a (mythical) RealTronics Atomic Tracking System, your

          function might be



               int vrl_ATSDevice(vrl_DeviceCommand cmd, vrl_Device *device)

                                  {

                                  [...]

                                  }



          You should add an entry for your new function to the list in

          avrildrv.h, and possibly to the cfg.c file.



               Your driver routine will get called periodically by the

          application.  The vrl_Device struct is pre-allocated by AVRIL, so

          you just have to fill in the various fields.  The cmd is one of

          VRL_DEVICE_INIT, VRL_DEVICE_RESET, VRL_DEVICE_POLL, or

          VRL_DEVICE_QUIT.



               When a device is first opened, AVRIL will set all the fields

          in the vrl_Device struct to reasonable values.  The

          VRL_DEVICE_INIT call should fill in the following fields with

          driver-specific information:



               char *desc;                 /* user-readable device description */

                          int nchannels;              /* number of input channels the device has */

                          vrl_DeviceChannel *channels;   /* pointer to array of channels */



          The desc is a string describing the device, the nchannels value

          is the number of input channels the device has (should be at

          least 6) and the channels field is set to point to an array of

          vrl_DeviceChannel structs, one per channel.  These channels

          should be dynamically allocated, rather than using a static

          struct; this is to allow multiple instances of the same type of

          device (for example, a Cyberman on each of COM1 and COM2, each

          with its own channel-specific data).  For this same reason, your

          driver shouldn't use any global variables; you should instead

          dynamically allocate memory for any additional per-device-

          instance data and store the pointer to that data in the localdata

          field of the vrl_Device struct.  The VRL_DEVICE_INIT call should

          also fill in the appropriate values for all the channels.



          The VRL_DEVICE_INIT call may also choose to fill in some or all

          of the following:



               int nbuttons;               /* number of buttons the device has */

                          int noutput_channels;       /* number of output channels */

                          vrl_DeviceOutputFunction *outfunc;  /* function to call to generate output */

                          vrl_DeviceMotionMode rotation_mode;     /* rotation mode for this device */



                                                                                                                            76













                          vrl_DeviceMotionMode translation_mode;  /* translation mode for this device */

                          vrl_Buttonmap *buttonmap;   /* button mapping table for 2D devices */

                          int version;                /* device struct version number */

                          int mode;                   /* mode of operation */

                          vrl_Time period;            /* milliseconds between reads */



          The number of buttons the device has is assumed to be zero unless

          you set it otherwise, as is the number of output channels.  The

          outfunc field is a pointer to a function (probably declared

          static in the same source file as your driver function) that

          handles output to the device; this is described in more detail

          below.  If your device doesn't do output, leave this field at its

          default value of NULL.



               The meaning of the two vrl_DeviceMotionMode type fields was

          described earlier in this document, in the section on Devices. 

          They both default to VRL_MOTION_RELATIVE.  The mode is driver-

          specific, and can be initialized to whatever value you like

          (since the value is only interpreted by your driver).  The

          version field should be left at its default value of zero by

          drivers following this version of the driver specification; as

          the driver specification evolves, this value will increase.



               The period defaults to zero, meaning that a call to

          vrl_DevicePoll() will always result in your driver function being

          called with a cmd of VRL_DEVICE_POLL.  If you don't want to be

          polled every cycle, set the period to the minimum number of ticks

          (milliseconds) that should elapse between polls.  Note that this

          is a minimum value; the delay between polls may be even longer if

          the system is busy doing other things.



               The VRL_DEVICE_RESET command is very similar to

          VRL_DEVICE_INIT, and may in fact be the same for some devices. 

          The difference is that VRL_DEVICE_INIT does one-time

          initializations (such as taking over interrupt vectors).



               The VRL_DEVICE_QUIT command should "shut down" the device,

          putting it in a quiescent state and undoing anything that was

          done in VRL_DEVICE_INIT and VRL_DEVICE_RESET (for example,

          restoring interrupt vectors).  It's also responsible for

          releasing any memory that was dynamically allocated by

          VRL_DEVICE_INIT, including that pointed to by the channels field

          and the localdata field if it was used.



               Serial devices can assume that when VRL_DEVICE_INIT is

          called, the port they'll be using is already open, and that the

          port field is set; the driver also does not need to (and should

          not) close the port.  However, devices that actually use the port

          should check that it's not NULL, and return -4 if it is.



               The VRL_DEVICE_POLL command should read the raw data from

          the hardware (for example, by calling vrl_DeviceGetPacket()) and



                                                                         77













          decoding the values into the rawdata fields of the appropriate

          channels.  You should be sure to set the changed field for any

          channels that you update.



          There are a number of values associated with each channel; they

          are as follows:



          struct _vrl_device_channel

                          {

                          vrl_32bit centerpoint;        /* value of center point in raw device coords */

                          vrl_32bit deadzone;           /* minimum acceptable value in device coords */

                          vrl_32bit range;              /* maximum absolute value relative to zero */

                          vrl_Scalar scale;             /* maximum returned value */

                          vrl_Boolean accumulate : 1;   /* if set, accumulate values */

                          vrl_Boolean changed : 1;      /* set if rawvalue has changed */

                          vrl_32bit rawvalue;           /* current value in raw device coordinates */

                          vrl_32bit oldvalue;           /* previous value in raw device coordinates */

                          vrl_Scalar value;             /* current value (processed) */

                          };



          The only fields you must set are the centerpoint, deadzone,

          range, scale and accumulate.  The centerpoint is the current

          "zero" value of the device; for example, the value an analog

          joystick on a PC-compatible reports when the stick is at rest can

          be considered its centerpoint.



               The deadzone has two different interpretations.  If the

          accumulate flag is set, then the deadzone is the minimum

          displacement from the centerpoint that will be recognized.  If

          the accumulate flag is clear, then the value is the minimum

          change from the previous value (as stored in oldvalue by the

          higher-level routines) that will be recognized.



               The scale, and range values are used to convert the rawvalue

          into units more suitable for the application.  The scale is the

          number of world-space units corresponding to the range in device

          units.  The scale, and deadzone should both be positive values,

          and can be changed by the application.  The range value can be

          negative; this is useful for reversing the direction of a device

          axis.  The range value is only ever set by your driver.



               The accumulate flag, in addition to controlling how the

          deadzone is interpreted, causes the value to be scaled by the

          elapsed time in seconds since the last poll.



               The best way to understand all this is to consider what

          happens when you read new values from the device.  First, you

          store the data for each channel in the corresponding channel's

          rawvalue field; you can re-map axes at this point (device

          coordinate Y goes into the Z channel, for example).  You should

          set the changed flag, to indicate there's a new value there.





                                                                         78













               Next, the higher-level code takes your rawvalue and

          subtracts the centerpoint.  If the channel is in accumlate mode,

          it checks if the absolute value of the data is less than

          deadzone; if it is, it truncates it to zero.  If the channel is

          not in accumulate mode, the data is compared to the oldvalue

          field; if it's within plus or minus deadzone of it, the data is

          ignored.



               Once the value has been centered and deadzoned, it is

          multiplied by the scale and divided by the range.  If accumulate

          is set, the resulting value is multiplied by the elapsed time in

          milliseconds and then divided by 1000 to convert to seconds.



          Buttonmaps



               Some 2D devices (such as mice and joysticks) can be used as

          6D devices, by using their buttons to map their input axes to the

          6 possible degrees of freedom.  Such devices should set their

          nbuttons field to zero (or at least to the number of buttons that

          will not be used for mapping).  They should also set their

          buttonmap field to point to a default set of axis mappings.



               The buttonmap field is a pointer to a two-dimensional array. 

          Each row of the array corresponds to a button combination; on a

          two-button device, row 0 is for no buttons down, row 1 is for the

          first button down, row 2 is for the second button down and row

          three is for both buttons down.  There are two columns in the

          array, the first of which contains the index of the channel that

          the input device's X value should be stored in, and the second of

          which contains the index of the Y channel.



          For example,



               static vrl_Buttonmap default_map =

                                  { { YROT, Z }, { X, Y }, { ZROT, XROT }, { X, Y }};



          Would mean that when no buttons are down, the device's X axis

          corresponds to a Y rotation, and its Y channel to a Z

          translation.  When the first button is down, the device's X axis

          corresponds to an X translation, and its Y axis to a Y

          translation, and so on.



               The application can change the buttonmap field (using

          vrl_DeviceSetButtonmap()) to point to a different set of

          mappings.



               One thing to watch out for: since only two channels at a

          time are active, the others should have their rawvalue set equal

          to their centerpoint, and their changed flags set; otherwise,

          they'll retain whatever values they had last time a particular

          button combination was active.





                                                                         79













          Output



               Some devices are capable of tactile or auditory feedback;

          those that are should set the outfunc field in the vrl_Device

          struct to point to a function that does the actual work, and set

          the noutput_channels field to the number of output channels the

          device has.  Such a function for our mythical ATS device might

          look like this:



               int vrl_SpaceballOutput(vrl_Device *dev, int parm1, vrl_Scalar parm2)

                                  {

                                  [...]

                                  }



                 The parm1 parameter is the output channel number, and parm2 is

          the value to output (in the range 0 to 255).  The routine should

          return 0 on success and non-zero on failure, although those

          values are not currently used or reported.



