Chris Pollett > Old Classes >
CS116b

( Print View )

Student Corner:
  [Grades Sec1]

  [Submit Sec1]

  [Email List Sec1]

  [
Lecture Notes]

Course Info:
  [Texts & Links]
  [Topics]
  [Grading]
  [HW Info]
  [Exam Info]
  [Regrades]
  [Honesty]
  [Announcements]

HW Assignments:
  [Hw1]  [Hw2]  [Hw3]
  [Hw4]  [Hw5]

Practice Exams:
  [Mid1]  [Mid2]  [Final]

                           












HW3 Solutions Page

Return to homework page.

Overview

My solution on this homework was more than I expected of students; however, I am still less than completely satisfied with it. Basically, I created my own XML scripting language which can be run by my program. The fractal/shape grammar is generated by a separate program which outputs a textures parameter file. The main program loads these two files to generate the scene. The auxiliary files are by default assumed to be in same directory and named script.txt and textures.txt. Alternatively, you can specify them in that order as command line arguments.

My feeling is my parser could easily be improved, I used stringstream mainly because it was easy. However, the way it was done has the effect that the files are essentially read twice which is slow. Also, if YACC or BISON were used the code would be much smaller. Right now, the texture mapping for sphere shapes is using automatically generated texture coordinates and the GLUT sphere drawing command. This could also be improved.

Example script.txt

<event>
   <framesTillAction>0</framesTillAction>
   <shape>
      <id>ground</id>
      <lifespan>0</lifespan> 
      <appearance> 
         <type>rectangularprism</type>
         <dimensions>50 .1 50</dimensions>
         <color>0 .4 0</color>  
      </appearance>
      <physics>
         <position>0 0 0</position>
         <up>0 1 0</up>
         <velocity>0 0 1</velocity>
         <stationary>1</stationary>
         <mass>1</mass>       
      </physics>
   </shape>
   <shape>
      <id>building1</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>2 8 2</dimensions> 
         <color>.1 .1 .1</color>  
      </appearance>
      <physics>
         <position>0 4 0</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
   <shape>
      <id>building2</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>3 6 3</dimensions> 
         <color>.2 .2 .2</color>  
      </appearance>
      <physics>
         <position>5 3 0</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
  <shape>
      <id>building3</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>4 9 3</dimensions> 
         <color>.2 .2 .2</color>  
      </appearance>
      <physics>
         <position>6 4.5 -5</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
  <shape>
      <id>building4</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>2 4 3</dimensions> 
         <color>.1 .2 .1</color>  
      </appearance>
      <physics>
         <position>-1 2 -5</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
  <shape>
      <id>building5</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>4 5 3</dimensions> 
         <color>.3 .2 .2</color>  
      </appearance>
      <physics>
         <position>0 2.5 10</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
  <shape>
      <id>building6</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>2 3 2</dimensions> 
         <color>.3 .2 .2</color>  
      </appearance>
      <physics>
         <position>7 2.5 11</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
 <shape>
      <id>building7</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>3 4 4</dimensions> 
         <texture>fractal</texture>  
      </appearance>
      <physics>
         <position>12 2 10</position>
	 <up>0 1 0</up>
         <velocity>0 0 1</velocity>
          <stationary>1</stationary>
        <mass>1</mass>         
      </physics>
   </shape>
   <shape>
      <id>carpetFront</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>3 .1 2</dimensions> 
         <texture>fractal</texture>  
      </appearance>
      <physics>
         <position>-5 1 3</position>
		   <up>0 1 0</up>
         <velocity>.1 .03 0</velocity>
         <stationary>0</stationary>
         <mass>1</mass>         
	 <force><gravity>0 -.0004 0</gravity></force>

      </physics>
   </shape>
   <shape>
      <id>carpetBack</id>
      <lifespan>0</lifespan>

      <appearance> 
         <type>rectangularprism</type>
         <dimensions>3 .1 2</dimensions> 
         <texture>fractal</texture>  
      </appearance>
      <physics>
         <position> -8.3 .8 3</position>
		   <up>0 1  0</up>
         <velocity>.0001 0 0</velocity>
         <stationary>0</stationary>
	 <force>
		<spring>carpetFront .004  1.5 0 0 -1.5 0 0</spring>	</force>
	 <force><gravity>0 -.0004 0</gravity></force>


         <mass>.3</mass>       
      </physics>
   </shape>
</event>
<event>
   <framesTillAction>50</framesTillAction>
   <particles>
       <id>fireworks</id>
	   <number>100</number>
	   <dispersionSpeed>.4</dispersionSpeed>
	   <shape>
		  <id>baseparticle</id>
		  <lifespan>150</lifespan>

		  <appearance> 
			 <type>sphere</type>
			 <dimensions>.1 .1 .1</dimensions> 
			 <color>1 1 1</color>  
		  </appearance>
		  <physics>
			 <position>5 10 0</position>
          		 <up>0 0 1</up>
			 <velocity>0 .2 0</velocity>
			 <stationary>0</stationary>
			 <force><gravity>0 -.03 0</gravity></force>
         	 <mass>1</mass>          
		  </physics>
	   </shape>	      
   </particles>
</event>
<event>
   <framesTillAction>100</framesTillAction>
   <particles>
       <id>fireworks</id>
	   <number>200</number>
	   <dispersionSpeed>.5</dispersionSpeed>
	   <shape>
		  <id>baseparticle</id>
		  <lifespan>150</lifespan>

		  <appearance> 
			 <type>rectangularprism</type>
			 <dimensions>.1 .1 .1</dimensions> 
			 <color>1 1 0</color>  
		  </appearance>
		  <physics>
			 <position>-5 10 -5</position>
          		 <up>0 0 1</up>
			 <velocity>0 .7 0</velocity>
			 <stationary>0</stationary>
			 <force><gravity>0 -.05 0</gravity></force>
         	 <mass>1</mass>          
		  </physics>
	   </shape>	      
   </particles>
</event>
<event>
   <framesTillAction>50</framesTillAction>
   <particles>
       <id>fireworks</id>
	   <number>200</number>
	   <dispersionSpeed>.5</dispersionSpeed>
	   <shape>
		  <id>baseparticle</id>
		  <lifespan>150</lifespan>

		  <appearance> 
			 <type>sphere</type>
			 <dimensions>.1 .1 .1</dimensions> 
			 <color>0 0 2</color>  
		  </appearance>
		  <physics>
			 <position>-5 13 5</position>
          		 <up>0 0 1</up>
			 <velocity>0 .7 0</velocity>
			 <stationary>0</stationary>
			 <force><gravity>0 -.05 0</gravity></force>
         	 <mass>1</mass>          
		  </physics>
	   </shape>	      
   </particles>
</event>

<event>
   <framesTillAction>200</framesTillAction>
   <reset></reset>
</event>

Program to Generate textures.txt

/******************************************************
* Project:         CS116B Homework #3b
* File:              fractal.cpp          
* Purpose:         This program is used to generate the fractal texture used by carpet.cpp
*
* Start date:      May 8, 2005
* Programmer:      Chris Pollett
*
* Remarks: Compile this program with a line like:
*     g++ fractal.cpp -o fractal
*
*  Then run on the command line with:
*     fractal >> texture.txt
*
*  Afterwards, texture.txt will contain xml data of the following type:
* The start and
*
*******************************************************/

#include <iostream>
#include <string>

using namespace std;

/*
   CONSTANTS
*/

//Texture Dimensions
const int WIDTH = 128; //must be a power of 2
const int HEIGHT = 128;

const string identity = "fractal";
const string COLOR1 = "1.0 0.0 0.0 1.0 "; //red (RGBA value)  
const string COLOR2 = "0.0 0.0 1.0 1.0 "; //blue (RGBA value)  

/* 
   GLOBALS
*/   
int data[WIDTH][HEIGHT];

/*
   PROTOTYPES
*/
 void makeFractalDataPattern1(int lox, int loy, int hix, int hiy);
 void makeFractalDataPattern2(int lox, int loy, int hix, int hiy);

/*-----------------------------------------------*/
 void makeFractalDataBlankPattern(int lox, int loy, int hix, int hiy)
/*
PURPOSE: Generate blank data for non-fractal part of texture
RECEIVES: Nothing
RETURNS: Nothing
REMARKS:    
*/
{
      for(int i = lox; i < hix; i++)
      {
         for(int j = loy; j < hiy; j++)
         {
            data[i][j] = 0;
         }
      }
   
}

/*-----------------------------------------------*/
 void makeFractalDataPattern2(int lox, int loy, int hix, int hiy)
/*
PURPOSE: One of two recursive pattern generators to make this texture
   pattern. Looks like

   x o
   o x
    
RECEIVES: Nothing
RETURNS: Nothing
REMARKS:    
*/
{
   int midx = (lox+hix)/2;
   int midy = (loy+hiy)/2;
   
   if(hix <= lox + 1 &&  hiy <= loy + 1)
   {
      data[lox][loy] = 1;
      return;
   }

   makeFractalDataBlankPattern(lox, loy, midx, midy);
   makeFractalDataPattern1(midx, loy, hix, midy);      
   makeFractalDataBlankPattern(midx, midy, hix, hiy);   
   makeFractalDataPattern1(lox, midy, midx, hiy);      
   
}
/*-----------------------------------------------*/
 void makeFractalDataPattern1(int lox, int loy, int hix, int hiy)
/*
PURPOSE: One of two recursive pattern generators to make this texture
   pattern. Looks like

   o x
   x o
 
RECEIVES: Nothing
RETURNS: Nothing
REMARKS:    
*/
{
   int midx = (lox+hix)/2;
   int midy = (loy+hiy)/2;
   
   if(hix <= lox + 1 &&  hiy <= loy + 1)
   {
      data[lox][loy] = 1;
      return;      
   }

   makeFractalDataPattern2(lox, loy, midx, midy);
   makeFractalDataBlankPattern(midx, loy, hix, midy);      
   makeFractalDataPattern2(midx, midy, hix, hiy);   
   makeFractalDataBlankPattern(lox, midy, midx, hiy);      
   
}

/*-----------------------------------------------*/
 void makeTexture()
