static R3Vector calculate_sink_force(R3Scene* scene, R3Particle* particle)
   {
      R3ParticleSink* sink;
      R3Vector force = R3Vector(0,0,0);  
   	
      for (int i = 0; i < scene->NParticleSinks(); i++)
      {
         sink = scene->ParticleSink(i);
      	
         if (sink->shape->type == R3_SPHERE_SHAPE)
         {
            R3Sphere* sphere = sink->shape->sphere;
            R3Point center = sphere->Center();
            
            R3Vector f = -(particle->position - center);
            double d = f.Length() - sphere->Radius();
            f.Normalize();
         	
            double mag = sink->intensity / (sink->constant_attenuation + 
               						sink->linear_attenuation*d + 
               						sink->quadratic_attenuation*d*d);
            force += f*mag;
         }
         else if (sink->shape->type == R3_MESH_SHAPE)
      	{
      		R3Mesh* mesh = sink->shape->mesh;
      		R3Point center = R3Point(0,0,0);
      		for (unsigned int j = 0; j < mesh->vertices.size(); j++)
      		{
      			center += mesh->vertices[i]->position / mesh->vertices.size();
      		}
      		R3Vector f = -(particle->position - center);
            double d = f.Length();
            f.Normalize();
         	
            double mag = sink->intensity / (sink->constant_attenuation + 
               						sink->linear_attenuation*d + 
               						sink->quadratic_attenuation*d*d);
            force += f*mag;
      	}
      	else if (sink->shape->type == R3_BOX_SHAPE)
      	{
      		R3Box* box = sink->shape->box;
      		R3Point center = R3Point((box->XMax() - box->XMin())/2 + box->XMin(),
      										 (box->YMax() - box->YMin())/2 + box->YMin(),
      										 (box->ZMax() - box->ZMin())/2 + box->ZMin());
      		R3Vector f = -(particle->position - center);
            double d = f.Length();
            f.Normalize();
         	
            double mag = sink->intensity / (sink->constant_attenuation + 
               						sink->linear_attenuation*d + 
               						sink->quadratic_attenuation*d*d);
            force += f*mag;

      	}
      }
      return force;
   }
