/****************************************************************************
*                parse.c
*
*  This module implements a parser for the scene description files.
*
*  from Persistence of Vision Raytracer
*  Copyright 1993 Persistence of Vision Team
*---------------------------------------------------------------------------
*  NOTICE: This source code file is provided so that users may experiment
*  with enhancements to POV-Ray and to port the software to platforms other 
*  than those supported by the POV-Ray Team.  There are strict rules under
*  which you are permitted to use this file.  The rules are in the file
*  named POVLEGAL.DOC which should be distributed with this file. If 
*  POVLEGAL.DOC is not available or for more info please contact the POV-Ray
*  Team Coordinator by leaving a message in CompuServe's Graphics Developer's
*  Forum.  The latest version of POV-Ray may be found there as well.
*
* This program is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
*****************************************************************************/

#include "frame.h"
#include "vector.h"
#include "povproto.h"
#include "parse.h"

/* This file implements a simple recursive-descent parser for reading the
input file.  */

extern DBL Max_Trace_Level;
extern char VerboseFormat;
extern unsigned int Options;
extern int Use_Slabs;
extern char Stat_File_Name[FILE_NAME_LENGTH];

extern struct Reserved_Word_Struct Reserved_Words [];
extern DBL Antialias_Threshold;

extern struct Token_Struct Token;
extern char String[MAX_STRING_INDEX];

extern COLOUR_MAP_ENTRY *Build_Entries;
extern FRAME Frame;
extern DBL Clock_Value;
extern char **Symbol_Table;
extern int Max_Intersections;
extern DBL Language_Version;
extern METHODS Csg_Height_Field_Methods;
extern METHODS CSG_Union_Methods;

static void Parse_Image_Map PARAMS((PIGMENT *Pigment));
static void Parse_Bump_Map PARAMS((TNORMAL *Tnormal));
static void Parse_Pigment PARAMS((PIGMENT **Pigment_Ptr));
static void Parse_Tnormal PARAMS((TNORMAL **Tnormal_Ptr));
static void Parse_Finish PARAMS((FINISH **Finish_Ptr));
static TEXTURE *Parse_Texture PARAMS((void));
static void Token_Init PARAMS((void));
static void Frame_Init PARAMS((void));
static void Parse_Coeffs PARAMS((int order, DBL *Coeffs));
static IMAGE *Parse_Image PARAMS((int Legal));
static TRANSFORM *Parse_Transform PARAMS((void));
static void Parse_Object_Mods PARAMS((OBJECT *Object));
static OBJECT *Parse_Bicubic_Patch PARAMS((void));
static OBJECT *Parse_Blob PARAMS((void));
static OBJECT *Parse_Box PARAMS((void));
static OBJECT *Parse_Cone PARAMS((void));
static OBJECT *Parse_Cylinder PARAMS((void));
static OBJECT *Parse_Disc PARAMS((void));
static OBJECT *Parse_Height_Field PARAMS((void));
static OBJECT *Parse_Plane PARAMS((void));
static OBJECT *Parse_Poly PARAMS((int order));
static OBJECT *Parse_Quadric PARAMS((void));
static OBJECT *Parse_Smooth_Triangle PARAMS((void));
static OBJECT *Parse_Sphere PARAMS((void));
static OBJECT *Parse_Torus PARAMS((void));
static OBJECT *Parse_Triangle PARAMS((void));
static OBJECT *Parse_CSG PARAMS((int CSG_Type));
static OBJECT *Parse_Light_Source PARAMS((void));
static OBJECT *Parse_Object PARAMS((void));
static void Parse_Fog PARAMS((void));
static void Parse_Frame PARAMS((void));
static void Parse_Camera PARAMS((CAMERA **Camera_Ptr));
static void Parse_Declare PARAMS((void));
static void Link PARAMS((OBJECT *New_Object,OBJECT **Field,OBJECT **Old_Object_List));
static void Link_Textures PARAMS((TEXTURE **Old_Texture, TEXTURE *New_Texture));
static char *Get_Token_String PARAMS((TOKEN Token_Id));
static void Where_Error PARAMS((void));
static int Test_Redefine PARAMS((int a));
static OBJECT *Parse_Bound_Clip PARAMS((void));
static void Found_Instead PARAMS((void));
/*static void Parse_Warn PARAMS((TOKEN Token_Id));*/
static void Warn_State PARAMS((TOKEN Token_Id, TOKEN Type));
static void Post_Process PARAMS((OBJECT *Object, OBJECT *Parent));
static void Destroy_Constants PARAMS((void));
static OBJECT *Parse_Object_Id PARAMS((void));
static void Link_To_Frame PARAMS((OBJECT *Object));

extern struct Constant_Struct Constants[MAX_CONSTANTS];

int Number_Of_Constants;
int Previous;
short Have_Vector;
short Not_In_Default;

TOKEN *Brace_Stack;
int Brace_Index;

TEXTURE *Default_Texture;
CAMERA *Default_Camera;

/* Parse the file. */
void Parse ()
  {
   Build_Entries  = NULL;
   if ((Brace_Stack = (TOKEN *) malloc(MAX_BRACES*sizeof (TOKEN))) == NULL)
     MAError ("brace stack");
   Brace_Index = 0;
   Token_Init ();
   Default_Camera = Create_Camera();
   Default_Texture = Create_PNF_Texture();
   Default_Texture->Pigment = Create_Pigment();
   Default_Texture->Tnormal = NULL;
   Default_Texture->Finish  = Create_Finish();
   Not_In_Default = TRUE;
   Frame_Init ();
   Parse_Frame ();
   if (Frame.Objects==NULL)
     Error("No objects in scene");
   Destroy_Constants ();
   Destroy_Textures(Default_Texture);
   Destroy_Camera(Default_Camera);
   if (Build_Entries != NULL)
     free (Build_Entries);
   free (Brace_Stack);
  }

static void Token_Init ()
  {
   Number_Of_Constants = 0;
  }

/* Set up the fields in the frame to default values. */
static
void Frame_Init ()
  {
   Frame.Camera = Copy_Camera(Default_Camera);
   Frame.Light_Sources = NULL;
   Frame.Objects = NULL;
   Frame.Atmosphere_IOR = 1.0;
   Frame.Antialias_Threshold = Antialias_Threshold;
   Frame.Fog_Distance = 0.0;
   Make_Colour (&(Frame.Fog_Colour), 0.0, 0.0, 0.0);
  }

void Parse_Begin ()
  {
   char *front;

   Brace_Stack[++Brace_Index]=Token.Token_Id;
   Get_Token ();

   if (Token.Token_Id == LEFT_CURLY_TOKEN)
     return;

   Where_Error ();

   front = Get_Token_String (Brace_Stack[Brace_Index]);
   fprintf (stderr, "Missing { after %s, ", front);
   Found_Instead ();
   exit (1);
  }

void Parse_End ()
  {
   char *front;

   Get_Token ();

   if (Token.Token_Id == RIGHT_CURLY_TOKEN)
     {
      Brace_Index--;
      return;
     }

   Where_Error ();

   front = Get_Token_String (Brace_Stack[Brace_Index]);
   fprintf (stderr, "No matching } in %s,", front);
   Found_Instead ();
   exit (1);
  }

static OBJECT *Parse_Object_Id ()
  {
   OBJECT *Object;
   
   EXPECT
     CASE (OBJECT_ID_TOKEN)
       Warn_State(OBJECT_ID_TOKEN, OBJECT_TOKEN);
       Object = Copy_Object((OBJECT *) Token.Constant_Data);
       Parse_Object_Mods (Object);
       EXIT
     END_CASE

     OTHERWISE
       Object = NULL;
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   return (Object);
  }

void Parse_Comma ()
  {
   Get_Token();
   if (Token.Token_Id != COMMA_TOKEN)
     {
      UNGET;
     }
  }

static void Parse_Coeffs(order, Coeffs)
  int order;
  DBL *Coeffs;
  {
   int i;

   EXPECT
     CASE (LEFT_ANGLE_TOKEN)
       Coeffs[0] = Parse_Float();
       for (i = 1; i < term_counts(order); i++)
         {
          Parse_Comma();
          Coeffs[i] = Parse_Float();
         }
       GET (RIGHT_ANGLE_TOKEN);
       EXIT
     END_CASE

     OTHERWISE
       Parse_Error (LEFT_ANGLE_TOKEN);
     END_CASE
   END_EXPECT
  }

static
IMAGE *Parse_Image (Legal)
  int Legal;
  {
   IMAGE *Image;
   VECTOR Local_Vector;

   Image = Create_Image ();

   if (Legal & GRAD_FILE)
     {
      EXPECT
        CASE_VECTOR
          Warn("Should use map_type keyword and/or eliminate orientation.",1.5);
          Have_Vector = FALSE;
          Parse_Vector_Float (&Local_Vector);
          if (Have_Vector)
            Image->Gradient = Local_Vector;
          else
            Image->Map_Type = (int)Local_Vector.x;
        END_CASE

        OTHERWISE
          UNGET
          EXIT
        END_CASE
      END_EXPECT
     }

   EXPECT
     CASE (IFF_TOKEN)
       Image->File_Type = IFF_FILE;
       GET (STRING_TOKEN);
       Read_Iff_Image (Image, Token.Token_String);
       EXIT
     END_CASE

     CASE (GIF_TOKEN)
       Image->File_Type = GIF_FILE;
       GET (STRING_TOKEN);
       Read_Gif_Image(Image, Token.Token_String);
       EXIT
     END_CASE

     CASE (POT_TOKEN)
       Image->File_Type = POT_FILE;
       GET (STRING_TOKEN);
       Read_Gif_Image(Image, Token.Token_String);
       EXIT
     END_CASE

     CASE (DUMP_TOKEN)
       Image->File_Type = DUMP_FILE;
       GET (STRING_TOKEN);
       Read_Dump_Image(Image, Token.Token_String);
       EXIT
     END_CASE

     CASE (TGA_TOKEN)
       Image->File_Type = TGA_FILE;
       GET (STRING_TOKEN);
       Read_Targa_Image(Image, Token.Token_String);
       EXIT
     END_CASE

     OTHERWISE
       Parse_Error_Str ("map file spec");
     END_CASE
   END_EXPECT

   if (!(Image->File_Type & Legal))
     Error ("File type not supported here");
   return (Image);
  }