/*
PURPOSE: Outputs to standard output an xml data file
   containing a fractal texture
RECEIVES: Nothing
RETURNS: Nothing
REMARKS:    
*/
{
   cout << "<texture>" << endl;
   cout << "<id>" <<identity << "</id>" <<endl;
   cout << "<width>"<< WIDTH <<"</width><height>" << HEIGHT << "</height>"
<<endl;
   cout << "<data>" << endl;
   
   makeFractalDataPattern1(0, 0, WIDTH, HEIGHT);
   
   for(int i = 0; i < WIDTH; i++)
   {
      for(int j = 0; j < HEIGHT; j++)
      {
         if(data[i][j] == 0)
         {
            cout << COLOR1;
         }
         else
         {
            cout << COLOR2;
         }
      }
      cout << endl;      
   }
      
   cout << "</data>" <<endl;
   cout << "</texture> " << endl;
}

int main (int argc, char * const argv[]) 
{
   makeTexture();

   return 0;
}

Main Program

/******************************************************
* Project:         CS116B Homework #3
* File:              carpet.cpp          
* Purpose:         Code to draw a flying carpet, flying betwixt a city landscape
*                  with fireworks
*                  Actually, this program reads and parses a script file that generates 
*                  the scene. The format of the script file is as a xml document,
*                  consisting of a sequence of <event></event> tags.
*                  <event>'s have a <framesTillAction> </framesTillAction> tag which says
*                  how many frames since the last event was performed until this event
*                  is performed. A frame by default is 1/30 of a second. An <event> can
*                  also have a sequence of <shape></shape>, <particles></particles>,
*                  or <reset></reset> objects on it. The first two of these in turn
*                  have sub-tags. Please consult the example provided with the homework
*                  solution to get a rough idea of the language.
*  
* Start date:      May 1, 2005
* Programmer:      Chris Pollett
*
* Remarks: In retrospect parsing is done in a way more complicated than it needs to be --
* I should have faked some YACC - like rule interpreter
*
*******************************************************/

#ifdef WIN32
   #include<windows.h> //for windows
   #include <GL/glut.h>
   #include <GL/glu.h>
#endif

#ifdef __APPLE__ //for MAC
/*
        I created my project under xcode. I chose new C++ tool as the kind of project.
        Then under External frameworks and libraries I added the two frameworks:
        OpenGL.framework and GLUT.framework. (Frameworks are in /Library/Frameworks)

*/
   #include <OpenGL/gl.h> 
   #include <OpenGL/glu.h>
   #include <GLUT/glut.h>
#endif

#ifdef linux // for linux
 /*My compile line was:
        g++ -I /usr/X11R6/include -L /usr/X11R6/lib -lglut -lGL \
         -lGLU -lX11 -lXmu -lXi -lm carpet.cpp -o carpet
 */
      #include <GL/glut.h>
      #include <GL/glu.h>
#endif

#include <cstdlib>
#include <cmath>
#include <ctime>
#include <iostream>
#include <fstream>
#include <vector>
#include <list>
#include <string>
#include <sstream>
using namespace std;

/*
   CONSTANTS
*/

//Light  constants
const GLfloat AMBIENT_LIGHT[] = {.3, .3, .3, 1 };
const GLfloat SUN_VECTOR[] = {80., 30., 50 }; //sun's position
const GLfloat SUN_AMBIENT_SPECULAR[] = {1, 1, 1, 1 };
const GLfloat SUN_DIFFUSE[] = {1, 1, 1, 1 };

//Camera constants
const GLfloat CAMERA_X = 15.0; //camera's position (looks at origin)
const GLfloat CAMERA_Y = 8.0;
const GLfloat CAMERA_Z = 20.0;
const GLfloat UP_X = 0.0; //camera's up vector
const GLfloat UP_Y = 1.0;
const GLfloat UP_Z = 0.0;

//View Volume
const GLfloat NEAR_CLIP = 2.0;
const GLfloat FAR_CLIP = 70;

//Animation Speed
clock_t FRAME_TIME = CLOCKS_PER_SEC/30;

//CLASS AND FUNCTION PROTOTYPES
class Physics;
class World;
void crossProduct(GLfloat vec1[], GLfloat vec2[], GLfloat vecResult[]);
void normalize(GLfloat vec[]);
void randomUnit(GLfloat vec[]);

/*
   CLASS DEFINITIONS
*/



/*-----------------------------------------------*/
class Force
/*
PURPOSE: encapsulate the notion of a physical force that might be
   applied to a Shape in our World to effect its motion
REMARK: By default the force is a uniform force of gravity in some fixed
   direction
         
*/
{
	protected:
		GLfloat _forceVec[3];
     
	public:
		Force(GLfloat fVec[]);
		Force(Force *f);
                  
      virtual ~Force(){};
		virtual void exert(Physics *physics, World *world, GLfloat vec[]);
};

/*-----------------------------------------------*/
class SpringForce : public Force
/*
PURPOSE: Used to model a spring force F= k(x-x0) on a Shape
   For a SpringForce _forceVec is used to say where the force is
   applied on the shape and _offsetOtherVec says where the force
   is atatched to the other Shape
REMARK: none
         
*/
{
   private:
      string _id;
      GLfloat _springConstant;
      GLfloat 	_offsetOtherVec[3];
      
      Physics *_otherPhysics;
            
	public:
		SpringForce(string id, GLfloat springConstant, GLfloat fVec[], GLfloat oVec[]);
		SpringForce(SpringForce *f);
            
		void exert(Physics *physics, World *world, GLfloat vec[]);
};

/*-----------------------------------------------*/
class ForceFactory
/*
PURPOSE: Used to create different kinds of forces according to a supplied
   context 
REMARK: none
         
*/
{
	public:
      ForceFactory() {}
		static Force* create(stringstream *forceStream);
		static Force* copy(Force *f);      		
};


/*-----------------------------------------------*/
class Physics
/*
PURPOSE: Used to encapsulate the dynamical state of a Shape in our World.
   It keeps track of position, velocity, acceleration, and mass, as
   well as all forces that are to be applied to the Shape. A boolean
   flag _stationary controls whether or not the given Shape, if it uses
   this Physics, can move.
REMARK:         
*/
{
	private:
		GLfloat _position[3];
		GLfloat _up[3];
		GLfloat _velocity[3];
		
      GLfloat _acceleration[3];
      bool _stationary;
							
		GLfloat _mass;
		vector<Force*> *_forces;
		
	public:
		Physics(GLfloat p[], GLfloat u[], GLfloat v[], bool s, GLfloat m, vector<Force*> *f);
      Physics(Physics *p);
		~Physics();
		
		void addForce(Force* force);
		void update(World *world); 
		void move(int framesElapsed);
		void increaseVelocity(GLfloat vec[]);
            
      GLfloat mass(){return _mass;}
      GLfloat* position() {return _position;} /*this is somewhat bogus as it defeats
                                                 the privateness of these arrays */           
      GLfloat* up() {return _up;}   
      GLfloat* velocity() {return _velocity;}
      GLfloat* acceleration() {return _acceleration;}
      void attitude(GLfloat attitudeMatrix[]);
      
      vector<Force*>* forces() {return _forces;} 

               
      bool stationary() {return _stationary;}		
};

/*-----------------------------------------------*/
class PhysicsFactory
/*
PURPOSE: Used to create Physics objects according to a supplied 
   stringstream context
REMARK: none
         
*/
{

	public:
      PhysicsFactory() {}
		static Physics* create(stringstream *physicsStream);
		static Physics* copy(Physics *physics);		
};

/*-----------------------------------------------*/
class Appearance
/*
PURPOSE: Object of this class are used to control the size and surface 
   properties for Shape's
REMARK: right now the useColor flag is used to determine if an object
  has a texture or one fixed color. This is a little awkward
         
*/
{
	private:
		GLfloat _dimension[3]; //will be size of a Shape
      
		GLfloat _color[3]; 

      string _textureID;

   protected:
      bool _useColor;
            
	public:
      Appearance(GLfloat dimension[], GLfloat color[], string textureID, bool useColor);
      Appearance(Appearance *a);
      virtual ~Appearance(){};
		void preDraw(Physics *physics, World *world); 
      virtual void draw(Physics *physics) {};     
		void postDraw(); 
};

/*-----------------------------------------------*/
class RectangularPrismAppearance : public Appearance
/*
PURPOSE: Appearance used to draw any Shape that looks like a RectangularPrism
 i.e., a box.
REMARK:
         
*/
{
	public:
      RectangularPrismAppearance(GLfloat d[], GLfloat c[], 
         string t, bool u): Appearance(d,c,t, u){}
      RectangularPrismAppearance(RectangularPrismAppearance *a): Appearance(a){}         
      void draw(Physics *physics);      
};

/*-----------------------------------------------*/
class SpheroidAppearance : public Appearance
/*
PURPOSE: Appearance used to draw any Shape that looks like a Sphere 
   or a squished Sphere
REMARK:
         
*/
{
	public:
      SpheroidAppearance(GLfloat d[], GLfloat c[], 
         string t, bool u): Appearance(d,c,t, u){}
      SpheroidAppearance(SpheroidAppearance *a): Appearance(a){}         

      void draw(Physics *physics);      
};

/*-----------------------------------------------*/
class AppearanceFactory
/*
PURPOSE:  Used to create Appearance objects according to a supplied 
   stringstream context 
REMARK:
         
*/
{

	public:
      AppearanceFactory() {};
		static Appearance* create(stringstream *appearanceStream);
		static Appearance* copy(Appearance* appearance);		
};


/*-----------------------------------------------*/
class Shape
/*
PURPOSE: Used to encapsulate information about a Shape that
   might exist in a World 
REMARK:
         
*/
{
	private:
      string _id;
		Physics *_physics;
		Appearance *_appearance;
		int _age;
		int _lifespan;
		
	public:
		Shape(string id, int l, Physics* p, Appearance* ap);
		Shape(Shape *s);      
		~Shape();
      

		bool cullable();
		void draw(World *world);
		void update(int framesElasped, World *world);
		void move(int framesElapsed);	

		string id() {return _id;}
      int age() {return _age;}
      int lifespan() {return _lifespan;}
            
      Physics* physics() {return _physics;}
      Appearance* appearance() {return _appearance;}    
};