Exemple #2
0
void GLUTMotion(int x, int y)
{
  // Invert y coordinate
  y = GLUTwindow_height - y;
  
  // Compute mouse movement
  int dx = x - GLUTmouse[0];
  int dy = y - GLUTmouse[1];
  
  // Process mouse motion event
  if ((dx != 0) || (dy != 0)) {
    R3Point mesh_center = mesh->Center();
    if ((GLUTbutton[0] && (GLUTmodifiers & GLUT_ACTIVE_SHIFT)) || GLUTbutton[1]) {
      // Scale world 
      double factor = (double) dx / (double) GLUTwindow_width;
      factor += (double) dy / (double) GLUTwindow_height;
      factor = exp(2.0 * factor);
      factor = (factor - 1.0) / factor;
      R3Vector translation = (mesh_center - camera_eye) * factor;
      camera_eye += translation;
      glutPostRedisplay();
    }
    else if (GLUTbutton[0] && (GLUTmodifiers & GLUT_ACTIVE_CTRL)) {
      // Translate world
      double length = R3Distance(mesh_center, camera_eye) * tan(camera_yfov);
      double vx = length * (double) dx / (double) GLUTwindow_width;
      double vy = length * (double) dy / (double) GLUTwindow_height;
      R3Vector camera_right = camera_up % camera_towards;
      R3Vector translation = -((camera_right * vx) + (camera_up * vy));
      camera_eye += translation;
      glutPostRedisplay();
    }
    else if (GLUTbutton[0]) {
      // Rotate world
      double vx = (double) dx / (double) GLUTwindow_width;
      double vy = (double) dy / (double) GLUTwindow_height;
      double theta = 4.0 * (fabs(vx) + fabs(vy));
      R3Vector camera_right = camera_up % camera_towards;
      R3Vector vector = (camera_right * vx) + (camera_up * vy);
      R3Vector rotation_axis = vector % camera_towards;
      rotation_axis.Normalize();
      camera_eye.Rotate(R3Line(mesh_center, rotation_axis), theta);
      camera_towards.Rotate(rotation_axis, theta);
      camera_up.Rotate(rotation_axis, theta);
      camera_right = camera_up % camera_towards;
      camera_up = camera_towards % camera_right;
      camera_towards.Normalize();
      camera_up.Normalize();
      glutPostRedisplay();
    }
  }

  // Remember mouse position 
  GLUTmouse[0] = x;
  GLUTmouse[1] = y;
}
RNRgb R3DirectionalLight::
SpecularReflection(const R3Brdf& brdf, const R3Point& eye, 
    const R3Point& point, const R3Vector& normal) const
{
    // Check if light is active
    if (!IsActive()) return RNblack_rgb;

    // Get material properties
    const RNRgb& Sc = brdf.Specular();
    RNScalar s = brdf.Shininess();

    // Get light properties
    const RNRgb& Ic = Color();
    RNScalar I = Intensity();
    R3Vector L = -(Direction());

    // Compute geometric stuff
    RNScalar NL = normal.Dot(L);
    if (RNIsNegativeOrZero(NL)) return RNblack_rgb;
    R3Vector R = (2.0 * NL) * normal - L;
    R3Vector V = eye - point;
    V.Normalize();
    RNScalar VR = V.Dot(R);
    if (RNIsNegativeOrZero(VR)) return RNblack_rgb;

    // Return specular component of reflection
    return (I * pow(VR,s)) * Sc * Ic;
}
RNRgb R3PointLight::
Reflection(const R3Brdf& brdf, const R3Point& eye, 
    const R3Point& point, const R3Vector& normal) const
{
    // Check if light is active
    if (!IsActive()) return RNblack_rgb;

    // Get material properties
    const RNRgb& Dc = brdf.Diffuse();
    const RNRgb& Sc = brdf.Specular();
    RNScalar s = brdf.Shininess();

    // Get light properties
    RNScalar I = IntensityAtPoint(point);
    R3Vector L = DirectionFromPoint(point);
    const RNRgb& Ic = Color();

    // Compute geometric stuff
    RNScalar NL = normal.Dot(L);
    if (RNIsNegativeOrZero(NL)) return RNblack_rgb;
    R3Vector R = (2.0 * NL) * normal - L;
    R3Vector V = eye - point;
    V.Normalize();
    RNScalar VR = V.Dot(R);

    // Compute diffuse reflection
    RNRgb rgb = (I * NL) * Dc * Ic;

    // Compute specular reflection
    if (RNIsPositive(VR)) rgb += (I * pow(VR,s)) * Sc * Ic;

    // Return total reflection
    return rgb;
}
Exemple #5
0
int Bubble::Collides(R3Mesh* mesh, R3Vector offset) {
  R3Box box = mesh -> bbox;

  R3Vector SepAxis = pos - (box.Centroid() + offset);

  double dist = SepAxis.Length();
  SepAxis.Normalize();

  double x = SepAxis.X();
  double y = SepAxis.Y();
  double z = SepAxis.Z();

  if (x >= y && x >= z && x != 0)
    SepAxis /= x;
  else if (y >= x && y >= z != 0)
    SepAxis /= y;
  else if (z != 0)
    SepAxis /= z;

  double x_len = box.XLength();
  double y_len = box.YLength();
  double z_len = box.ZLength();

  //effective radius
  SepAxis.SetX(x * x_len/2.0);
  SepAxis.SetY(y * y_len/2.0);
  SepAxis.SetZ(z * z_len/2.0);

  if (dist <= (size + SepAxis.Length()))
    return 1;	
  return 0;
}
Exemple #6
0
void R3MeshFace::
UpdatePlane(void)
{
  // Check number of vertices
  int nvertices = vertices.size();
  if (nvertices < 3) { 
    plane = R3null_plane; 
    return; 
  }

  // Compute centroid
  R3Point centroid = R3zero_point;
  for (int i = 0; i < nvertices; i++) 
    centroid += vertices[i]->position;
  centroid /= nvertices;
  
  // Compute best normal for counter-clockwise array of vertices using newell's method
  R3Vector normal = R3zero_vector;
  const R3Point *p1 = &(vertices[nvertices-1]->position);
  for (int i = 0; i < nvertices; i++) {
    const R3Point *p2 = &(vertices[i]->position);
    normal[0] += (p1->Y() - p2->Y()) * (p1->Z() + p2->Z());
    normal[1] += (p1->Z() - p2->Z()) * (p1->X() + p2->X());
    normal[2] += (p1->X() - p2->X()) * (p1->Y() + p2->Y());
    p1 = p2;
  }
  
  // Normalize normal vector
  normal.Normalize();
  
  // Update face plane
  plane.Reset(centroid, normal);
}
R3Vector R3PointLight::
DirectionFromPoint(const R3Point& point) const
{
    // Return direction to point
    R3Vector L = position - point;
    L.Normalize();
    return L;
}
Exemple #8
0
void R3Triad::
Rotate(const R3Vector& from, const R3Vector& to)
{
    // Rotate each axis
    RNAngle angle = R3InteriorAngle(from, to);
    R3Vector rotaxis = from % to;
    rotaxis.Normalize();
    Rotate(rotaxis, angle);
}
// compute intersection between a sphere and a ray
R3Intersection ComputeIntersection(R3Sphere *sphere, R3Ray &ray)
{
  R3Intersection i;
  bool in_front_of_center = false;
  bool internal = false;

  R3Vector l = sphere->Center() - ray.Start();
  double tca = l.Dot(ray.Vector());
  if (tca < 0) // case where ray originates within sphere and points away from its center
  {
    tca = -tca;
    in_front_of_center = true;
    internal = true;
  }
  double d2 = l.Dot(l) - tca * tca;
  double r2 = sphere->Radius() * sphere->Radius();
  if (d2 > r2) // case where ray misses sphere entirely
  {
    i.hit = false;
    return i;
  }
  double thc = sqrt(r2 - d2);
  double t;
  if (!in_front_of_center)
  {
    t = tca - thc;
    if (t < 0)
    {
      t = tca + thc;
      internal = true;
    }
  }
  else
  {
    t = thc - tca;
  }

  if (t < 0)
  {
    i.hit = false;
    return i;
  }

  R3Point p = ray.Point(t);
  R3Vector n = p - sphere->Center();
  n.Normalize();
  if (internal)
    n = -n;

  // populate intersection data
  i.hit = true;
  i.normal = n;
  i.position = p;
  i.t = t;

  return i;
}
Exemple #10
0
void R4Matrix:: 
Rotate(const R3Vector& from, const R3Vector& to)
{
    // rotate matrix that takes direction of vector "from" -> "to"
    // This is a quickie hack -- there's got to be a better way
    RNAngle radians = R3InteriorAngle(from, to);
    R3Vector axis = from % to;
    axis.Normalize();
    Rotate(axis, radians);
}
Exemple #11
0
void R3Matrix::Rotate(const R3Vector& from, const R3Vector& to)
{
    // rotate matrix that takes direction of vector "from" -> "to"
    // This is a quickie hack -- there's got to be a better way
    double d1 = from.Length();
    if (d1 == 0) return;
    double d2 = to.Length();
    if (d2 == 0) return;
    double cosine = from.Dot(to) / (d1 * d2);
    double radians = acos(cosine);
    R3Vector axis = from % to;
    axis.Normalize();
    Rotate(axis, radians);
}
Exemple #12
0
R3Ray ConstructRay(R3Camera c, int x, int y, int width, int height) {
//	c.xfov = 1;
//	c.yfov = 1;
	R3Point pcenter = c.eye + c.towards * c.neardist;
	R3Vector vright = c.right * c.neardist * tan(c.xfov);
	R3Vector vup = c.up * c.neardist * tan(c.yfov);

	double halfwidth = (double) width / 2.0;
	double halfheight = (double) height / 2.0;
	R3Point p = pcenter + ((double)x - halfwidth)/halfwidth * vright + ((double)y - halfheight)/halfheight * vup;
	
	R3Vector v = p - c.eye;
	v.Normalize();
	return R3Ray(c.eye, v);
}
Exemple #13
0
int IntersectMesh(R3Mesh *m, R3Ray r, R3Point *position, R3Vector *normal, double *t) {
	*t = DBL_MAX;
	for(int i=0; i < m->NFaces(); i++) {
		R3MeshFace *f = m->Face(i);
		if(f->vertices.size() != 3) continue;
		
		R3Vector trianglenormal = (f->vertices[1]->position - f->vertices[0]->position);
		trianglenormal.Cross(f->vertices[2]->position - f->vertices[0]->position);
		trianglenormal.Normalize();

		R3Plane triangleplane(f->vertices[0]->position, trianglenormal);

		R3Point intersectionpoint;
		double t_intersection;
		if(IntersectPlane(&triangleplane, r, &intersectionpoint, &trianglenormal, &t_intersection) !=0 ) {
			// check inside triangle
			int withintriangle = 1;
			for(int j=0; j<3; j++) {
				R3Vector v1 = f->vertices[j%3]->position - r.Start();
				R3Vector v2 = f->vertices[(j+1)%3]->position - r.Start();
				R3Vector n1 = v2;
				n1.Cross(v1);
				n1.Normalize();
				R3Plane p(r.Start(), n1);
				if(R3SignedDistance(p, intersectionpoint) < 0) {
					withintriangle = 0;
					break;
				}
			}
			
			if(withintriangle == 1 && t_intersection > 0 && t_intersection < *t) {
				*t = t_intersection;
				*position = intersectionpoint;
				*normal = trianglenormal;
			}
		}

	}

	if(*t < DBL_MAX) {
		return 1;
	}
	else {
		return 0;
	}
}
// Compute the luminance for a light at a specific point
R3Rgb ComputeLuminance(R3Light *light, R3Point &point)
{
  R3Rgb il(0, 0, 0, 1);

  switch (light->type)
  {
    // quadratic attenuation factor
    case R3_POINT_LIGHT:
    {
      double d = (light->position - point).Length();
      double att = light->constant_attenuation + light->linear_attenuation * d + light->quadratic_attenuation * d * d;
      il = light->color / att;
      break;
    }
    // constant luminance
    case R3_DIRECTIONAL_LIGHT:
    {
      il = light->color;
      break;
    }
    // quadratic attenuation with directional falloff
    case R3_SPOT_LIGHT:
    {
      R3Vector l = (point - light->position);
      l.Normalize();
      double cost = l.Dot(light->direction);
      if (acos(cost) <= light->angle_cutoff)
      {
        double d = (light->position - point).Length();
        double att = light->constant_attenuation + light->linear_attenuation * d + light->quadratic_attenuation * d * d;
        il = light->color * pow(cost, light->angle_attenuation) / att;
      }
      break;
    }
    // not implemented
    case R3_AREA_LIGHT:
      break;
    case R3_NUM_LIGHT_TYPES:
      break;
  }

  return il;
}
 static R3Vector calculate_attractive_force(R3Scene* scene, R3Particle* particle)
 {
    R3Vector attractive_force = R3Vector(0,0,0);
 	
    double G = 6.67300e-11;
 	
    for (int i = 0; i < scene->NParticles(); i++)
    {
       if (scene->Particle(i) == particle)
          continue;
    	
       R3Vector r = particle->position - scene->Particle(i)->position;
       double m1 = particle->mass;
       double m2 = scene->Particle(i)->mass;
    	
       double mag = -G * m1 * m2 / (r.Length() * r.Length());
       r.Normalize();
    	
       attractive_force += mag * r;
    }
 	
    return attractive_force;
 }