static void Parse_Image_Map (Pigment)
  PIGMENT *Pigment;
  {
   int reg;

   Pigment->Type = IMAGE_MAP_PIGMENT;

   Parse_Begin ();

   Pigment->Image = Parse_Image (IMAGE_FILE);
   Pigment->Image->Use_Colour_Flag = TRUE;

   EXPECT                   /* Look for image_attribs */
     CASE (ONCE_TOKEN)
       Pigment->Image->Once_Flag=TRUE;
     END_CASE

     CASE (INTERPOLATE_TOKEN)
       Pigment->Image->Interpolation_Type = (int)Parse_Float();
     END_CASE

     CASE (MAP_TYPE_TOKEN)
       Pigment->Image->Map_Type = (int) Parse_Float ();
     END_CASE

     CASE (USE_COLOUR_TOKEN)
       Pigment->Image->Use_Colour_Flag = TRUE;
     END_CASE

     CASE (USE_INDEX_TOKEN)
       Pigment->Image->Use_Colour_Flag = FALSE;
     END_CASE

     CASE (ALPHA_TOKEN)
       Warn("Keyword ALPHA discontinued.  Use FILTER instead.",1.55);
         
     CASE (FILTER_TOKEN)
       EXPECT
         CASE (ALL_TOKEN)
           {
            DBL filter;
            filter = Parse_Float();
            for (reg = 0 ; reg < Pigment->Image->Colour_Map_Size ; reg++)
              Pigment->Image->Colour_Map[reg].Filter
                  = (unsigned short) (filter *255.0);
           }
           EXIT
         END_CASE

         OTHERWISE
           UNGET
           reg = (int)(Parse_Float() + 0.01);
           if (Pigment->Image->Colour_Map == NULL)
             Error ("Can't apply FILTER to a non colour-mapped image\n");
           if ((reg < 0) || (reg >= Pigment->Image->Colour_Map_Size))
             Error ("FILTER colour register value out of range.\n");

           Parse_Comma();
           Pigment->Image->Colour_Map[reg].Filter
                  = (unsigned short) (255.0 * Parse_Float());
           EXIT
         END_CASE

       END_EXPECT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   Parse_End ();
}

static void Parse_Bump_Map (Tnormal)
  TNORMAL *Tnormal;
  {
   Tnormal->Type = BUMP_MAP;

   Parse_Begin ();

   Tnormal->Image = Parse_Image (NORMAL_FILE);

   Tnormal->Image->Use_Colour_Flag = TRUE;

   EXPECT
     CASE (ONCE_TOKEN)
       Tnormal->Image->Once_Flag=TRUE;
     END_CASE

     CASE (MAP_TYPE_TOKEN)
       Tnormal->Image->Map_Type = (int) Parse_Float ();
     END_CASE

     CASE (INTERPOLATE_TOKEN)
       Tnormal->Image->Interpolation_Type = (int)Parse_Float();
     END_CASE

     CASE (BUMP_SIZE_TOKEN)
       Tnormal->Amount = Parse_Float ();
     END_CASE

     CASE (USE_COLOUR_TOKEN)
       Tnormal->Image->Use_Colour_Flag = TRUE;
     END_CASE

     CASE (USE_INDEX_TOKEN)
       Tnormal->Image->Use_Colour_Flag = FALSE;
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT
   Parse_End ();
}