/*-----------------------------------------------*/
class ShapeFactory
/*
PURPOSE:  Used to create Shape objects according to a supplied 
   stringstream context
REMARK:
         
*/
{

	public:
      ShapeFactory() {};
		static Shape* create(stringstream *shapeStream);
		
};

/*-----------------------------------------------*/
class Event
/*
PURPOSE: Used to represent the collection of actions
  that might occur on a World in a given time frame.
  It also holds a _framesTillAction variable used to
  say the amount of time since the last event.
REMARK:
         
*/
{
	private:
		int _framesTillAction;
      vector<string>* _deleteShapes; 
         // IDs of shapes to be delete from the world by this Event 
      vector<Shape *>* _insertShapes;
         // Shape's to be added ot the world by this Event
      
      bool _reset; // used to control whether this Event reset's the world
      
	public:
      Event(){};
      Event(int f, vector<string>* d, vector<Shape *>* iS, bool reset);
      virtual ~Event();
      void addDeleteShapeByID(string shapeID);
      void addInsertShape(Shape* shape);
		int framesTillAction() {return _framesTillAction;} 
		virtual void action(World *world);
};


/*-----------------------------------------------*/
class EventFactory
/*
PURPOSE: Used to create different kinds of Event's that might
   occur in a Script of what happens in a World
REMARK: none
         
*/
{

	public:
      EventFactory() {};   
		static Event* create(stringstream *eventStream);
		static void createParticles(stringstream *eventStream, vector<Shape *> *shapes);
		
};

/*-----------------------------------------------*/
class Script
/*
PURPOSE: a Script is a vector of Events that are played in sequence on a World to
 tell a story
REMARK: none
         
*/
{
	private:
		int _currentFrame;
		unsigned int _currentEventIndex;
		vector<Event*> _events;
		
	public:
		Script();
		~Script();
		int step(int framesElapsed, World *world);
      void addEvent(Event *evt);
		void reset();				
};

/*-----------------------------------------------*/
class ScriptFactory
/*
PURPOSE: used to generate a Script (a sequence of events) that can be played on a World
REMARK: none 
         
*/
{

	public:
      ScriptFactory() {};
		static Script* create(stringstream *scriptStream);
		
};

/*-----------------------------------------------*/
class Texture
/*
PURPOSE: Class to encapsulate a texture map that might be applied to a Shape's in a World
REMARK: none 
         
*/
{
   private:
      string _identity;
      int _width;
      int _height;
      GLfloat *_data;

	public:
      Texture(string id, int w, int h, GLfloat *d);
		~Texture();
      
      string id(){return _identity;}
      int width(){return _width;}
      int height(){return _height;}
      GLfloat* data(){return _data;}		
};

/*-----------------------------------------------*/
class TexturesFactory
/*
PURPOSE: used to parse a vector Texture's to be added to a World. These Texture's 
   used for texture maps on Shape's in a World
REMARK: none 
         
*/
{

	public:
      TexturesFactory() {};
		static vector<Texture* >* create(stringstream *textureStream);
      static Texture* createTexture(stringstream *textureStream);
		
};

/*-----------------------------------------------*/
class World
/*
PURPOSE: This class encapsulates the scene to be draw by this program
REMARK: none
         
*/
{
	private:
		list<Shape* > *_shapes; //using list since slightly better for deleting elements
      vector<Texture* > *_textures; 
      
		Script *_script;
      bool _resetting;
		
	public:
		World();
		~World();

      list<Shape*>* shapes() {return _shapes;}
      vector<Texture*>* textures() {return _textures;}
      
		void setScript(Script *s);
		void setTextures(vector<Texture*>* t);
            
      Shape *findShapeByID(string id);
      Texture *findTextureByID(string id);
      
		void cull();
		void draw();
		void move(int framesElapsed);
		void update(int framesElapsed);
		void step(int framesElapsed);
      
      void setResetting(bool r){_resetting=r; }
      bool isResetting(){return _resetting; }       
		void reset();	   
};


/*
   GLOBALS
*/
GLsizei winWidth = 500, winHeight = 500; // used for size of window
GLsizei initX = 50, initY = 50; // used for initial position of window


World world;
string scriptName;
string textureName;

clock_t animationTime = clock(); // keeps time of last frame update

/*
	IMPLEMENTATIONS
*/

//Appearance Class Implementations

/*-----------------------------------------------*/
Appearance::Appearance(GLfloat dimension[], GLfloat color[], string id, bool useColor)
/*
PURPOSE: initialize an Appearance object. This object controls what is actually rendered for a Shape.
RECEIVES: dimension - 3-vector x,y,z size of shape
          color - if the shape is drawn with a single color, what it is
          id -- identity of the Texture in World's _textures vector to use.
          useColor - whether to use a color or texture (in the future this lame flag could be replaced
             with a surface object)          

RETURNS: the Appearance object
REMARKS: None   
*/
{
   for(int i = 0; i < 3; i++)
   {
      _dimension[i] = dimension[i];
      _color[i] = color[i];
   }
   _textureID = id;
   
   _useColor = useColor;

} 

/*-----------------------------------------------*/
Appearance::Appearance(Appearance *a)
/*
PURPOSE: copy constructor 
RECEIVES: An Appearance to make a copy of   
RETURNS: the Appearance object
REMARKS: None   
*/
{
   for(int i = 0; i < 3; i++)
   {
      _dimension[i] = a->_dimension[i];
      _color[i] = a->_color[i];
   }
   _textureID = a->_textureID;
   
   _useColor = a->_useColor;

}   
  

/*-----------------------------------------------*/
void Appearance::preDraw(Physics *physics, World *world)
/*
PURPOSE: Part of a template pattern used to draw this Appearance
   of a Shape according to the supplied Physics. This part
   is fixed and is before the part that actually draws and is used to
   set up the relevant matrices 
RECEIVES: physics -- Physics object used to set up modelview matrices
RETURNS: 
REMARKS:    
*/
{
   GLfloat *pos = physics->position();   
   
   glPushMatrix();

	glLoadIdentity();
   
   //set origin to Physics object's
	glTranslatef(pos[0], pos[1], pos[2]);


   //Now switch into Physics object's coordinate system
   GLfloat attitudeMatrix[16];
   
   physics->attitude(attitudeMatrix);
         
   glMultMatrixf(attitudeMatrix);
	glScalef(_dimension[0], _dimension[1], _dimension[2]);
      
   if(_useColor)
   {
      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, _color);    
      glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, _color); 
      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, _color); 
      glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, _color); 

   }
   else
   {
      Texture *texture = world->findTextureByID(_textureID);
      if(texture)
      {
	 GLfloat color[] = {.8, .8, .8};
     	 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);    
         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); 
         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); 
         glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, color); 

         glEnable(GL_TEXTURE_2D);
         
         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width(), texture->height(), 
            0, GL_RGBA, GL_FLOAT, texture->data());                  
 

          

         
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
                  
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                   GL_NEAREST);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                   GL_NEAREST);         
      }
   }
} 

/*-----------------------------------------------*/
void Appearance::postDraw()
/*
PURPOSE:  Part of a template pattern used to draw this Appearance
   of a Shape according to the supplied Physics. This part
   is fixed and is after the part that actually draws and is used to
   restore matrix stack to its setting prior to the draw activity
RECEIVES: nothing
RETURNS: nothing
REMARKS:    
*/
{	
   if(!_useColor)
   {
      glDisable(GL_TEXTURE_GEN_S); 
      glDisable(GL_TEXTURE_GEN_T);   
      glDisable(GL_TEXTURE_2D);
   }
	glPopMatrix();
} 


//RectangularPrismAppearance Class Implementations

/*-----------------------------------------------*/
void RectangularPrismAppearance::draw(Physics *physics)
/*
PURPOSE: Part of a template pattern used to draw this Appearance
   of a Shape according to the supplied Physics. This part
   is can be changed. In this particular case, it draws a cube.
RECEIVES: physics -- a Physics object which might be used in the
   drawing process (but isn't in this case).
RETURNS: 
REMARKS:    
*/
{

   if(_useColor)
   {
      //lazy
      glutSolidCube(1.0);
   }
   else
   { // should output with a QUAD_STRIP and give Normals
      glBegin(GL_POLYGON);
         glTexCoord2f(0, 0); glVertex3f(-.5, .5, -.5);
         glTexCoord2f(1, 0); glVertex3f(.5, .5, -.5);
         glTexCoord2f(1, 1); glVertex3f(.5, .5, .5);
         glTexCoord2f(0, 1); glVertex3f(-.5, .5, .5);      
      glEnd();
      glBegin(GL_POLYGON);
         glTexCoord2f(0, 0); glVertex3f(-.5, -.5, -.5);
         glTexCoord2f(1, 0); glVertex3f(.5, -.5, -.5);
         glTexCoord2f(1, 1); glVertex3f(.5, -.5, .5);
         glTexCoord2f(0, 1); glVertex3f(-.5, -.5, .5);      
      glEnd();
      glBegin(GL_POLYGON);
         glTexCoord2f(0, 0); glVertex3f( -.5, -.5, -.5);
         glTexCoord2f(1, 0); glVertex3f( -.5, .5,  -.5);
         glTexCoord2f(1, 1); glVertex3f( -.5, .5,  .5);
         glTexCoord2f(0, 1); glVertex3f( -.5, -.5, .5);      
      glEnd(); 
      glBegin(GL_POLYGON);
         glTexCoord2f(0, 0); glVertex3f( .5, -.5, -.5);
         glTexCoord2f(1, 0); glVertex3f( .5, .5,  -.5);
         glTexCoord2f(1, 1); glVertex3f( .5, .5,  .5);
         glTexCoord2f(0, 1); glVertex3f( .5, -.5, .5);      
      glEnd();  
      glBegin(GL_POLYGON);
         glTexCoord2f(0, 0); glVertex3f(  -.5, -.5, -.5);
         glTexCoord2f(1, 0); glVertex3f(  .5,  -.5, -.5);
         glTexCoord2f(1, 1); glVertex3f(  .5,  .5, -.5);
         glTexCoord2f(0, 1); glVertex3f( -.5, .5, -.5);      
      glEnd(); 
       glBegin(GL_POLYGON);
         glTexCoord2f(0, 0); glVertex3f(  -.5, -.5, .5);
         glTexCoord2f(1, 0); glVertex3f(  .5,  -.5, .5);
         glTexCoord2f(1, 1); glVertex3f(  .5,  .5, .5);
         glTexCoord2f(0, 1); glVertex3f( -.5, .5, .5);      
      glEnd();                     
   }
}      