Exemple #16
0
void Explode(R3Scene *scene, R3Enemy *enemy) {
	if (enemy->shape->type == R3_MESH_SHAPE) {
		for (unsigned int i = 0; i < enemy->shape->mesh->vertices.size(); i++) {
			int percent = 1;
			//int percent = player->shape->mesh->vertices.size() / 150;
			/*if (enemy->shape->mesh->vertices.size() < 300) {
				percent = 10;
				}*/
			if (i % percent == 0) {
				R3Particle *particle = new R3Particle();
				double speed = 1 * RandomNumber();
				double x1 = 10 * RandomNumber();
				double x2 = 10 * RandomNumber();
				double x3 = 10 * RandomNumber();
				double mass = 0.00000001;
				double drag = 0.0;
				double elasticity = 0.0;
				R3Vector velocity = R3Vector(x1, x2, x3);
				velocity.Normalize();
				
				static R3Material sink;
				static R3Material sink_material;
				static R3Material sink_material2;
				static R3Material sink_material3;
				
				if (sink.id != 33) {
					sink.ka.Reset(0.2,0.2,0.2,1);
					sink.kd.Reset(1,0,0,1);
					sink.ks.Reset(1,0,0,1);
					sink.kt.Reset(0,0,0,1);
					sink.emission.Reset(1, 0, 0,1);
					sink.shininess = 1;
					sink.indexofrefraction = 1;
					sink.texture = NULL;
					sink.texture_index = -1;
					sink.id = 33;
				} 
				if (sink_material.id != 33) {
					sink_material.ka.Reset(0.2,0.2,0.2,1);
					sink_material.kd.Reset(1,0,0,1);
					sink_material.ks.Reset(1,0,0,1);
					sink_material.kt.Reset(0,0,0,1);
					sink_material.emission.Reset(1, 0, 0,1);
					sink_material.shininess = 1;
					sink_material.indexofrefraction = 1;
					sink_material.texture = NULL;
					sink_material.texture_index = -1;
					sink_material.id = 33;
				} 
				if (sink_material2.id != 33) {
					sink_material2.ka.Reset(0.2,0.2,0.2,1);
					sink_material2.kd.Reset(0.96,0.44,0.11,1);
					sink_material2.ks.Reset(0.96,0.44,0.11,1);
					sink_material2.kt.Reset(0,0,0,1);
					sink_material2.emission.Reset(.96, .44, .11,1);
					sink_material2.shininess = 1;
					sink_material2.indexofrefraction = 1;
					sink_material2.texture = NULL;
					sink_material2.texture_index = -1;
					sink_material2.id = 33;
				}
				if (sink_material3.id != 33) {
					sink_material3.ka.Reset(0.2,0.2,0.2,1);
					sink_material.kd.Reset(1,0.83,0,1);
					sink_material.ks.Reset(1,0.83,0,1);
					sink_material3.kt.Reset(0,0,0,1);
					sink_material3.emission.Reset(1, 0.83, 0,1);
					sink_material3.shininess = 1;
					sink_material3.indexofrefraction = 1;
					sink_material3.texture = NULL;
					sink_material3.texture_index = -1;
					sink_material3.id = 33;
				}
				
				particle->position = R3Point(enemy->shape->mesh->vertices[i]->position);
				particle->velocity = speed * velocity;
				particle->mass = mass;
				particle->fixed = false;
				particle->drag = drag;
				particle->elasticity = elasticity;
				particle->lifetimeactive = true;
				particle->lifetime = 1.0;
				if (x1 < 0.5)
					particle->material = &sink_material;
				else if (x1 < 3.33)
					particle->material = &sink;
				else if (x1 < 6.67) 
					particle->material = &sink_material2;
				else
					particle->material = &sink_material3;
				scene->particles.push_back(particle);
			}
		}
	}

	pid_t pid;
	pid = fork();
	if (pid == 0) {
	  if (LINUX)
	    system("avplay -nodisp -autoexit boomEnemy.wav");
	  else
	    system("afplay boomEnemy.wav");
	  exit(0);
	}

	scene->enemies.erase(scene->enemies.begin());
}
Exemple #17
0
void GenerateParticles(R3Scene *scene, double current_time, double delta_time)
{
    for (int i = 0; i < scene->NParticleSources(); i++) {
        
        R3ParticleSource *source = scene->ParticleSource(i);
        
        // spheres
        if (source->shape->type == R3_SPHERE_SHAPE) {
            
            // remainder term for fraction of particle
            double numberweshouldmake = delta_time * source->rate + source->remainder;
            int nparts = (int) floor(numberweshouldmake);
            source->remainder = numberweshouldmake - nparts;
            
            for (int p = 0; p < nparts; p++) {
                R3Particle *newpart = new R3Particle();
                
                // calculate position and velocity
                double u = RandomNumber();
                double theta = 2*PI*u;
                double v = RandomNumber();
                double phi = acos(2*v-1);
                double r = source->shape->sphere->Radius();
                R3Point center = source->shape->sphere->Center();
                
                double x = r*cos(theta)*sin(phi);
                double y = r*sin(theta)*sin(phi);
                double z = r*cos(phi);
                
                R3Vector n = R3Vector(x,y,z);
                n.Normalize();
                
                // find tangent vector, use lecture notes to get velocity
                R3Plane plane = R3Plane(R3Point(0,0,0),n);
                R3Vector a;
                do {
                    a = R3Vector(RandomNumber(),RandomNumber(),RandomNumber());
                    a.Project(plane);
                } while (a.Length() == 0.0);
                a.Normalize();
                double t1 = 2*PI*RandomNumber();
                double t2 = sin(source->angle_cutoff)*RandomNumber();
                a.Rotate(n,t1);
                R3Vector vec = R3Vector(a);
                R3Vector cross = R3Vector(vec);
                cross.Cross(n);
                vec.Rotate(cross,acos(t2));
                
                newpart->position = center + n*r;
                newpart->velocity = vec*source->velocity;
                
                //update
                newpart->mass = source->mass;
                newpart->fixed = source->fixed;
                newpart->drag = source->drag;
                newpart->elasticity = source->elasticity;
                newpart->lifetime = source->lifetime;
                newpart->lifetimeactive = source->lifetimeactive;
                newpart->material = source->material;
                
                scene->particles.push_back(newpart);
            }
        }
        

        // CIRCLE
        if (source->shape->type == R3_CIRCLE_SHAPE) {
            double numberweshouldmake = delta_time * source->rate + source->remainder;
            int nparts = (int) floor(numberweshouldmake);
            source->remainder = numberweshouldmake - nparts;
            
            for (int p = 0; p < nparts; p++) {
                R3Particle *newpart = new R3Particle();
                
                // calculate position and velocity
                
                double r = source->shape->circle->Radius();
                R3Point center = source->shape->circle->Center();
                R3Plane plane = source->shape->circle->Plane();
                R3Vector n = plane.Normal();
                n.Normalize();
                
                // get a random point on a circle
                double xcirc, ycirc;
                do {
                    xcirc = 2*r*(RandomNumber() - 0.5);
                    ycirc = 2*r*(RandomNumber() - 0.5);
                } while (xcirc*xcirc + ycirc*ycirc > r*r);
                
                // get basis vectors of circle
                R3Vector tang;
                do {
                    tang = R3Vector(RandomNumber(),RandomNumber(),RandomNumber());
                    tang.Project(plane);
                } while (tang.Length() == 0.0);
                R3Vector othertang = R3Vector(tang);
                othertang.Cross(n);
                othertang.Normalize();
                tang.Normalize();
                
                R3Point pos = center + tang*xcirc + othertang*ycirc;
                
                
                R3Vector a = R3Vector(tang);
                double t1 = 2*PI*RandomNumber();
                double t2 = sin(source->angle_cutoff)*RandomNumber();
                a.Rotate(n,t1);
                R3Vector vec = R3Vector(a);
                R3Vector cross = R3Vector(vec);
                cross.Cross(n);
                vec.Rotate(cross,acos(t2));
                
                newpart->position = pos;
                newpart->velocity = vec*source->velocity;
                
                
                newpart->mass = source->mass;
                newpart->fixed = source->fixed;
                newpart->drag = source->drag;
                newpart->elasticity = source->elasticity;
                newpart->lifetime = source->lifetime;
                newpart->lifetimeactive = source->lifetimeactive;
                newpart->material = source->material;
                
                scene->particles.push_back(newpart);
            }
        }
        
    }
	
	
	 for (int i = 0; i < (int)scene->enemies.size(); i++) {
        
        R3Enemy *enemy = scene->enemies[i];
        
        // spheres
        if (enemy->shape->type == R3_SPHERE_SHAPE) {
            
            // remainder term for fraction of particle
            double numberweshouldmake = delta_time * enemy->rate + enemy->remainder;
            int nparts = (int) floor(numberweshouldmake);
            enemy->remainder = numberweshouldmake - nparts;
            
            for (int p = 0; p < nparts; p++) {
                R3Particle *newpart = new R3Particle();
                
                // calculate position and velocity
                double u = RandomNumber();
                double theta = 2*PI*u;
                double v = RandomNumber();
                double phi = acos(2*v-1);
                double r = enemy->shape->sphere->Radius();
                R3Point center = enemy->shape->sphere->Center();
                
                double x = r*cos(theta)*sin(phi);
                double y = r*sin(theta)*sin(phi);
                double z = r*cos(phi);
                
                R3Vector n = R3Vector(x,y,z);
                n.Normalize();
                
                // find tangent vector, use lecture notes to get velocity
                R3Plane plane = R3Plane(R3Point(0,0,0),n);
                R3Vector a;
                do {
                    a = R3Vector(RandomNumber(),RandomNumber(),RandomNumber());
                    a.Project(plane);
                } while (a.Length() == 0.0);
                a.Normalize();
                double t1 = 2*PI*RandomNumber();
                double t2 = sin(enemy->angle_cutoff)*RandomNumber();
                a.Rotate(n,t1);
                R3Vector vec = R3Vector(a);
                R3Vector cross = R3Vector(vec);
                cross.Cross(n);
                vec.Rotate(cross,acos(t2));
                
                newpart->position = center + n*r;
                newpart->velocity = vec*enemy->velocity;
                
                //update
                newpart->mass = enemy->mass;
                newpart->fixed = enemy->fixed;
                newpart->drag = enemy->drag;
                newpart->elasticity = enemy->elasticity;
                newpart->lifetime = enemy->lifetime;
                newpart->lifetimeactive = enemy->lifetimeactive;
                newpart->material = enemy->material;
                
                scene->particles.push_back(newpart);
            }
        }
        
        
        // CIRCLE
        if (enemy->shape->type == R3_CIRCLE_SHAPE) {
            
            double numberweshouldmake = delta_time * enemy->rate + enemy->remainder;
            int nparts = (int) floor(numberweshouldmake);
            enemy->remainder = numberweshouldmake - nparts;
            
            for (int p = 0; p < nparts; p++) {
                R3Particle *newpart = new R3Particle();
                
                // calculate position and velocity
                
                double r = enemy->shape->circle->Radius();
                R3Point center = enemy->shape->circle->Center();
                R3Plane plane = enemy->shape->circle->Plane();
                R3Vector n = plane.Normal();
                n.Normalize();
                
                // get a random point on a circle
                double xcirc, ycirc;
                do {
                    xcirc = 2*r*(RandomNumber() - 0.5);
                    ycirc = 2*r*(RandomNumber() - 0.5);
                } while (xcirc*xcirc + ycirc*ycirc > r*r);
                
                // get basis vectors of circle
                R3Vector tang;
                do {
                    tang = R3Vector(RandomNumber(),RandomNumber(),RandomNumber());
                    tang.Project(plane);
                } while (tang.Length() == 0.0);
                R3Vector othertang = R3Vector(tang);
                othertang.Cross(n);
                othertang.Normalize();
                tang.Normalize();
                
                R3Point pos = center + tang*xcirc + othertang*ycirc;
                
                
                R3Vector a = R3Vector(tang);
                double t1 = 2*PI*RandomNumber();
                double t2 = sin(enemy->angle_cutoff)*RandomNumber();
                a.Rotate(n,t1);
                R3Vector vec = R3Vector(a);
                R3Vector cross = R3Vector(vec);
                cross.Cross(n);
                vec.Rotate(cross,acos(t2));
                
                newpart->position = pos;
                newpart->velocity = vec * 1.0;
                
                
                newpart->mass = enemy->mass;
                newpart->fixed = enemy->fixed;
                newpart->drag = enemy->drag;
                newpart->elasticity = enemy->elasticity;
                newpart->lifetime = enemy->lifetime;
                newpart->lifetimeactive = enemy->lifetimeactive;
                newpart->material = enemy->material;
                
                scene->particles.push_back(newpart);
            }
        }
        
    }
	
    
}
Exemple #18
0
void UpdateParticles(R3Scene *scene, double current_time, double delta_time, int integration_type)
{
    // array of ints to be deleted
    std::vector<int> tobedeleted;
    
    // for each particle
    for(int i = 0; i < scene->NParticles(); i++) {
        R3Particle *particle = scene->Particle(i);
        bool needstobedeleted = false;
        
        // check lifetime deletion
        if (particle->lifetimeactive) {
            if (particle->lifetime < 0) needstobedeleted = true;
            particle->lifetime -= delta_time;
        }
        
        R3Vector positionchange;
        R3Vector velocitychange;
        
        // Euler
        if (integration_type == EULER_INTEGRATION) {
            R3Vector f = ForceVector(scene, current_time, particle, particle->position, particle->velocity);
            positionchange = particle->velocity*delta_time;
            velocitychange = delta_time*f/particle->mass;
        }
        
        // midpoint
        else if (integration_type == MIDPOINT_INTEGRATION) {
            R3Vector f = ForceVector(scene, current_time, particle, particle->position, particle->velocity);
            R3Point xmid = particle->position + delta_time*particle->velocity/2.0;
            R3Vector vmid = particle->velocity + delta_time*f/particle->mass/2.0;
            R3Vector midf = ForceVector(scene, current_time, particle, xmid, vmid);
            positionchange = vmid*delta_time;
            velocitychange = delta_time*midf/particle->mass;
        }
        
        // rk4
        else if (integration_type == RK4_INTEGRATION) {
            R3Vector fk1 = ForceVector(scene, current_time, particle, particle->position, particle->velocity);
            R3Vector v1 = particle->velocity;
            R3Point x2 = particle->position + delta_time*particle->velocity/2.0;
            R3Vector v2 = particle->velocity + delta_time*fk1/particle->mass/2.0;
            R3Vector fk2 = ForceVector(scene, current_time, particle, x2, v2);
            R3Vector v3 = particle->velocity + delta_time*fk2/particle->mass/2.0;
            R3Vector fk3 = ForceVector(scene, current_time, particle, x2, v3);
            R3Point x4 = particle->position + delta_time*particle->velocity;
            R3Vector v4 = particle->velocity + delta_time*fk3/particle->mass;
            R3Vector fk4 = ForceVector(scene, current_time,particle,x4, v4);
            R3Vector finalf = (fk1+ 2*fk2 + 2*fk3 + fk4)/(6.0);
            R3Vector finalv = (v1+ 2*v2 +2*v3 + v4)/(6.0);
            
            positionchange = finalv*delta_time;
            velocitychange = delta_time*finalf/particle->mass;
        }
        
        
        // adaptive method
        else if (integration_type == ADAPTIVE_STEP_SIZE_INTEGRATION) {
            int numbersteps = 1;
            
            R3Vector poschange1;
            R3Vector velchange1;
            R3Vector poschange2;
            R3Vector velchange2;
            double error;
            
            // do first euler
            for (int step = 0; step < numbersteps; step++) {
                poschange2 = R3Vector(0,0,0);
                velchange2 = R3Vector(0,0,0);
                R3Vector f = ForceVector(scene, current_time, particle, particle->position + poschange2, particle->velocity + velchange2);
                poschange2 += particle->velocity*delta_time/numbersteps;
                velchange2 += delta_time*f/particle->mass/numbersteps;
            }
            // half below threshold
            do {
                poschange1 = poschange2;
                velchange1 = velchange2;
                numbersteps *= 2;
                
                // euler integrate with twice the number of steps
                for (int step = 0; step < numbersteps; step++) {
                    poschange2 = R3Vector(0,0,0);
                    velchange2 = R3Vector(0,0,0);
                    R3Vector f = ForceVector(scene, current_time, particle, particle->position + poschange2, particle->velocity + velchange2);
                    poschange2 += particle->velocity*delta_time/numbersteps/2.0;
                    velchange2 += delta_time*f/particle->mass/numbersteps/2.0;
                }
                
                double d1 = (poschange1 - poschange2).Length();
                double d2 = (velchange1 - velchange2).Length();
                
                // error for both velocity and position
                error = d1 + d2;
                
                
                
            } while (error > ADAPTIVE_THRESHOLD);
            
            positionchange = poschange2;
            velocitychange = velchange2;
            
            
        }
        
        else {
            fprintf(stderr, "invalid integration type\n");
            return;
        }
        
        
        // new values
        R3Point nextpos = particle->position + positionchange;
        R3Vector nextvel = particle->velocity + velocitychange;
        
        // check if particle needs to deleted
        int i = 0;
        for (; i < scene->NParticleSinks(); i++) {
            R3ParticleSink *sink = scene->ParticleSink(i);
            
            //sphere sink
            if (sink->shape->type == R3_SPHERE_SHAPE) {
                double r = sink->shape->sphere->Radius();
                R3Point center = sink->shape->sphere->Center();
                
                // inside the sphere?
                if (R3Distance(nextpos,center) < r) needstobedeleted = true;
                
                R3Vector toward1 = particle->position - center;
                R3Vector toward2 = nextpos - center;
                
                // goes flying through the sphere?
                if (toward1.Dot(toward2) < 0) needstobedeleted = true;
                
            }
        }
        
        if (!needstobedeleted) {
            // check for bouncing in scene
            R3Vector to = nextpos - particle->position;
            to.Normalize();
            R3Ray *r = new R3Ray(particle->position, to);
            R3Intersect intersect = ComputeIntersect(scene,scene->Root(),r);
            
            if (intersect.intersected && intersect.t < R3Distance(nextpos,particle->position)) {
                
                R3Plane plane = R3Plane(intersect.pos,intersect.norm);
                
                R3Point first = particle->position;
                R3Point second = intersect.pos;
                R3Point third = nextpos;
                
                // calculate new position
                R3Vector posreflect = third-second;
                R3Vector posreflectplanar = R3Vector(posreflect);
                posreflectplanar.Project(plane);
                R3Vector posreflectnorm = posreflect - posreflectplanar;
                R3Vector purenorm = R3Vector(posreflectnorm);
                posreflectnorm *= -1*(particle->elasticity);
                purenorm.Normalize();
                purenorm *= -1;
                nextpos = second + posreflectplanar + posreflectnorm + eps*purenorm;
                
                // calculate new velocity
                
                R3Vector velreflect = R3Vector(nextvel);
                R3Vector velreflectplanar = R3Vector(velreflect);
                velreflectplanar.Project(plane);
                R3Vector velreflectnorm = velreflect - velreflectplanar;
                purenorm = R3Vector(velreflectnorm);
                velreflectnorm *= -1*(particle->elasticity);
                purenorm *= -1;
                purenorm.Normalize();
                nextvel = velreflectnorm + velreflectplanar + eps*purenorm;
                
            }
            
            
            // update position
            if (!particle->fixed) particle->position = nextpos;
            if (!particle->fixed) particle->velocity = nextvel;
            
            
        }
        
        // push on deletion vector
        else {
            int del = i;
            tobedeleted.push_back(del);
        }
        
    }
    
    // deleting particles
    for (unsigned int i = 0; i < tobedeleted.size(); i++) {
        //swap contents of partcile vector with last element
        R3Particle *temp = scene->particles.back();
        scene->particles[scene->particles.size() - 1] = scene->particles[tobedeleted[i]];
        scene->particles[tobedeleted[i]] = temp;
        
        // delete last element
        scene->particles.pop_back();
    }
}
Exemple #19
0
R3Vector ForceVector(R3Scene *scene, double current_time, R3Particle *particle, R3Point partpos, R3Vector partvel) {
    
    // set to 0
    R3Vector f = R3Vector(0,0,0);
    
    // gravity
    f += particle->mass*scene->gravity;
    
    // drag
    f += -1*partvel*particle->drag;
    
    // springs
    for (unsigned int i = 0; i < scene->particle_springs.size(); i++) {
        R3ParticleSpring *spring = scene->particle_springs[i];
        
        if (spring->particles[0] == particle && spring->particles[1] == particle) {
            
        }
        else if (spring->particles[0] == particle) {
            R3Point q = partpos;
            R3Point p = spring->particles[1]->position;
            R3Vector vq = partvel;
            R3Vector vp = spring->particles[1]->velocity;
            
            
            
            double d = R3Distance(p,q);
            if (d > eps) {
                R3Vector D = (q-p)/d;
                double mult = spring->kd*(vq - vp).Dot(D);
                f -= (spring->ks*(d - spring->rest_length) + mult)*D;
            }
        }
        
        else if (spring->particles[1] == particle) {
            R3Point q = partpos;
            R3Point p = spring->particles[0]->position;
            R3Vector vq = partvel;
            R3Vector vp = spring->particles[0]->velocity;
            double d = R3Distance(p,q);
            if (d > eps) {
                R3Vector D = (q-p)/d;
                double mult = spring->kd*(vq - vp).Dot(D);
                f -= (spring->ks*(d - spring->rest_length) + mult)*D;
            }
        }
    }
    
    // particle interactions
    
    for (int i = 0; i < scene->NParticles(); i++) {
        R3Particle *otherparticle = scene->Particle(i);
        if (otherparticle != particle) {
            R3Vector to = otherparticle->position - partpos;
            double d = to.Length();
            to.Normalize();
            if (d > eps) f += to*GRAV_CONSTANT*particle->mass*otherparticle->mass/d/d;
        }
    }
    
    // sink interaction
    for (int i = 0; i < scene->NParticleSinks(); i++) {
        R3ParticleSink *sink = scene->ParticleSink(i);
        
        //sphere sink
        if (sink->shape->type == R3_SPHERE_SHAPE) {
            // calculate closests point
            R3Point center = sink->shape->sphere->Center();
            double r = sink->shape->sphere->Radius();
            R3Vector to = partpos - center;
            to.Normalize();
            to *= r;
            R3Point closest = center + to;
            
            // calculate force
            R3Vector toward = closest - partpos;
            double d = toward.Length();
            toward.Normalize();
            if (d > eps) {
                toward *= sink->intensity/(sink->constant_attenuation + sink->linear_attenuation*d + sink->quadratic_attenuation*d*d);
                f += toward;
            }
        }
    }
    
    return f;
}
////////////////////////////////////////////////////////////
// Generating Particles
////////////////////////////////////////////////////////////
   void GenerateParticles(R3Scene *scene, double current_time, double delta_time)
   {
   // Generate new particles for every source
      R3ParticleSource* source;
      for (int i = 0; i < scene->NParticleSources(); i++)
      {
         source = scene->ParticleSource(i);
         int num_particles = source->elapsed_time * source->rate;
      	
         if ((num_particles) >= 1)
         {
            source->elapsed_time = 0;
         	
            for (int m = 0; m < num_particles; m++)
            {
               if (source->shape->type == R3_SEGMENT_SHAPE)
               {
                  R3Segment* seg = source->shape->segment;
                  R3Point pos = seg->Start() + RandomNumber() * (seg->End() - seg->Start());
                  R3Vector dir = seg->Vector();
                  dir.Cross(R3Vector(RandomNumber() - .5, RandomNumber() - .5, RandomNumber() - .5));
                  dir.Normalize();
               	
                  R3Particle* particle = new R3Particle();
                  particle->position = pos;
                  particle->velocity = source->velocity * dir;
                  particle->mass = source->mass;
                  particle->fixed = source->fixed;
                  particle->drag = source->drag;
                  particle->elasticity = source->elasticity;
                  particle->lifetime = source->lifetime;
                  particle->material = source->material; 
               
                  scene->particles.push_back(particle);
               }
            
               if (source->shape->type == R3_BOX_SHAPE)
               {
                  R3Particle* particle = generate_random_box_particle(source);
                  scene->particles.push_back(particle);
               }
               
               else if (source->shape->type == R3_MESH_SHAPE)
               {
                  R3Mesh* mesh = source->shape->mesh;
                  double area_sum = 0.0;
                  double face_prob[mesh->faces.size()];
               
                  for (unsigned int i = 0; i < mesh->faces.size(); i++)
                  {
                     face_prob[i] = mesh->faces[i]->Area();
                     area_sum += face_prob[i];
                  }
                  for (unsigned int i = 0; i < mesh->faces.size(); i++)
                     face_prob[i] /= area_sum;	
               	
                  int source_face = discrete(face_prob, mesh->faces.size());
                  int num_verts = mesh->faces[source_face]->vertices.size();
                
                	R3Point pos = R3Point(0,0,0);
                     R3Vector dir = R3Vector(0,0,0);
                  if (num_verts == 3)
                  {
                     double a = 0;
                     double b = 0;
                     double c;
                     while (a + b <= 1)
                     {
                        a = RandomNumber();
                        b = RandomNumber();  
                     }
                  
                     a = 1 - a;
                     b = 1 - b;
                     c = 1 - a - b;
                  
                     pos = a * mesh->faces[source_face]->vertices[0]->position
                        + b * mesh->faces[source_face]->vertices[1]->position
                        + c * mesh->faces[source_face]->vertices[2]->position;
                     mesh->faces[source_face]->vertices[0]->UpdateNormal();
                     mesh->faces[source_face]->vertices[1]->UpdateNormal();
                     mesh->faces[source_face]->vertices[2]->UpdateNormal();
                  
                     dir = a * mesh->faces[source_face]->vertices[0]->normal
                        + b * mesh->faces[source_face]->vertices[0]->normal
                        + c * mesh->faces[source_face]->vertices[0]->normal;
                  }
                  else
                  {
                     int num_verts = mesh->faces[source_face]->vertices.size();
                     double vert_sum = 0.0;
                     double vert_prob[num_verts];
                  
                     for (int i = 0; i < num_verts; i++)
                     {
                        vert_prob[i] = RandomNumber();
                        vert_sum += vert_prob[i];
                     }
                  
                     R3Point pos = R3Point(0,0,0);
                     R3Vector dir = R3Vector(0,0,0);
                     for (int i = 0; i < num_verts; i++)
                     {
                        vert_prob[i] /= vert_sum;
                        pos += vert_prob[i] * mesh->faces[source_face]->vertices[i]->position;
                        mesh->faces[source_face]->vertices[i]->UpdateNormal();
                        dir += vert_prob[i] * mesh->faces[source_face]->vertices[i]->normal;
                     }
                  }
               
                  dir.Normalize();
               
                  R3Particle* particle = new R3Particle();
                  particle->position = pos;
                  particle->velocity = source->velocity * dir;
                  particle->mass = source->mass;
                  particle->fixed = source->fixed;
                  particle->drag = source->drag;
                  particle->elasticity = source->elasticity;
                  particle->lifetime = source->lifetime;
                  particle->material = source->material; 
               
                  scene->particles.push_back(particle);
               	
               }
               else if (source->shape->type == R3_SPHERE_SHAPE)
               {
                  R3Sphere* sphere = source->shape->sphere;
                  double radius = sphere->Radius();
                  R3Point center = sphere->Center();
               
                  double z = (RandomNumber() * 2 - 1);
                  double t = RandomNumber() * 2 * PI;
               
                  double r = sqrt(1.0 - z*z);
                  double x = r * cos(t) * radius;
                  double y = r * sin(t) * radius;
               
                  R3Point pos = R3Point(center.X() + x, center.Y() + y, center.Z() + z*radius);
                  R3Vector dir = (pos - center);
                  dir.Normalize();
               
                  R3Particle* particle = new R3Particle();
                  particle->position = pos;
                  particle->velocity = source->velocity * dir;
                  particle->mass = source->mass;
                  particle->fixed = source->fixed;
                  particle->drag = source->drag;
                  particle->elasticity = source->elasticity;
                  particle->lifetime = source->lifetime;
                  particle->material = source->material; 
               
                  scene->particles.push_back(particle);
               }
            }
         }
         else
            source->elapsed_time += delta_time;
      }
   }
