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; }
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; }
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; }
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; }
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; }
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); }
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); }
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); }
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; }
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()); }
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); } } } }
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(); } }
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; }