//SpheroidAppearance Class Implementations

/*-----------------------------------------------*/
void SpheroidAppearance::draw(Physics *physics)
/*
PURPOSE:  Part of a template pattern used to draw this Appearance
   of a Shape according to the supplied Physics. This part
   is can be changed. In this particular case, it draws a sphere.
RECEIVES: physics -- a Physics object which might be used in the
   drawing process (but isn't in this case).
RETURNS: 
REMARKS:    
*/
{
   if(!_useColor)
   {
         //Enable automatic generation of texture coordinates

           glEnable(GL_TEXTURE_GEN_S);
           glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);

           glEnable(GL_TEXTURE_GEN_T);
           glTexGeni(GL_T, GL_TEXTURE_GEN_MODE,  GL_EYE_LINEAR);   
   }
	glutSolidSphere(1.0, 20, 20);

}

//AppearanceFactory Class Implementations

/*-----------------------------------------------*/
Appearance* AppearanceFactory::create(stringstream *appearanceStream)
/*
PURPOSE:  Used to create an Appearance for a Shape based on the supplied 
   stringstream context. (Parses this stream)
RECEIVES: appearanceStream -- stringstream to parse an Appearance from
RETURNS: 
REMARKS:    
*/
{
   string token;
   unsigned int mask = 0;
   
   string type;

   GLfloat dimensions[3];
      
   GLfloat color[3] = {0, 0, 0};
   string textureID;
      
   bool useColor = false;
      
   Appearance *appearance;
   
   try
   {
      for(int i = 0; i < 3; i++)
      {
         *appearanceStream >> token;
         if(token == "<type>")
         {
            *appearanceStream >> type;
            *appearanceStream >> token;
            if(token != "</type>") throw string("No close </type> " + token);
            
            mask |=1;
         }
         else if(token == "<dimensions>")
         {
            *appearanceStream >> dimensions[0] >> dimensions[1] >> dimensions[2];
            *appearanceStream >> token;
            if(token != "</dimensions>") throw string("No close </dimensions> " + token);
            
            mask |=2;
         }            
         else if(token == "<color>")
         {
            *appearanceStream >> color[0] >> color[1] >> color[2];
            *appearanceStream >> token;
            if(token != "</color>") throw string("No close </color> " + token);
            
            useColor = true;
            mask |=4;
         }
         else if(token == "<texture>")
         {
            useColor = false;
            *appearanceStream >> textureID;
            *appearanceStream >> token;
            if(token != "</texture>") throw string("No close </texture> " + token);
            
            mask |=4;
         }                    
         else
         {
            throw string("Unrecognized token " + token);
         }
      }
      
      if(mask != 7) throw string("Missing Appearance Parameter");
      
      if(type == "sphere") 
      {
         appearance = new SpheroidAppearance(dimensions, color, textureID, useColor);
      }
      else if(type == "rectangularprism") 
      {
         appearance = new RectangularPrismAppearance(dimensions, color, textureID, useColor);
      }
      else
      {
         throw string("Unrecognized Appearance Type: "+type);
      }
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }  
   
   return appearance;
}      

/*-----------------------------------------------*/
Appearance* AppearanceFactory::copy(Appearance *a)
/*
PURPOSE:  Used to copy an Appearance object on a Shape object.
RECEIVES: a -- Appearance object
RETURNS: a copy of a.
REMARKS:    
*/
{
   Appearance *appearance;
  
    
   if(typeid(RectangularPrismAppearance) == typeid(*a))
   {
      appearance = new RectangularPrismAppearance((RectangularPrismAppearance*)a);
   }
   else if(typeid(SpheroidAppearance) == typeid(*a))
   {
 
      appearance = new SpheroidAppearance((SpheroidAppearance*)a);
   }
   else
   {
      appearance = new Appearance(a);
   }  
   return appearance;
}

//Force Class Implementations
/*-----------------------------------------------*/
Force::Force(GLfloat fVec[])
/*
PURPOSE: Creates a gravitional force 
RECEIVES: 
 fVec - direction and magnitude of the force
RETURNS: Force object
REMARKS:    
*/
{
	for(int i = 0; i < 3; i++)
	{
		_forceVec[i] = fVec[i]; 
	}
}

//Force Class Implementations
/*-----------------------------------------------*/
Force::Force(Force *f)
/*
PURPOSE:  Copies the passed Force object
RECEIVES: f - Force object to copy
RETURNS: copy of f
REMARKS:    
*/
{
	for(int i = 0; i < 3; i++)
	{
		_forceVec[i] = f->_forceVec[i]; 
	}
}

/*-----------------------------------------------*/
void Force::exert(Physics *physics, World *world, GLfloat vec[])
/*
PURPOSE: Returns the force exerted by this Force on the
   supplied Physics object physics into the vector vec
RECEIVES: 
   physics -- Physics object to use in a caluclating the Force
   world --  ambient World in which this Force lives  
   vec -- vector to copy force into. 
RETURNS: 
REMARKS: For gravitional forces do not need to looks at the Physics object
*/
{
	for(int i = 0; i < 3; i++)
	{
		vec[i] = _forceVec[i];
	}
}

//SpringForce Class Implementations
/*-----------------------------------------------*/
SpringForce::SpringForce(string id, GLfloat springConstant, GLfloat fVec[], 
	GLfloat oVec[]) : Force(fVec)
/*
PURPOSE: Creates a SpringForce which is attached to the Shape object 
   with the given string id
   which has the given springConstant and offset vector
RECEIVES: 
   id - string id of Object this Force is attached to
   springConstant -- used in Hooke's law
   fVec -- offset vector for the Shape this Force is applied to
       (where on shape Force applied)
   oVec -- offset vector for the other Shape this Force is attached to
       (where on other Shape Force is attached) 
RETURNS: a SpringForce object
REMARKS:    
*/
{
   _id = id;
   _springConstant = springConstant;
   
   for(int i = 0; i < 3; i++)
	{
      _offsetOtherVec[i] = oVec[i];
   }
   
   _otherPhysics = NULL;
}

/*-----------------------------------------------*/
SpringForce::SpringForce(SpringForce *f) : Force(f)
/*
PURPOSE: Copies the passed SpringForce object
RECEIVES: 
   f --  SpringForce object to copy
RETURNS: a copy of f
REMARKS:    
*/
{
   _id = f->_id;
   _springConstant = f->_springConstant;
   _otherPhysics = f->_otherPhysics;
   
   for(int i = 0; i < 3; i++)
   {
      _offsetOtherVec[i] = f->_offsetOtherVec[i];
   }
}

/*-----------------------------------------------*/
void SpringForce::exert(Physics *physics, World *world, GLfloat vec[])
/*
PURPOSE: Returns a vector of how strong the spring force is currently
   being applied to the supplied Physics object
RECEIVES: 
   physics -- Physics object to apply the Force to
   world -- used to look up Shape that SpringForce is between   
   vec - Glfloat vector to return the vector of force in
RETURNS: 
REMARKS: 
(1) At this point, this method could crash if the other Shape the SpringForce
is on is deleted

(2)Note this force is exerted assymmetrically.
That is, the object the other end of the spring is attached to
is assumed to have infinite mass so isn't pussed toward this object
    
*/
{
   if(!_otherPhysics)
   {
      Shape *otherShape = world->findShapeByID(_id);
      
      if(!otherShape) return; 
      
      _otherPhysics = otherShape->physics();
   }
   
   
   
   int i;
   GLfloat *positionThis = physics->position();
   GLfloat *positionOther = _otherPhysics->position();
   
   GLfloat attitudeThis[16]; 
   GLfloat attitudeOther[16];
   
   physics->attitude(attitudeThis);
  
   _otherPhysics->attitude(attitudeOther); 

   physics->attitude(attitudeOther);    
   
   GLfloat springPositionThis[3];
   GLfloat springPositionOther[3];
   
   GLfloat localOffsetThis[3];
   GLfloat localOffsetOther[3];
   
   for(i = 0; i < 3; i++)
   {
      localOffsetThis[i] = _forceVec[i]*(attitudeThis[i] + attitudeThis[i+3] +
         attitudeThis[i+6]);   
      localOffsetOther[i] = _offsetOtherVec[i]*(attitudeOther[i] + attitudeOther[i+3] +
         attitudeOther[i+6]);         
   }
  
   for(i = 0; i < 3; i++)
   {
      springPositionThis[i] = positionThis[i] + localOffsetThis[i];
      springPositionOther[i] = positionOther[i] + _offsetOtherVec[i];
      
      vec[i] = _springConstant*(springPositionOther[i]-springPositionThis[i]);
   } 
   
}

//ForceFactory Class Implementations
/*-----------------------------------------------*/
Force* ForceFactory::create(stringstream *forceStream)
/*
PURPOSE: Used to create a Force object that can be applied
   to a Physics object based on the supplied stringstream object
RECEIVES: forceStream -- stringstream object to parse a Force out of
RETURNS: a Force object
REMARKS:    
*/
{
   Force *force;
   string token;
   string id;
   GLfloat springConstant;
   GLfloat forceVec[3];
   GLfloat offsetOtherVec[3];
   try
   {

      *forceStream >> token;
      if(token == "<gravity>")
      {
         *forceStream >> forceVec[0] >> forceVec[1] >> forceVec[2];
         
         *forceStream >> token;
         if(token != "</gravity>") throw string("No close </gravity> " + token);
         
         force = new Force(forceVec);
      }
      else if(token == "<spring>")
      {
         *forceStream >> id;
         *forceStream >> springConstant;
         *forceStream >> forceVec[0] >> forceVec[1] >> forceVec[2];
         *forceStream >> offsetOtherVec[0] >> offsetOtherVec[1] >> offsetOtherVec[2];
                  
         *forceStream >> token;
         if(token != "</spring>") throw string("No close </spring> " + token);
         
         force = new SpringForce(id, springConstant, forceVec, offsetOtherVec);
      }         
      else
      {
         throw string("Unrecognized token " + token + " before </force>");
      }

   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }      
   return force;
}

