home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / RTrace 1.0 / source / scene.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-04  |  17.0 KB  |  628 lines  |  [TEXT/KAHL]

  1. /*
  2.  * Copyright (c) 1988, 1992 Antonio Costa, INESC-Norte.
  3.  * All rights reserved.
  4.  *
  5.  * This code received contributions from the following people:
  6.  *
  7.  *  Roman Kuchkuda      - basic ray tracer
  8.  *  Mark VandeWettering - MTV ray tracer
  9.  *  Augusto Sousa       - overall, shading model
  10.  *
  11.  * Redistribution and use in source and binary forms are permitted
  12.  * provided that the above copyright notice and this paragraph are
  13.  * duplicated in all such forms and that any documentation,
  14.  * advertising materials, and other materials related to such
  15.  * distribution and use acknowledge that the software was developed
  16.  * by Antonio Costa, at INESC-Norte. The name of the author and
  17.  * INESC-Norte may not be used to endorse or promote products derived
  18.  * from this software without specific prior written permission.
  19.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  20.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  21.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  22.  */
  23. #include "defs.h"
  24. #include "extern.h"
  25.  
  26. /**********************************************************************
  27.  *    RAY TRACING - Scene - Version 7.3.1                             *
  28.  *                                                                    *
  29.  *    MADE BY    : Antonio Costa, INESC-Norte, October 1988           *
  30.  *    ADAPTED BY : Antonio Costa, INESC-Norte, June 1989              *
  31.  *    MODIFIED BY: Antonio Costa, INESC-Norte, July 1992              *
  32.  **********************************************************************/
  33.  
  34. /***** Read scene *****/
  35. void
  36. get_valid(file, value, low, high, s)
  37.   file_ptr        file;
  38.   real           *value, low, high;
  39.   char_ptr        s;
  40. {
  41.   double          data;
  42.  
  43.   READ_REAL(file, &data);
  44.   if (IO_status != IO_OK)
  45.   {
  46.     WRITE(results, "Error: bad %s\n", s);
  47.     HALT;
  48.   }
  49.   *value = (real) data;
  50.   if (*value < low)
  51.   {
  52.     WRITE(results, "Error: too small %s\n", s);
  53.     HALT;
  54.   }
  55.   if (*value > high)
  56.   {
  57.     WRITE(results, "Error: too big %s\n", s);
  58.     HALT;
  59.   }
  60.  
  61.     PROCESS_MAC_EVENT
  62.  
  63. }
  64. static void
  65. get_texture(file, scene_objects, first_object, last_object)
  66.   file_ptr        file;
  67.   int             scene_objects;
  68.   int            *first_object, *last_object;
  69. {
  70.   real            value;
  71.  
  72.   get_valid(file, &value, 0.0, (real) TEXTURE_KINDS_MAX, "TEXTURE ID");
  73.   switch (ROUND(value))
  74.   {
  75.   case NULL_TYPE:
  76.     get_texture_null(scene_objects, first_object, last_object);
  77.     break;
  78.   case CHECKER_TYPE:
  79.     get_texture_checker(scene_objects, first_object, last_object);
  80.     break;
  81.   case BLOTCH_TYPE:
  82.     get_texture_blotch(scene_objects, first_object, last_object);
  83.     break;
  84.   case BUMP_TYPE:
  85.     get_texture_bump(scene_objects, first_object, last_object);
  86.     break;
  87.   case MARBLE_TYPE:
  88.     get_texture_marble(scene_objects, first_object, last_object);
  89.     break;
  90.   case FBM_TYPE:
  91.     get_texture_fbm(scene_objects, first_object, last_object);
  92.     break;
  93.   case FBM_BUMP_TYPE:
  94.     get_texture_fbm_bump(scene_objects, first_object, last_object);
  95.     break;
  96.   case WOOD_TYPE:
  97.     get_texture_wood(scene_objects, first_object, last_object);
  98.     break;
  99.   case ROUND_TYPE:
  100.     get_texture_round(scene_objects, first_object, last_object);
  101.     break;
  102.   case BOZO_TYPE:
  103.     get_texture_bozo(scene_objects, first_object, last_object);
  104.     break;
  105.   case RIPPLES_TYPE:
  106.     get_texture_ripples(scene_objects, first_object, last_object);
  107.     break;
  108.   case WAVES_TYPE:
  109.     get_texture_waves(scene_objects, first_object, last_object);
  110.     break;
  111.   case SPOTTED_TYPE:
  112.     get_texture_spotted(scene_objects, first_object, last_object);
  113.     break;
  114.   case DENTS_TYPE:
  115.     get_texture_dents(scene_objects, first_object, last_object);
  116.     break;
  117.   case AGATE_TYPE:
  118.     get_texture_agate(scene_objects, first_object, last_object);
  119.     break;
  120.   case WRINKLES_TYPE:
  121.     get_texture_wrinkles(scene_objects, first_object, last_object);
  122.     break;
  123.   case GRANITE_TYPE:
  124.     get_texture_granite(scene_objects, first_object, last_object);
  125.     break;
  126.   case GRADIENT_TYPE:
  127.     get_texture_gradient(scene_objects, first_object, last_object);
  128.     break;
  129.   case IMAGE_MAP_TYPE:
  130.     get_texture_image_map(scene_objects, first_object, last_object);
  131.     break;
  132.   case GLOSS_TYPE:
  133.     get_texture_gloss(scene_objects, first_object, last_object);
  134.     break;
  135.   case BUMP3_TYPE:
  136.     get_texture_bump3(scene_objects, first_object, last_object);
  137.     break;
  138.   }
  139. }
  140. typedef
  141. struct
  142. {
  143.   char            node;    /* Left/Right */
  144.   csg_ptr         data;
  145.   int             first, start;
  146. } csg_scene_struct;
  147.  
  148. static csg_scene_struct csg_scene[CSG_LEVEL_MAX];
  149.  
  150. #define LEFT_NODE  (0)
  151. #define RIGHT_NODE (1)
  152.  
  153. typedef
  154. struct
  155. {
  156.   int             first, start;
  157. } list_scene_struct;
  158.  
  159. static list_scene_struct list_scene[LIST_LEVEL_MAX];
  160.  
  161. static int
  162. csg_empty_node(start)
  163.   int             start;
  164. {
  165.   csg_ptr         csg;
  166.  
  167.   csg = (csg_ptr) object[start]->data;
  168.   if (object[csg->left]->object_type != CSG_TYPE)
  169.     return csg->left;
  170.   if (object[csg->right]->object_type != CSG_TYPE)
  171.     return csg->right;
  172.   if (RANDOM < 0.5)
  173.     return csg_empty_node(csg->left);
  174.   return csg_empty_node(csg->right);
  175. }
  176. static void
  177. csg_convert(level)
  178.   int             level;
  179. {
  180.   REG int         i, j, count, first, second;
  181.   int             count2;
  182.   csg_ptr         csg;
  183.  
  184.   count = 0;
  185.   for (i = csg_scene[level].start; i <= objects; POSINC(i))
  186.     if (object[i]->object_type == CSG_TYPE)
  187.       POSDEC(count);
  188.     else
  189.       POSINC(count);
  190.   if (count == 1)
  191.     return;
  192.   /* Insert CSG objects */
  193.   for (i = 1; i < count; POSINC(i))
  194.   {
  195.     if (i == 1)
  196.     {
  197.       first = csg_scene[level].start;
  198.       second = first + 1;
  199.     } else
  200.     {
  201.       first = csg_empty_node(csg_scene[level].start);
  202.       POSINC(second);
  203.     }
  204.     if (objects == OBJECTS_MAX - 1)
  205.       runtime_abort("too many OBJECTS");
  206.     /* Shift objects */
  207.     for (j = objects; j >= csg_scene[level].start; POSDEC(j))
  208.     {
  209.       if (object[j]->object_type == CSG_TYPE)
  210.       {
  211.     csg = (csg_ptr) object[j]->data;
  212.         if (csg->left > first)
  213.       POSINC(csg->left);
  214.         if (csg->right > first)
  215.       POSINC(csg->right);
  216.       }
  217.       if (j >= first)
  218.       {
  219.         POSINC(object[j]->id);
  220.         object[j + 1] = object[j];
  221.       }
  222.     }
  223.     POSINC(objects);
  224.     /* Insert new CSG object */
  225.     ALLOCATE(object[first], object_struct, 1);
  226.     object[first]->id = first;
  227.     object[first]->surface_id = object[csg_scene[level].first]->surface_id;
  228.     object[first]->refraction = object[csg_scene[level].first]->refraction;
  229.     ALLOCATE(object[first]->min, xyz_struct, 1);
  230.     ALLOCATE(object[first]->max, xyz_struct, 1);
  231.     object[first]->transf = NULL;
  232.     object[first]->inv_transf = NULL;
  233.     object[first]->texture = NULL;
  234.     object[first]->texture_modify_normal = FALSE;
  235.     object[first]->object_type = CSG_TYPE;
  236.     ALLOCATE(csg, csg_struct, 1);
  237.     object[first]->data = (void_ptr) csg;
  238.     csg->op = CSG_UNION;
  239.     csg->left = first + 1;
  240.     POSINC(second);
  241.     csg->right = second;
  242.     if (object[second]->object_type == CSG_TYPE)
  243.     {
  244.       count2 = -1;
  245.       while (count2 != 1)
  246.       {
  247.         POSINC(second);
  248.     if (object[second]->object_type == CSG_TYPE)
  249.       POSDEC(count2);
  250.     else
  251.       POSINC(count2);
  252.       }
  253.     }
  254.   }
  255. }
  256. static void
  257. list_convert(level)
  258.   int             level;
  259. {
  260.   REG int         i;
  261.   list_ptr        list;
  262.   list_node_ptr   head;
  263.  
  264.   list = (list_ptr) object[list_scene[level].first]->data;
  265.   list->head = NULL;
  266.   for (i = objects; i >= list_scene[level].start; POSDEC(i))
  267.   {
  268.     if (CLOSED(object[i]->object_type))
  269.       WRITE(results, "Warning: CLOSED OBJECT (%d) inside a LIST\n", i);
  270.     if (list->head == NULL)
  271.     {
  272.       ALLOCATE(list->head, list_node_struct, 1);
  273.       list->head->next = NULL;
  274.     } else
  275.     {
  276.       head = list->head;
  277.       ALLOCATE(list->head, list_node_struct, 1);
  278.       list->head->next = (void_ptr) head;
  279.     }
  280.     /* Move object */
  281.     list->head->object = object[i];
  282.     object[i] = NULL;
  283.     POSDEC(objects);
  284.   }
  285. }
  286. void
  287. get_scene()
  288. {
  289.   real            value;
  290.   int             scene_objects;
  291.   int            *first_object, *last_object;
  292.   short int       csg_level, list_level;
  293.  
  294.   ALLOCATE(first_object, int, OBJECTS_MAX);
  295.   ALLOCATE(last_object, int, OBJECTS_MAX);
  296.   /* View data */
  297.   ADVANCE(scene);
  298.   get_valid(scene, &value, X_MIN, X_MAX, "EYE POINT X");
  299.   eye.x = value;
  300.   get_valid(scene, &value, Y_MIN, Y_MAX, "EYE POINT Y");
  301.   eye.y = value;
  302.   get_valid(scene, &value, Z_MIN, Z_MAX, "EYE POINT Z");
  303.   eye.z = value;
  304.   ADVANCE(scene);
  305.   get_valid(scene, &value, X_MIN, X_MAX, "LOOK POINT X");
  306.   look.x = value;
  307.   get_valid(scene, &value, Y_MIN, Y_MAX, "LOOK POINT Y");
  308.   look.y = value;
  309.   get_valid(scene, &value, Z_MIN, Z_MAX, "LOOK POINT Z");
  310.   look.z = value;
  311.   ADVANCE(scene);
  312.   gaze.x = look.x - eye.x;
  313.   gaze.y = look.y - eye.y;
  314.   gaze.z = look.z - eye.z;
  315.   gaze_distance = LENGTH(gaze);
  316.   if (gaze_distance < ROUNDOFF)
  317.     runtime_abort("EYE POINT equal to LOOK POINT");
  318.   NORMALIZE(gaze);
  319.   get_valid(scene, &value, X_MIN, X_MAX, "UP VECTOR X");
  320.   up.x = value;
  321.   get_valid(scene, &value, Y_MIN, Y_MAX, "UP VECTOR Y");
  322.   up.y = value;
  323.   get_valid(scene, &value, Z_MIN, Z_MAX, "UP VECTOR Z");
  324.   up.z = value;
  325.   ADVANCE(scene);
  326.   if (LENGTH(up) < ROUNDOFF)
  327.     runtime_abort("no UP VECTOR");
  328.   NORMALIZE(up);
  329.   if (ABS(DOT_PRODUCT(gaze, up)) > COS(ANGLE_MIN))
  330.     runtime_abort("bad UP VECTOR");
  331.   get_valid(scene, &value, 0.5, 89.5, "HORIZONTAL VIEW Angle");   /* Degrees */
  332.   view_angle_x = DEGREE_TO_RADIAN(value);
  333.   get_valid(scene, &value, 0.5, 89.5, "VERTICAL VIEW Angle");     /* Degrees */
  334.   view_angle_y = DEGREE_TO_RADIAN(value);
  335.   ADVANCE(scene);
  336.  
  337.   /* RGB data */
  338.   ADVANCE(scene);
  339.   get_valid(scene, &value, 0.0, 1.0, "BACK COLOR Red");
  340.   back_color.r = value;
  341.   get_valid(scene, &value, 0.0, 1.0, "BACK COLOR Green");
  342.   back_color.g = value;
  343.   get_valid(scene, &value, 0.0, 1.0, "BACK COLOR Blue");
  344.   back_color.b = value;
  345.   ADVANCE(scene);
  346.   get_valid(scene, &value, 0.0, 1.0, "LIGHT AMBIENT Red");
  347.   light_ambient.r = value;
  348.   get_valid(scene, &value, 0.0, 1.0, "LIGHT AMBIENT Green");
  349.   light_ambient.g = value;
  350.   get_valid(scene, &value, 0.0, 1.0, "LIGHT AMBIENT Blue");
  351.   light_ambient.b = value;
  352.   ADVANCE(scene);
  353.  
  354.   /* Light data */
  355.   
  356. #ifdef THINK_C
  357.  
  358.     /* On the mac, we change the status text to show we're reading light data */
  359.     if (status_dialog_visible) set_status_text("\pReading Lights…");
  360.  
  361. #endif
  362.  
  363.   ADVANCE(scene);
  364.   lights = 0;
  365.   while (NOT END_OF_LINE(scene))
  366.   {
  367.  
  368.     if (lights >= LIGHTS_MAX)
  369.       runtime_abort("too many LIGHTS");
  370.     get_valid(scene, &value, 1.0, 255.0, "ID");
  371.     if (ROUND(value) > LIGHT_KINDS_MAX)
  372.       runtime_abort("invalid LIGHT ID");
  373.     switch (ROUND(value))
  374.     {
  375.     case POINT_LIGHT_TYPE:
  376.       get_point_light();
  377.       break;
  378.     case DIRECT_LIGHT_TYPE:
  379.       get_dir_light();
  380.       break;
  381.     case EXTENDED_LIGHT_TYPE:
  382.       get_ext_light();
  383.       break;
  384.     }
  385.     POSINC(lights);
  386.     ADVANCE(scene);
  387.   }
  388.   if (lights == 0)
  389.     runtime_abort("no LIGHTS");
  390.   ADVANCE(scene);
  391.   /* Surface data */
  392.  
  393. #ifdef THINK_C
  394.  
  395.     /* On the mac, we change the status text to show we're reading surface data */
  396.     if (status_dialog_visible) set_status_text("\pReading Surfaces…");
  397.  
  398. #endif
  399.  
  400.   ADVANCE(scene);
  401.   surfaces = 0;
  402.   while (NOT END_OF_LINE(scene))
  403.   {
  404.     if (surfaces >= SURFACES_MAX)
  405.       runtime_abort("too many SURFACES");
  406.     get_valid(scene, &value, 1.0, 255.0, "ID");
  407.     if (ROUND(value) > SURFACE_KINDS_MAX)
  408.       runtime_abort("invalid SURFACE ID");
  409.     switch (ROUND(value))
  410.     {
  411.     case SURFACE_TYPE1:
  412.       get_surface_type1();
  413.       break;
  414.     case SURFACE_TYPE2:
  415.       get_surface_type2();
  416.       break;
  417.     }
  418.     POSINC(surfaces);
  419.     ADVANCE(scene);
  420.   }
  421.   if (surfaces == 0)
  422.     runtime_abort("no SURFACES");
  423.   ADVANCE(scene);
  424.   /* Objects */
  425.  
  426. #ifdef THINK_C
  427.  
  428.     /* On the mac, we change the status text to show we're reading objects data */
  429.     if (status_dialog_visible) set_status_text("\pReading Objects…");
  430.  
  431.     /* Set the maximum of the progress bar to the maximum number of objects */
  432.     set_progress_bar_max(number_noncluster_objects);
  433.  
  434. #endif
  435.  
  436.   ADVANCE(scene);
  437.   objects = 0;
  438.   scene_objects = 0;
  439.   csg_level = -1;
  440.   list_level = -1;
  441.   while (NOT END_OF_LINE(scene))
  442.   {
  443.   
  444. #ifdef THINK_C
  445.  
  446.     /* On the mac, we update the progress bar periodically to show the number
  447.         of objects read */
  448.     if (status_dialog_visible) set_progress_bar_value(objects);
  449.     
  450.     /* And update the free memory available now and then */
  451.     if (!(scene_objects % 10)) update_status_free_memory();
  452.  
  453. #endif
  454.  
  455.     get_valid(scene, &value, 1.0, 255.0, "ID");
  456.     /* Texture */
  457.     if (ROUND(value) == TEXTURE_TYPE)
  458.     {
  459.       if (texture_mode == 1)
  460.       {
  461.         if (scene_objects == 0)
  462.           runtime_abort("no OBJECTS before TEXTURE");
  463.         get_texture(scene, scene_objects, first_object, last_object);
  464.         continue;
  465.       } else
  466.       {
  467.         ADVANCE(scene);
  468.         continue;
  469.       }
  470.     }
  471.     /* Transformation */
  472.     if (ROUND(value) == TRANSF_TYPE)
  473.     {
  474.       if (scene_objects == 0)
  475.         runtime_abort("no OBJECTS before TRANSFORMATION");
  476.       get_object_transform(scene_objects, first_object, last_object);
  477.       continue;
  478.     }
  479.     /* CSG */
  480.     if (ROUND(value) == CSG_OP_TYPE)
  481.     {
  482.       get_valid(scene, &value, 0.0, 2.0, "CSG CODE");
  483.       switch (ROUND(value))
  484.       {
  485.       case 0:    /* CSG BEGIN */
  486.         POSINC(csg_level);
  487.         if (csg_level == CSG_LEVEL_MAX)
  488.       runtime_abort("too many CSG LEVELS");
  489.         POSINC(objects);
  490.         if (objects >= OBJECTS_MAX)
  491.           runtime_abort("too many OBJECTS");
  492.         first_object[scene_objects] = objects;
  493.         last_object[scene_objects] = objects;
  494.         POSINC(scene_objects);
  495.     get_csg();
  496.     csg_scene[csg_level].node = LEFT_NODE;
  497.     csg_scene[csg_level].data = (csg_ptr) object[objects]->data;
  498.     csg_scene[csg_level].data->left = objects + 1;
  499.     csg_scene[csg_level].start = objects + 1;
  500.     csg_scene[csg_level].first = objects;
  501.     break;
  502.       case 1:    /* CSG NEXT */
  503.     if (csg_level < 0)
  504.       runtime_abort("no CSG tree");
  505.     if (csg_scene[csg_level].node == RIGHT_NODE)
  506.       runtime_abort("invalid CSG NODE (already has right subtree)");
  507.     if (csg_scene[csg_level].data->left == objects + 1)
  508.       runtime_abort("invalid CSG NODE (empty left subtree)");
  509.     csg_convert(csg_level);
  510.     csg_scene[csg_level].node = RIGHT_NODE;
  511.     csg_scene[csg_level].data->right = objects + 1;
  512.     csg_scene[csg_level].start = objects + 1;
  513.         ADVANCE(scene);
  514.     break;
  515.       case 2:    /* CSG END */
  516.     if (csg_level < 0)
  517.       runtime_abort("no CSG TREE");
  518.     if (csg_scene[csg_level].node == LEFT_NODE)
  519.       runtime_abort("invalid CSG NODE (no right subtree)");
  520.     if (csg_scene[csg_level].data->right == objects + 1)
  521.       runtime_abort("invalid CSG NODE (empty right subtree)");
  522.     csg_convert(csg_level);
  523.     POSDEC(csg_level);
  524.         ADVANCE(scene);
  525.     break;
  526.       }
  527.       continue;
  528.     }
  529.     /* List */
  530.     if (ROUND(value) == LIST_OP_TYPE)
  531.     {
  532.       get_valid(scene, &value, 0.0, 1.0, "LIST CODE");
  533.       switch (ROUND(value))
  534.       {
  535.       case 0:    /* LIST BEGIN */
  536.         POSINC(list_level);
  537.         if (list_level == LIST_LEVEL_MAX)
  538.       runtime_abort("too many LIST LEVELS");
  539.         POSINC(objects);
  540.         if (objects >= OBJECTS_MAX)
  541.           runtime_abort("too many OBJECTS");
  542.         first_object[scene_objects] = objects;
  543.         last_object[scene_objects] = objects;
  544.         POSINC(scene_objects);
  545.     get_list();
  546.     list_scene[list_level].start = objects + 1;
  547.     list_scene[list_level].first = objects;
  548.     break;
  549.       case 1:    /* LIST END */
  550.     if (list_level < 0)
  551.       runtime_abort("no LIST");
  552.     if (objects - list_scene[list_level].first <= 1)
  553.       runtime_abort("invalid LIST (must have at least 2 OBJECTS)");
  554.     list_convert(list_level);
  555.     POSDEC(list_level);
  556.         ADVANCE(scene);
  557.     break;
  558.       }
  559.       continue;
  560.     }
  561.     /* Object */
  562.     if (ROUND(value) > OBJECT_KINDS_MAX)
  563.       runtime_abort("invalid OBJECT ID");
  564.     POSINC(objects);
  565.     if (objects >= OBJECTS_MAX)
  566.       runtime_abort("too many OBJECTS");
  567.     first_object[scene_objects] = objects;
  568.     switch (ROUND(value))
  569.     {
  570.     case SPHERE_TYPE:
  571.       get_sphere();
  572.       break;
  573.     case BOX_TYPE:
  574.       get_box();
  575.       break;
  576.     case PATCH_TYPE:
  577.       get_patch();
  578.       break;
  579.     case CONE_TYPE:
  580.       get_cone();
  581.       break;
  582.     case POLYGON_TYPE:
  583.       get_polygon();
  584.       break;
  585.     case TRIANGLE_TYPE:
  586.       get_triangle();
  587.       break;
  588.     case TEXT_TYPE:
  589.       get_text();
  590.       if ((sampling_levels > 0) AND(intersect_adjust_mode == 0))
  591.       {
  592.         intersect_adjust_mode = 1;
  593.         if (verbose_mode > 0)
  594.           WRITE(results, "Inters. adjust mode: changed to %s\n", "ON");
  595.       }
  596.       break;
  597.     }
  598.     last_object[scene_objects] = objects;
  599.     POSINC(scene_objects);
  600.   }
  601.   if (objects == 0)
  602.     runtime_abort("no OBJECTS");
  603.   if (csg_level >= 0)
  604.     runtime_abort("incomplete CSG OBJECT(S)");
  605.   if (list_level >= 0)
  606.     runtime_abort("incomplete LIST of OBJECT(S)");
  607.   end_pp_get();
  608.   if (texture_mode == 2)
  609.   {
  610.     ADVANCE(scene);
  611.     /* Textures */
  612.     ADVANCE(scene);
  613.     while (NOT END_OF_LINE(scene))
  614.       get_texture(scene, scene_objects, first_object, last_object);
  615.   }
  616.   FREE(first_object);
  617.   FREE(last_object);
  618.   if (verbose_mode > 1)
  619.   {
  620.     WRITE(results, "%d Scene object(s) read, %d Total object(s) created\n",
  621.           scene_objects, objects);
  622.     WRITE(results, "Info: scene ok\n");
  623.     FLUSH(results);
  624.   }
  625.   if (scene != INPUT)
  626.     CLOSE(scene);
  627. }
  628.