#include "scene.h" #include #include using namespace std; /* * Utility function to convert a string to int */ static int ival(string inp) { return (int) atoi(inp.c_str()); } /* * Utility function to convert a string to double */ static double dval(string inp) { return (double) atof(inp.c_str()); } /* * Utility function to tokenize a string on spaces */ static vector split(string inp) { vector ans; inp += " "; for (int i = 0, j = 0; i < inp.length(); i++) if (inp[i] == ' ') { ans.push_back(inp.substr(j, i - j)); j = i + 1; } return ans; } Shape* Scene::getObj( Ray& r ) { Shape* res = NULL; double distance = MAX_DIST; for ( ObjVec::iterator I = objs.begin(); I != objs.end(); ++I ) { Vec3D* v = (*I)->rayIntersectsMe( r ); if (v) { Vec3D delta = (*v)-r.getOrig(); if ( delta.dot(r.getDir()) > 0 && delta.mag() < distance ) { distance = ((*v) - r.getOrig()).mag(); res = (*I); r.collide(v); } else delete v; } } return res; } /* * Transforms given point so that it "lines up" with the gaze direction */ Vec3D Scene::transform(const Vec3D & p) { Vec3D result(0,0,0); // Get the three basis vectors corresponding to this camera position Vec3D w = (gaze*-1).norm(); Vec3D u = (up.cross(w)).norm(); Vec3D v = w.cross(u); #ifdef DEBUG cout << "u: " << u << endl; cout << "v: " << v << endl; cout << "w: " << w << endl; #endif // Now, simply transform the point result.setValues(u.dot(p - eye), v.dot(p - eye), w.dot(p - eye)); #ifdef DEBUG cout << "Point transformed to: " << endl; cout << p << endl; #endif return result; } Vec3D Scene::rayTrace( Ray& r, int d ) { Shape* s = getObj( r ); if (!s) return Vec3D(0,0,0); Vec3D E = (eye - *r.collide()).norm(); double specula = 0.0, diffuse = 0.0; for (int i = 0; i < lights.size(); i++) { Vec3D l = lights[i]->getCenter(); // does a ray from the light source hit the object? Ray lr = Ray::mkRayPoints( l, *r.collide() ); getObj(lr); // if not, don't do local lighting, an object is blocking the light if ( lr.collide() != NULL && (*lr.collide()) == (*r.collide()) ) { Vec3D L = (l - *r.collide()).norm(); // diffuse = Kd * L dot N // Kd diffuse constant; light to surface; double tmpDiffuse = 0.7*L.dot( s->normalAt( *(r.collide()) ).norm() ); if (tmpDiffuse > 0) diffuse += tmpDiffuse; // specular = Ks * (H dot N) ^ n double tmpSpec = s->normalAt(*r.collide()).norm().dot( (E+L).norm() ); specula += 0.5*pow(tmpSpec, 2); } } // Local illumination Vec3D objCol = s->getColVec(); if (s->textured()) objCol = objCol * s->intensityAt(*r.collide()); Vec3D iLocal = objCol*(diffuse + 0.05) + Vec3D(1,1,1)*specula; Vec3D iReflect(0,0,0); #ifdef DEBUG if (MAX_DEPTH <= d) { cout << "Hit max recursion, returning " << iLocal << " base " << s->getColVec() << endl; cout << "diff: " << diffuse << " spec: " << specula << endl; } #endif if (s->getReflect() > 0 && d < MAX_DEPTH) { Vec3D N = s->normalAt( *r.collide() ).norm(); Vec3D refl = N + N*(2*E.dot(N)); Ray reflectRay = Ray::mkRayDirected( *r.collide()+refl*EPS, refl ); iReflect = rayTrace( reflectRay, d+1 )*s->getReflect(); } #ifdef DEBUG if (d > 1) cout << "Ray reflected with colour " << iReflect << endl; #endif return iLocal + iReflect; } int Scene::rayTrace(double x, double y) { Ray r = Ray::mkRayPoints(eye,Vec3D(x, y, -1)); Vec3D colour = rayTrace( r, 0 ); return Shape::rgbToInt( colour ); } Scene::Scene( string fn ) { ifstream sc(fn.c_str()); // process the scene file and create all the objects we need char buf[200]; vector toks; // First line = 3 coords for eye position sc.getline(buf, 200); #ifdef DEBUG cout << "eye position: " << string(buf) << endl; #endif toks = split(string(buf)); setEyePos(Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2]))); // Second line = 3 coords for gaze vector toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "gaze vector: " << string(buf) << endl; #endif toks = split(string(buf)); setGazeVec(Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2]))); // Third line = 3 coords for up vector toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "up vector: " << string(buf) << endl; #endif toks = split(string(buf)); setUpVec(Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2]))); // Now we read in our objects while (!sc.eof()) { toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "object type: " << string(buf) << endl; #endif toks = split(string(buf)); if (toks[0] == "SPHERE") { // SPHERE ==> lines like "cx cy cz radius [R G B]" while (toks[0] != "END") { toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "sphere descriptor: " << string(buf) << endl; #endif toks = split(string(buf)); if (toks[0] != "END") { bool colorSupplied = toks.size() > 4; Shape* s; if (colorSupplied) { Vec3D center = Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2])); center = transform(center); s = new Sphere(center[0], center[1], center[2], dval(toks[3]), // radius dval(toks[4]), dval(toks[5]), dval(toks[6])); } else { Vec3D center = Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2])); center = transform(center); s = new Sphere(center[0], center[1], center[2], dval(toks[3])); // radius } addObj(s); } } } else if (toks[0] == "PLANE") { // PLANE ==> lines like "x1 y1 z1 v1x v1y v1z v2x v2y v2z a_lo b_lo a_hi b_hi [R G B]" while (toks[0] != "END") { toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "plane descriptor: " << string(buf) << endl; #endif toks = split(string(buf)); if (toks[0] != "END") { bool colorSupplied = toks.size() > 12; Shape* s; if (colorSupplied) { Vec3D p1 = Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2])); Vec3D p2 = Vec3D(dval(toks[3]), dval(toks[4]), dval(toks[5])); Vec3D p3 = Vec3D(dval(toks[6]), dval(toks[7]), dval(toks[8])); Vec3D p4 = Vec3D(dval(toks[9]), dval(toks[10]), dval(toks[11])); p1 = transform(p1); p2 = transform(p2); p3 = transform(p3); p4 = transform(p4); s = new Plane(p1, p2, p3, p4, dval(toks[12]), dval(toks[13]), dval(toks[14])); } else { Vec3D p1 = Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2])); Vec3D p2 = Vec3D(dval(toks[3]), dval(toks[4]), dval(toks[5])); Vec3D p3 = Vec3D(dval(toks[6]), dval(toks[7]), dval(toks[8])); Vec3D p4 = Vec3D(dval(toks[9]), dval(toks[10]), dval(toks[11])); p1 = transform(p1); p2 = transform(p2); p3 = transform(p3); p4 = transform(p4); s = new Plane(p1, p2, p3, p4); } addObj(s); } } } else if (toks[0] == "CYLINDER") { // CYLINDER ==> lines like "z r uy ly [r g b]" while (toks[0] != "END") { toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "cylinder descriptor: " << string(buf) << endl; #endif toks = split(string(buf)); if (toks[0] != "END") { bool colorSupplied = toks.size() > 4; Shape* s; if (colorSupplied) { double _z = dval(toks[0]); double _radius = dval(toks[1]); double _uy = dval(toks[2]); double _ly = dval(toks[3]); s = new Cylinder(transform(Vec3D(0., 0., _z)).getV2(), _radius, transform(Vec3D(0., _uy, 0.)).getV1(), transform(Vec3D(0., _ly, 0.)).getV1(), ival(toks[4]), ival(toks[5]), ival(toks[6])); } else { double _z = dval(toks[0]); double _radius = dval(toks[1]); double _uy = dval(toks[2]); double _ly = dval(toks[3]); s = new Cylinder(transform(Vec3D(0., 0., _z)).getV2(), _radius, transform(Vec3D(0., _uy, 0.)).getV1(), transform(Vec3D(0., _ly, 0.)).getV1()); } addObj(s); } } } else if (toks[0] == "LIGHT") { // LIGHT ==> lines like "cx cy cz radius" while (toks[0] != "END") { toks.clear(); sc.getline(buf, 200); #ifdef DEBUG cout << "light descriptor: " << string(buf) << endl; #endif toks = split(string(buf)); if (toks[0] != "END") { Vec3D center = Vec3D(dval(toks[0]), dval(toks[1]), dval(toks[2])); center = transform(center); Sphere * s = new Sphere(center[0], center[1], center[2], dval(toks[3])); // radius = light intensity addLight(s); } } } } eye.setValues(0, 0, 0); sc.close(); }