/*-----------------------------------------------*/
Force* ForceFactory::copy(Force *f)
/*
PURPOSE: Used to copy a Force object on a Physics object.
RECEIVES: f - pointer to Force to copy
RETURNS: copy of f
REMARKS:    
*/
{
   Force *force;
   
   if(typeid(*f) == typeid(SpringForce))
   {
      force = new SpringForce((SpringForce *)f);
   }
  else
   {
      force =  new Force(f);
   }
   
   return force;
}

//Physics Class Implementations

/*-----------------------------------------------*/
Physics::Physics(GLfloat p[], GLfloat u[], GLfloat v[], bool s, 
	GLfloat m, vector<Force*> *f)
/*
PURPOSE: Creates a Physics object with a given position, up direction, 
	velocity, mass and forces
RECEIVES: 
 p - vector for position
 u - vector for the up direction for this object
 v - velocity of this object (if object is stationary still set 
	non-zero to get a coordinate system)
 s - whether or not the object is stationary
 m - objects mass
 f - vector of Force pointers that are applied to this object
RETURNS: a Physics object
REMARKS:    
*/
{
   for(int i = 0;  i< 3; i++)
   {
      _position[i] = p[i];
      _up[i] = u[i];
      _velocity[i] = v[i];
      _acceleration[i] = 0;
   }
   _stationary = s;
   _mass = m;
   _forces = f;   
}
/*-----------------------------------------------*/
Physics::Physics(Physics *p)
/*
PURPOSE: Used to create a deep copy of a supplied
   Physics object
RECEIVES: p - pointer to Physics object to copy 
RETURNS: the copy
REMARKS:
*/
{
   unsigned int i;
   for(i = 0; i < 3; i++)
   {
      _position[i] = p->_position[i];
      _up[i] = p->_up[i];
      _velocity[i] = p->_velocity[i];
      _acceleration[i] = p->_acceleration[i];             
   }
   
   _mass = p->_mass;
   _stationary = p->_stationary;

   _forces = new vector<Force*>();
   
   vector<Force *> *pforces = p->_forces; 
       
   for(i = 0; i < pforces->size(); i++)
   { 
      _forces->push_back(ForceFactory::copy((*pforces)[i]));
    
   }   
}


/*-----------------------------------------------*/
Physics::~Physics()
/*
PURPOSE: Deletes the dynamically allocated objects
   of this Physics object. In this case, just
   the Force objects on it.
RECEIVES: nothing
RETURNS: nothing
REMARKS:    
*/
{
   for(unsigned int i = 0; i < _forces->size(); i++)
   {
      if((*_forces)[i]) delete (*_forces)[i];
   }
   delete _forces;  
}

/*-----------------------------------------------*/
void Physics::attitude(GLfloat attitudeMatrix[])
/*
PURPOSE: Computes the attitude matrix (the local coordinates)
   of this Physics object
RECEIVES: 
   attitudeMatrix - array to store attitude matrix into 
RETURNS:nothing
REMARKS:    
*/
{
   GLfloat tangent[3];
   GLfloat normal[3];
   GLfloat binormal[3];
   
   GLfloat *up = _up;

   if(_acceleration[0] || _acceleration[1] || _acceleration[2]) up = _acceleration;
   
   int i;
   
   for(i = 0; i < 3; i++)
   {
      tangent[i] = _velocity[i];
      normal[i] = up[i];
      attitudeMatrix[4*i + 3] = 0;
      attitudeMatrix[i + 12] = 0;
   }
   attitudeMatrix[15] = 1; 
   
   if(up==_acceleration)
   {
      crossProduct( normal, tangent,  binormal);
   }
   else
   {
      crossProduct(  tangent, normal, binormal);   
   }
   normalize(tangent);
   normalize(binormal);
   crossProduct(tangent, binormal, normal);
   normalize(normal);
   
   int m;
   for(i = 0; i < 3; i++)
   {
      m = 4*i;
      attitudeMatrix[m] = tangent[i];
      attitudeMatrix[m+1] = normal[i];
      attitudeMatrix[m+2] = binormal[i];      
   }

}

/*-----------------------------------------------*/
void Physics::addForce(Force* force)
/*
PURPOSE: Adds supplied Force to the vector of Force's
   on this Physic's object
RECEIVES: force -- the Force to add
RETURNS: nothing
REMARKS:    
*/
{
	_forces->push_back(force); 
}

/*-----------------------------------------------*/
void Physics::update(World *world)
/*
PURPOSE: updates the acceleration of this Physics
   object according to current values of the Force's
   on this object.
RECEIVES: 
   world -- ambient World that this Physics belongs to
      useful for looking up object that focres might be between
RETURNS: Nothing
REMARKS:    
*/
{
	unsigned int i, j;
	GLfloat forceVec[3];
	GLfloat tmpVec[3];

	for(i = 0; i < 3; i++)
	{
		forceVec[i] = 0.0;
	}

	for(i = 0; i < _forces->size(); i++)
	{
		(*_forces)[i]->exert(this, world, tmpVec);

		for(j = 0; j < 3; j++)
		{
			forceVec[j] += tmpVec[j];
		}		

	}
	
	for(i = 0; i < 3; i++)
	{
		_acceleration[i] = forceVec[i]/_mass;
	}
}        


/*-----------------------------------------------*/
void Physics::move(int framesElapsed)
/*
PURPOSE: change the _position and _velocity of this
   Physics according to the current _acceleration
   and the number of frames that have passed since the
   last movement
RECEIVES: framesElapsed -- number of frames since the
   last movement.
RETURNS: 
REMARKS:    
*/
{
   if(!_stationary)
   {
      for(int i = 0; i < 3; i++)
      { 
         _velocity[i] += _acceleration[i]*framesElapsed;
         _position[i] += _velocity[i]*framesElapsed;
      }
   }
}

/*-----------------------------------------------*/
void Physics::increaseVelocity(GLfloat vec[])
/*
PURPOSE: Adds to the _velocity vector stored in this
   Physics object the supplied vector vec.
RECEIVES: vec -- vector to add
RETURNS: nothing
REMARKS:    
*/
{
	for(int i = 0; i < 3; i++)
	{ 
		_velocity[i] += vec[i];
	}
}

//PhysicsFactory Class Implementations