static void Parse_Pigment (Pigment_Ptr)
  PIGMENT **Pigment_Ptr;
  {
   PIGMENT *New;
   VECTOR Local_Vector;

   Parse_Begin ();

   EXPECT            /* Look for [pigment_id] */
     CASE (PIGMENT_ID_TOKEN)
       Destroy_Pigment(*Pigment_Ptr);
       *Pigment_Ptr = Copy_Pigment ((PIGMENT *) Token.Constant_Data);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End pigment_id */

   New = *Pigment_Ptr;

   EXPECT
     CASE (AGATE_TOKEN)
       New->Type = AGATE_PIGMENT;
       EXIT
     END_CASE

     CASE (BOZO_TOKEN)
       New->Type = BOZO_PIGMENT;
       EXIT
     END_CASE

     CASE (GRANITE_TOKEN)
       New->Type = GRANITE_PIGMENT;
       EXIT
     END_CASE

     CASE (LEOPARD_TOKEN)
       New->Type = LEOPARD_PIGMENT;
       EXIT
     END_CASE

     CASE (MARBLE_TOKEN)
       New->Type = MARBLE_PIGMENT;
       EXIT
     END_CASE

     CASE (MANDEL_TOKEN)
       New->Type = MANDEL_PIGMENT;
       New->Iterations = (int)Parse_Float();
       EXIT
     END_CASE

     CASE (ONION_TOKEN)
       New->Type = ONION_PIGMENT;
       EXIT
     END_CASE

     CASE (PAINTED1_TOKEN)
       New->Type = PAINTED1_PIGMENT;
       EXIT
     END_CASE

     CASE (PAINTED2_TOKEN)
       New->Type = PAINTED2_PIGMENT;
       EXIT
     END_CASE

     CASE (PAINTED3_TOKEN)
       New->Type = PAINTED2_PIGMENT;
       EXIT
     END_CASE

     CASE (SPOTTED_TOKEN)
       New->Type = SPOTTED_PIGMENT;
       EXIT
     END_CASE

     CASE (WOOD_TOKEN)
       New->Type = WOOD_PIGMENT;
       EXIT
     END_CASE

     CASE (GRADIENT_TOKEN)
       New->Type = GRADIENT_PIGMENT;
       Parse_Vector (&(New->Colour_Gradient));
       EXIT
     END_CASE

     CASE (RADIAL_TOKEN)
       New->Type = RADIAL_PIGMENT;
     END_CASE

     CASE (COLOUR_TOKEN)
       New->Type = COLOUR_PIGMENT;
       New->Colour1 = Create_Colour ();
       Parse_Colour (New->Colour1);
       New->Quick_Colour = *New->Colour1;
       EXIT
     END_CASE

     CASE5 (COLOUR_ID_TOKEN, RGB_TOKEN, RGBF_TOKEN, RED_TOKEN, BLUE_TOKEN)
     CASE3 (GREEN_TOKEN, ALPHA_TOKEN, FILTER_TOKEN)
       UNGET
       New->Type = COLOUR_PIGMENT;
       New->Colour1 = Create_Colour ();
       Parse_Colour (New->Colour1);
       New->Quick_Colour = *New->Colour1;
       EXIT
     END_CASE

     CASE (CHECKER_TOKEN)
       New->Type = CHECKER_PIGMENT;
       New->Colour_Map = Parse_Colour_List(2);
       EXIT
     END_CASE

     CASE (HEXAGON_TOKEN)
       New->Type = HEXAGON_PIGMENT;
       New->Colour_Map = Parse_Colour_List(3);
       EXIT
     END_CASE

     CASE (IMAGE_MAP_TOKEN)
       Parse_Image_Map (New);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT     /* Concludes pigment_body */

   EXPECT         /* Look for pigment_modifier */
     CASE (TURBULENCE_TOKEN)
       Parse_Vector_Float(&(New->Turbulence));
       if ((New->Turbulence.x !=0.0) || (New->Turbulence.y !=0.0) ||
           (New->Turbulence.z !=0.0))
          New->Flags |= HAS_TURB;
     END_CASE

     CASE (COLOUR_MAP_TOKEN)
       if (New->Type == CHECKER_PIGMENT ||
           New->Type == HEXAGON_PIGMENT ||
           New->Type == COLOUR_PIGMENT ||
           New->Type == IMAGE_MAP_PIGMENT)
         Warn ("Cannot use color map with this pigment type",1.5);
       New->Colour_Map = Parse_Colour_Map ();
     END_CASE

     CASE (QUICK_COLOUR_TOKEN)
       Parse_Colour (&New->Quick_Colour);
     END_CASE

     CASE (OCTAVES_TOKEN)
       New->Octaves = (int)Parse_Float();
         if(New->Octaves < 1)
            New->Octaves = 1;
         if(New->Octaves > 10)  /* Avoid DOMAIN errors */
            New->Octaves = 10;
     END_CASE

     CASE (OMEGA_TOKEN)
       New->omega = Parse_Float();
     END_CASE

     CASE (LAMBDA_TOKEN)
       New->lambda = Parse_Float();
     END_CASE

     CASE (FREQUENCY_TOKEN)
       New->Frequency = Parse_Float();
     END_CASE

     CASE (PHASE_TOKEN)
       New->Phase = Parse_Float();
     END_CASE

     CASE (AGATE_TURB_TOKEN)
       if (Not_In_Default && (New->Type != AGATE_PIGMENT))
          Warn("Attempt to use agate_turb on non-agate",1.9);
       New->Agate_Turb_Scale = Parse_Float();
     END_CASE

     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Translate_Pigment (New, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Rotate_Pigment (New, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Scale_Pigment (New, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       Transform_Pigment (New, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   if (Not_In_Default && (New->Type == NO_PIGMENT))
     Warn("Pigment type unspecified or not 1st item",1.7);

   Parse_End ();
  }

static void Parse_Tnormal (Tnormal_Ptr)
  TNORMAL **Tnormal_Ptr;
  {
   TNORMAL *New;
   VECTOR Local_Vector;

   Parse_Begin ();

   EXPECT            /* Look for [tnormal_id] */
     CASE (TNORMAL_ID_TOKEN)
       Destroy_Tnormal(*Tnormal_Ptr);
       *Tnormal_Ptr = Copy_Tnormal ((TNORMAL *) Token.Constant_Data);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End [tnormal_id] */

   if (*Tnormal_Ptr == NULL)
     if ((Default_Texture->Tnormal) != NULL)
       *Tnormal_Ptr = Copy_Tnormal ((Default_Texture->Tnormal));
     else
       *Tnormal_Ptr = Create_Tnormal ();

   New = *Tnormal_Ptr;

   EXPECT  /* [tnormal_body] */
     CASE (BUMPS_TOKEN)
       New->Type = BUMPS;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (BUMPY1_TOKEN)
       New->Type = BUMPY1;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (BUMPY2_TOKEN)
       New->Type = BUMPY2;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (BUMPY3_TOKEN)
       New->Type = BUMPY3;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (DENTS_TOKEN)
       New->Type = DENTS;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (RIPPLES_TOKEN)
       New->Type = RIPPLES;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (WAVES_TOKEN)
       New->Type = WAVES;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (WRINKLES_TOKEN)
       New->Type = WRINKLES;
       New->Amount = Parse_Float ();
       EXIT
     END_CASE

     CASE (BUMP_MAP_TOKEN)
       Parse_Bump_Map (New);
       EXIT
     END_CASE

     OTHERWISE
       if (Not_In_Default && (New->Type == NO_NORMAL))
         Parse_Error_Str("normal body");
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End of tnormal_body */

   EXPECT        /* Look for tnormal_mods */

     CASE (TURBULENCE_TOKEN)
       Parse_Vector_Float(&(New->Turbulence));
       if ((New->Turbulence.x !=0.0) || (New->Turbulence.y !=0.0) ||
           (New->Turbulence.z !=0.0))
          New->Flags |= HAS_TURB;
     END_CASE

     CASE (OCTAVES_TOKEN)
       New->Octaves = (int)Parse_Float();
     END_CASE

     CASE (OMEGA_TOKEN)
       New->omega = Parse_Float();
     END_CASE

     CASE (LAMBDA_TOKEN)
       New->lambda = Parse_Float();
     END_CASE

     CASE (FREQUENCY_TOKEN)
       if (!(New->Type == RIPPLES || New->Type == WAVES))
         if (Language_Version >= 1.5)
           Warn ("Cannot use frequency with this normal",1.5);
       New->Frequency = Parse_Float();
     END_CASE

     CASE (PHASE_TOKEN)
       if (!(New->Type == RIPPLES || New->Type == WAVES))
         if (Language_Version >= 1.5)
            Warn ("Cannot use phase with this normal",1.5);
       New->Phase = Parse_Float();
     END_CASE

     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Translate_Tnormal (New, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Rotate_Tnormal (New, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Scale_Tnormal (New, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       Transform_Tnormal (New, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End of tnormal_mods */

   Parse_End ();
  }

static void Parse_Finish (Finish_Ptr)
  FINISH **Finish_Ptr;
  {
   FINISH *New;

   Parse_Begin ();

   EXPECT        /* Look for zero or one finish_id */
     CASE (FINISH_ID_TOKEN)
       Destroy_Finish(*Finish_Ptr);
       *Finish_Ptr = Copy_Finish ((FINISH *) Token.Constant_Data);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End finish_id */

   New = *Finish_Ptr;

   EXPECT        /* Look for zero or more finish_body */
     CASE (AMBIENT_TOKEN)
       New->Ambient = Parse_Float ();
     END_CASE

     CASE (BRILLIANCE_TOKEN)
       New->Brilliance = Parse_Float ();
     END_CASE

     CASE (DIFFUSE_TOKEN)
       New->Diffuse = Parse_Float ();
     END_CASE

     CASE (REFLECTION_TOKEN)
       New->Reflection = Parse_Float ();
     END_CASE

     CASE (REFRACTION_TOKEN)
       New->Refraction = Parse_Float ();
     END_CASE

     CASE (IOR_TOKEN)
       New->Index_Of_Refraction = Parse_Float ();
     END_CASE

     CASE (PHONG_TOKEN)
       New->Phong = Parse_Float ();
     END_CASE

     CASE (PHONG_SIZE_TOKEN)
       New->Phong_Size = Parse_Float ();
/*     if (New->Phong_Size < 1.0)
           New->Phong_Size = 1.0;
       if (New->Phong_Size > 100)
           New->Phong_Size = 100; */
     END_CASE

     CASE (SPECULAR_TOKEN)
       New->Specular = Parse_Float ();
     END_CASE

     CASE (ROUGHNESS_TOKEN)
       New->Roughness = Parse_Float ();
/*     if (New->Roughness > 1.0)
           New->Roughness = 1.0;
       if (New->Roughness < 0.001)
           New->Roughness = 0.001;  */
       New->Roughness = 1.0/New->Roughness; /* CEY 12/92 */
     END_CASE

     CASE (METALLIC_TOKEN)
       New->Metallic_Flag = TRUE;
     END_CASE

     CASE (CRAND_TOKEN)
       New->Crand = Parse_Float();
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End of finish_body */

   EXPECT        /* Look for finish_mods */

/*   CASE none implemented
     END_CASE     */

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT    /* End of finish_mods */

   Parse_End ();
  }

#define ADD_TNORMAL if (Tnormal == NULL) {if ((Default_Texture->Tnormal) != NULL) \
 Tnormal = Copy_Tnormal ((Default_Texture->Tnormal)); else Tnormal = Create_Tnormal ();\
 Texture->Tnormal=Tnormal;};

static
TEXTURE *Parse_Texture ()
  {
   VECTOR Local_Vector;
   TEXTURE *Texture, *Local_Texture;
   PIGMENT *Pigment;
   TNORMAL *Tnormal;
   FINISH *Finish;

   Parse_Begin ();

   EXPECT                      /* Look for texture_body */
     CASE (TILES_TOKEN)
       Parse_Begin ();

       Texture = (TEXTURE *)Create_Tiles_Texture ();

       EXPECT
         CASE (TEXTURE_TOKEN)
           Local_Texture = Parse_Texture ();
           Link_Textures(&(((TILES *)Texture)->Tile1),Local_Texture);
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT

       GET (TILE2_TOKEN);

       EXPECT
         CASE (TEXTURE_TOKEN)
           Local_Texture = Parse_Texture ();
           Link_Textures(&(((TILES *)Texture)->Tile2),Local_Texture);
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT
       Parse_End ();
       EXIT
     END_CASE

     CASE (MATERIAL_MAP_TOKEN)
       Parse_Begin ();

       Texture = (TEXTURE *)Create_Material_Texture ();

       ((MATERIAL *)Texture)->Image = Parse_Image(MATERIAL_FILE);
       ((MATERIAL *)Texture)->Image->Use_Colour_Flag = FALSE;

       EXPECT
         CASE (ONCE_TOKEN)
           ((MATERIAL *)Texture)->Image->Once_Flag=TRUE;
         END_CASE

         CASE (INTERPOLATE_TOKEN)
           ((MATERIAL *)Texture)->Image->Interpolation_Type=(int)Parse_Float();
         END_CASE

         CASE (MAP_TYPE_TOKEN)
           ((MATERIAL *)Texture)->Image->Map_Type = (int) Parse_Float ();
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT

       GET (TEXTURE_TOKEN)                /* First material */
       ((MATERIAL *)Texture)->Materials = Local_Texture = Parse_Texture ();
       ((MATERIAL *)Texture)->Num_Of_Mats++;

       EXPECT                             /* Subsequent materials */
         CASE (TEXTURE_TOKEN)
           Local_Texture->Next_Material = Parse_Texture ();
           Local_Texture = Local_Texture->Next_Material;
           ((MATERIAL *)Texture)->Num_Of_Mats++;
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT
       Parse_End ();
       EXIT
     END_CASE
     
     CASE (TEXTURE_ID_TOKEN)
       Texture = Copy_Textures((TEXTURE *) Token.Constant_Data);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       Texture = Copy_Textures (Default_Texture);
       EXIT
     END_CASE
   END_EXPECT
   
   /* Look for [pnf_texture] */
   if (Texture->Type == PNF_TEXTURE)
     {
       EXPECT   /* Look for [pnf_ids] */
         CASE (PIGMENT_ID_TOKEN)
           Destroy_Pigment(Texture->Pigment);
           Texture->Pigment = Copy_Pigment ((PIGMENT *) Token.Constant_Data);
         END_CASE

         CASE (TNORMAL_ID_TOKEN)
           Destroy_Tnormal(Texture->Tnormal);
           Texture->Tnormal = Copy_Tnormal ((TNORMAL *) Token.Constant_Data);
         END_CASE

         CASE (FINISH_ID_TOKEN)
           Destroy_Finish(Texture->Finish);
           Texture->Finish = Copy_Finish ((FINISH *) Token.Constant_Data);
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT

       Pigment = Texture->Pigment;
       Tnormal = Texture->Tnormal;
       Finish  = Texture->Finish;

       EXPECT
         CASE (PIGMENT_TOKEN)
           Parse_Pigment ( &(Texture->Pigment) );
         END_CASE

         CASE (TNORMAL_TOKEN)
           Parse_Tnormal ( &(Texture->Tnormal) );
         END_CASE

         CASE (FINISH_TOKEN)
           Parse_Finish ( &(Texture->Finish) );
         END_CASE

/***********************************************************************
 PIGMENT STUFF OUTSIDE PIGMENT{}
***********************************************************************/
         CASE (AGATE_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = AGATE_PIGMENT;
         END_CASE

         CASE (BOZO_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = BOZO_PIGMENT;
         END_CASE

         CASE (GRANITE_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = GRANITE_PIGMENT;
         END_CASE

         CASE (LEOPARD_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = LEOPARD_PIGMENT;
         END_CASE

         CASE (MARBLE_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = MARBLE_PIGMENT;
         END_CASE

         CASE (MANDEL_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = MANDEL_PIGMENT;
           Pigment->Iterations = (int)Parse_Float();
         END_CASE

         CASE (ONION_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = ONION_PIGMENT;
         END_CASE

         CASE (PAINTED1_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = PAINTED1_PIGMENT;
         END_CASE

         CASE (PAINTED2_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = PAINTED2_PIGMENT;
         END_CASE

         CASE (PAINTED3_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = PAINTED2_PIGMENT;
         END_CASE

         CASE (SPOTTED_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = SPOTTED_PIGMENT;
         END_CASE

         CASE (WOOD_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = WOOD_PIGMENT;
         END_CASE

         CASE (GRADIENT_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = GRADIENT_PIGMENT;
           Parse_Vector (&(Pigment->Colour_Gradient));
         END_CASE

         CASE (COLOUR_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = COLOUR_PIGMENT;
           Pigment->Colour1 = Create_Colour ();
           Parse_Colour (Pigment->Colour1);
           Pigment->Quick_Colour = *Pigment->Colour1;
         END_CASE

         CASE5 (COLOUR_ID_TOKEN, RGB_TOKEN, RGBF_TOKEN, RED_TOKEN, BLUE_TOKEN)
         CASE3 (GREEN_TOKEN, ALPHA_TOKEN, FILTER_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           UNGET
           Pigment->Type = COLOUR_PIGMENT;
           Pigment->Colour1 = Create_Colour ();
           Parse_Colour (Pigment->Colour1);
           Pigment->Quick_Colour = *Pigment->Colour1;
         END_CASE

         CASE (CHECKER_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = CHECKER_PIGMENT;
           Pigment->Colour_Map = Parse_Colour_List(2);
         END_CASE

         CASE (HEXAGON_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Pigment->Type = HEXAGON_PIGMENT;
           Pigment->Colour_Map = Parse_Colour_List(3);
         END_CASE

         CASE (IMAGE_MAP_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Parse_Image_Map (Pigment);
         END_CASE

         CASE (TURBULENCE_TOKEN)
           Parse_Vector_Float(&(Pigment->Turbulence));
           if ((Pigment->Turbulence.x !=0.0) ||
               (Pigment->Turbulence.y !=0.0) ||
               (Pigment->Turbulence.z !=0.0))
             Pigment->Flags |= HAS_TURB;
         END_CASE

         CASE (COLOUR_MAP_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);
           if (Pigment->Type == CHECKER_PIGMENT ||
               Pigment->Type == HEXAGON_PIGMENT ||
               Pigment->Type == COLOUR_PIGMENT ||
               Pigment->Type == IMAGE_MAP_PIGMENT)
             Warn ("Cannot use color map with this pigment type",1.5);
           Pigment->Colour_Map = Parse_Colour_Map ();
         END_CASE

         CASE (QUICK_COLOUR_TOKEN)
           Warn_State(Token.Token_Id, PIGMENT_TOKEN);           
           Parse_Colour (&Pigment->Quick_Colour);
         END_CASE

         CASE (OCTAVES_TOKEN)
           Pigment->Octaves = (int)Parse_Float();
             if(Pigment->Octaves < 1)
                Pigment->Octaves = 1;
             if(Pigment->Octaves > 10)  /* Avoid DOMAIN errors */
                Pigment->Octaves = 10;
         END_CASE

         CASE (OMEGA_TOKEN)
           Pigment->omega = Parse_Float();
         END_CASE

         CASE (LAMBDA_TOKEN)
           Pigment->lambda = Parse_Float();
         END_CASE

/***********************************************************************
TNORMAL STUFF OUTSIDE NORMAL{}
***********************************************************************/
         CASE (BUMPS_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = BUMPS;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (BUMPY1_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = BUMPY1;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (BUMPY2_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = BUMPY2;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (BUMPY3_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = BUMPY3;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (DENTS_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = DENTS;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (RIPPLES_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = RIPPLES;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (WAVES_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = WAVES;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (WRINKLES_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Tnormal->Type = WRINKLES;
           Tnormal->Amount = Parse_Float ();
         END_CASE

         CASE (BUMP_MAP_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           Parse_Bump_Map (Tnormal);
         END_CASE

         CASE (FREQUENCY_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           if (!(Tnormal->Type == RIPPLES || Tnormal->Type == WAVES))
             if (Language_Version >= 1.5)
               Warn ("Cannot use frequency with this normal",1.5);
           Tnormal->Frequency = Parse_Float();
         END_CASE

         CASE (PHASE_TOKEN)
           Warn_State(Token.Token_Id, TNORMAL_TOKEN);           
           ADD_TNORMAL
           if (!(Tnormal->Type == RIPPLES || Tnormal->Type == WAVES))
             if (Language_Version >= 1.5)
               Warn ("Cannot use phase with this normal",1.5);
           Tnormal->Phase = Parse_Float();
         END_CASE


/***********************************************************************
FINISH STUFF OUTSIDE FINISH{}
***********************************************************************/
         CASE (AMBIENT_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Ambient = Parse_Float ();
         END_CASE

         CASE (BRILLIANCE_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Brilliance = Parse_Float ();
         END_CASE

         CASE (DIFFUSE_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Diffuse = Parse_Float ();
         END_CASE

         CASE (REFLECTION_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Reflection = Parse_Float ();
         END_CASE

         CASE (REFRACTION_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Refraction = Parse_Float ();
         END_CASE

         CASE (IOR_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Index_Of_Refraction = Parse_Float ();
         END_CASE

         CASE (PHONG_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Phong = Parse_Float ();
         END_CASE

         CASE (PHONG_SIZE_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Phong_Size = Parse_Float ();
    /*     if (Finish->Phong_Size < 1.0)
               Finish->Phong_Size = 1.0;
           if (Finish->Phong_Size > 100)
               Finish->Phong_Size = 100; */
         END_CASE

         CASE (SPECULAR_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Specular = Parse_Float ();
         END_CASE

         CASE (ROUGHNESS_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Roughness = Parse_Float ();
    /*     if (Finish->Roughness > 1.0)
               Finish->Roughness = 1.0;
           if (Finish->Roughness < 0.001)
               Finish->Roughness = 0.001;  */
           Finish->Roughness = 1.0/Finish->Roughness; /* CEY 12/92 */
         END_CASE

         CASE (METALLIC_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Metallic_Flag = TRUE;
         END_CASE

         CASE (CRAND_TOKEN)
           Warn_State(Token.Token_Id, FINISH_TOKEN);           
           Finish->Crand = Parse_Float();
         END_CASE

         CASE_FLOAT
           Finish->Crand = Parse_Float();
           Warn("Should use crand keyword in finish statement.",1.5);           
         END_CASE

         CASE (TRANSLATE_TOKEN)
           Parse_Vector (&Local_Vector);
           Translate_Textures (Texture, &Local_Vector);
         END_CASE

         CASE (ROTATE_TOKEN)
           Parse_Vector (&Local_Vector);
           Rotate_Textures (Texture, &Local_Vector);
         END_CASE

         CASE (SCALE_TOKEN)
           Parse_Scale_Vector (&Local_Vector);
           Scale_Textures (Texture, &Local_Vector);
         END_CASE

         CASE (TRANSFORM_TOKEN)
           GET(TRANSFORM_ID_TOKEN)
           Transform_Textures (Texture, (TRANSFORM *)Token.Constant_Data);
         END_CASE

         CASE (TEXTURE_ID_TOKEN)
           Warn("Texture identifier overwriting previous values.",0);
           Destroy_Textures(Texture);
           Texture = Copy_Textures((TEXTURE *) Token.Constant_Data);
           Pigment = Texture->Pigment;
           Tnormal = Texture->Tnormal;
           Finish  = Texture->Finish;
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
/***********************************************************************/

       END_EXPECT

       if (Not_In_Default && (Texture->Pigment->Type == NO_PIGMENT) &&
           !(Language_Version < 1.5))
         Parse_Error(PIGMENT_ID_TOKEN);

     }

   EXPECT            /* Look for texture_mods */
     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Translate_Textures (Texture, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Rotate_Textures (Texture, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Scale_Textures (Texture, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       Transform_Textures (Texture, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT        /* End of texture */

   Parse_End ();
   return (Texture);
  }

static
OBJECT *Parse_Bound_Clip ()
  {
   VECTOR Local_Vector;
   OBJECT *First, *Current, *Prev;

   First = Prev = NULL;

   while ((Current = Parse_Object ()) != NULL)
     {
      if (Current->Type & (TEXTURED_OBJECT+PATCH_OBJECT))
        Error ("Illegal texture or patch in clip or bound");
      if (First == NULL)
        First = Current;
      if (Prev != NULL)
        Prev->Sibling = Current;
      Prev = Current;
     }

   EXPECT
     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       for (Current = First;
            Current != NULL;
            Current = Current->Sibling)
         Translate_Object (Current, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       for (Current = First;
            Current != NULL;
            Current = Current->Sibling)
         Rotate_Object (Current, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       for (Current = First;
            Current != NULL;
            Current = Current->Sibling)
         Scale_Object (Current, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       for (Current = First;
            Current != NULL;
            Current = Current->Sibling)
       Transform_Object (Current, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   return (First);
  }

static void Parse_Object_Mods (Object)
  OBJECT *Object;
  {
   VECTOR Local_Vector;
   TEXTURE *Local_Texture;
   OBJECT *Temp1_Object;
   OBJECT *Temp2_Object;
   COLOUR Local_Colour;
   DBL Temp_Water_Level;

   EXPECT
     CASE (COLOUR_TOKEN)
       Parse_Colour (&Local_Colour);
       if (Language_Version < 1.5)
         if (Object->Texture != NULL) 
           if (Object->Texture->Type == PNF_TEXTURE)
             {
              Object->Texture->Pigment->Quick_Colour = Local_Colour;
              break;  /* acts like END_CASE */
             }
       Warn("Quick color belongs in texture.  Color ignored.",0.0);
     END_CASE

     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Translate_Object (Object, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Rotate_Object (Object, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Scale_Object (Object, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       Transform_Object (Object, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     CASE (BOUNDED_BY_TOKEN)
       Parse_Begin ();
       if (Object->Bound != NULL)
         if (Object->Clip == Object->Bound)
           Error ("Cannot add bounds after linking bounds and clips");

       EXPECT
         CASE (CLIPPED_BY_TOKEN)
           if (Object->Bound != NULL)
             Error ("Cannot link clips with previous bounds");
           Object->Bound = Object->Clip;
           EXIT
         END_CASE

         OTHERWISE
           UNGET
           Temp1_Object = Temp2_Object = Parse_Bound_Clip ();
           while (Temp2_Object->Sibling != NULL)
             Temp2_Object = Temp2_Object->Sibling;
           Temp2_Object->Sibling = Object->Bound;
           Object->Bound = Temp1_Object;
           EXIT
         END_CASE
       END_EXPECT
       
       Parse_End ();
     END_CASE

     CASE (CLIPPED_BY_TOKEN)
       Parse_Begin ();
       if (Object->Clip != NULL)
         if (Object->Clip == Object->Bound)
           Error ("Cannot add clips after linking bounds and clips");

       EXPECT
         CASE (BOUNDED_BY_TOKEN)
           if (Object->Clip != NULL)
             Error ("Cannot link bounds with previous clips");
           Object->Clip = Object->Bound;
           EXIT
         END_CASE

         OTHERWISE
           UNGET
           Temp1_Object = Temp2_Object = Parse_Bound_Clip ();
           while (Temp2_Object->Sibling != NULL)
             Temp2_Object = Temp2_Object->Sibling;
           Temp2_Object->Sibling = Object->Clip;
           Object->Clip = Temp1_Object;
           EXIT
         END_CASE
       END_EXPECT

       Parse_End ();
     END_CASE

     CASE (TEXTURE_TOKEN)
       Object->Type |= TEXTURED_OBJECT;
       Local_Texture = Parse_Texture ();
       Link_Textures(&(Object->Texture), Local_Texture);
     END_CASE

     CASE3 (PIGMENT_TOKEN, TNORMAL_TOKEN, FINISH_TOKEN)
       Object->Type |= TEXTURED_OBJECT;
       if (Object->Texture == NULL)
         Object->Texture = Copy_Textures(Default_Texture);
       else
         if (Object->Texture->Type != PNF_TEXTURE)
           Link_Textures(&(Object->Texture), Copy_Textures(Default_Texture));
       UNGET
       EXPECT
         CASE (PIGMENT_TOKEN)
           Parse_Pigment ( &(Object->Texture->Pigment) );
         END_CASE

         CASE (TNORMAL_TOKEN)
           Parse_Tnormal ( &(Object->Texture->Tnormal) );
         END_CASE

         CASE (FINISH_TOKEN)
           Parse_Finish ( &(Object->Texture->Finish) );
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT
     END_CASE

     CASE (INVERSE_TOKEN)
       if (Object->Type & PATCH_OBJECT)
         Warn ("Cannot invert a patch object",0.0);
       Invert_Object (Object);
     END_CASE

     CASE (STURM_TOKEN)
       if (!(Object->Type & STURM_OK_OBJECT))
         Error ("Cannot use STRUM here");
       ((POLY *) Object)->Sturm_Flag = TRUE;
     END_CASE

     CASE (WATER_LEVEL_TOKEN)
       if (!(Object->Type & WATER_LEVEL_OK_OBJECT))
         Error ("Cannot use WATER_LEVEL here");
       Temp_Water_Level = Parse_Float();
       if (Language_Version < 2.0)
         Temp_Water_Level /=256.0;
       ((HEIGHT_FIELD *) Object)->bounding_box->bounds[0].y = 65536.0 * Temp_Water_Level;
     END_CASE

     CASE (SMOOTH_TOKEN)
       if (!(Object->Type & SMOOTH_OK_OBJECT))
         Error ("Cannot use SMOOTH here");
       ((HEIGHT_FIELD *) Object)->Smoothed = TRUE;
       Object->Type |= DOUBLE_ILLUMINATE;
     END_CASE

     CASE (NO_SHADOW_TOKEN)
       Object->No_Shadow_Flag = TRUE;
     END_CASE

     CASE (LIGHT_SOURCE_TOKEN)
       Error("Light source must be defined using new syntax");
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   if (Object->Bound != NULL)
     {
      Object->Bounds.Lower_Left = Object->Bound->Bounds.Lower_Left;
      Object->Bounds.Lengths    = Object->Bound->Bounds.Lengths;
     }
   Parse_End ();
  }

static
OBJECT *Parse_Sphere ()
  {
   SPHERE *Object;

   Parse_Begin ();

   if ( (Object = (SPHERE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Sphere();

   Parse_Vector(&(Object -> Center));   Parse_Comma();
   Object -> Radius = Parse_Float();
   Object -> Radius_Squared = Object -> Radius * Object -> Radius;
   Object -> Inverse_Radius = 1.0 / Object -> Radius;

   Make_Vector(&Object->Bounds.Lower_Left,
	       Object->Center.x - Object->Radius,
	       Object->Center.y - Object->Radius,
	       Object->Center.z - Object->Radius);
   Make_Vector(&Object->Bounds.Lengths,
	       2.0 * Object->Radius,
	       2.0 * Object->Radius,
	       2.0 * Object->Radius);

   Parse_Object_Mods ((OBJECT *) Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Plane ()
  {
   PLANE *Object;

   Parse_Begin ();

   if ( (Object = (PLANE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Plane();

   Parse_Vector(&(Object -> Normal_Vector));   Parse_Comma();
   VNormalize(Object->Normal_Vector, Object->Normal_Vector);
   Object->Distance = -Parse_Float();

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Height_Field ()
  {
   HEIGHT_FIELD *Object;
   VECTOR Local_Vector;
   IMAGE *Image;

   Parse_Begin ();

   if ( (Object = (HEIGHT_FIELD *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Height_Field();

   Image = Parse_Image (HF_FILE);
   Image->Use_Colour_Flag = FALSE;

   Object->bounding_box->bounds[0].x = 1.0;
   Object->bounding_box->bounds[0].y = 0.0;
   Object->bounding_box->bounds[0].z = 1.0;
   if (Image->File_Type == POT_FILE)
     {
      Object->bounding_box->bounds[1].x = Image -> width/2.0 - 2.0;
      Make_Vector(&Local_Vector,2.0/Image->width,1.0/65536.0,1.0/Image->height);
     }
   else
     {
      Object->bounding_box->bounds[1].x = Image -> width - 2.0;
      Make_Vector(&Local_Vector,1.0/(Image->width),1.0/65536.0,1.0/(Image->height));
     }
   Object->bounding_box->bounds[1].y = 65536.0;
   Object->bounding_box->bounds[1].z = Image -> height - 2.0;
   Compute_Scaling_Transform(Object->Trans,&Local_Vector);

   Parse_Object_Mods ((OBJECT *)Object);

   Find_Hf_Min_Max(Object, Image);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Triangle ()
  {
   TRIANGLE *Object;

   Parse_Begin ();

   if ( (Object = (TRIANGLE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Triangle();

   Parse_Vector (&Object->P1);    Parse_Comma();
   Parse_Vector (&Object->P2);    Parse_Comma();
   Parse_Vector (&Object->P3);
   if (!Compute_Triangle (Object,FALSE))
     fprintf (stdout, "Degenerate triangle on line %d.  Please remove.\n",
              Token.Token_Line_No+1);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Smooth_Triangle ()
  {
   SMOOTH_TRIANGLE *Object;
   short degen;                                                   /* LSK */
   DBL vlen;                                                      /* LSK */

   degen=FALSE;

   Parse_Begin ();

   if ( (Object = (SMOOTH_TRIANGLE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);

   Object = Create_Smooth_Triangle();

   Parse_Vector (&Object->P1);    Parse_Comma();
   Parse_Vector (&Object->N1);    Parse_Comma();

   VLength(vlen,Object->N1);                                     /* LSK */
   if (vlen<1E-09)                                               /* LSK */
     degen=TRUE;                                                 /* LSK */
   else                                                          /* LSK */
     VNormalize (Object->N1, Object->N1);

   Parse_Vector (&Object->P2);    Parse_Comma();
   Parse_Vector (&Object->N2);    Parse_Comma();

   VLength(vlen,Object->N2);                                     /* LSK */
   if (vlen<1E-09)                                               /* LSK */
     degen=TRUE;                                                 /* LSK */
   else                                                          /* LSK */
     VNormalize (Object->N2, Object->N2);

   Parse_Vector (&Object->P3);    Parse_Comma();
   Parse_Vector (&Object->N3);

   VLength(vlen,Object->N3);                                     /* LSK */
   if (vlen<1E-09)                                               /* LSK */
     degen=TRUE;                                                 /* LSK */
   else                                                          /* LSK */
     VNormalize (Object->N3, Object->N3);

   if (!degen) {                                                 /* LSK */
     degen=!Compute_Triangle ((TRIANGLE *) Object,TRUE);         /* LSK */
   }

   if (degen)                                                    /* LSK */
     fprintf (stdout, "Degenerate triangle on line %d.  Please remove.\n",
              Token.Token_Line_No+1);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
 }

static
OBJECT *Parse_Quadric ()
  {
   QUADRIC *Object;

   Parse_Begin ();

   if ( (Object = (QUADRIC *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Quadric();

   Parse_Vector(&(Object -> Square_Terms));     Parse_Comma();
   Parse_Vector(&(Object -> Mixed_Terms));      Parse_Comma();
   Parse_Vector(&(Object -> Terms));            Parse_Comma();
   Object -> Constant = Parse_Float();
   Object -> Non_Zero_Square_Term =
     !( (Object -> Square_Terms.x == 0.0)
     && (Object -> Square_Terms.y == 0.0)
     && (Object -> Square_Terms.z == 0.0)
     && (Object -> Mixed_Terms.x == 0.0)
     && (Object -> Mixed_Terms.y == 0.0)
     && (Object -> Mixed_Terms.z == 0.0));

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Box ()
  {
   BOX *Object;
   DBL temp;

   Parse_Begin ();

   if ( (Object = (BOX *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Box();

   Parse_Vector(&(Object->bounds[0]));     Parse_Comma();
   Parse_Vector(&(Object->bounds[1]));

    if (Object->bounds[0].x > Object->bounds[1].x) {
       temp = Object->bounds[0].x;
       Object->bounds[0].x = Object->bounds[1].x;
       Object->bounds[1].x = temp;
       }
    if (Object->bounds[0].y > Object->bounds[1].y) {
       temp = Object->bounds[0].y;
       Object->bounds[0].y = Object->bounds[1].y;
       Object->bounds[1].y = temp;
       }
    if (Object->bounds[0].z > Object->bounds[1].z) {
       temp = Object->bounds[0].z;
       Object->bounds[0].z = Object->bounds[1].z;
       Object->bounds[1].z = temp;
       }

   Object->Bounds.Lower_Left = Object->bounds[0];
   VSub(Object->Bounds.Lengths, Object->bounds[1],Object->bounds[0]);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

 

static
OBJECT *Parse_Disc ()
  {
   DISC *Object;
   DBL tmpf;
   VECTOR lengths;

   Parse_Begin ();

   if ( (Object = (DISC *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Disc();

   Parse_Vector(&(Object->center)); Parse_Comma ();
   Parse_Vector(&(Object->normal)); Parse_Comma ();
   VNormalize(Object->normal, Object->normal);

   tmpf = Parse_Float(); Parse_Comma ();
   Object->oradius2 = tmpf * tmpf;

   EXPECT
     CASE_FLOAT
       tmpf = Parse_Float();
       Object->iradius2 = tmpf * tmpf;
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   /* Calculate info needed for ray-disc intersections */
   VDot(tmpf, Object->center, Object->normal);
   Object->d = -tmpf;

   /* Calculate the bounds */
   tmpf = sqrt(Object->oradius2);
   Make_Vector(&lengths, tmpf, tmpf, tmpf);
   VSub(Object->Bounds.Lower_Left, Object->center, lengths);
   VScale(Object->Bounds.Lengths, lengths, 2.0);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Cylinder ()
  {
   CONE *Object;

   Parse_Begin ();

   if ( (Object = (CONE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Cylinder();

   Parse_Vector(&(Object->apex));  Parse_Comma ();
   Parse_Vector(&(Object->base));  Parse_Comma ();
   Object->apex_radius = Parse_Float();
   Object->base_radius = Object->apex_radius;

   EXPECT
     CASE(OPEN_TOKEN)
       Object->closed = 0;
       EXIT
     END_CASE
     
     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   Compute_Cylinder_Data((OBJECT *)Object);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Cone ()
  {
   CONE *Object;

   Parse_Begin ();

   if ( (Object = (CONE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Cone();

   Parse_Vector(&(Object->apex));  Parse_Comma ();
   Object->apex_radius = Parse_Float();  Parse_Comma ();

   Parse_Vector(&(Object->base));  Parse_Comma ();
   Object->base_radius = Parse_Float();
   
   EXPECT
     CASE(OPEN_TOKEN)
       Object->closed = 0;
       EXIT
     END_CASE
     
     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   /* Compute run-time values for the cone */
   Compute_Cone_Data((OBJECT *)Object);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Blob ()
  {
   BLOB *Object;
   DBL threshold;
   int npoints;
   blobstackptr blob_components, blob_component;

   Parse_Begin ();

   if ( (Object = (BLOB *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Blob();

   blob_components = NULL;
   npoints = 0;
   threshold = 1.0;

   EXPECT
     CASE (THRESHOLD_TOKEN)
       threshold = Parse_Float();
     END_CASE

     CASE (COMPONENT_TOKEN)
       blob_component = (blobstackptr) malloc(sizeof(struct blob_list_struct));
       if (blob_component == NULL)
          MAError("blob component");
       blob_component->elem.coeffs[2] = Parse_Float(); Parse_Comma();
       blob_component->elem.radius2   = Parse_Float(); Parse_Comma();
       Parse_Vector(&blob_component->elem.pos);
       blob_component->next = blob_components;
       blob_components = blob_component;
       npoints++;
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   /* Finally, process the information */
   MakeBlob(Object, threshold, blob_components, npoints, 0);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Torus ()
  {
   POLY *Object;
   DBL iradius, oradius, *Coeffs;

   Parse_Begin ();

   if ( (Object = (POLY *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Poly(4);

   /* Read in the two radii */
   iradius = Parse_Float(); /* Big radius */
   Parse_Comma();
   oradius = Parse_Float(); /* Little radius */

   /* Build the coefficients of a torus lying in the x-z plane */
   Coeffs = Object->Coeffs;
   Coeffs[ 0] =  1.0;
   Coeffs[ 4] =  2.0;
   Coeffs[ 7] =  2.0;
   Coeffs[ 9] = -2.0 * (iradius * iradius + oradius * oradius);
   Coeffs[20] =  1.0;
   Coeffs[23] =  2.0;
   Coeffs[25] =  2.0 * (iradius * iradius - oradius * oradius);
   Coeffs[30] =  1.0;
   Coeffs[32] = -2.0 * (iradius * iradius + oradius * oradius);
   Coeffs[34] = (iradius * iradius - oradius * oradius) *
	        (iradius * iradius - oradius * oradius);

   Make_Vector(&Object->Bounds.Lower_Left, -(iradius + oradius),
	       -iradius, -(iradius + oradius))
   Make_Vector(&Object->Bounds.Lengths, 2.0 * (iradius + oradius),
	       2.0 * iradius, 2.0 * (iradius + oradius));

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Poly (order)
  int order;
  {
   POLY *Object;

   Parse_Begin ();

   if ( (Object = (POLY *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   if (order == 0)
     {
      order = (int)Parse_Float();      Parse_Comma();
      if (order < 2 || order > MAX_ORDER)
        Error("Order of poly is out of range");
     }

   Object = Create_Poly(order);

   Parse_Coeffs(Object->Order, &(Object->Coeffs[0]));

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Bicubic_Patch ()
  {
   BICUBIC_PATCH *Object;
   int i, j;

   Parse_Begin ();

   if ( (Object = (BICUBIC_PATCH *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Bicubic_Patch();

   EXPECT
     CASE_FLOAT
       Warn("Should use keywords for bicubic parameters.",1.5);
       Object->Patch_Type = (int)Parse_Float();
       if (Object->Patch_Type == 2 ||
           Object->Patch_Type == 3)
           Object->Flatness_Value = Parse_Float();
         else
           Object->Flatness_Value = 0.1;
       Object->U_Steps = (int)Parse_Float();
       Object->V_Steps = (int)Parse_Float();
       EXIT
     END_CASE       
       
     CASE (TYPE_TOKEN)
       Object->Patch_Type = (int)Parse_Float();
     END_CASE

     CASE (FLATNESS_TOKEN)
       Object->Flatness_Value = Parse_Float();
     END_CASE

     CASE (V_STEPS_TOKEN)
       Object->V_Steps = (int)Parse_Float();
     END_CASE

     CASE (U_STEPS_TOKEN)
       Object->U_Steps = (int)Parse_Float();
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   if (Object->Patch_Type > 1)
     {
      Object->Patch_Type = 1;
      Warn("Patch type no longer supported. Using type 1.",0.0);
     }

   if ((Object->Patch_Type < 0) || (Object->Patch_Type > MAX_PATCH_TYPE))
     Error("Undefined bicubic patch type");

   Parse_Comma();
   for (i=0;i<4;i++)
     for (j=0;j<4;j++)
       {
        Parse_Vector(&(Object -> Control_Points[i][j]));
        if (!((i==3)&&(j==3)))
          Parse_Comma();
       };
   Precompute_Patch_Values(Object); /* interpolated mesh coords */

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_CSG (CSG_Type)
  int CSG_Type;
  {
   CSG *Object;
   OBJECT *Local;
   int Object_Count = 0;

   Parse_Begin ();

   if ( (Object = (CSG *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   if (CSG_Type & CSG_UNION_TYPE)
     Object = Create_CSG_Union ();
   else
     if (CSG_Type & CSG_MERGE_TYPE)
       Object = Create_CSG_Merge ();
     else
       Object = Create_CSG_Intersection ();

   Object->Children = NULL;

   while ((Local = Parse_Object ()) != NULL)
     {
      if ((CSG_Type & CSG_INTERSECTION_TYPE) && (Local->Type & PATCH_OBJECT))
        Warn ("Patch objects not allowed in intersection",0.0);
      Object_Count++;
      if ((CSG_Type & CSG_DIFFERENCE_TYPE) && (Object_Count > 1))
        Invert_Object (Local);
      Object->Type |=  (Local->Type & CHILDREN_FLAGS);
      Local->Type |= IS_CHILD_OBJECT;
      Link(Local, &Local->Sibling, &Object->Children);
     };

   if ((Object_Count < 2) && (Language_Version >= 1.5))
     Warn ("Should have at least 2 objects in csg",1.5);
   Compute_CSG_Bounds((OBJECT *)Object);

   Parse_Object_Mods ((OBJECT *)Object);

   return ((OBJECT *) Object);
  }

static
OBJECT *Parse_Light_Source ()
  {
   VECTOR Local_Vector;
   LIGHT_SOURCE *Object;

   Parse_Begin ();

   if ( (Object = (LIGHT_SOURCE *)Parse_Object_Id()) != NULL)
      return ((OBJECT *) Object);
      
   Object = Create_Light_Source ();

   Parse_Vector(&Object->Center);

   GET (COLOUR_TOKEN)

   Parse_Colour (&Object->Colour);

   EXPECT
     CASE (LOOKS_LIKE_TOKEN)
       if (Object->Children != NULL)
         Error("Only one looks_like allowed per light_source");
       Parse_Begin ();
       Object->Type &= ~(int)PATCH_OBJECT;
       if ((Object->Children = Parse_Object ()) == NULL)
         Parse_Error_Str ("object");
       Translate_Object (Object->Children, &Object->Center);
       Parse_Object_Mods (Object->Children);
       Object->Children->No_Shadow_Flag = TRUE;
       Object->No_Shadow_Flag = TRUE;
       Object->Type |= (Object->Children->Type & CHILDREN_FLAGS);
     END_CASE

     CASE (SPOTLIGHT_TOKEN)
       Object->Light_Type = SPOT_SOURCE;
     END_CASE

     CASE (POINT_AT_TOKEN)
       if (Object->Light_Type == SPOT_SOURCE)
         Parse_Vector(&Object->Points_At);
       else
         Error("Spotlight param illegal in standard light source");
     END_CASE

     CASE (TIGHTNESS_TOKEN)
       if (Object->Light_Type == SPOT_SOURCE)
         Object->Coeff = Parse_Float();
       else
         Error("Spotlight param illegal in standard light source");
     END_CASE

     CASE (RADIUS_TOKEN)
       if (Object->Light_Type == SPOT_SOURCE)
         Object->Radius = cos(Parse_Float() * M_PI / 180.0);
       else
         Error("Spotlight param illegal in standard light source");
     END_CASE

     CASE (FALLOFF_TOKEN)
       if (Object->Light_Type == SPOT_SOURCE)
         Object->Falloff = cos(Parse_Float() * M_PI / 180.0);
       else
         Error("Spotlight param illegal in standard light source");
     END_CASE

     CASE (AREA_LIGHT_TOKEN)
       Object -> Area_Light = TRUE;
       Parse_Vector (&(Object -> Axis1)); Parse_Comma ();
       Parse_Vector (&(Object -> Axis2)); Parse_Comma ();
       Object -> Area_Size1 = (int)Parse_Float(); Parse_Comma ();
       Object -> Area_Size2 = (int)Parse_Float();
       Object -> Light_Grid = Create_Light_Grid (Object -> Area_Size1,
            Object -> Area_Size2);
     END_CASE

     CASE (JITTER_TOKEN)
       Object -> Jitter = TRUE;
     END_CASE

     CASE (TRACK_TOKEN)
       Object -> Track = TRUE;
     END_CASE

     CASE (ADAPTIVE_TOKEN)
       Object -> Adaptive_Level = (int)Parse_Float();
     END_CASE

     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Translate_Object ((OBJECT *)Object, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Rotate_Object ((OBJECT *)Object, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Scale_Object ((OBJECT *)Object, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       Transform_Object ((OBJECT *)Object, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   Parse_End ();

   return ((OBJECT *)Object);
  }


static
OBJECT *Parse_Object ()
  {
   OBJECT *Object = NULL;

   EXPECT
     CASE (SPHERE_TOKEN)
       Object = Parse_Sphere ();
       EXIT
     END_CASE

     CASE (PLANE_TOKEN)
       Object = Parse_Plane ();
       EXIT
     END_CASE

     CASE (CONE_TOKEN)
       Object = Parse_Cone ();
       EXIT
     END_CASE

     CASE (CYLINDER_TOKEN)
       Object = Parse_Cylinder ();
       EXIT
     END_CASE

     CASE (DISC_TOKEN)
       Object = Parse_Disc ();
       EXIT
     END_CASE

     CASE (QUADRIC_TOKEN)
       Object = Parse_Quadric ();
       EXIT
     END_CASE

     CASE (CUBIC_TOKEN)
       Object = Parse_Poly (3);
       EXIT
     END_CASE

     CASE (QUARTIC_TOKEN)
       Object = Parse_Poly (4);
       EXIT
     END_CASE

     CASE (POLY_TOKEN)
       Object = Parse_Poly (0);
       EXIT
     END_CASE

     CASE (TORUS_TOKEN)
       Object = Parse_Torus ();
       EXIT
     END_CASE

     CASE (OBJECT_ID_TOKEN)
       Object = Copy_Object((OBJECT *) Token.Constant_Data);
       EXIT
     END_CASE

     CASE (UNION_TOKEN)
       Object = Parse_CSG (CSG_UNION_TYPE);
       EXIT
     END_CASE

     CASE (COMPOSITE_TOKEN)
       Warn("Use union instead of composite",1.5);
       Object = Parse_CSG (CSG_UNION_TYPE);
       EXIT
     END_CASE

     CASE (MERGE_TOKEN)
       Object = Parse_CSG (CSG_MERGE_TYPE);
       EXIT
     END_CASE

     CASE (INTERSECTION_TOKEN)
       Object = Parse_CSG (CSG_INTERSECTION_TYPE);
       EXIT
     END_CASE

     CASE (DIFFERENCE_TOKEN)
       Object = Parse_CSG (CSG_DIFFERENCE_TYPE+CSG_INTERSECTION_TYPE);
       EXIT
     END_CASE

     CASE (BICUBIC_PATCH_TOKEN)
       Object = Parse_Bicubic_Patch ();
       EXIT
     END_CASE

     CASE (TRIANGLE_TOKEN)
       Object = Parse_Triangle ();
       EXIT
     END_CASE

     CASE (SMOOTH_TRIANGLE_TOKEN)
       Object = Parse_Smooth_Triangle ();
       EXIT
     END_CASE

     CASE (HEIGHT_FIELD_TOKEN)
       Object = Parse_Height_Field ();
       EXIT
     END_CASE

     CASE (BOX_TOKEN)
       Object = Parse_Box ();
       EXIT
     END_CASE

     CASE (BLOB_TOKEN)
       Object = Parse_Blob ();
       EXIT
     END_CASE

     CASE (LIGHT_SOURCE_TOKEN)
       Object = Parse_Light_Source ();
       EXIT
     END_CASE

     CASE (OBJECT_TOKEN)
       Parse_Begin ();
       Object = Parse_Object ();
       if (!Object)
         Parse_Error_Str ("object");
       Parse_Object_Mods ((OBJECT *)Object);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   return ((OBJECT *) Object);
  }

static void Parse_Fog ()
  {
   Parse_Begin ();

   EXPECT
     CASE (COLOUR_TOKEN)
       Parse_Colour (&Frame.Fog_Colour);
     END_CASE

     CASE (DISTANCE_TOKEN)
       Frame.Fog_Distance = Parse_Float ();
     END_CASE

     CASE_FLOAT
       Warn("Should use distance keyword.",1.5);
       Frame.Fog_Distance = Parse_Float ();
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT
   Parse_End ();
  }

static void Parse_Frame ()
  {
   OBJECT *Object;
   TEXTURE *Local_Texture;
   PIGMENT *Local_Pigment;
   TNORMAL *Local_Tnormal;
   FINISH  *Local_Finish;

   EXPECT
     CASE (FOG_TOKEN)
       Parse_Fog();
     END_CASE

     CASE (BACKGROUND_TOKEN)
       Parse_Begin();
       GET (COLOUR_TOKEN)
       Parse_Colour (&Frame.Background_Colour);
       Parse_End();
     END_CASE

     CASE (CAMERA_TOKEN)
       Parse_Camera (&Frame.Camera);
     END_CASE

     CASE (DECLARE_TOKEN)
       Parse_Declare ();
     END_CASE

     CASE (MAX_TRACE_LEVEL_TOKEN)
       Max_Trace_Level = Parse_Float ();
     END_CASE

     CASE (VERSION_TOKEN)
       Language_Version = Parse_Float ();
     END_CASE

     CASE (MAX_INTERSECTIONS)
       Max_Intersections = (int)Parse_Float ();
     END_CASE

     CASE (DEFAULT_TOKEN)
       Not_In_Default = FALSE;
       Parse_Begin();
       EXPECT
         CASE (TEXTURE_TOKEN)
           Local_Texture = Default_Texture;
           Default_Texture = Parse_Texture();
           if (Default_Texture->Type != PNF_TEXTURE)
             Error("Default texture cannot be material map or tiles");
           if (Default_Texture->Next_Layer != NULL)
             Error("Default texture cannot be layered");
           Destroy_Textures(Local_Texture);
         END_CASE

         CASE (PIGMENT_TOKEN)
           Local_Pigment = Copy_Pigment((Default_Texture->Pigment));
           Parse_Pigment (&Local_Pigment);
           Destroy_Pigment(Default_Texture->Pigment);
           Default_Texture->Pigment = Local_Pigment;
         END_CASE

         CASE (TNORMAL_TOKEN)
           Local_Tnormal = Copy_Tnormal((Default_Texture->Tnormal));
           Parse_Tnormal (&Local_Tnormal);
           Destroy_Tnormal(Default_Texture->Tnormal);
           Default_Texture->Tnormal = Local_Tnormal;
         END_CASE

         CASE (FINISH_TOKEN)
           Local_Finish = Copy_Finish((Default_Texture->Finish));
           Parse_Finish (&Local_Finish);
           Destroy_Finish(Default_Texture->Finish);
           Default_Texture->Finish = Local_Finish;
         END_CASE

         CASE (CAMERA_TOKEN)
           Parse_Camera (&Default_Camera);
         END_CASE

         OTHERWISE
           UNGET
           EXIT
         END_CASE
       END_EXPECT
       Parse_End();
       Not_In_Default = TRUE;
     END_CASE

     CASE (END_OF_FILE_TOKEN)
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       Object = Parse_Object();
       if (Object == NULL)
         Parse_Error_Str ("object or directive");
       Post_Process (Object, NULL);
       Link_To_Frame (Object);
     END_CASE
   END_EXPECT
  }

static void Parse_Camera (Camera_Ptr)
  CAMERA **Camera_Ptr;
  {
   VECTOR Local_Vector, Temp_Vector;
   DBL Direction_Length, Up_Length, Right_Length, Handedness;
   CAMERA *New;

   Parse_Begin ();

   EXPECT
     CASE (CAMERA_ID_TOKEN)
       Destroy_Camera(*Camera_Ptr);
       *Camera_Ptr = Copy_Camera ((CAMERA *) Token.Constant_Data);
       EXIT
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   New = *Camera_Ptr;

   EXPECT
     CASE (LOCATION_TOKEN)
       Parse_Vector(&(New->Location));
     END_CASE

     CASE (DIRECTION_TOKEN)
       Parse_Vector(&(New->Direction));
     END_CASE

     CASE (UP_TOKEN)
       Parse_Vector(&(New->Up));
     END_CASE

     CASE (RIGHT_TOKEN)
       Parse_Vector(&(New->Right));
     END_CASE

     CASE (SKY_TOKEN)
       Parse_Vector(&(New->Sky));
     END_CASE

     CASE (LOOK_AT_TOKEN)
       VLength (Direction_Length, New->Direction);
       VLength (Up_Length,        New->Up);
       VLength (Right_Length,     New->Right);
       VCross  (Temp_Vector,      New->Direction, New->Up);
       VDot    (Handedness,       Temp_Vector,   New->Right);

       Parse_Vector (&New->Direction);

       VSub       (New->Direction, New->Direction, New->Location);
       VNormalize (New->Direction, New->Direction);
       VCross     (New->Right,     New->Direction, New->Sky);
       VNormalize (New->Right,     New->Right);
       VCross     (New->Up,        New->Right,     New->Direction);
       VScale     (New->Direction, New->Direction, Direction_Length);

       if (Handedness >= 0.0)
         VScale (New->Right, New->Right, Right_Length)
       else
         VScale (New->Right, New->Right, -Right_Length);

       VScale (New->Up, New->Up, Up_Length);
     END_CASE

     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Translate_Camera (New, &Local_Vector);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Rotate_Camera (New, &Local_Vector);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Scale_Camera (New, &Local_Vector);
     END_CASE

     CASE (TRANSFORM_TOKEN)
       GET(TRANSFORM_ID_TOKEN)
       Transform_Camera (New, (TRANSFORM *)Token.Constant_Data);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT
   Parse_End ();
  }

static
TRANSFORM *Parse_Transform ()
  {
   TRANSFORM *New, Local_Trans;
   VECTOR Local_Vector;

   Parse_Begin ();
   New = Create_Transform ();

   EXPECT
     CASE(TRANSFORM_ID_TOKEN)
       Compose_Transforms (New, (TRANSFORM *)Token.Constant_Data);
       EXIT
     END_CASE

     CASE (TRANSLATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Compute_Translation_Transform(&Local_Trans, &Local_Vector);
       Compose_Transforms (New, &Local_Trans);
     END_CASE

     CASE (ROTATE_TOKEN)
       Parse_Vector (&Local_Vector);
       Compute_Rotation_Transform(&Local_Trans, &Local_Vector);
       Compose_Transforms (New, &Local_Trans);
     END_CASE

     CASE (SCALE_TOKEN)
       Parse_Scale_Vector (&Local_Vector);
       Compute_Scaling_Transform(&Local_Trans, &Local_Vector);
       Compose_Transforms (New, &Local_Trans);
     END_CASE

     OTHERWISE
       UNGET
       EXIT
     END_CASE
   END_EXPECT

   Parse_End ();
   return (New);
  }

static void Parse_Declare ()
  {
  VECTOR Local_Vector;
  COLOUR *Local_Colour;
  PIGMENT *Local_Pigment;
  TNORMAL *Local_Tnormal;
  FINISH *Local_Finish;
  TEXTURE *Local_Texture;
  COLOUR_MAP *Local_Colour_Map;
  TRANSFORM *Local_Trans;
  OBJECT *Local_Object;
  CAMERA *Local_Camera;

  struct Constant_Struct *Constant_Ptr;

  EXPECT
    CASE (IDENTIFIER_TOKEN)
      if (++Number_Of_Constants >= MAX_CONSTANTS)
        Error ("Too many constants \"DECLARED\"");
      else
        Constant_Ptr = &(Constants[Number_Of_Constants]);
      EXIT
    END_CASE

    CASE4 (COLOUR_ID_TOKEN, VECTOR_ID_TOKEN, FLOAT_ID_TOKEN, PIGMENT_ID_TOKEN)
    CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
    CASE3 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN)
      Constant_Ptr = &(Constants[Token.Constant_Index]);
      EXIT
    END_CASE

    OTHERWISE
      Parse_Error(IDENTIFIER_TOKEN);
    END_CASE
  END_EXPECT

  Previous = Token.Token_Id;

  GET (EQUALS_TOKEN);

  EXPECT
    CASE (COLOUR_TOKEN)
      if (Test_Redefine(COLOUR_ID_TOKEN))
        Destroy_Colour((COLOUR *)Constant_Ptr->Constant_Data);
      Local_Colour = Create_Colour();
      Parse_Colour (Local_Colour);
      Constant_Ptr -> Constant_Data = (char *) Local_Colour;
      Constant_Ptr -> Constant_Type = COLOUR_CONSTANT;
      EXIT
    END_CASE

    CASE_VECTOR
      Have_Vector = FALSE;
      Parse_Vector_Float (&Local_Vector);
      if (Have_Vector)
        {
         if (Test_Redefine(VECTOR_ID_TOKEN))
           Destroy_Vector((VECTOR *)Constant_Ptr->Constant_Data);
         Constant_Ptr -> Constant_Type = VECTOR_CONSTANT;
         Constant_Ptr -> Constant_Data = (char *) Create_Vector();
         *((VECTOR *)Constant_Ptr -> Constant_Data) = Local_Vector;
        }
      else
        {
         if (Test_Redefine(FLOAT_ID_TOKEN))
           Destroy_Float((DBL *)Constant_Ptr->Constant_Data);
         Constant_Ptr -> Constant_Type = FLOAT_CONSTANT;
         Constant_Ptr -> Constant_Data = (char *) Create_Float();
         *((DBL *) Constant_Ptr -> Constant_Data) = Local_Vector.x;
        }
      EXIT
    END_CASE

    CASE (PIGMENT_TOKEN)
      if (Test_Redefine(PIGMENT_ID_TOKEN))
        Destroy_Pigment((PIGMENT *)Constant_Ptr->Constant_Data);
      Local_Pigment = Copy_Pigment((Default_Texture->Pigment));
      Parse_Pigment (&Local_Pigment);
      Constant_Ptr -> Constant_Type = PIGMENT_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *)Local_Pigment;
      EXIT
    END_CASE

    CASE (TNORMAL_TOKEN)
      if (Test_Redefine(TNORMAL_ID_TOKEN))
        Destroy_Tnormal((TNORMAL *)Constant_Ptr->Constant_Data);
      Local_Tnormal = Copy_Tnormal((Default_Texture->Tnormal));
      Parse_Tnormal (&Local_Tnormal);
      Constant_Ptr -> Constant_Type = TNORMAL_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *) Local_Tnormal;
      EXIT
    END_CASE

    CASE (FINISH_TOKEN)
      if (Test_Redefine(FINISH_ID_TOKEN))
        Destroy_Finish((FINISH *)Constant_Ptr->Constant_Data);
      Local_Finish = Copy_Finish((Default_Texture->Finish));
      Parse_Finish (&Local_Finish);
      Constant_Ptr -> Constant_Type = FINISH_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *) Local_Finish;
      EXIT
    END_CASE

    CASE (CAMERA_TOKEN)
      if (Test_Redefine(CAMERA_ID_TOKEN))
        Destroy_Camera((CAMERA *)Constant_Ptr->Constant_Data);
      Local_Camera = Copy_Camera(Default_Camera);
      Parse_Camera (&Local_Camera);
      Constant_Ptr -> Constant_Type = CAMERA_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *) Local_Camera;
      EXIT
    END_CASE

    CASE (TEXTURE_TOKEN)
      if (Test_Redefine(TEXTURE_ID_TOKEN))
        Destroy_Textures((TEXTURE *)Constant_Ptr->Constant_Data);
      Local_Texture = Parse_Texture ();
      Constant_Ptr -> Constant_Type = TEXTURE_CONSTANT;
      Constant_Ptr -> Constant_Data = NULL;
      Link_Textures((TEXTURE **) &Constant_Ptr->Constant_Data, Local_Texture);
      EXPECT
        CASE (TEXTURE_TOKEN)
          Local_Texture = Parse_Texture ();
          Link_Textures((TEXTURE **) &Constant_Ptr->Constant_Data, Local_Texture);
        END_CASE

        OTHERWISE
          UNGET
          EXIT
        END_CASE
      END_EXPECT
      EXIT
    END_CASE

    CASE (COLOUR_MAP_TOKEN)
      if (Test_Redefine(COLOUR_MAP_ID_TOKEN))
        Destroy_Colour_Map((COLOUR_MAP *)Constant_Ptr->Constant_Data);
      Local_Colour_Map = Parse_Colour_Map ();
      Constant_Ptr -> Constant_Type = COLOUR_MAP_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *) Local_Colour_Map;
      EXIT
    END_CASE

    CASE (TRANSFORM_TOKEN)
      if (Test_Redefine(TRANSFORM_ID_TOKEN))
        Destroy_Transform((TRANSFORM *)Constant_Ptr->Constant_Data);
      Local_Trans = Parse_Transform ();
      Constant_Ptr -> Constant_Type = TRANSFORM_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *) Local_Trans;
      EXIT
    END_CASE

    OTHERWISE
      UNGET
      if (Test_Redefine(OBJECT_ID_TOKEN))
        Destroy_Object((OBJECT *)Constant_Ptr->Constant_Data);
      Local_Object = Parse_Object ();
      Constant_Ptr -> Constant_Type = OBJECT_CONSTANT;
      Constant_Ptr -> Constant_Data = (char *) Local_Object;
      EXIT
    END_CASE

  END_EXPECT
  }

static void Link (New_Object, Field, Old_Object_List)
  OBJECT *New_Object, **Field, **Old_Object_List;
  {
  *Field = *Old_Object_List;
  *Old_Object_List = New_Object;
  }

static void Link_Textures (Old_Textures, New_Textures)
  TEXTURE **Old_Textures;
  TEXTURE  *New_Textures;
  {
   TEXTURE *Layer;

   for (Layer = New_Textures ;
        Layer->Next_Layer != NULL ;
        Layer = Layer->Next_Layer)
     {}
        Layer->Next_Layer = *Old_Textures;
        *Old_Textures = New_Textures;
  }

static
char *Get_Token_String (Token_Id)
  TOKEN Token_Id;
  {
  register int i;

  for (i = 0 ; i < LAST_TOKEN ; i++)
     if (Reserved_Words[i].Token_Number == Token_Id)
        return (Reserved_Words[i].Token_Name);
  return ("");
  }

static
void Where_Error ()
  {
  fprintf (stderr, "\nError in file %s line %d\n", Token.Filename,
                                                 Token.Token_Line_No+1);
  }

static int Test_Redefine(a)
  int a;
  {
  char *old, *new;

  if (Previous == IDENTIFIER_TOKEN)
    return (FALSE);
  if (Previous != a)
    {old = Get_Token_String (Previous);
     new = Get_Token_String (a);
     Where_Error ();
     fprintf (stderr, "Attempted to redefine %s as %s", old, new);
     exit (1);
    }
  return (TRUE);
  }

void Parse_Error (Token_Id)
  TOKEN Token_Id;
  {
  char *expected;

  expected = Get_Token_String (Token_Id);
  Parse_Error_Str(expected);
  }

void Parse_Error_Str (str)
  char *str;
  {
   Where_Error ();
   fprintf (stderr, "%s expected but", str);
   Found_Instead ();
   exit (1);
  }

static void Found_Instead ()
  {
  char *found;

  if (Token.Token_Id == IDENTIFIER_TOKEN)
    fprintf (stderr,
      " undeclared identifier '%s' found instead.\n", Token.Token_String);
  else
   {
    found = Get_Token_String (Token.Token_Id);
    fprintf (stderr, " %s found instead.\n", found);
   }
  }
/*
static void Parse_Warn (Token_Id)
  TOKEN Token_Id;
  {
  char *expected;

  fprintf (stderr, "\nWarning in file %s line %d\n", Token.Filename,
                                                   Token.Token_Line_No+1);
  expected = Get_Token_String (Token_Id);
  fprintf (stderr, "%s expected but", expected);
  Found_Instead ();
  }
*/
static void Warn_State (Token_Id,Type)
  TOKEN Token_Id, Type;
  {
  char *found;
  char *should;

  if (Language_Version < 1.5)
     return;

  fprintf (stderr, "\nWarning in file %s line %d\n", Token.Filename,
                                                   Token.Token_Line_No+1);
  found = Get_Token_String (Token_Id);
  should = Get_Token_String (Type);
  fprintf (stderr, "Found %s that should be in %s statement", found, should);
  }

void Warn (str,Level)
  char *str;
  DBL Level;
  {
  if (Language_Version < Level)
    return;
    
  fprintf (stdout, "\nWarning in file %s line %d\n", Token.Filename,
                                                   Token.Token_Line_No+1);
  fputs (str, stdout);
  }

void Error (str)
  char *str;
  {
  Where_Error ();
  fputs (str, stderr);
  exit (1);
  }

void MAError (str)
  char *str;
  {
  Where_Error ();
  fprintf (stderr, "Out of memory.  Cannot allocate %s.\n",str);
  exit (1);
  }

/* Write a token out to the token file */

void Write_Token (Token_Id, Data_File)
  TOKEN Token_Id;
  DATA_FILE *Data_File;

  {
   Token.Token_Id = Token_Id;
   Token.Token_Line_No = Data_File->Line_Number;
   Token.Filename = Data_File->Filename;
   Token.Token_String = String;
   Token.Constant_Data = NULL;
   Token.Constant_Index = (int) Token.Token_Id - (int) LAST_TOKEN;

   if (Token.Constant_Index >= 0)
     {if (Token.Constant_Index <= Number_Of_Constants)
        {Token.Constant_Data = Constants[Token.Constant_Index].Constant_Data;
         switch (Constants[Token.Constant_Index].Constant_Type)
           {CASEID(COLOUR_CONSTANT,     COLOUR_ID_TOKEN)
            CASEID(VECTOR_CONSTANT,     VECTOR_ID_TOKEN)
            CASEID(FLOAT_CONSTANT,      FLOAT_ID_TOKEN)
            CASEID(PIGMENT_CONSTANT,    PIGMENT_ID_TOKEN)
            CASEID(TNORMAL_CONSTANT,    TNORMAL_ID_TOKEN)
            CASEID(FINISH_CONSTANT,     FINISH_ID_TOKEN)
            CASEID(TEXTURE_CONSTANT,    TEXTURE_ID_TOKEN)
            CASEID(OBJECT_CONSTANT,     OBJECT_ID_TOKEN)
            CASEID(COLOUR_MAP_CONSTANT, COLOUR_MAP_ID_TOKEN)
            CASEID(TRANSFORM_CONSTANT,  TRANSFORM_ID_TOKEN)
            CASEID(CAMERA_CONSTANT,     CAMERA_ID_TOKEN)
           }
        }
      else Token.Token_Id = IDENTIFIER_TOKEN;
     }
  }

static void Post_Process (Object,Parent)
  OBJECT *Object, *Parent;
  {
   OBJECT *Sib;

   if (Object == NULL)
     return;

   if (Parent != NULL)
     {
      if (Object->Texture == NULL)
        Object->Texture = Parent->Texture;
/*
      else
        if (Parent->Texture != NULL)
          {Local_Texture = Copy_Textures (Parent->Texture);
           Link_Textures (&(Object->Texture), Local_Texture);
          }
*/ /* Removed for backward compat with 1.0.  May put back in. CEY 12/92 */
      Object->No_Shadow_Flag |= Parent->No_Shadow_Flag;
     }
     
   if (     (Object->Texture == NULL) 
        && !(Object->Type & TEXTURED_OBJECT) 
        && !(Object->Type & LIGHT_SOURCE_OBJECT))
     Object->Texture = Copy_Textures(Default_Texture);

   if (Object->Type & COMPOUND_OBJECT)
     {
      if (Object->Type & LIGHT_SOURCE_OBJECT)
        {
         ((LIGHT_SOURCE *)Object)->Next_Light_Source = Frame.Light_Sources;
         Frame.Light_Sources = (LIGHT_SOURCE *)Object;
        }
      for (Sib = ((CSG *)Object)->Children;
           Sib != NULL;
           Sib = Sib->Sibling)
        Post_Process(Sib, Object);
     }
   else
     {
      if (Object->Texture == NULL)
        Object->Texture = Copy_Textures(Default_Texture);
      if (Object->Texture->Type == PNF_TEXTURE) 
        if (Object->Texture->Tnormal != NULL)
          Object->Type |= DOUBLE_ILLUMINATE;
     }
   Post_Textures (Object->Texture);
   if ((Object->Type & WATER_LEVEL_OK_OBJECT) &&
       (Object->Type & IS_CHILD_OBJECT))
     Object->Methods = &Csg_Height_Field_Methods;
  }

static void Destroy_Constants ()
  {
   int i;
   char *Ptr;

   for (i=1; i <= Number_Of_Constants; i++)
     {
      Ptr = Constants[i].Constant_Data;
      switch (Constants[i].Constant_Type)
        {
         case COLOUR_CONSTANT:
           Destroy_Colour((COLOUR *)Ptr);
           break;
         case VECTOR_CONSTANT:
           Destroy_Vector((VECTOR *)Ptr);
           break;
         case FLOAT_CONSTANT:
           Destroy_Float((DBL *)Ptr);
           break;
         case PIGMENT_CONSTANT:
           Destroy_Pigment((PIGMENT *)Ptr);
           break;
         case TNORMAL_CONSTANT:
           Destroy_Tnormal((TNORMAL *)Ptr);
           break;
         case FINISH_CONSTANT:
           Destroy_Finish((FINISH *)Ptr);
           break;
         case TEXTURE_CONSTANT:
           Destroy_Textures((TEXTURE *)Ptr);
           break;
         case OBJECT_CONSTANT:
           Destroy_Object((OBJECT *)Ptr);
           break;
         case COLOUR_MAP_CONSTANT:
           Destroy_Colour_Map((COLOUR_MAP *)Ptr);
           break;
         case TRANSFORM_CONSTANT:
           Destroy_Transform((TRANSFORM *)Ptr);
           break;
         case CAMERA_CONSTANT:
           Destroy_Camera((CAMERA *)Ptr);
           break;
        }
     }
  }

static void Link_To_Frame (Object)
 OBJECT *Object;
 {
  OBJECT *This_Sib, *Next_Sib;
  
  if ((Object->Methods != &CSG_Union_Methods) ||
      (Object->Bound != NULL) ||
      (Object->Clip != NULL) ||
      (!Use_Slabs))
    {
     Link(Object, &(Object -> Sibling), &(Frame.Objects));
     return;
    }
  
  for (This_Sib = ((CSG *)Object)->Children;
       This_Sib != NULL;
       This_Sib = Next_Sib)
       {
        Next_Sib = This_Sib->Sibling; /*L2F changes Sibling so save it */
        Link_To_Frame (This_Sib);
       }
  Object->Texture = NULL;
  Object->Sibling = NULL;
  ((CSG *)Object)->Children = NULL;
  Destroy_Object (Object);
 }