int R3Scene::
Read(const char *filename, R3Node *node)
{
    // Open file
    FILE *fp;
    if (!(fp = fopen(filename, "r"))) {
        fprintf(stderr, "Unable to open file %s", filename);
        return 0;
    }
    
    // Create array of materials
    vector<R3Material *> materials;
    
    // Create default material
    R3Material *default_material = new R3Material();
    default_material->ka = R3Rgb(0.2, 0.2, 0.2, 1);
    default_material->kd = R3Rgb(0.5, 0.5, 0.5, 1);
    default_material->ks = R3Rgb(0.5, 0.5, 0.5, 1);
    default_material->kt = R3Rgb(0.0, 0.0, 0.0, 1);
    default_material->emission = R3Rgb(0, 0, 0, 1);
    default_material->shininess = 10;
    default_material->indexofrefraction = 1;
    default_material->texture = NULL;
    default_material->id = 0;
    
    // Create stack of group information
    const int max_depth = 1024;
    R3Node *group_nodes[max_depth] = { NULL };
    R3Material *group_materials[max_depth] = { NULL };
    group_nodes[0] = (node) ? node : root;
    group_materials[0] = default_material;
    int depth = 0;
    
    // Read body
    char cmd[128];
    int command_number = 1;
    while (fscanf(fp, "%s", cmd) == 1) {
        if (cmd[0] == '#') {
            // Comment -- read everything until end of line
            do { cmd[0] = fgetc(fp); } while ((cmd[0] >= 0) && (cmd[0] != '\n'));
        }
        else if (!strcmp(cmd, "tri")) {
            // Read data
            int m;
            R3Point p1, p2, p3;
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf%lf%lf%lf%lf", &m, 
                       &p1[0], &p1[1], &p1[2], &p2[0], &p2[1], &p2[2], &p3[0], &p3[1], &p3[2]) != 10) {
                fprintf(stderr, "Unable to read triangle at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at tri command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create mesh
            R3Mesh *mesh = new R3Mesh();
            vector<R3MeshVertex *> vertices;
            vertices.push_back(mesh->CreateVertex(p1, R3zero_vector, R2zero_point));
            vertices.push_back(mesh->CreateVertex(p2, R3zero_vector, R2zero_point));
            vertices.push_back(mesh->CreateVertex(p3, R3zero_vector, R2zero_point));
            mesh->CreateFace(vertices);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_MESH_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = mesh;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = R3null_box;
            node->bbox.Union(p1);
            node->bbox.Union(p2);
            node->bbox.Union(p3);
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "box")) {
            // Read data
            int m;
            R3Point p1, p2;
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf%lf", &m, &p1[0], &p1[1], &p1[2], &p2[0], &p2[1], &p2[2]) != 7) {
                fprintf(stderr, "Unable to read box at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at box command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create box
            R3Box *box = new R3Box(p1, p2);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_BOX_SHAPE;
            shape->box = box;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = NULL;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = *box;
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "sphere")) {
            // Read data
            int m;
            R3Point c;
            double r;
            if (fscanf(fp, "%d%lf%lf%lf%lf", &m, &c[0], &c[1], &c[2], &r) != 5) {
                fprintf(stderr, "Unable to read sphere at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at sphere command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create sphere
            R3Sphere *sphere = new R3Sphere(c, r);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_SPHERE_SHAPE;
            shape->box = NULL;
            shape->sphere = sphere;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = NULL;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = sphere->BBox();
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "cylinder")) {
            // Read data
            int m;
            R3Point c;
            double r, h;
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf", &m, &c[0], &c[1], &c[2], &r, &h) != 6) {
                fprintf(stderr, "Unable to read cylinder at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at cyl command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create cylinder
            R3Cylinder *cylinder = new R3Cylinder(c, r, h);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_CYLINDER_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = cylinder;
            shape->cone = NULL;
            shape->mesh = NULL;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = cylinder->BBox();
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "mesh")) {
            // Read data
            int m;
            char meshname[256];
            if (fscanf(fp, "%d%s", &m, meshname) != 2) {
                fprintf(stderr, "Unable to parse mesh command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at cone command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Get mesh filename
            char buffer[2048];
            strcpy(buffer, filename);
            char *bufferp = strrchr(buffer, '/');
            if (bufferp) *(bufferp+1) = '\0';
            else buffer[0] = '\0';
            strcat(buffer, meshname);
            
            // Create mesh
            R3Mesh *mesh = new R3Mesh();
            if (!mesh) {
                fprintf(stderr, "Unable to allocate mesh\n");
                return 0;
            }
            
            // Read mesh file
            if (!mesh->Read(buffer)) {
                fprintf(stderr, "Unable to read mesh: %s\n", buffer);
                return 0;
            }
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_MESH_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = mesh;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = mesh->bbox;
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        //in order to set the arwing node as a global in GlutTest.cpp
        else if (!strcmp(cmd, "arwing")) {
            // Read data
            int m;
            char meshname[256];
            if (fscanf(fp, "%d%s", &m, meshname) != 2) {
                fprintf(stderr, "Unable to parse mesh command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at cone command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Get mesh filename
            char buffer[2048];
            strcpy(buffer, filename);
            char *bufferp = strrchr(buffer, '/');
            if (bufferp) *(bufferp+1) = '\0';
            else buffer[0] = '\0';
            strcat(buffer, meshname);
            
            // Create mesh
            R3Mesh *mesh = new R3Mesh();
            if (!mesh) {
                fprintf(stderr, "Unable to allocate mesh\n");
                return 0;
            }
            
            // Read mesh file
            if (!mesh->Read(buffer)) {
                fprintf(stderr, "Unable to read mesh: %s\n", buffer);
                return 0;
            }
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_MESH_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = mesh;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = mesh->bbox;
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
            arwingNode = node;
        }
        /* unneeded
        //turret - basically a box that shoots
        else if (!strcmp(cmd, "turret")) {
            // Read data
            int m;
            R3Point p1, p2;
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf%lf", &m, &p1[0], &p1[1], &p1[2], &p2[0], &p2[1], &p2[2]) != 7) {
                fprintf(stderr, "Unable to read box at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at box command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create box
            R3Box *box = new R3Box(p1, p2);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_BOX_SHAPE;
            shape->box = box;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = NULL;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->cumulativeTransformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = *box;
            node->enemy = new SFEnemy();
            
            //list properties of the turret
            node->enemy->position = box->Centroid();
            node->enemy->projectileSource = box->Centroid();
            node->enemy->node = node;
            
        //    node->enemy->projectileSource.InverseTransform(node->transformation);
        //    node->enemy->projectileSource.SetZ(node->enemy->projectileSource.Z() - .5 * (box->ZMax() - box->ZMin()));
            
            enemies.push_back(node->enemy);
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        } */
        //enemy - a mesh that moves and shoots
        else if (!strcmp(cmd, "enemy")) {
            // Read data
            int fixed;
            int m;
            float vx, vy, vz;
            int h;
            char meshname[256];
				float particle_velocity;
				int firing_rate;
            if (fscanf(fp, "%d%d%s%f%f%f%d%f%d", &fixed, &m, meshname, &vx, &vy, &vz, &h,&particle_velocity, &firing_rate) != 9) {
                fprintf(stderr, "Unable to parse enemy command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at cone command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Get mesh filename
            char buffer[2048];
            strcpy(buffer, filename);
            char *bufferp = strrchr(buffer, '/');
            if (bufferp) *(bufferp+1) = '\0';
            else buffer[0] = '\0';
            strcat(buffer, meshname);
            
            
            R3Vector *initialVelocity = new R3Vector(vx, vy, vz);
            
            // Create mesh
            R3Mesh *mesh = new R3Mesh();
            if (!mesh) {
                fprintf(stderr, "Unable to allocate mesh\n");
                return 0;
            }
            
            // Read mesh file
            if (!mesh->Read(buffer)) {
                fprintf(stderr, "Unable to read mesh: %s\n", buffer);
                return 0;
            }
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_MESH_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = mesh;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->cumulativeTransformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = mesh->bbox;
            node->enemy = new SFEnemy(fixed, mesh, *initialVelocity, h, particle_velocity, firing_rate);
            
            node->enemy->position = shape->mesh->Center();
            node->enemy->projectileSource = shape->mesh->Center();
            node->enemy->node = node;
            
            enemies.push_back(node->enemy);
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "cone")) {
            // Read data
            int m;
            R3Point c;
            double r, h;
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf", &m, &c[0], &c[1], &c[2], &r, &h) != 6) {
                fprintf(stderr, "Unable to read cone at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at cone command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create cone
            R3Cone *cone = new R3Cone(c, r, h);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_CONE_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = cone;
            shape->mesh = NULL;
            shape->segment = NULL;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = cone->BBox();
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "line")) {
            // Read data
            int m;
            R3Point p1, p2;
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf%lf", &m, &p1[0], &p1[1], &p1[2], &p2[0], &p2[1], &p2[2]) != 7) {
                fprintf(stderr, "Unable to read line at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at line command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create segment
            R3Segment *segment = new R3Segment(p1, p2);
            
            // Create shape
            R3Shape *shape = new R3Shape();
            shape->type = R3_SEGMENT_SHAPE;
            shape->box = NULL;
            shape->sphere = NULL;
            shape->cylinder = NULL;
            shape->cone = NULL;
            shape->mesh = NULL;
            shape->segment = segment;
            
            // Create shape node
            R3Node *node = new R3Node();
            node->transformation = R3identity_matrix;
            node->material = material;
            node->shape = shape;
            node->bbox = segment->BBox();
            node->enemy = NULL;
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "begin")) {
            // Read data
            int m;
            double matrix[16];
            if (fscanf(fp, "%d%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", &m, 
                       &matrix[0], &matrix[1], &matrix[2], &matrix[3], 
                       &matrix[4], &matrix[5], &matrix[6], &matrix[7], 
                       &matrix[8], &matrix[9], &matrix[10], &matrix[11], 
                       &matrix[12], &matrix[13], &matrix[14], &matrix[15]) != 17) {
                fprintf(stderr, "Unable to read begin at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get material
            R3Material *material = group_materials[depth];
            if (m >= 0) {
                if (m < (int) materials.size()) {
                    material = materials[m];
                }
                else {
                    fprintf(stderr, "Invalid material id at cone command %d in file %s\n", command_number, filename);
                    return 0;
                }
            }
            
            // Create new group node
            R3Node *node = new R3Node();
            node->transformation = R3Matrix(matrix);
            node->material = NULL;
            node->shape = NULL;
            node->bbox = R3null_box;
            node->enemy = NULL;
            
            // Push node onto stack
            depth++;
            group_nodes[depth] = node;
            group_materials[depth] = material;
        }
        else if (!strcmp(cmd, "end")) {
            // Pop node from stack
            R3Node *node = group_nodes[depth];
            depth--;
            
            // Transform bounding box
            node->bbox.Transform(node->transformation);
            
            // Insert node
            group_nodes[depth]->bbox.Union(node->bbox);
            group_nodes[depth]->children.push_back(node);
            node->parent = group_nodes[depth];
        }
        else if (!strcmp(cmd, "material")) {
            // Read data
            R3Rgb ka, kd, ks, kt, e;
            double n, ir;
            char texture_name[256];
            if (fscanf(fp, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%s", 
                       &ka[0], &ka[1], &ka[2], &kd[0], &kd[1], &kd[2], &ks[0], &ks[1], &ks[2], &kt[0], &kt[1], &kt[2], 
                       &e[0], &e[1], &e[2], &n, &ir, texture_name) != 18) {
                fprintf(stderr, "Unable to read material at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Create material
            R3Material *material = new R3Material();
            material->ka = ka;
            material->kd = kd;
            material->ks = ks;
            material->kt = kt;
            material->emission = e;
            material->shininess = n;
            material->indexofrefraction = ir;
            material->texture = NULL;
            
            // Read texture
            if (strcmp(texture_name, "0")) {
                // Get texture filename
                char buffer[2048];
                strcpy(buffer, filename);
                char *bufferp = strrchr(buffer, '/');
                if (bufferp) *(bufferp+1) = '\0';
                else buffer[0] = '\0';
                strcat(buffer, texture_name);
                
                // Read texture image
                material->texture = new R2Image();
                if (!material->texture->Read(buffer)) {
                    fprintf(stderr, "Unable to read texture from %s at command %d in file %s\n", buffer, command_number, filename);
                    return 0;
                }
            }
            
            // Insert material
            materials.push_back(material);
        }
        else if (!strcmp(cmd, "dir_light")) {
            // Read data
            R3Rgb c;
            R3Vector d;
            if (fscanf(fp, "%lf%lf%lf%lf%lf%lf", 
                       &c[0], &c[1], &c[2], &d[0], &d[1], &d[2]) != 6) {
                fprintf(stderr, "Unable to read directional light at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Normalize direction
            d.Normalize();
            
            // Create light
            R3Light *light = new R3Light();
            light->type = R3_DIRECTIONAL_LIGHT;
            light->color = c;
            light->position = R3Point(0, 0, 0);
            light->direction = d;
            light->radius = 0;
            light->constant_attenuation = 0;
            light->linear_attenuation = 0;
            light->quadratic_attenuation = 0;
            light->angle_attenuation = 0;
            light->angle_cutoff = M_PI;
            
            // Insert light
            lights.push_back(light);
        }
        else if (!strcmp(cmd, "point_light")) {
            // Read data
            R3Rgb c;
            R3Point p;
            double ca, la, qa;
            if (fscanf(fp, "%lf%lf%lf%lf%lf%lf%lf%lf%lf", &c[0], &c[1], &c[2], &p[0], &p[1], &p[2], &ca, &la, &qa) != 9) {
                fprintf(stderr, "Unable to read point light at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Create light
            R3Light *light = new R3Light();
            light->type = R3_POINT_LIGHT;
            light->color = c;
            light->position = p;
            light->direction = R3Vector(0, 0, 0);
            light->radius = 0;
            light->constant_attenuation = ca;
            light->linear_attenuation = la;
            light->quadratic_attenuation = qa;
            light->angle_attenuation = 0;
            light->angle_cutoff = M_PI;
            
            // Insert light
            lights.push_back(light);
        }
        else if (!strcmp(cmd, "spot_light")) {
            // Read data
            R3Rgb c;
            R3Point p;
            R3Vector d;
            double ca, la, qa, sc, sd;
            if (fscanf(fp, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", 
                       &c[0], &c[1], &c[2], &p[0], &p[1], &p[2], &d[0], &d[1], &d[2], &ca, &la, &qa, &sc, &sd) != 14) {
                fprintf(stderr, "Unable to read point light at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Normalize direction
            d.Normalize();
            
            // Create light
            R3Light *light = new R3Light();
            light->type = R3_SPOT_LIGHT;
            light->color = c;
            light->position = p;
            light->direction = d;
            light->radius = 0;
            light->constant_attenuation = ca;
            light->linear_attenuation = la;
            light->quadratic_attenuation = qa;
            light->angle_attenuation = sd;
            light->angle_cutoff = sc;
            
            // Insert light
            lights.push_back(light);
        }
        else if (!strcmp(cmd, "area_light")) {
            // Read data
            R3Rgb c;
            R3Point p;
            R3Vector d;
            double radius, ca, la, qa;
            if (fscanf(fp, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", 
                       &c[0], &c[1], &c[2], &p[0], &p[1], &p[2], &d[0], &d[1], &d[2], &radius, &ca, &la, &qa) != 13) {
                fprintf(stderr, "Unable to read area light at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Normalize direction
            d.Normalize();
            
            // Create light
            R3Light *light = new R3Light();
            light->type = R3_AREA_LIGHT;
            light->color = c;
            light->position = p;
            light->direction = d;
            light->radius = radius;
            light->constant_attenuation = ca;
            light->linear_attenuation = la;
            light->quadratic_attenuation = qa;
            light->angle_attenuation = 0;
            light->angle_cutoff = M_PI;
            
            // Insert light
            lights.push_back(light);
        }
        else if (!strcmp(cmd, "camera")) {
            // Read data
            double px, py, pz, dx, dy, dz, ux, uy, uz, xfov, neardist, fardist;
            if (fscanf(fp, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", &px, &py, &pz, &dx, &dy, &dz, &ux, &uy, &uz, &xfov, &neardist, &fardist) != 12) {
                fprintf(stderr, "Unable to read camera at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Assign camera
            camera.eye = R3Point(px, py, pz);
            camera.towards = R3Vector(dx, dy, dz);
            camera.towards.Normalize();
            camera.up = R3Vector(ux, uy, uz);
            camera.up.Normalize();
            camera.right = camera.towards % camera.up;
            camera.right.Normalize();
            camera.up = camera.right % camera.towards;
            camera.up.Normalize();
            camera.xfov = xfov;
            camera.yfov = xfov;
            camera.neardist = neardist;
            camera.fardist = fardist;
        }
        else if (!strcmp(cmd, "include")) {
            // Read data
            char scenename[256];
            if (fscanf(fp, "%s", scenename) != 1) {
                fprintf(stderr, "Unable to read include command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Get scene filename
            char buffer[2048];
            strcpy(buffer, filename);
            char *bufferp = strrchr(buffer, '/');
            if (bufferp) *(bufferp+1) = '\0';
            else buffer[0] = '\0';
            strcat(buffer, scenename);
            
            // Read scene from included file
            if (!Read(buffer, group_nodes[depth])) {
                fprintf(stderr, "Unable to read included scene: %s\n", buffer);
                return 0;
            }
        }
        else if (!strcmp(cmd, "background")) {
            // Read data
            double r, g, b;
            if (fscanf(fp, "%lf%lf%lf", &r, &g, &b) != 3) {
                fprintf(stderr, "Unable to read background at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Assign background color
            background = R3Rgb(r, g, b, 1);
        }
        else if (!strcmp(cmd, "ambient")) {
            // Read data
            double r, g, b;
            if (fscanf(fp, "%lf%lf%lf", &r, &g, &b) != 3) {
                fprintf(stderr, "Unable to read ambient at command %d in file %s\n", command_number, filename);
                return 0;
            }
            
            // Assign ambient color
            ambient = R3Rgb(r, g, b, 1);
        }
        else {
            fprintf(stderr, "Unrecognized command %d in file %s: %s\n", command_number, filename, cmd);
            return 0;
        }
        
        // Increment command number
        command_number++;
    }
    
    // Update bounding box
    bbox = root->bbox;
    
    // Provide default camera
    if (camera.xfov == 0) {
        double scene_radius = bbox.DiagonalRadius();
        R3Point scene_center = bbox.Centroid();
        camera.towards = R3Vector(0, 0, -1);
        camera.up = R3Vector(0, 1, 0);
        camera.right = R3Vector(1, 0, 0);
        camera.eye = scene_center - 3 * scene_radius * camera.towards;
        camera.xfov = 0.25;
        camera.yfov = 0.25;
        camera.neardist = 0.01 * scene_radius;
        camera.fardist = 100 * scene_radius;
    }
    
    // Provide default lights
    if (lights.size() == 0) {
        // Create first directional light
        R3Light *light = new R3Light();
        R3Vector direction(-3,-4,-5);
        direction.Normalize();
        light->type = R3_DIRECTIONAL_LIGHT;
        light->color = R3Rgb(1,1,1,1);
        light->position = R3Point(0, 0, 0);
        light->direction = direction;
        light->radius = 0;
        light->constant_attenuation = 0;
        light->linear_attenuation = 0;
        light->quadratic_attenuation = 0;
        light->angle_attenuation = 0;
        light->angle_cutoff = M_PI;
        lights.push_back(light);
        
        // Create second directional light
        light = new R3Light();
        direction = R3Vector(3,2,3);
        direction.Normalize();
        light->type = R3_DIRECTIONAL_LIGHT;
        light->color = R3Rgb(0.5, 0.5, 0.5, 1);
        light->position = R3Point(0, 0, 0);
        light->direction = direction;
        light->radius = 0;
        light->constant_attenuation = 0;
        light->linear_attenuation = 0;
        light->quadratic_attenuation = 0;
        light->angle_attenuation = 0;
        light->angle_cutoff = M_PI;
        lights.push_back(light);
    }
    
    // Close file
    fclose(fp);
    
    // Return success
    return 1;
}
// Compute the radiance emitted back along a given ray from a given intersection
R3Rgb ComputeRadiance(R3Scene *scene, R3Ray &ray, R3Intersection &intersection, int depth, int max_depth, int distrib)
{
  R3Rgb il(0, 0, 0, 1);
  R3Material *mat = intersection.node->material;
  // add a small epsilon normal to the intersection point so light and secondary rays don't intersect with the same surface
  R3Point offset_intersection_pos = intersection.position + intersection.normal * EPSILON;
  // v = a unit vector pointing from the intersection to the ray origin
  R3Vector v = -ray.Vector();

  // if we are not using distributed ray tracing...
  if (distrib == 0)
  {
    // include lighting from all scene lights
    for (int i = 0; i < scene->NLights(); i++)
    {
      R3Light *light = scene->Light(i);

      // l = a unit vector pointing from the intersection to a light
      // d = the distance between the intersection and a light
      R3Vector l;
      double d;
      if (light->type == R3_DIRECTIONAL_LIGHT)
      {
        l = -light->direction;
        d = numeric_limits<double>::infinity();
      }
      else
      {
        l = (light->position - intersection.position);
        l.Normalize();
        d = (light->position - intersection.position).Length();
      }

      // construct a ray from the intersection toward the light
      R3Ray light_ray(offset_intersection_pos, l);

      // compute a shadow intersection and if one exists (that is between the intersection and the light), ignore the light
      R3Intersection shadow_intersection = ComputeIntersection(scene, scene->Root(), light_ray, d);
      if (shadow_intersection.hit && shadow_intersection.t < d)
        continue;

      // compute the luminance of the light at the intersection position
      R3Rgb light_il = ComputeLuminance(light, intersection.position);

      // evaluate the Phong BRDF equations for diffuse and specular reflection
      double cos_nl = l.Dot(intersection.normal);

      // r = l reflected across the intersection normal
      R3Vector r = 2 * cos_nl * intersection.normal - l; 
      double cos_vr = v.Dot(r);
      // if the light is shining on the front of the surface, include diffuse lighting
      // if the light is also less than 90 degrees from the camera vector, include specular lighting
      if (cos_nl > 0)
      {
        il += light_il * mat->kd * cos_nl;
        if (cos_vr > 0)
          il += light_il * mat->ks * pow(cos_vr, mat->shininess);
      }
    }
  }
  else // if we ARE using distributed...
  {
    if (depth < max_depth)
    {
      int nrays = 0;
      R3Rgb dist_il(0, 0, 0, 1);
      while (nrays < distrib)
      {
        // randomly sample outgoing directions using random variables with normal distributions
        double u1 = (double)rand()/RAND_MAX;
        double u2 = (double)rand()/RAND_MAX;
        double u3 = (double)rand()/RAND_MAX;
        double u4 = (double)rand()/RAND_MAX;

        double s1 = sqrt(-2 * log(u1));
        double rx = s1 * cos(2 * 3.14159 * u2);
        double ry = s1 * sin(2 * 3.14159 * u2);
        double s2 = sqrt(-2 * log(u3));
        double rz = s2 * cos(2 * 3.14159 * u4);

        R3Vector l(rx, ry, rz);
        l.Normalize();

        // check if vector is in the right hemisphere
        if (l.Dot(intersection.normal) < 0)
          continue;

        // generate a random ray and recursively compute the radiance it contributes
        R3Ray rray(offset_intersection_pos, l);
        R3Rgb rgb = ComputeRadiance(scene, rray, depth + 1, max_depth, distrib);
        
        // evaluate the Phong BRDF equations for diffuse and specular reflection
        double cos_nl = l.Dot(intersection.normal);

        // r = l reflected across the intersection normal
        R3Vector r = 2 * cos_nl * intersection.normal - l; 
        double cos_vr = v.Dot(r);
        // if the light is shining on the front of the surface, include diffuse lighting
        // if the light is also less than 90 degrees from the camera vector, include specular lighting
        if (cos_nl > 0)
        {
          dist_il += rgb * mat->kd * cos_nl;
          if (cos_vr > 0)
            dist_il += rgb * mat->ks * pow(cos_vr, mat->shininess);
        }

        nrays++;
      }

      il += dist_il / nrays;
    }
  }

  // if the material has a specular component and the maximum depth hasn't been reached, reflect the camera ray from the
  // intersection surface and recusively evaluate its radiance
  if (!mat->ks.IsBlack() && depth < max_depth)
  {
    R3Ray spec_ray = GetReflectedRay(intersection, ray);
    R3Rgb spec = ComputeRadiance(scene, spec_ray, depth + 1, max_depth, distrib);
    il += spec * mat->ks;
  }

  // add refraction component for both distributed and non-distributed techniques
  if (!mat->kt.IsBlack() && depth < max_depth)
  {
    R3Ray trans_ray = GetRefractedRay(intersection, ray, mat->indexofrefraction, scene);
    R3Rgb trans = ComputeRadiance(scene, trans_ray, depth + 1, max_depth, distrib);
    il += trans * mat->kt;
  }

  // add ambient and emission components
  il += mat->emission;
  il += mat->ka * scene->ambient;

  return il;
}