/*-----------------------------------------------*/
Physics* PhysicsFactory::create(stringstream *physicsStream)
/*
PURPOSE:  Used to create a Physics for a Shape based on the supplied 
   stringstream context. (Parses this stream). The Physics of a Shape
   will control how the Shape will move according to Physical
   principles 
RECEIVES: physicsStream --  stringstream to parse the Physics object out of
RETURNS: nothing
REMARKS:    
*/
{
   string token;

   GLfloat position[3];
   GLfloat velocity[3];
   GLfloat up[3];
   GLfloat mass;
   vector<Force *> *forces = new vector<Force *>;
   bool stationary;
   unsigned int mask = 0;
   bool exitFlag = false;

   try
   {
      while(*physicsStream && !exitFlag)
      {   

         *physicsStream >> token;
         if(token == "<position>")
         {
            if((mask & 1) == 1) 
               throw string("Can only define a position once within a <physics> tag");
          
            *physicsStream >> position[0] >> position[1] >> position[2];
            *physicsStream >> token;            
            if(token != "</position>") throw string("No close </position> " + token);
            
            mask |= 1;
         }
         else if(token == "<up>")
         {
            if((mask & 2) == 2) 
               throw string("Can only define an <up> vector once within a <physics> tag");

            *physicsStream >> up[0] >> up[1] >> up[2];
            *physicsStream >> token;            
            if(token != "</up>") throw string("No close </up> " + token);
            
            mask |= 2;
         }
         else if(token == "<velocity>")
         {
            if((mask & 4) == 4) 
               throw string("Can only define an <velocity> vector once within a <physics> tag");

            *physicsStream >> velocity[0] >> velocity[1] >> velocity[2];
            *physicsStream >> token;            
            if(token != "</velocity>") throw string("No close </velocity> " + token);
            
            mask |= 4;
         }
         else if(token == "<stationary>")
         {
            if((mask & 8) == 8) 
               throw string("Can only define an <stationary> flag once within a <physics> tag");

            *physicsStream >> stationary;
            *physicsStream >> token;            
            if(token != "</stationary>") throw string("No close </stationary> " + token);
            
            mask |= 8;
         }
         else if(token == "<mass>")
         {
            if((mask & 16) == 16) 
               throw string("Can only define an <mass> flag once within a <physics> tag");

            *physicsStream >> mass;
            *physicsStream >> token;            
            if(token != "</mass>") throw string("No close </mass> " + token);
            
            mask |= 26;
         }                                               
         else if(token == "<force>")
         {
            forces->push_back(ForceFactory::create(physicsStream));
            *physicsStream >> token;
            if(token != "</force>") throw string("No close </force> " + token);
         }
         else if(token == "</physics>")
         {

            exitFlag = true;
         }             
         else
         {
            throw string("Unrecognized token " + token + " before </physics>");
         }
      }
      if( mask < 31) throw string("Parameters missing from <physics> description.");

   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   return new Physics(position, up, velocity, stationary, mass,  forces);  
}      

/*-----------------------------------------------*/
Physics* PhysicsFactory::copy(Physics *p)
/*
PURPOSE:  Used to copy the Physics object of a Shape. 
RECEIVES: p - Physics object to copy
RETURNS: a copy of p
REMARKS:    
*/
{
   
   Physics *physics = new Physics(p); //could check for type and call the appropriate constructor

   return physics;
}

//Shape Class Implementations

/*-----------------------------------------------*/
Shape::Shape(string id, int l, Physics* p, Appearance* ap)
/*
PURPOSE: Constructs a Shape object. Shape's represent one object in our World
They have an id, lifespan, physics and appearance.
RECEIVES: 
   id -- string name for this Shape
   l -- lifespan in frames that this Shape will live in the World
   p -- Physics used to control this Shape's motion
   ap -- Appearance shape will be drawn with
RETURNS: Shape object
REMARKS:    
*/
{
   _id = id;
	_lifespan = l;
      
	_physics = p;
	_appearance = ap;
	_age = 0;
}

/*-----------------------------------------------*/
Shape::Shape(Shape *s)
/*
PURPOSE: Copy constructs the Shape pointed to by s.
RECEIVES: s -- Shape to copy
RETURNS: a copy of s
REMARKS:    
*/
{
   _id = s->_id;
   _lifespan = s->_lifespan;
   
   _physics = PhysicsFactory::copy(s->_physics);
   _appearance = AppearanceFactory::copy(s->_appearance);
   _age = s->_age;
}

/*-----------------------------------------------*/
Shape::~Shape()
/*
PURPOSE: Used to delete the dynamically created objects of this Shape.
   i.e., the _physics and _appearance.
RECEIVES: nothing
RETURNS: nothing
REMARKS:    
*/
{
	if(_physics) delete _physics; 
	if(_appearance) delete _appearance; 
}

/*-----------------------------------------------*/
bool Shape::cullable()
/*
PURPOSE: Used to determine if this Shape is ready to be deleted
RECEIVES:  nothing
RETURNS: Whether this Shape has lived more that _lifespan many frames
REMARKS: If the _lifespan is set to a value <= 0 the Shape is immortal
*/
{
	if(_lifespan > 0)
	{
		return (_lifespan < _age);
	}
	else
	{
		return false;
	}
}

/*-----------------------------------------------*/
void Shape::draw(World *world)
/*
PURPOSE: Use to draw this Shape in by its World according
  to its Appearance
RECEIVES: nothing
RETURNS: nothing
REMARKS:    
*/
{
	_appearance->preDraw(_physics, world);
	_appearance->draw(_physics);
	_appearance->postDraw();   
}

/*-----------------------------------------------*/
void Shape::update(int framesElapsed, World *world)
/*
PURPOSE: Updates the acceleration of this Shape stored in
   its Physic's object according to the Force's acting on the
   Shape
RECEIVES: framesElapsed -- number of frames since last update
   world -- World this shape belongs to, useful for looking
   up other shapes which may be connected to this shape
RETURNS: nothing
REMARKS:    
*/
{
	_age += framesElapsed;

	_physics->update(world);
}

/*-----------------------------------------------*/
void Shape::move(int framesElapsed)
/*
PURPOSE: Used to move this Shape by changing 
 its position and velocity stroed in its Physic's
 object 
RECEIVES: framesElapsed -- used to figure out how far
   to move based on the number of frames since last movement 
RETURNS: 
REMARKS:    
*/
{
	_physics->move(framesElapsed);
}	

//ShapeFactory Implementations

/*-----------------------------------------------*/
Shape* ShapeFactory::create(stringstream *shapeStream)
/*
PURPOSE:  Used to create a Shape for a World based on the supplied 
   stringstream context. (Parses this stream)
RECEIVES: shapeStream -- stringstream object to parse Shape out of
RETURNS: Shape object
REMARKS:    
*/
{
   string token;
   string id;
   int lifespan;
   int mask = 0;
   
   Appearance *appearance;
   Physics *physics;
   
   try
   {
      for(int i = 0; i < 4; i++)
      {
         *shapeStream >> token;
         if(token == "<id>")
         {
            *shapeStream >> id;            

            *shapeStream >> token;
            if(token != "</id>") throw string("No close </id> " + token);
            
            mask |=1;
         }

         else if(token == "<lifespan>")
         {
            *shapeStream >> lifespan;
            *shapeStream >> token;
            if(token != "</lifespan>") throw string("No close </lifespan> " + token);
  
            mask |=2;          	
         }
         else if(token == "<appearance>")
         {
            appearance = AppearanceFactory::create(shapeStream);
            *shapeStream >> token;
            if(token != "</appearance>") throw string("No close </appearance> " + token);
           
            mask |=4; 
         }    
         else if(token == "<physics>")
         {
           
           physics = PhysicsFactory::create(shapeStream);
           
            mask |=8;            	
         } 
         else
         {
            throw string("Unrecognized token " + token);
         }
      }
      
      if(mask != 15) throw string("Missing Shape Parameter");
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   return new Shape(id, lifespan, physics, appearance);
}	

//Event Implementations

/*-----------------------------------------------*/
Event::Event(int f, vector<string>* d, vector<Shape *>* iS, bool reset=false)
/*
PURPOSE: Used to consrtuct an Event that can be added to a Script that can be played
   on a world
RECEIVES: 
   f - number of frames after the preceding Event in the Script completes before
       carrying out the actions of this Event
   d - string ID's of Shape objects that will be deleted when this Event is carried out
   iS - Shape's to be inserted into the World when this Event is carried out     
RETURNS: 
REMARKS:    
*/
{
   _framesTillAction = f;
   _deleteShapes = d;
   _insertShapes = iS;
   _reset = reset;
}


/*-----------------------------------------------*/
Event::~Event()
/*
PURPOSE: Destructor for an Event. Deletes dynamically
   created vectors used to store the actions of an Event
RECEIVES: nothing
RETURNS: 
REMARKS:    
*/
{

   if(_insertShapes)
   {
      for(unsigned int i = 0; i < _insertShapes->size(); i++)
      {
         if((*_insertShapes)[i]) delete  (*_insertShapes)[i];       
      }
      delete _insertShapes;    
   }
   
   if(_deleteShapes) delete _deleteShapes;
}


/*-----------------------------------------------*/
void Event::addInsertShape(Shape *shape)
/*
PURPOSE: Add to this Event's vector of shapes to insert into a world
RECEIVES: shape -- Shape object to be inserted into the world by this Event  
RETURNS: nothing
REMARKS:    
*/
{
	_insertShapes->push_back(shape);
}

/*-----------------------------------------------*/
void Event::addDeleteShapeByID(string shapeID)
/*
PURPOSE: Add to the vector of Shapes that will be deleted by this Event
RECEIVES: shapeId -- string containing name of a Shape to delete
RETURNS: nothing
REMARKS:    
*/
{
	_deleteShapes->push_back(shapeID);
}

/*-----------------------------------------------*/
void Event::action(World *world)
/*
PURPOSE: Carries out the actions of a this Event on the 
   supplied world, this could be to delete some shapes,
   add some new ones, or to reset the World 
RECEIVES: world - World that this event s to be carried  out on
RETURNS: 
REMARKS:    
*/
{
	unsigned int i;
   
   list<Shape *>* shapes = world->shapes();
   
   for( i = 0 ; i < _deleteShapes->size(); i++)
   {
      for(list<Shape* >::iterator iter = shapes->begin(); iter != shapes->end(); iter++)
      {
         if( (*iter)->id() == (*_deleteShapes)[i])
         {
            
            if(*iter) delete (*iter);
            iter = shapes->erase(iter);
         }
      }	
   }
   
   for(unsigned i = 0 ; i < _insertShapes->size(); i++)
   {
      shapes->push_back(new Shape((*_insertShapes)[i]));
   }
   
   world->setResetting(_reset);
}
		
//EventFactory Implementations

/*-----------------------------------------------*/
Event* EventFactory::create(stringstream *eventStream)
/*
PURPOSE: To create an Event for a Script for a World  based on parsing
   the supplied stringstream
RECEIVES: eventStream -- pointer to a stringstream parsed for an Event
RETURNS: the Event parsed from the stringstream
REMARKS:    
*/
{
   bool haveFramesTillAction = false;
   bool exitFlag = false;
   bool reset = false;
   
   int framesTillAction = 0;
   Shape* shape;
   string deleteID;
   vector<Shape *> *insertShapes = new vector<Shape *>;
   vector<string> *deleteShapes = new vector<string>;

   string token;
   
   try
   {
      while(*eventStream && !exitFlag)
      {   
         *eventStream >> token;
         if(token == "<framesTillAction>")
         {
            *eventStream >> framesTillAction;
            *eventStream >> token;
            if(token != "</framesTillAction>") 
               throw string("No close </framesTillAction> " + token);

            haveFramesTillAction = true;
         }
         else if(token == "<shape>")
         {
            
            shape = ShapeFactory::create(eventStream); 
            insertShapes->push_back(shape);

            *eventStream >> token;
             
            if(token != "</shape>") throw string("No close </shape> " + token);

         }
         else if(token == "<delete>")
         {
            *eventStream >> deleteID;
            *eventStream >> token;
 
            deleteShapes->push_back(deleteID);
             
            if(token != "</delete>") throw string("No close </delete> " + token);
 
         }
         else if(token == "<particles>")
         {
            
            EventFactory::createParticles(eventStream, insertShapes);
            *eventStream >> token;
             
            if(token != "</particles>") throw string("No close </particle> " + token);
 
         }
         else if(token == "<reset>")
         {
            
            reset = true;
            *eventStream >> token;
             
            if(token != "</reset>") throw string("No close </reset> " + token);
         }         
         else if(token == "</event>")
         {
            exitFlag = true;
         }                                 
         else
         {
            throw string("Unrecognized token " + token + " before </event>");
         }
      }
      
      if(!haveFramesTillAction && exitFlag) throw string("Missing framesTillAction in event");
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   return new Event(framesTillAction, deleteShapes, insertShapes, reset);
}	

/*-----------------------------------------------*/
void EventFactory::createParticles(stringstream *eventStream, vector<Shape *> *shapes)
/*
PURPOSE: Parses information about a particle system from the supplied event stream
  so that the Shape's of this particle system can be created
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{

   string token;
   
   string id;
   int number;
   GLfloat dispersionSpeed;
   Shape *shape;
    
   int mask = 0;
   
   try
   {
      for(int i = 0; i < 4; i++)
      {
         *eventStream >> token;
         if(token == "<id>")
         {
            *eventStream >> id;
            *eventStream >> token;
            if(token != "</id>") throw string("No close </id> " + token);
            
            mask |=1;
         }

         else if(token == "<number>")
         {
            *eventStream >> number;
            *eventStream >> token;
            if(token != "</number>") throw string("No close </number> " + token);
  
            mask |=2;          	
         }
         
         else if(token == "<dispersionSpeed>")
         {
            *eventStream >> dispersionSpeed;
            *eventStream >> token;
            if(token != "</dispersionSpeed>") 
              throw string("No close </dispersionSpeed> " + token);
           
            mask |=4; 
         }   

         else if(token == "<shape>")
         {
           
           shape = ShapeFactory::create(eventStream);
           *eventStream >> token;
           if(token != "</shape>") throw string("No close </shape> " + token);
           
            mask |=8;            	
         }   
         else
         {
            throw string("Unrecognized token " + token);
         }
      }
      
      if(mask != 15) throw string("Missing particles Parameter");
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   Physics* physics;
   Appearance* appearance;
   int lifespan = shape->lifespan();
   
   Shape* tmpShape;

   GLfloat randVec[3];
   
     

   for(int i = 0; i < number; i++)
   {
      randomUnit(randVec);
      
      for(int j = 0; j < 3; j++)
      {
         randVec[j] *= dispersionSpeed;
      }
      
      appearance = AppearanceFactory::copy(shape->appearance());
      physics = PhysicsFactory::copy(shape->physics());

      physics->increaseVelocity(randVec);
      
      tmpShape = new Shape(id, lifespan, physics, appearance);
      shapes->push_back(tmpShape);
   }

   delete shape;
}



//Script Class Implementations

/*-----------------------------------------------*/
Script::Script()
/*
PURPOSE: Used to create a Script with no Events
   and whose frame parameters are set to zero
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
   _currentEventIndex = 0;
   _currentFrame = 0;   
}

/*-----------------------------------------------*/
Script::~Script()
/*
PURPOSE: Deletes all of the dynamically created Event's in the Script
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
	for(unsigned int i = 0; i < _events.size(); i++)
	{
		if(_events[i]) delete _events[i];
	}
}

/*-----------------------------------------------*/
int Script::step(int framesElapsed, World *world)
/*
PURPOSE: Processes framesElapsed many frames of the current Script on the supplied World
RECEIVES: framesElapsed -- number of frames to process
          world -- World that the Script will be carried out on 
RETURNS: Nothing 
REMARKS:    
*/
{
	if(_currentEventIndex < _events.size())
	{
		_currentFrame += framesElapsed;
		int framesTillAction = _events[_currentEventIndex]->framesTillAction();
		if(framesTillAction <= _currentFrame)
		{
			_currentFrame -= framesTillAction;
         _events[_currentEventIndex]->action(world);
         
         _currentEventIndex++;				

		}
	}
	return framesElapsed;
}

/*-----------------------------------------------*/ 
void Script::addEvent(Event *evt)
/*
PURPOSE: Adds an Event to the current Script
RECEIVES: evt - an Event to add to the end of this Script
RETURNS: Nothing 
REMARKS:    
*/
{
   _events.push_back(evt);
}

/*-----------------------------------------------*/
void Script::reset()
/*
PURPOSE: Returns a Script back to its start state so it can be played again
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
	_currentFrame = 0;
	_currentEventIndex = 0;
    
}

//ScriptFactory Class Implementations

/*-----------------------------------------------*/
Script* ScriptFactory::create(stringstream *scriptStream)
/*
PURPOSE: Used to create a Script for a World based on the supplied 
   stringstream context. (Parses this stream)
RECEIVES: scriptStream - stream to parse into a Script for world
RETURNS: a Script playable on a World
REMARKS:    
*/
{
   string token;
   Event *event;
   Script *script = new Script();

   scriptStream->setf(ios::skipws); //skip whitespace

   
   try
   {
      *scriptStream >> token;   
      while(*scriptStream)
      {   

         if(token == "<event>")
         {
            event = EventFactory::create(scriptStream);
            script->addEvent(event);
         }
         else
         {
            throw string("Unrecognized token " + token + " before </event>");
         }
         *scriptStream >> token;         
      }
      
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   return script;         
}	

//Texture Class Implementations

/*-----------------------------------------------*/
Texture::Texture(string id, int w, int h, GLfloat *d)
/*
PURPOSE: Creates a Texture with the given id, of the
 given width and height, and with the given texture data.
 Such a Texture might be applied to a Shape to 
 give it a more realistic appearance.
RECEIVES:
   id - string name of the Texture
   w - width of the Texture
   h - height of the Texture
   d - RGBA color data for this Texture 
RETURNS:
REMARKS:    
*/
{
   _identity = id;
   _width = w;
   _height = h;
   _data = d;
}

/*-----------------------------------------------*/
Texture::~Texture()
/*
PURPOSE: Dectructor. Gets rid of _data whose contents is
   dynamically allocated
RECEIVES: nothing
RETURNS: nothing
REMARKS:    
*/
{
   if(_data) delete _data;
}

//TexturesFactory Class Implementations

/*-----------------------------------------------*/
vector<Texture *>* TexturesFactory::create(stringstream *textureStream)
/*
PURPOSE: Used to create a vector of Texture's for a World based on the supplied 
   stringstream context. (Parses this stream) These Texture's can be texture
   mapped to Shape's in the world as needed.
RECEIVES: textureStream - stream to parse Texture's from
RETURNS: a pointer to a vector of Texture pointers.
REMARKS:    
*/
{
   string token;
   Texture *texture;
   vector<Texture *> *textureRepository = new vector<Texture *>();

   textureStream->setf(ios::skipws); //skip whitespace

   
   try
   {

      *textureStream >> token;   
      while(*textureStream)
      {   
         if(token == "<texture>")
         {
            texture = TexturesFactory::createTexture(textureStream);
            textureRepository->push_back(texture);
            *textureStream >> token;
            if(token != "</texture>") throw string("Expecting token </texture> rather than"
               +token);
         }
         else
         {
            throw string("Unrecognized token " + token + " before </texture>");
         }
        *textureStream >> token;        
      }
      
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   return textureRepository;         
}

/*-----------------------------------------------*/
Texture* TexturesFactory::createTexture(stringstream *textureStream)
/*
PURPOSE: Used to create a single Texture based on the supplied 
   stringstream context. (Parses this stream) These Texture's can be texture
   mapped to Shape's in the world as needed.
RECEIVES: textureStream - stream to parse Texture from
RETURNS: a pointer to a Texture.
REMARKS:    
*/
{
   string token;
   string id;
   int width;
   int height;
    GLfloat *data;

   int mask = 0;
   
   try
   {
      for(int i = 0; i < 4; i++)
      {
         *textureStream >> token;
         if(token == "<id>")
         {
            *textureStream >> id;
            *textureStream >> token;
   
            if(token != "</id>") throw string("No close </id> " + token);
            
            mask |=1;

         }
         else if(token == "<width>")
         {
            *textureStream >> width;
            *textureStream >> token;
                        
            if(token != "</width>") throw string("No close </width> " + token);
  
            mask |=2;  
                            	
         }
         else if(token == "<height>")
         {
            *textureStream >> height;
            *textureStream >> token;
            if(token != "</height>") throw string("No close </height> " + token);
  
            mask |=4;                      	
         }
         else if(token == "<data>")
         {
            if((mask & 6) != 6) 
               throw string("<width> and <height> info must come before <data>.");
            
            int textureLength = 4*width*height;
            data = new GLfloat[textureLength];
            
            for(int j = 0; j < textureLength; j++)
            {
               *textureStream >> data[j];
            }
            
            *textureStream >> token;
            if(token != "</data>") throw string("No close </data> " + token);
  
            mask |=8;          	
         }
         else
         {
            throw string("Unrecognized token " + token);
         }
      }

      if(mask != 15) throw string("Missing texture Parameter");
   }
   catch(string error)
   {
      cerr <<"Parse error:" << error;
      exit(1);
   }
   
   return new Texture(id, width, height, data);
}

//World Class Implementations

/*-----------------------------------------------*/
World::World()
/*
PURPOSE: Creates and initializes a Word with a blank Script
   and no Shape's. 
RECEIVES: Nothing
RETURNS: Nothing
REMARKS:    
*/
{
   _shapes = new list<Shape *>;
	_script = new Script();
}

/*-----------------------------------------------*/
World::~World()
/*
PURPOSE: Deletes are the dynamically created objects on this World.
   Mainly, Shape's. 
RECEIVES: Nothing
RETURNS: Nothing
REMARKS:    
*/
{
	reset();
   if(_shapes) delete _shapes;
   if(_script ) delete _script;
   if(_textures)
   {
      for(int i = 0 ; i < _textures->size(); i++)
      {
         delete (*_textures)[i];
      }
      delete _textures;
   }    
}

/*-----------------------------------------------*/
void World::setScript(Script *s)
/*
PURPOSE: Sets what Script is to be played out on this world. 
RECEIVES: s - pointer to Script to use
RETURNS: Nothing 
REMARKS: Called at start up after script file read. Also, called
  after the space key is hit and the script file is re-read 
  (useful for debugging)    
*/
{
   if(_script) delete _script;
	_script = s;
}

/*-----------------------------------------------*/
void World::setTextures(vector<Texture*> *t)
/*
PURPOSE: Sets what Texture's are available on this world. 
RECEIVES: t - pointer to a vector of Texture pointer than can be used
RETURNS: Nothing 
REMARKS: Called at start up after Texture file read. Also, called
  after the space key is hit and the script file is re-read 
  (useful for debugging)    
*/
{
   if(_textures)
   {
      for(int i = 0 ; i <_textures->size(); i++)
      {
         delete (*_textures)[i];
      }
      delete _textures;
   }	
   _textures = t;
}
	
/*-----------------------------------------------*/
Shape* World::findShapeByID(string id)
/*
PURPOSE: Finds and returns a pointer to a Shape of this
   World by string id
RECEIVES: 
   s - string ID to use
RETURNS: Desired Shape* if it exists; NULL otherwise 
REMARKS:  
*/
{
	for(list<Shape* >::iterator iter = _shapes->begin(); iter != _shapes->end(); iter++)
	{
      if(id == (*iter)->id()) return *iter;
   }
   
   return NULL;
}

/*-----------------------------------------------*/
Texture* World::findTextureByID(string id)
/*
PURPOSE: Finds and returns a pointer to a Texture of this
   World by string id
RECEIVES: 
   s - string ID to use
RETURNS: Desired TExture* if it exists; NULL otherwise 
REMARKS:  
*/
{
	for(int i=0; _textures->size(); i++)
	{
      if(id == (*_textures)[i]->id()) return (*_textures)[i];
   }
   
   return NULL;
}

         		
/*-----------------------------------------------*/
void World::cull()
/*
PURPOSE: Removes all shapes from the World that had been previously marked as deletable
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
	
	for(list<Shape* >::iterator iter = _shapes->begin(); iter != _shapes->end(); iter++)
	{
		if((*iter)->cullable())
		{
			delete (*iter);
			iter = _shapes->erase(iter);
		}
	}			
}

/*-----------------------------------------------*/
void World::draw()
/*
PURPOSE: Draws all the Shape's in this World
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
	for(list<Shape* >::iterator iter = _shapes->begin(); iter != _shapes->end(); iter++)
	{
		(*iter)->draw(this);
	}
}

		
/*-----------------------------------------------*/
void World::move(int framesElapsed)
/*
PURPOSE: Calculates new positions and velocities for all objects in our World
RECEIVES: framesElapsed- numbers of frames that have passed since the last time this operation was applied
RETURNS: Nothing 
REMARKS:    
*/
{
	for(list<Shape* >::iterator iter = _shapes->begin(); iter != _shapes->end(); iter++)
	{
		(*iter)->move(framesElapsed);
	}
}

/*-----------------------------------------------*/
void World::update(int framesElapsed)
/*
PURPOSE: Calculates new accelerations based on current forces for all objects in our World
   It also determines which objects have lived beyond their lifespan and marks them 
RECEIVES: framesElapsed- numbers of frames that have passed since the last time this operation was applied
RETURNS: Nothing 
REMARKS:    
*/
{
	for(list<Shape* >::iterator iter = _shapes->begin(); iter != _shapes->end(); iter++)
	{
		(*iter)->update(framesElapsed, this);
	}			
}

/*-----------------------------------------------*/
void World::step(int framesElapsed)
/*
PURPOSE: does one complete update/move/redraw of all the objects in the world
   Dead Shapes are culled
RECEIVES: framesElapsed- numbers of frames that have passed since the last time this operation was applied
RETURNS: Nothing 
REMARKS:    
*/
{
	framesElapsed = _script->step(framesElapsed, this); 
	  //if framesElapsed was greater then the next script action the framesElasped is reduced
	  
	update(framesElapsed);
	cull();
	move(framesElapsed);
   
   if(_resetting) reset();
}


/*-----------------------------------------------*/
void World::reset()
/*
PURPOSE: Returns the world to the same state as when the program was first run
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
   _resetting = false;
   
	for(list<Shape* >::iterator iter = _shapes->begin(); iter != _shapes->end(); iter++)
	{
		delete (*iter);
	}		

	_shapes->clear();
	_script->reset();
}

/*-----------------------------------------------*/
void  readScriptFile()
/*
PURPOSE: Reads a file containing a script of what events
   will happen on this program's World
RECEIVES: filename - name of file with script for world.
RETURNS: nothing
REMARKS:    
*/
{
   Script *script;
   
   ifstream fin;
   

   stringstream scriptStream(stringstream::in | stringstream::out);
   char c;
   try
   {
	   fin.open(scriptName.c_str());
	   if(fin.fail()) throw string("Failed to open World Script");

	   while (!fin.eof())
	   {
        fin.get(c);
        if(c=='<') scriptStream << ' '; //this is a hack
		  scriptStream << c;
        if(c=='>') scriptStream << ' ';
	   }
      
	   script = ScriptFactory::create(&scriptStream);

	   fin.close();
   }
   catch(string error)
   {
      cerr << error << endl;
      cerr << "No world script" << endl;
	   exit(1);
   }

	world.reset();
	world.setScript(script);
   cout << "Done reading script!!\n";
	
}

/*-----------------------------------------------*/
void  readTexturesFile()
/*
PURPOSE: Reads a file containing Texture maps for the World and
   add those Texture's
RECEIVES: filename - name of file with textures for world.
RETURNS: nothing
REMARKS:    
*/
{
   vector<Texture*> *textures;
   
   ifstream fin;
   

   stringstream textureStream(stringstream::in | stringstream::out);
   char c;
   try
   {
	   fin.open(textureName.c_str());
	   if(fin.fail()) throw string("Failed to open World Script");

	   while (!fin.eof())
	   {
        fin.get(c);
        if(c=='<') textureStream << ' '; //this is a hack
		  textureStream << c;
        if(c=='>') textureStream << ' ';
	   }
      
	   textures = TexturesFactory::create(&textureStream);

	   fin.close();
   }
   catch(string error)
   {
      cerr << error << endl;
      cerr << "No world texture" << endl;
	   exit(1);
   }

	world.setTextures(textures);
   cout << "Done reading Texture File!!\n";
	
}


//CLASSLESS FUNCTIONS


/*-----------------------------------------------*/
void normalize(GLfloat vec[])
/*
PURPOSE: change supplied vector to be unit vector in same direction
RECEIVES: vec - an array of three elements 
RETURNS: Nothing
REMARKS:    
*/
{
   int i;
   GLfloat sum = 0;
   
   for(i = 0; i < 3; i++)
   {
         sum += vec[i]*vec[i];
   }
   GLfloat length = sqrt(sum);
   
   for(i = 0; i < 3; i++)
   {
      vec[i] /=length;
   }
}

/*-----------------------------------------------*/
void crossProduct(GLfloat vec1[], GLfloat vec2[], GLfloat resultVec[])
/*
PURPOSE: computes the cross product of vec1 and vec2 storing the result in resultVec
RECEIVES: 
   vec1 - first vector in crossProduct
   vec2 - second vector in crossProduct
   resultVec - result of the crossProduct
RETURNS: Nothing
REMARKS:    
*/
{
   resultVec[0] = vec1[1]*vec2[2] - vec2[1]*vec1[2];
   resultVec[1] = vec1[2]*vec2[0] - vec2[2]*vec1[0];
   resultVec[2] = vec1[0]*vec2[1] - vec2[0]*vec1[1];

}



/*-----------------------------------------------*/
void randomUnit(GLfloat vec[])
/*
PURPOSE: generate a random vector of length 1
RECEIVES: vec - an array of three elements 
RETURNS: Nothing
REMARKS: Vectors along coordinate axes are impossible because of my
   hack to avoid the zero vector
*/
{

   //generate random point within unit sphere
   int i=0;
   while( i < 3)
   {
         vec[i] = GLfloat(rand())/(RAND_MAX+1.0) - .5;
         if(vec[i] == 0) i--; //hack to avoid the zero vector
         i++;
   }
   
   normalize(vec); //push out to unit sphere
}

/*-----------------------------------------------*/
void init()
/*
PURPOSE: sets the background color to black
RECEIVES:
RETURNS: Nothing
REMARKS:    
*/
{
   glClearColor(0.0, 0.0, 0.0, 0.0);
   
   glShadeModel(GL_SMOOTH);
   
   readScriptFile();
   readTexturesFile();
   
}

/*-----------------------------------------------*/
void makeLighting()
/*
PURPOSE: sets the evening lighting for our city landscape
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
	glEnable(GL_LIGHTING);

	
			
	glLightfv(GL_LIGHT0, GL_POSITION, SUN_VECTOR);
	glLightfv(GL_LIGHT0, GL_AMBIENT, SUN_AMBIENT_SPECULAR);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, SUN_DIFFUSE);
	glLightfv(GL_LIGHT0, GL_SPECULAR, SUN_AMBIENT_SPECULAR);		
	glEnable(GL_LIGHT0);	
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, AMBIENT_LIGHT);
}


/*-----------------------------------------------*/
void drawFn()
/*
PURPOSE: Used to craw the complete scene saturn + probe
		and to swap scene buffers
RECEIVES: Nothing
RETURNS:  Nothing
REMARKS:    
*/
{

	
	//Compute Depths
	glEnable(GL_DEPTH_TEST); 

	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

	makeLighting();
	
	world.draw();
	
	glutSwapBuffers();
}

/*-----------------------------------------------*/
void keyFn(unsigned char key, int xPos, int yPos)
/*
PURPOSE: Handle nonspecial key events (mainly space key and 1 key)
RECEIVES: key - key being pressed
          xPos - x position of mouse
		  yPos - y position of mouse  
RETURNS: Nothing 
REMARKS:    
*/
{
	switch(key)
	{
		case ' ':
			init();
		break;
	}

		
	glutPostRedisplay(); // cause scene to be redrawn
	
}


/*-----------------------------------------------*/
void animateFn()
/*
PURPOSE: Uses system clock to update the angle of the cassini space probe
RECEIVES: Nothing
RETURNS: Nothing 
REMARKS:    
*/
{
    clock_t newTime = clock();
	
	int framesElapsed = (newTime - animationTime)/FRAME_TIME;

	if(framesElapsed >= 1)
	{
		animationTime = newTime;
				
		world.step(framesElapsed);
  
		glutPostRedisplay(); // cause scene to be redrawn
	}
}

/*-----------------------------------------------*/
void reshapeFn(int newWidth, int newHeight)
/*
PURPOSE: To redraw scene when  window gets resized.
RECEIVES: newWidth -- new window x size
          newHeight -- new window y size
RETURNS: Nothing 
REMARKS:    
*/
{
	glViewport(0, 0, newWidth, newHeight);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glFrustum(-1.0, 1.0, -1.0, 1.0, NEAR_CLIP, FAR_CLIP);
	gluLookAt(CAMERA_X, CAMERA_Y, CAMERA_Z, 0.0, 0.0, 0.0, UP_X, UP_Y, UP_Z);
	
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();


}

int main (int argc, char **argv) 
{
   if(argc >= 2)
   {
	  scriptName = argv[1];
   }
   else
   {
      scriptName = "script.txt";
   }
   if(argc >= 3)
   {
	  textureName = argv[2];
   }
   else
   {
      textureName = "textures.txt";
   }   


   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); 
	/*Notice added GLUT_DEPTH to do depth detection 
	  I am using double buffering for the animation
	*/
   glutInitWindowPosition(initX, initY);
   glutInitWindowSize(winWidth, winHeight);
   glutCreateWindow("Flying Carpet!!");

   init();
   glutDisplayFunc(drawFn);
   glutReshapeFunc(reshapeFn);
   glutIdleFunc(animateFn);
   glutKeyboardFunc(keyFn);
      
   glutMainLoop();

   return 0;
}


Return to homework page.