home *** CD-ROM | disk | FTP | other *** search
/ Virtual Reality Zone / VRZONE.ISO / mac / PC / VDF / VDF.ZIP / VDFREAD.C < prev    next >
C/C++ Source or Header  |  1994-11-21  |  43KB  |  1,418 lines

  1. /* Parser for VDF -- the Virtual world Description Format */
  2.  
  3. /* 
  4.  *  This implementation written by:
  5.  *      Bernie Roehl (broehl@uwaterloo.ca)
  6.  *      November, 1994
  7.  *  Redistribute at will.
  8.  *
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include <setjmp.h>
  16.  
  17. #include "vdf.h"
  18.  
  19. #define color_zero(v) ((v)->red = (v)->green = (v)->blue = 0.0)
  20. #define vector_zero(v) ((v)->x = (v)->y = (v)->z = 0.0)
  21.  
  22. typedef enum
  23.     {
  24.     VDF_TAG_IGNORE = 0, VDF_TAG_UNKNOWN,
  25.     VDF_TAG_LEFT_BRACKET, VDF_TAG_RIGHT_BRACKET, VDF_TAG_STRING,
  26.     VDF_TAG_INCLUDE, VDF_TAG_COUNT, VDF_TAG_IDENTIFIER, VDF_TAG_NAME,
  27.     VDF_TAG_APPLICATION_HANDLE, VDF_TAG_FILENAME, VDF_TAG_COLOR,
  28.     VDF_TAG_NORMAL3D, VDF_TAG_INDEX,
  29.     VDF_TAG_PALETTE, VDF_TAG_COLOR_TABLE, VDF_TAG_HUE_TABLE,
  30.     VDF_TAG_COLOR_ENTRY, VDF_TAG_HUE_ENTRY,
  31.     VDF_TAG_MAP,
  32.     VDF_TAG_MATERIAL, VDF_TAG_RENDERING_MODE, VDF_TAG_HUE, VDF_TAG_ALBEDO,
  33.     VDF_TAG_DIFFUSE_COLOR, VDF_TAG_AMBIENT_COLOR, VDF_TAG_TRANSPARENCY_COLOR,
  34.     VDF_TAG_SPECULAR_COLOR, VDF_TAG_SPECULAR_EXPONENT,
  35.     VDF_TAG_REFRACTIVE_INDEX, VDF_TAG_TEXTURE_MAP, VDF_TAG_BUMP_MAP,
  36.     VDF_TAG_OPACITY_MAP, VDF_TAG_REFLECTION_MAP, VDF_TAG_REFLECTION_BLUR,
  37.     VDF_TAG_TRANSPARENCY_FALLOFF,
  38.     VDF_TAG_MATERIAL_TABLE, VDF_TAG_MATERIAL_REFERENCE,
  39.     VDF_TAG_SHAPE, VDF_TAG_LOD_SIZE, VDF_TAG_LOD_REPLACES, VDF_TAG_IS_CONVEX,
  40.     VDF_TAG_BOUNDING_BOX, VDF_TAG_USES_MATERIAL_TABLE, VDF_TAG_VERTEX_LIST,
  41.     VDF_TAG_FACET_LIST,    VDF_TAG_FACET, VDF_TAG_VERTEX, VDF_TAG_POINT3D,
  42.     VDF_TAG_VERTEX_DATA, VDF_TAG_VERTEX_INFO, VDF_TAG_IS_DOUBLESIDED,
  43.     VDF_TAG_IS_INTERIOR, VDF_TAG_BASE_FACET,
  44.     VDF_TAG_FRONT_MATERIAL, VDF_TAG_BACK_MATERIAL,
  45.     VDF_TAG_OBJECT, VDF_TAG_INSTANCE_OF_SHAPE, VDF_TAG_SCALED_BY,
  46.     VDF_TAG_LOCATION, VDF_TAG_ROTATION,
  47.     VDF_TAG_ATTACHED_TO, VDF_TAG_CONTAINED_WITHIN, VDF_TAG_IS_INVISIBLE,
  48.     VDF_TAG_LAYER, VDF_TAG_TEXT, VDF_TAG_FACET_BEHIND,
  49.     VDF_TAG_CAMERA, VDF_TAG_FIELD_OF_VIEW, VDF_TAG_ASPECT_RATIO,
  50.     VDF_TAG_ASSOCIATED_WITH, VDF_TAG_PROJECTION_TYPE,
  51.     VDF_TAG_LIGHT, VDF_TAG_LIGHT_TYPE, VDF_TAG_HOTSPOT, VDF_TAG_FALLOFF,
  52.     VDF_TAG_IS_ON, VDF_TAG_CASTS_SHADOWS,
  53.     VDF_TAG_SOUND, VDF_TAG_VOLUME, VDF_TAG_SAMPLE_NAME,
  54.     VDF_TAG_WORLD_ATTRIBUTES, VDF_TAG_GRAVITY_VECTOR, VDF_TAG_AMBIENT_LIGHT,
  55.     VDF_TAG_HAS_HORIZON, VDF_TAG_SKY_COLOR, VDF_TAG_GROUND_COLOR,
  56.     VDF_TAG_FOG_COLOR, VDF_TAG_SCALE,
  57.     VDF_TAG_WORLD_INFO, VDF_TAG_CREATED_BY, VDF_TAG_MODIFIED_BY,
  58.     VDF_TAG_DATE, VDF_TAG_PERSON, VDF_TAG_COPYRIGHT_MESSAGE,
  59.     VDF_TAG_USAGE_RESTRICTIONS, VDF_TAG_COMMENT, VDF_TAG_TITLE
  60.     } VDF_TAG;
  61.  
  62. static char *tagnames[] =
  63.     {
  64.     "", "", "{", "}", "",
  65.     "INCLUDE", "COUNT", "IDENTIFIER", "NAME",
  66.     "APPLICATION_HANDLE", "FILENAME", "COLOR",
  67.     "NORMAL3D", "INDEX",
  68.     "PALETTE", "COLOR_TABLE", "HUE_TABLE",
  69.     "COLOR_ENTRY", "HUE_ENTRY",
  70.     "MAP",
  71.     "MATERIAL", "RENDERING_MODE", "HUE", "ALBEDO",
  72.     "DIFFUSE_COLOR", "AMBIENT_COLOR", "TRANSPARENCY_COLOR",
  73.     "SPECULAR_COLOR", "SPECULAR_EXPONENT",
  74.     "REFRACTIVE_INDEX", "TEXTURE_MAP", "BUMP_MAP",
  75.     "OPACITY_MAP", "REFLECTION_MAP", "REFLECTION_BLUR",
  76.     "TRANSPARENCY_FALLOFF",
  77.     "MATERIAL_TABLE", "MATERIAL_REFERENCE",
  78.     "SHAPE", "LOD_SIZE", "LOD_REPLACES", "IS_CONVEX",
  79.     "BOUNDING_BOX", "USES_MATERIAL_TABLE", "VERTEX_LIST",
  80.     "FACET_LIST", "FACET", "VERTEX", "POINT3D",
  81.     "VERTEX_DATA", "VERTEX_INFO", "IS_DOUBLESIDED",
  82.     "IS_INTERIOR", "BASE_FACET",
  83.     "FRONT_MATERIAL", "BACK_MATERIAL",
  84.     "OBJECT", "INSTANCE_OF_SHAPE", "SCALED_BY",
  85.     "LOCATION", "ROTATION",
  86.     "ATTACHED_TO", "CONTAINED_WITHIN", "IS_INVISIBLE",
  87.     "LAYER", "TEXT", "FACET_BEHIND",
  88.     "CAMERA", "FIELD_OF_VIEW", "ASPECT_RATIO",
  89.     "ASSOCIATED_WITH", "PROJECTION_TYPE",
  90.     "LIGHT", "TYPE", "HOTSPOT", "FALLOFF",
  91.     "IS_ON", "CASTS_SHADOWS",
  92.     "SOUND", "VOLUME", "SAMPLE_NAME",
  93.     "WORLD_ATTRIBUTES", "GRAVITY_VECTOR", "AMBIENT_LIGHT", 
  94.     "HAS_HORIZON", "SKY_COLOR", "GROUND_COLOR",
  95.     "FOG_COLOR", "SCALE",
  96.     "WORLD_INFORMATION", "CREATED_BY", "MODIFIED_BY",
  97.     "DATE", "PERSON", "COPYRIGHT_MESSAGE",
  98.     "USAGE_RESTRICTIONS", "COMMENT", "TITLE",
  99.     NULL
  100.     };
  101.  
  102. #define START_STRING -2   /* returned for opening double-quotes */
  103. #define END_STRING   -3   /* returned for closing double-quotes */
  104.  
  105. static char *earlyEOF = "encountered unexpected EOF";
  106.  
  107. /* these "end" variables point to the last entry in each of the singly-linked
  108.    lists; that way new items can be added onto the end of the list easily */
  109.  
  110. static VDF_PALETTE *end_palettes;
  111. static VDF_MAP *end_maps;
  112. static VDF_MATERIAL *end_materials;
  113. static VDF_MATERIAL_TABLE *end_material_tables;
  114. static VDF_SHAPE *end_shapes;
  115. static VDF_OBJECT *end_objects;
  116. static VDF_CAMERA *end_cameras;
  117. static VDF_LIGHT *end_lights;
  118. static VDF_SOUND *end_sounds;
  119. static VDF_WORLD_ATTRIBUTES *end_world_attributes;
  120.  
  121. static char *render_mode_names[] =
  122.     {
  123.     "WIREFRAME", "UNLIT", "FLAT", "GOURAUD", "PHONG", NULL
  124.     };
  125.  
  126. static char *light_type_names[] =
  127.     {
  128.     "DIRECTIONAL", "POINT", "SPOT", NULL
  129.     };
  130.  
  131. static char *projection_type_names[] =
  132.     {
  133.     "PERSPECTIVE", "PARALLEL", NULL
  134.     };
  135.  
  136. /* Open include files are kept in a stack (implemented as a linked list) */
  137.  
  138. typedef struct _file_info FILEINFO;
  139.  
  140. struct _file_info
  141.     {
  142.     char *name;        /* name of file at top of inclusion stack */
  143.     FILE *file;        /* pointer to open file */
  144.     int line;          /* current line in file at top of inclusion stack */
  145.     FILEINFO *next;    /* stack is a linked list */
  146.     };
  147.  
  148. FILEINFO *filestack = NULL;
  149.  
  150. /**** error-handling routines ****/
  151.  
  152. static jmp_buf error_jmp;
  153.  
  154. static char emsg[500];
  155.  
  156. char *vdf_get_read_error(void)
  157.     {
  158.     return emsg;
  159.     }
  160.  
  161. static void error_abort(char *str)
  162.     {
  163.     strcpy(emsg, str);
  164.     longjmp(error_jmp, 1);
  165.     }
  166.  
  167. static void syntax(char *str)
  168.     {
  169.     if (filestack)
  170.         sprintf(emsg, "Error encountered on line %d of file '%s':\n\t%s\n",
  171.             filestack->line, filestack->name, str);
  172.     else
  173.         sprintf(emsg, "Early EOF detected!\n");
  174.     longjmp(error_jmp, 1);
  175.     }
  176.  
  177. /**** error-handling versions of various routines ****/
  178.  
  179. static void *emalloc(unsigned int nbytes)
  180.     {
  181.     void *ptr = vdf_malloc(nbytes);
  182.     if (ptr) return ptr;
  183.     error_abort("out of memory");
  184.     return NULL;  /* should never get here */
  185.     }
  186.  
  187. static char *estrdup(char *string)
  188.     {
  189.     char *ptr = emalloc(strlen(string)+1);
  190.     strcpy(ptr, string);
  191.     return ptr;
  192.     }
  193.  
  194. /**** list-handling routines ****/
  195.  
  196. /* used to map an identifier into a pointer to something */
  197.  
  198. typedef struct _listnode LISTNODE;
  199.  
  200. struct _listnode
  201.     {
  202.     VDF_IDENTIFIER id;
  203.     void *ptr;
  204.     LISTNODE *next;
  205.     };
  206.  
  207. static void add_to_list(LISTNODE **list, VDF_IDENTIFIER id, void *ptr)
  208.     {
  209.     LISTNODE *p = emalloc(sizeof(LISTNODE));
  210.     p->id = id;  p->ptr = ptr;
  211.     p->next = *list;  *list = p;
  212.     }
  213.  
  214. static void *find_on_list(LISTNODE *list, VDF_IDENTIFIER id)
  215.     {
  216.     while (list)
  217.         if (list->id == id) return list->ptr;
  218.         else list = list->next;
  219.     return NULL;
  220.     }
  221.  
  222. /**** file-handling routines ****/
  223.  
  224. static void open_file(char *filename)
  225.     {
  226.     FILEINFO *inf = emalloc(sizeof(FILEINFO));
  227.     inf->file = fopen(filename, "r");
  228.     if (inf->file == NULL)
  229.         {
  230.         char buff[200];
  231.         sprintf(buff, "could not open '%s'", filename);
  232.         error_abort(buff);
  233.         }
  234.     inf->name = estrdup(filename);
  235.     inf->line = 0;
  236.     inf->next = filestack;  filestack = inf;
  237.     }
  238.  
  239. static int check_filestack_empty = 1;
  240.  
  241. static void close_file(void)
  242.     {
  243.     FILEINFO *f = filestack;
  244.     if (filestack == NULL) return;
  245.     if (filestack->file) fclose(filestack->file);
  246.     if (filestack->name) vdf_free(filestack->name);
  247.     filestack = filestack->next;
  248.     vdf_free(f);
  249.     if (check_filestack_empty)
  250.         if (filestack == NULL)
  251.             syntax(earlyEOF);
  252.     }
  253.  
  254. static void close_all(void)
  255.     {
  256.     check_filestack_empty = 0;
  257.     while (filestack)
  258.         close_file();
  259.     check_filestack_empty = 1;
  260.     }
  261.  
  262. static int egetc(void)
  263.     {
  264.     static int hold_char = 0, instring = 0;
  265.     int c;
  266.     if (hold_char)
  267.         {
  268.         c = hold_char;
  269.         hold_char = 0;
  270.         }
  271.     else
  272.         c = getc(filestack->file);
  273.     switch (c)
  274.         {
  275.         case '\n': ++filestack->line;
  276.         case '\t':
  277.         case ' ': return ' ';
  278.         case EOF: return EOF;
  279.         case '"':
  280.             if (instring)
  281.                 {
  282.                 instring = 0;
  283.                 return END_STRING;
  284.                 }
  285.             instring = 1;
  286.             return START_STRING;
  287.         case '\\':
  288.             switch (c = getc(filestack->file))
  289.                 {
  290.                 case '\n': ++filestack->line;  break;
  291.                 case 'n': return '\n';
  292.                 case 'r': return '\r';
  293.                 case 'b': return '\b';
  294.                 case 't': return '\t';
  295.                 default: break;
  296.                 }
  297.             return c;
  298.         case '/':
  299.             c = getc(filestack->file);
  300.             if (c == '/')
  301.                 {
  302.                 do c = getc(filestack->file); while (c != EOF && c != '\n');
  303.                 if (c == EOF) return EOF;
  304.                 ++filestack->line;
  305.                 return ' ';
  306.                 }
  307.             hold_char = c;
  308.             return '/';
  309.         default:
  310.             if (!isprint(c))
  311.                 syntax("non-printable character");
  312.             break;
  313.         }
  314.     return c;
  315.     }
  316.  
  317. static VDF_TAG lookup_tag(char *name)
  318.     {
  319.     int i;
  320.     for (i = 0; tagnames[i]; ++i)
  321.         if (!stricmp(name, tagnames[i]))
  322.             return i;
  323.     return 0;
  324.     }
  325.  
  326. /* the need_left_bracket() and need_right_bracket() are called when we know
  327.    that a particular kind of bracket is the only acceptable next token */
  328.  
  329. static char nb_buff[1000];
  330.  
  331. static void need_left_bracket(void)
  332.     {
  333.     VDF_TAG get_token(char *buff, int len);
  334.     if (get_token(nb_buff, sizeof(nb_buff)) != VDF_TAG_LEFT_BRACKET)
  335.         syntax("missing {");
  336.     }
  337.  
  338. static void need_right_bracket(void)
  339.     {
  340.     VDF_TAG get_token(char *buff, int len);
  341.     if (get_token(nb_buff, sizeof(nb_buff)) != VDF_TAG_RIGHT_BRACKET)
  342.         syntax("missing }");
  343.     }
  344.  
  345. /* pulls a token from the input stream, storing it in the buffer and
  346.    returning a VDF_TAG identifying which tag (if any) was encountered */
  347.  
  348. static VDF_TAG get_token(char *buff, int len)
  349.     {
  350.     char *p = buff;
  351.     int c;
  352.     VDF_TAG tag;
  353.     --len;  /* allow for null byte at end */
  354.     do
  355.         {
  356.         switch (c = egetc())
  357.             {
  358.             case '{': return VDF_TAG_LEFT_BRACKET;
  359.             case '}': return VDF_TAG_RIGHT_BRACKET;
  360.             case START_STRING: break;
  361.             default: break;
  362.             }
  363.         } while (isspace(c));
  364.     if (c == START_STRING)  /* start of a doubly-quoted string */
  365.         {
  366.         c = egetc();
  367.         while (c != END_STRING && len > 0)
  368.             {
  369.             if (c == EOF) return EOF;
  370.             *p++ = c;
  371.             --len;
  372.             c = egetc();
  373.             }
  374.         *p = '\0';
  375.         return VDF_TAG_STRING;
  376.         }
  377.     while (!isspace(c) && len > 0)  /* copy characters until whitespace */
  378.         {
  379.         if (c == EOF) return EOF;
  380.         *p++ = c;
  381.         --len;
  382.         c = egetc();
  383.         }
  384.     *p = '\0';
  385.     tag = lookup_tag(buff);        /* find the tag */
  386.     if (tag == VDF_TAG_INCLUDE)    /* process included files */
  387.         {
  388.         char filename[100];
  389.         need_left_bracket();
  390.         get_token(filename, sizeof(filename));
  391.         need_right_bracket();
  392.         open_file(filename);
  393.         return VDF_TAG_IGNORE;
  394.         }
  395.     return tag;
  396.     }
  397.  
  398. /* skip_parms() skips a leading {, a bunch of stuff, and a closing } */
  399.  
  400. static char skipbuff[1000];
  401.  
  402. static void skip_parms(void)
  403.     {
  404.     int nbrackets = 1;
  405.     need_left_bracket();
  406.     while (nbrackets > 0)
  407.         switch (get_token(skipbuff, sizeof(skipbuff)))
  408.             {
  409.             case EOF: close_file(); break;
  410.             case VDF_TAG_LEFT_BRACKET: ++nbrackets; break;
  411.             case VDF_TAG_RIGHT_BRACKET: --nbrackets; break;
  412.             default: break;
  413.             }
  414.     }
  415.  
  416. /**** higher-level input functions ****/
  417.  
  418. /* these each pull a known data type from the input stream */
  419.  
  420. static void get_real(VDF_REAL *p)
  421.     {
  422.     char buff[100];
  423.     need_left_bracket();
  424.     get_token(buff, sizeof(buff));
  425.     need_right_bracket();
  426.     sscanf(buff, "%f", p);
  427.     }
  428.  
  429. static void get_angle(VDF_ANGLE *p)
  430.     {
  431.     char buff[100];
  432.     need_left_bracket();
  433.     get_token(buff, sizeof(buff));
  434.     need_right_bracket();
  435.     sscanf(buff, "%f", p);
  436.     }
  437.  
  438. static void get_boolean(VDF_BOOLEAN *p)
  439.     {
  440.     char buff[100];
  441.     need_left_bracket();
  442.     get_token(buff, sizeof(buff));
  443.     need_right_bracket();
  444.     if (!stricmp(buff, "TRUE")) *p = 1;
  445.     else if (!stricmp(buff, "FALSE")) *p = 0;
  446.     else syntax("value must be TRUE or FALSE");
  447.     }
  448.  
  449. static void get_count(VDF_COUNT *p)
  450.     {
  451.     char buff[100];
  452.     need_left_bracket();
  453.     get_token(buff, sizeof(buff));
  454.     need_right_bracket();
  455.     *p = strtoul(buff, NULL, 0);
  456.     }
  457.  
  458. static void get_index(VDF_INDEX *p)
  459.     {
  460.     char buff[100];
  461.     need_left_bracket();
  462.     get_token(buff, sizeof(buff));
  463.     need_right_bracket();
  464.     *p = strtoul(buff, NULL, 0);
  465.     }
  466.  
  467. static void get_handle(VDF_HANDLE *p)
  468.     {
  469.     char buff[100];
  470.     need_left_bracket();
  471.     get_token(buff, sizeof(buff));
  472.     need_right_bracket();
  473.     *p = strtoul(buff, NULL, 0);
  474.     }
  475.  
  476. static void get_identifier(VDF_IDENTIFIER *p)
  477.     {
  478.     char buff[100];
  479.     need_left_bracket();
  480.     get_token(buff, sizeof(buff));
  481.     need_right_bracket();
  482.     *p = strtoul(buff, NULL, 0);
  483.     }
  484.  
  485. static void get_color(VDF_COLOR *p)
  486.     {
  487.     char buff[100];
  488.     need_left_bracket();
  489.     get_token(buff, sizeof(buff));
  490.     sscanf(buff, "%f", &p->red);
  491.     get_token(buff, sizeof(buff));
  492.     sscanf(buff, "%f", &p->green);
  493.     get_token(buff, sizeof(buff));
  494.     sscanf(buff, "%f", &p->blue);
  495.     need_right_bracket();
  496.     }
  497.  
  498. static void get_vector(VDF_VECTOR *p)
  499.     {
  500.     char buff[100];
  501.     need_left_bracket();
  502.     get_token(buff, sizeof(buff));
  503.     sscanf(buff, "%f", &p->x);
  504.     get_token(buff, sizeof(buff));
  505.     sscanf(buff, "%f", &p->y);
  506.     get_token(buff, sizeof(buff));
  507.     sscanf(buff, "%f", &p->z);
  508.     need_right_bracket();
  509.     }
  510.  
  511. void get_string(char **str)
  512.     {
  513.     char buff[100];
  514.     need_left_bracket();
  515.     get_token(buff, sizeof(buff));
  516.     need_right_bracket();
  517.     *str = estrdup(buff);
  518.     }
  519.  
  520. static int get_enum(char *values[])
  521.     {
  522.     char buff[100];
  523.     int i;
  524.     need_left_bracket();
  525.     get_token(buff, sizeof(buff));
  526.     need_right_bracket();
  527.     for (i = 0; values[i]; ++i)
  528.         if (!stricmp(buff, values[i])) return i;
  529.     syntax("unrecognized enumerated value");
  530.     return 0;
  531.     }
  532.  
  533. /**** file processing ****/
  534.  
  535. static char tmpbuff[1000];   /* re-used repeatedly */
  536.  
  537. static LISTNODE
  538.     *object_list = NULL, *material_list = NULL, *material_tables_list = NULL,
  539.     *shape_list = NULL, *facet_list = NULL;
  540.  
  541. static VDF_DATA *world_ptr = NULL;
  542.  
  543. static char junkbuff[1000];  /* used to load tokens we don't care about */
  544.  
  545. /* this next routine gets called whenever we encounter a Color_table tag */
  546.  
  547. static void process_color_table(VDF_PALETTE *palette)
  548.     {
  549.     VDF_COUNT n_entries = 0;
  550.     VDF_TAG tag;
  551.     need_left_bracket();
  552.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  553.         switch (tag)
  554.             {
  555.             case EOF: close_file(); break;
  556.             case VDF_TAG_COUNT:
  557.                 get_count(&palette->ncolors);
  558.                 palette->colors = emalloc(palette->ncolors * sizeof(VDF_COLOR));
  559.                 n_entries = 0;
  560.                 break;
  561.             case VDF_TAG_COLOR_ENTRY:
  562.                 if (palette->ncolors == 0)
  563.                     {
  564.                     palette->ncolors = 1;
  565.                     palette->colors = emalloc(sizeof(VDF_COLOR));
  566.                     n_entries = 0;
  567.                     }
  568.                 if (n_entries < palette->ncolors)
  569.                     get_color(&palette->colors[n_entries++]);
  570.                 else
  571.                     skip_parms();
  572.                 break;
  573.             case VDF_TAG_IGNORE: break;
  574.             default: skip_parms(); break;
  575.             }
  576.     }
  577.  
  578. /* this next routine gets called whenever we encounter a Hue_table tag */
  579.  
  580. static void process_hue_table(VDF_PALETTE *palette)
  581.     {
  582.     VDF_COUNT n_entries = 0;
  583.     VDF_TAG tag;
  584.     need_left_bracket();
  585.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  586.         switch (tag)
  587.             {
  588.             case EOF: close_file(); break;
  589.             case VDF_TAG_COUNT:
  590.                 get_count(&palette->nhues);
  591.                 palette->hues = emalloc(palette->nhues * sizeof(VDF_HUE_DESCRIPTOR));
  592.                 n_entries = 0;
  593.                 break;
  594.             case VDF_TAG_HUE_ENTRY:
  595.                 if (palette->nhues == 0)
  596.                     {
  597.                     palette->nhues = 1;
  598.                     palette->hues = emalloc(sizeof(VDF_HUE_DESCRIPTOR));
  599.                     n_entries = 0;
  600.                     }
  601.                 if (n_entries < palette->nhues)
  602.                     {
  603.                     char tbuff[100];
  604.                     need_left_bracket();
  605.                     get_token(tbuff, sizeof(tbuff));
  606.                     palette->hues[n_entries].darkest = atof(tbuff);
  607.                     get_token(tbuff, sizeof(tbuff));
  608.                     need_right_bracket();
  609.                     palette->hues[n_entries].lightest = atof(tbuff);
  610.                     ++n_entries;
  611.                     }
  612.                 else
  613.                     skip_parms();
  614.                 break;
  615.             case VDF_TAG_IGNORE: break;
  616.             default: skip_parms(); break;
  617.             }
  618.     }
  619.  
  620. /* this next routine gets called whenever we encounter a Palette tag */
  621.  
  622. static void process_palette(void)
  623.     {
  624.     VDF_PALETTE *palette = emalloc(sizeof(VDF_PALETTE));
  625.     VDF_TAG tag;
  626.     palette->ncolors = 0;  palette->colors = NULL;
  627.     palette->nhues = 0;    palette->hues = NULL;
  628.     world_ptr->palette = palette;
  629.     need_left_bracket();
  630.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  631.         switch (tag)
  632.             {
  633.             case EOF: close_file(); break;
  634.             case VDF_TAG_COLOR_TABLE: process_color_table(palette); break;
  635.             case VDF_TAG_HUE_TABLE: process_hue_table(palette); break;
  636.             case VDF_TAG_IGNORE: break;
  637.             default: skip_parms(); break;
  638.             }
  639.     }
  640.  
  641. /* this next routine gets called whenever we encounter a Map tag */
  642.  
  643. static void process_map(void)
  644.     {
  645.     VDF_MAP *map = emalloc(sizeof(VDF_MAP));
  646.     VDF_TAG tag;
  647.     map->name = map->filename = NULL;
  648.     if (end_maps == NULL)
  649.         world_ptr->maps = end_maps = map;
  650.     else
  651.         end_maps->next = map;
  652.     end_maps = map;  map->next = NULL;
  653.     need_left_bracket();
  654.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  655.         switch (tag)
  656.             {
  657.             case EOF: close_file(); break;
  658.             case VDF_TAG_NAME: get_string(&map->name); break;
  659.             case VDF_TAG_FILENAME: get_string(&map->filename); break;
  660.             case VDF_TAG_IGNORE: break;
  661.             default: skip_parms(); break;
  662.             }
  663.     }
  664.  
  665. /* this next routine gets called whenever we encounter a Texture tag */
  666.  
  667. static void get_tmap(char **name)
  668.     {
  669.     VDF_TAG tag;
  670.     need_left_bracket();
  671.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  672.         switch (tag)
  673.             {
  674.             case EOF: close_file(); break;
  675.             case VDF_TAG_NAME: get_string(name); break;
  676.             default: skip_parms(); break;
  677.             }
  678.     }
  679.  
  680. /* this next routine gets called whenever we encounter a Material tag */
  681.  
  682. static void process_material(void)
  683.     {
  684.     VDF_MATERIAL *material = emalloc(sizeof(VDF_MATERIAL));
  685.     VDF_TAG tag;
  686.     material->name = NULL;  material->rendering_mode = VDF_RENDER_FLAT;
  687.     material->hue = -1;  material->specular_exponent = 0;
  688.     material->id = 0;
  689.     material->albedo = 0.5;  /* reflects half the light that falls on it */
  690.     color_zero(&material->diffuse_color);
  691.     color_zero(&material->ambient_color);
  692.     color_zero(&material->transparency_color);
  693.     color_zero(&material->specular_color);
  694.     material->refractive_index = 0.00;
  695.     material->reflection_blur = 0.00;
  696.     material->transparency_falloff = 0.00;
  697.     material->texture_map = material->bump_map = NULL;
  698.     material->opacity_map = material->reflection_map = NULL;
  699.     if (end_materials == NULL)
  700.         world_ptr->materials = end_materials = material;
  701.     else
  702.         end_materials->next = material;
  703.     end_materials = material;  material->next = NULL;
  704.     need_left_bracket();
  705.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  706.         switch (tag)
  707.             {
  708.             case EOF: close_file(); break;
  709.             case VDF_TAG_NAME: get_string(&material->name); break;
  710.             case VDF_TAG_RENDERING_MODE: material->rendering_mode = get_enum(render_mode_names); break;
  711.             case VDF_TAG_HUE: get_index(&material->hue); break;
  712.             case VDF_TAG_DIFFUSE_COLOR: get_color(&material->diffuse_color); break;
  713.             case VDF_TAG_AMBIENT_COLOR: get_color(&material->ambient_color); break;
  714.             case VDF_TAG_TRANSPARENCY_COLOR: get_color(&material->transparency_color); break;
  715.             case VDF_TAG_SPECULAR_COLOR: get_color(&material->specular_color); break;
  716.             case VDF_TAG_SPECULAR_EXPONENT: get_real(&material->specular_exponent); break;
  717.             case VDF_TAG_REFRACTIVE_INDEX: get_real(&material->refractive_index); break;
  718.             case VDF_TAG_REFLECTION_BLUR: get_real(&material->reflection_blur); break;
  719.             case VDF_TAG_TRANSPARENCY_FALLOFF: get_real(&material->transparency_falloff); break;
  720.             case VDF_TAG_TEXTURE_MAP: get_tmap(&material->texture_map); break;
  721.             case VDF_TAG_BUMP_MAP: get_tmap(&material->bump_map); break;
  722.             case VDF_TAG_OPACITY_MAP: get_tmap(&material->opacity_map); break;
  723.             case VDF_TAG_REFLECTION_MAP: get_tmap(&material->reflection_map); break;
  724.             case VDF_TAG_ALBEDO: get_real(&material->albedo); break;
  725.             case VDF_TAG_IDENTIFIER:
  726.                 get_identifier(&material->id);
  727.                 add_to_list(&material_list, material->id, material);
  728.                 break;
  729.             case VDF_TAG_IGNORE: break;
  730.             default: skip_parms(); break;
  731.             }
  732.     }
  733.  
  734. /* this next routine gets called whenever we encounter a Material tag */
  735.  
  736. static void process_material_table(void)
  737.     {
  738.     VDF_MATERIAL_TABLE *table = emalloc(sizeof(VDF_MATERIAL_TABLE));
  739.     VDF_TAG tag;
  740.     VDF_COUNT n_entries = 0;
  741.     table->name = NULL;  table->application_handle = 0;
  742.     table->count = 0;  table->material_references = NULL;
  743.     table->n_used = 0;  table->id = 0;
  744.     if (end_material_tables == NULL)
  745.         world_ptr->material_tables = end_material_tables = table;
  746.     else
  747.         end_material_tables->next = table;
  748.     end_material_tables = table;  table->next = NULL;
  749.     need_left_bracket();
  750.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  751.         switch (tag)
  752.             {
  753.             case EOF: close_file(); break;
  754.             case VDF_TAG_NAME: get_string(&table->name); break;
  755.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&table->application_handle); break;
  756.             case VDF_TAG_COUNT:
  757.                 get_count(&table->count);
  758.                 if (table->count > 0)
  759.                     {
  760.                     table->material_references = calloc(table->count, sizeof(VDF_MATERIAL *));
  761.                     n_entries = 0;
  762.                     }
  763.                 break;
  764.             case VDF_TAG_MATERIAL_REFERENCE:
  765.                 if (table->count == 0)
  766.                     {
  767.                     table->count = 1;
  768.                     table->material_references = emalloc(sizeof(VDF_MATERIAL *));
  769.                     n_entries = 0;
  770.                     }
  771.                 if (n_entries < table->count)
  772.                     {
  773.                     VDF_IDENTIFIER id;
  774.                     get_identifier(&id);
  775.                     table->material_references[n_entries++] = find_on_list(material_list, id);
  776.                     }
  777.                 else
  778.                     skip_parms();
  779.                 break;
  780.             case VDF_TAG_IDENTIFIER:
  781.                 get_identifier(&table->id);
  782.                 add_to_list(&material_tables_list, table->id, table);
  783.                 break;
  784.             case VDF_TAG_IGNORE: break;
  785.             default: skip_parms(); break;
  786.             }
  787.     }
  788.  
  789. /* this next routine gets called whenever we encounter a Vertex tag */
  790.  
  791. static void process_vertex(VDF_VERTEX *vertex)
  792.     {
  793.     VDF_TAG tag;
  794.     vector_zero(&vertex->vector);  vector_zero(&vertex->normal);
  795.     color_zero(&vertex->color);
  796.     vertex->application_handle = 0;
  797.     need_left_bracket();
  798.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  799.         switch (tag)
  800.             {
  801.             case EOF: close_file(); break;
  802.             case VDF_TAG_POINT3D: get_vector(&vertex->vector); break;
  803.             case VDF_TAG_NORMAL3D: get_vector(&vertex->normal); break;
  804.             case VDF_TAG_COLOR: get_color(&vertex->color); break;
  805.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&vertex->application_handle); break;
  806.             case VDF_TAG_IGNORE: break;
  807.             default: skip_parms(); break;
  808.             }
  809.     }
  810.  
  811. /* this next routine gets called whenever we encounter a Vertex_info tag */
  812.  
  813. static void process_vertex_info(VDF_FACET *facet, VDF_INDEX ind)
  814.     {
  815.     VDF_TAG tag;
  816.     need_left_bracket();
  817.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  818.         switch (tag)
  819.             {
  820.             case EOF: close_file(); break;
  821.             case VDF_TAG_INDEX: get_index(&facet->points[ind].vertnum); break;
  822.             case VDF_TAG_IGNORE: break;
  823.             default: skip_parms(); break;
  824.             }
  825.     }
  826.  
  827. /* this next routine gets called whenever we encounter a Vertex_data tag */
  828.  
  829. static void process_facet_points(VDF_FACET *facet)
  830.     {
  831.     VDF_TAG tag;
  832.     VDF_COUNT n_entries = 0;
  833.     need_left_bracket();
  834.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  835.         switch (tag)
  836.             {
  837.             case EOF: close_file(); break;
  838.             case VDF_TAG_COUNT:
  839.                 get_count(&facet->npoints);
  840.                 facet->points = emalloc(facet->npoints * sizeof(VDF_INDEX));
  841.                 n_entries = 0;
  842.                 break;
  843.             case VDF_TAG_VERTEX_INFO:
  844.                 if (facet->npoints == 0)
  845.                     {
  846.                     facet->npoints = 1;
  847.                     facet->points = emalloc(sizeof(VDF_INDEX));
  848.                     n_entries = 0;
  849.                     }
  850.                 if (n_entries < facet->npoints)
  851.                     process_vertex_info(facet, n_entries++);
  852.                 else
  853.                     skip_parms();
  854.                 break;
  855.             case VDF_TAG_IGNORE: break;
  856.             default: skip_parms(); break;
  857.             }
  858.     }
  859.  
  860. /* this next routine gets called whenever we encounter a Facet tag */
  861.  
  862. static void process_facet(VDF_FACET *facet)
  863.     {
  864.     VDF_TAG tag;
  865.     facet->is_doublesided = facet->is_interior = 0;
  866.     facet->base_facet = NULL;  facet->n_used = 0;
  867.     facet->front_material = facet->back_material = -1;
  868.     vector_zero(&facet->normal);  facet->application_handle = 0;
  869.     facet->npoints = 0;  facet->points = NULL;
  870.     facet->id = 0;
  871.     need_left_bracket();
  872.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  873.         switch (tag)
  874.             {
  875.             case EOF: close_file(); break;
  876.             case VDF_TAG_IS_DOUBLESIDED: get_boolean(&facet->is_doublesided); break;
  877.             case VDF_TAG_IS_INTERIOR: get_boolean(&facet->is_interior); break;
  878.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&facet->application_handle); break;
  879.             case VDF_TAG_BASE_FACET:
  880.                 {
  881.                 VDF_IDENTIFIER id;
  882.                 get_identifier(&id);
  883.                 facet->base_facet = find_on_list(facet_list, id);
  884.                 if (facet->base_facet)
  885.                     ++facet->base_facet->n_used;
  886.                 }
  887.                 break;
  888.             case VDF_TAG_FRONT_MATERIAL: get_index(&facet->front_material); break;
  889.             case VDF_TAG_BACK_MATERIAL: get_index(&facet->back_material); break;
  890.             case VDF_TAG_NORMAL3D: get_vector(&facet->normal); break;
  891.             case VDF_TAG_VERTEX_DATA: process_facet_points(facet); break;
  892.             case VDF_TAG_IDENTIFIER:
  893.                 get_identifier(&facet->id);
  894.                 add_to_list(&facet_list, facet->id, facet);
  895.                 break;
  896.             case VDF_TAG_IGNORE: break;
  897.             default: skip_parms(); break;
  898.             }
  899.     if (facet->front_material == -1)
  900.         facet->front_material = 0;
  901.     if (facet->back_material == -1)
  902.         facet->back_material = facet->front_material;
  903.     }
  904.  
  905. /* this next routine gets called whenever we encounter a Vertex_list tag */
  906.  
  907. static void process_vertices(VDF_SHAPE *shape)
  908.     {
  909.     VDF_COUNT n_entries = 0;
  910.     VDF_TAG tag;
  911.     need_left_bracket();
  912.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  913.         switch (tag)
  914.             {
  915.             case EOF: close_file(); break;
  916.             case VDF_TAG_COUNT:
  917.                 get_count(&shape->nvertices);
  918.                 shape->vertices = emalloc(shape->nvertices * sizeof(VDF_VERTEX));
  919.                 break;
  920.             case VDF_TAG_VERTEX:
  921.                 if (shape->nvertices == 0)
  922.                     {
  923.                     shape->nvertices = 1;
  924.                     shape->vertices = emalloc(sizeof(VDF_VERTEX));
  925.                     n_entries = 0;
  926.                     }
  927.                 if (n_entries < shape->nvertices)
  928.                     process_vertex(&shape->vertices[n_entries++]);
  929.                 else
  930.                     skip_parms();
  931.                 break;
  932.             case VDF_TAG_IGNORE: break;
  933.             default: skip_parms(); break;
  934.             }
  935.     shape->nvertices = n_entries;
  936.     }
  937.  
  938. /* this next routine gets called whenever we encounter a Facets_list tag */
  939.  
  940. static void process_facets(VDF_SHAPE *shape)
  941.     {
  942.     VDF_COUNT n_entries = 0;
  943.     VDF_TAG tag;
  944.     need_left_bracket();
  945.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  946.         switch (tag)
  947.             {
  948.             case EOF: close_file(); break;
  949.             case VDF_TAG_COUNT:
  950.                 get_count(&shape->nfacets);
  951.                 shape->facets = emalloc(shape->nfacets * sizeof(VDF_FACET));
  952.                 break;
  953.             case VDF_TAG_FACET:
  954.                 if (shape->nfacets == 0)
  955.                     {
  956.                     shape->nfacets = 1;
  957.                     shape->facets = emalloc(sizeof(VDF_FACET));
  958.                     n_entries = 0;
  959.                     }
  960.                 if (n_entries < shape->nfacets)
  961.                     process_facet(&shape->facets[n_entries++]);
  962.                 else
  963.                     skip_parms();
  964.                 break;
  965.             case VDF_TAG_IGNORE: break;
  966.             default: skip_parms(); break;
  967.             }
  968.     shape->nfacets = n_entries;
  969.     }
  970.  
  971. /* this next routine gets called whenever we encounter a Shape tag */
  972.  
  973. static void process_shape(void)
  974.     {
  975.     VDF_SHAPE *shape = emalloc(sizeof(VDF_SHAPE));
  976.     VDF_TAG tag;
  977.     shape->n_used = 0;  shape->id = 0;
  978.     shape->name = NULL;  shape->application_handle = 0;
  979.     shape->lod_size = 0;  shape->replaces = NULL;
  980.     shape->is_convex = 0;  shape->material_table = NULL;
  981.     vector_zero(&shape->bound_min);  vector_zero(&shape->bound_max);
  982.     shape->nvertices = 0;  shape->vertices = NULL;
  983.     shape->nfacets = 0;  shape->facets = NULL;
  984.     if (end_shapes == NULL)
  985.         world_ptr->shapes = end_shapes = shape;
  986.     else
  987.         end_shapes->next = shape;
  988.     end_shapes = shape;  shape->next = NULL;
  989.     need_left_bracket();
  990.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  991.         switch (tag)
  992.             {
  993.             case EOF: close_file(); break;
  994.             case VDF_TAG_LOD_SIZE: get_real(&shape->lod_size); break;
  995.             case VDF_TAG_LOD_REPLACES:
  996.                 {
  997.                 VDF_IDENTIFIER id;
  998.                 get_identifier(&id);
  999.                 shape->replaces = find_on_list(shape_list, id);
  1000.                 /* note: do not increment n_used */
  1001.                 }
  1002.                 break;
  1003.             case VDF_TAG_NAME: get_string(&shape->name); break;
  1004.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&shape->application_handle); break;
  1005.             case VDF_TAG_USES_MATERIAL_TABLE:
  1006.                 {
  1007.                 VDF_IDENTIFIER id;
  1008.                 get_identifier(&id);
  1009.                 shape->material_table = find_on_list(material_tables_list, id);
  1010.                 if (shape->material_table)
  1011.                     ++shape->material_table->n_used;
  1012.                 }
  1013.                 break;
  1014.             case VDF_TAG_BOUNDING_BOX:
  1015.                 need_left_bracket();
  1016.                 get_token(junkbuff, sizeof(junkbuff));
  1017.                 shape->bound_min.x = atof(junkbuff);
  1018.                 get_token(junkbuff, sizeof(junkbuff));
  1019.                 shape->bound_min.y = atof(junkbuff);
  1020.                 get_token(junkbuff, sizeof(junkbuff));
  1021.                 shape->bound_min.z = atof(junkbuff);
  1022.                 get_token(junkbuff, sizeof(junkbuff));
  1023.                 shape->bound_max.x = atof(junkbuff);
  1024.                 get_token(junkbuff, sizeof(junkbuff));
  1025.                 shape->bound_max.y = atof(junkbuff);
  1026.                 get_token(junkbuff, sizeof(junkbuff));
  1027.                 shape->bound_max.z = atof(junkbuff);
  1028.                 need_right_bracket();
  1029.                 break;
  1030.             case VDF_TAG_IS_CONVEX: get_boolean(&shape->is_convex); break;
  1031.             case VDF_TAG_VERTEX_LIST: process_vertices(shape); break;
  1032.             case VDF_TAG_FACET_LIST: process_facets(shape); break;
  1033.             case VDF_TAG_IDENTIFIER:
  1034.                 get_identifier(&shape->id);
  1035.                 add_to_list(&shape_list, shape->id, shape);
  1036.                 break;
  1037.             case VDF_TAG_IGNORE: break;
  1038.             default: skip_parms(); break;
  1039.             }
  1040.     }
  1041.  
  1042. /* this next routine gets called whenever we encounter an Object tag */
  1043.  
  1044. static void process_object(void)
  1045.     {
  1046.     VDF_OBJECT *object = emalloc(sizeof(VDF_OBJECT));
  1047.     VDF_TAG tag;
  1048.     object->name = NULL;  object->application_handle = 0;
  1049.     object->shape = NULL;  object->id = 0;
  1050.     object->scaled_by.x = object->scaled_by.y = object->scaled_by.z = 1.00;
  1051.     object->material_table = NULL;  vector_zero(&object->location);
  1052.     object->rotx = object->roty = object->rotz = 0;
  1053.     object->parent = NULL;
  1054.     object->container = NULL;  object->is_invisible = 0;
  1055.     object->layer = 0;  object->text = NULL;
  1056.     object->facet_behind = NULL;
  1057.     if (end_objects == NULL)
  1058.         world_ptr->objects = end_objects = object;
  1059.     else
  1060.         end_objects->next = object;
  1061.     end_objects = object;  object->next = NULL;
  1062.     need_left_bracket();
  1063.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1064.         switch (tag)
  1065.             {
  1066.             case EOF: close_file(); break;
  1067.             case VDF_TAG_NAME: get_string(&object->name); break;
  1068.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&object->application_handle); break;
  1069.             case VDF_TAG_INSTANCE_OF_SHAPE:
  1070.                 {
  1071.                 VDF_IDENTIFIER id;
  1072.                 get_identifier(&id);
  1073.                 object->shape = find_on_list(shape_list, id);
  1074.                 if (object->shape)
  1075.                     ++object->shape->n_used;
  1076.                 }
  1077.                 break;
  1078.             case VDF_TAG_SCALED_BY: get_vector(&object->scaled_by); break;
  1079.             case VDF_TAG_USES_MATERIAL_TABLE:
  1080.                 {
  1081.                 VDF_IDENTIFIER id;
  1082.                 get_identifier(&id);
  1083.                 object->material_table = find_on_list(material_tables_list, id);
  1084.                 if (object->material_table)
  1085.                     ++object->material_table->n_used;
  1086.                 }
  1087.                 break;
  1088.             case VDF_TAG_LOCATION: get_vector(&object->location); break;
  1089.             case VDF_TAG_ROTATION:
  1090.                 need_left_bracket();
  1091.                 get_token(junkbuff, sizeof(junkbuff));
  1092.                 object->rotx = atof(junkbuff);
  1093.                 get_token(junkbuff, sizeof(junkbuff));
  1094.                 object->roty = atof(junkbuff);
  1095.                 get_token(junkbuff, sizeof(junkbuff));
  1096.                 object->rotz = atof(junkbuff);
  1097.                 need_right_bracket();
  1098.                 break;
  1099.             case VDF_TAG_ATTACHED_TO:
  1100.                 {
  1101.                 VDF_IDENTIFIER id;
  1102.                 get_identifier(&id);
  1103.                 object->parent = find_on_list(object_list, id);
  1104.                 if (object->parent)
  1105.                     ++object->parent->n_used;
  1106.                 }
  1107.                 break;
  1108.             case VDF_TAG_CONTAINED_WITHIN:
  1109.                 {
  1110.                 VDF_IDENTIFIER id;
  1111.                 get_identifier(&id);
  1112.                 object->container = find_on_list(object_list, id);
  1113.                 if (object->container)
  1114.                     ++object->container->n_used;
  1115.                 }
  1116.                 break;
  1117.             case VDF_TAG_IS_INVISIBLE: get_boolean(&object->is_invisible); break;
  1118.             case VDF_TAG_LAYER: get_index(&object->layer); break;
  1119.             case VDF_TAG_TEXT: get_string(&object->text); break;
  1120.             case VDF_TAG_FACET_BEHIND:
  1121.                 {
  1122.                 VDF_IDENTIFIER id;
  1123.                 get_identifier(&id);
  1124.                 object->facet_behind = find_on_list(facet_list, id);
  1125.                 if (object->facet_behind)
  1126.                     ++object->facet_behind->n_used;
  1127.                 }
  1128.                 break;
  1129.             case VDF_TAG_IDENTIFIER:
  1130.                 get_identifier(&object->id);
  1131.                 add_to_list(&object_list, object->id, object);
  1132.                 break;
  1133.             case VDF_TAG_IGNORE: break;
  1134.             default: skip_parms(); break;
  1135.             }
  1136.     }
  1137.  
  1138. /* this next routine gets called whenever we encounter a Light tag */
  1139.  
  1140. static void process_light(void)
  1141.     {
  1142.     VDF_LIGHT *light = emalloc(sizeof(VDF_LIGHT));
  1143.     VDF_TAG tag;
  1144.     light->name = NULL;  light->application_handle = 0;
  1145.     light->associated_with = NULL;  light->is_on = 1;
  1146.     light->type = VDF_LIGHT_DIRECTIONAL;
  1147.     color_zero(&light->color);
  1148.     light->hotspot = 0;  light->falloff = 0;  light->casts_shadows = 0;
  1149.     if (end_lights == NULL)
  1150.         world_ptr->lights = end_lights = light;
  1151.     else
  1152.         end_lights->next = light;
  1153.     end_lights = light;  light->next = NULL;
  1154.     need_left_bracket();
  1155.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1156.         switch (tag)
  1157.             {
  1158.             case EOF: close_file(); break;
  1159.             case VDF_TAG_NAME: get_string(&light->name); break;
  1160.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&light->application_handle); break;
  1161.             case VDF_TAG_ASSOCIATED_WITH:
  1162.                 {
  1163.                 VDF_IDENTIFIER id;
  1164.                 get_identifier(&id);
  1165.                 light->associated_with = find_on_list(object_list, id);
  1166.                 if (light->associated_with)
  1167.                     ++light->associated_with->n_used;
  1168.                 }
  1169.                 break;
  1170.             case VDF_TAG_LIGHT_TYPE: light->type = get_enum(light_type_names); break;
  1171.             case VDF_TAG_COLOR: get_color(&light->color); break;
  1172.             case VDF_TAG_HOTSPOT: get_angle(&light->hotspot); break;
  1173.             case VDF_TAG_FALLOFF: get_angle(&light->falloff); break;
  1174.             case VDF_TAG_IS_ON: get_boolean(&light->is_on); break;
  1175.             case VDF_TAG_CASTS_SHADOWS: get_boolean(&light->casts_shadows); break;
  1176.             case VDF_TAG_IGNORE: break;
  1177.             default: skip_parms(); break;
  1178.             }
  1179.     }
  1180.  
  1181. /* this next routine gets called whenever we encounter a Camera tag */
  1182.  
  1183. static void process_camera(void)
  1184.     {
  1185.     VDF_CAMERA *camera = emalloc(sizeof(VDF_CAMERA));
  1186.     VDF_TAG tag;
  1187.     camera->name = NULL;  camera->application_handle = 0;
  1188.     camera->associated_with = NULL;  camera->field_of_view = 45.00;
  1189.     camera->aspect_ratio = 1.33;
  1190.     camera->projection_type = VDF_PROJ_PERSPECTIVE;
  1191.     if (end_cameras == NULL)
  1192.         world_ptr->cameras = end_cameras = camera;
  1193.     else
  1194.         end_cameras->next = camera;
  1195.     end_cameras = camera;  camera->next = NULL;
  1196.     need_left_bracket();
  1197.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1198.         switch (tag)
  1199.             {
  1200.             case EOF: close_file(); break;
  1201.             case VDF_TAG_NAME: get_string(&camera->name); break;
  1202.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&camera->application_handle); break;
  1203.             case VDF_TAG_ASSOCIATED_WITH:
  1204.                 {
  1205.                 VDF_IDENTIFIER id;
  1206.                 get_identifier(&id);
  1207.                 camera->associated_with = find_on_list(object_list, id);
  1208.                 if (camera->associated_with)
  1209.                     ++camera->associated_with->n_used;
  1210.                 }
  1211.                 break;
  1212.             case VDF_TAG_FIELD_OF_VIEW: get_real(&camera->field_of_view); break;
  1213.             case VDF_TAG_ASPECT_RATIO: get_real(&camera->aspect_ratio); break;
  1214.             case VDF_TAG_PROJECTION_TYPE: camera->projection_type = get_enum(projection_type_names); break;
  1215.             case VDF_TAG_IGNORE: break;
  1216.             default: skip_parms(); break;
  1217.             }
  1218.     }
  1219.  
  1220. /* this next routine gets called whenever we encounter a Sound tag */
  1221.  
  1222. static void process_sound(void)
  1223.     {
  1224.     VDF_SOUND *sound = emalloc(sizeof(VDF_SOUND));
  1225.     VDF_TAG tag;
  1226.     sound->name = NULL;  sound->application_handle = 0;
  1227.     sound->associated_with = NULL;  sound->is_on = 1;
  1228.     sound->volume = 1.00;  sound->sample_name = NULL;
  1229.     if (end_sounds == NULL)
  1230.         world_ptr->sounds = end_sounds = sound;
  1231.     else
  1232.         end_sounds->next = sound;
  1233.     end_sounds = sound;  sound->next = NULL;
  1234.     need_left_bracket();
  1235.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1236.         switch (tag)
  1237.             {
  1238.             case EOF: close_file(); break;
  1239.             case VDF_TAG_NAME: get_string(&sound->name); break;
  1240.             case VDF_TAG_APPLICATION_HANDLE: get_handle(&sound->application_handle); break;
  1241.             case VDF_TAG_ASSOCIATED_WITH:
  1242.                 {
  1243.                 VDF_IDENTIFIER id;
  1244.                 get_identifier(&id);
  1245.                 sound->associated_with = find_on_list(object_list, id);
  1246.                 if (sound->associated_with)
  1247.                     ++sound->associated_with->n_used;
  1248.                 }
  1249.                 break;
  1250.             case VDF_TAG_IS_ON: get_boolean(&sound->is_on); break;
  1251.             case VDF_TAG_VOLUME: get_real(&sound->volume); break;
  1252.             case VDF_TAG_SAMPLE_NAME: get_string(&sound->sample_name); break;
  1253.             case VDF_TAG_IGNORE: break;
  1254.             default: skip_parms(); break;
  1255.             }
  1256.     }
  1257.  
  1258. /* this next routine gets called whenever we encounter a World_attributes tag */
  1259.  
  1260. static void process_world_attributes(void)
  1261.     {
  1262.     VDF_WORLD_ATTRIBUTES *ptr = world_ptr->world_attributes;
  1263.     VDF_TAG tag;
  1264.     if (world_ptr->world_attributes == NULL)
  1265.         {
  1266.         ptr = world_ptr->world_attributes = emalloc(sizeof(VDF_WORLD_ATTRIBUTES));
  1267.         vector_zero(&ptr->gravity_vector);
  1268.         color_zero(&ptr->ambient_light);  ptr->has_horizon = 1;
  1269.         color_zero(&ptr->sky_color);  color_zero(&ptr->ground_color);
  1270.         color_zero(&ptr->fog_color);  ptr->scale = 1.00;
  1271.         }
  1272.     need_left_bracket();
  1273.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1274.         switch (tag)
  1275.             {
  1276.             case EOF: close_file(); break;
  1277.             case VDF_TAG_GRAVITY_VECTOR: get_vector(&ptr->gravity_vector); break;
  1278.             case VDF_TAG_AMBIENT_LIGHT: get_color(&ptr->ambient_light); break;
  1279.             case VDF_TAG_HAS_HORIZON: get_boolean(&ptr->has_horizon); break;
  1280.             case VDF_TAG_SKY_COLOR: get_color(&ptr->sky_color); break;
  1281.             case VDF_TAG_GROUND_COLOR: get_color(&ptr->ground_color); break;
  1282.             case VDF_TAG_FOG_COLOR: get_color(&ptr->fog_color); break;
  1283.             case VDF_TAG_SCALE: get_real(&ptr->scale); break;
  1284.             case VDF_TAG_IGNORE: break;
  1285.             default: skip_parms(); break;
  1286.             }
  1287.     }
  1288.  
  1289. /* this next routine gets called whenever we encounter either a
  1290.    Created_by or Modified_by tag */
  1291.  
  1292. static void process_human_data(VDF_HUMAN_DATA **data)
  1293.     {
  1294.     VDF_TAG tag;
  1295.     VDF_HUMAN_DATA *h = emalloc(sizeof(VDF_HUMAN_DATA));
  1296.     h->person = NULL;  h->date = NULL;  h->time = NULL;  h->next = NULL;
  1297.     h->comment = NULL;
  1298.     if (*data == NULL)
  1299.         *data = h;
  1300.     else
  1301.         {
  1302.         VDF_HUMAN_DATA *p;
  1303.         for (p = *data; p; p = p->next)
  1304.             if (p->next == NULL)
  1305.                 {
  1306.                 p->next = h;
  1307.                 break;
  1308.                 }
  1309.         }
  1310.     need_left_bracket();
  1311.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1312.         switch (tag)
  1313.             {
  1314.             case EOF: close_file(); break;
  1315.             case VDF_TAG_PERSON: get_string(&h->person); break;
  1316.             case VDF_TAG_DATE:
  1317.                 {
  1318.                 need_left_bracket();
  1319.                 get_token(tmpbuff, sizeof(tmpbuff));
  1320.                 h->date = estrdup(tmpbuff);
  1321.                 get_token(tmpbuff, sizeof(tmpbuff));
  1322.                 h->time = estrdup(tmpbuff);
  1323.                 need_right_bracket();
  1324.                 }
  1325.                 break;
  1326.             case VDF_TAG_COMMENT: get_string(&h->comment); break;
  1327.             case VDF_TAG_IGNORE: break;
  1328.             default: skip_parms(); break;
  1329.             }
  1330.     }
  1331.  
  1332. /* this next routine gets called whenever we encounter a World_information tag */
  1333.  
  1334. static void process_world_info(void)
  1335.     {
  1336.     VDF_TAG tag;
  1337.     need_left_bracket();
  1338.     while ((tag = get_token(junkbuff, sizeof(junkbuff))) != VDF_TAG_RIGHT_BRACKET)
  1339.         switch (tag)
  1340.             {
  1341.             case EOF: close_file(); break;
  1342.             case VDF_TAG_COPYRIGHT_MESSAGE: get_string(&world_ptr->copyright_message); break;
  1343.             case VDF_TAG_USAGE_RESTRICTIONS: get_string(&world_ptr->usage_restrictions); break;
  1344.             case VDF_TAG_CREATED_BY: process_human_data(&world_ptr->created); break;
  1345.             case VDF_TAG_MODIFIED_BY: process_human_data(&world_ptr->modified); break;
  1346.             case VDF_TAG_TITLE: get_string(&world_ptr->title); break;
  1347.             case VDF_TAG_IGNORE: break;
  1348.             default: skip_parms(); break;
  1349.             }
  1350.     }
  1351.  
  1352. /* this routine handles all the top-level tags */
  1353.  
  1354. static void process_file(void)
  1355.     {
  1356.     VDF_TAG tag;
  1357.     while ((tag = get_token(tmpbuff, sizeof(tmpbuff))) != EOF)
  1358.         switch (tag)
  1359.             {
  1360.             case VDF_TAG_PALETTE: process_palette(); break;
  1361.             case VDF_TAG_MAP: process_map(); break;
  1362.             case VDF_TAG_MATERIAL: process_material(); break;
  1363.             case VDF_TAG_MATERIAL_TABLE: process_material_table(); break;
  1364.             case VDF_TAG_SHAPE: process_shape(); break;
  1365.             case VDF_TAG_OBJECT: process_object(); break;
  1366.             case VDF_TAG_CAMERA: process_camera(); break;
  1367.             case VDF_TAG_LIGHT: process_light(); break;
  1368.             case VDF_TAG_SOUND: process_sound(); break;
  1369.             case VDF_TAG_WORLD_INFO: process_world_info(); break;
  1370.             case VDF_TAG_WORLD_ATTRIBUTES: process_world_attributes(); break;
  1371.             case VDF_TAG_IGNORE: break;
  1372.             default:
  1373.                 printf("Unknown tag '%s'\n", tmpbuff);
  1374.                 skip_parms();
  1375.                 break;
  1376.             }
  1377.     }
  1378.  
  1379. /**** main file-reading routine ****/
  1380.  
  1381. VDF_DATA *vdf_readfile(char *filename)
  1382.     {
  1383.     strcpy(emsg, "No read error");
  1384.     if (setjmp(error_jmp))
  1385.         return NULL;  /* return NULL on error */
  1386.     world_ptr = emalloc(sizeof(VDF_DATA));
  1387.     world_ptr->palette = NULL;  world_ptr->maps = NULL;
  1388.     world_ptr->materials = NULL;
  1389.     world_ptr->material_tables = NULL;  world_ptr->shapes = NULL;
  1390.     world_ptr->objects = NULL;  world_ptr->cameras = NULL;
  1391.     world_ptr->lights = NULL;  world_ptr->sounds = NULL;
  1392.     world_ptr->world_attributes = NULL;
  1393.     world_ptr->created = NULL;  world_ptr->modified = NULL;
  1394.     world_ptr->copyright_message = NULL;  world_ptr->usage_restrictions = NULL;
  1395.     world_ptr->title = NULL;
  1396.     end_palettes = NULL;  end_maps = NULL;
  1397.     end_materials = NULL;
  1398.     end_material_tables = NULL;  end_shapes = NULL;
  1399.     end_objects = NULL;  end_cameras = NULL;
  1400.     end_lights = NULL;  end_sounds = NULL;
  1401.     end_world_attributes = NULL;
  1402.     open_file(filename);
  1403.     process_file();
  1404.     close_all();
  1405.     return world_ptr;
  1406.     }
  1407.  
  1408. VDF_MATERIAL *vdf_map_material(VDF_OBJECT *object, VDF_INDEX material_index)
  1409.     {
  1410.     if (object->material_table)  /* if the object has a material table */
  1411.         return object->material_table->material_references[material_index];  /* use it */
  1412.     if (object->shape)  /* if the object has a shape */
  1413.         if (object->shape->material_table)  /* and the shape has a material table */
  1414.             return object->shape->material_table->material_references[material_index];  /* use it */
  1415.     return NULL;  /* couldn't map the material */
  1416.     }
  1417.         
  1418.