#include #include #include #include #include #include #include "vertex.h" #include "camera.h" #include "OBJLoader.h" using namespace std; struct Sphere{ double center[3]; double radius; double ambient[3]; double diffuse[3]; double specular[3]; double shininess; double emission[3]; Mat4 matrix; Mat4 inverseMatrix; double dielectric; double reflectivity; double translucency; }; struct Triangle{ vertex v[3]; //has normals }; struct Model{ ImageColor texture; Obj obj; vec3 center; double radius; //double location[3]; }; enum{DIRECTIONAL, POINT, AREA}; struct Light{ int type; double loc[3]; double radius; double dir[3]; double color[3]; double attenuation[3]; }; class Scene{ private: int maxverts, maxvertnorms;//required by assignment int curvert, curvertnorm;//required by assignment int lightnum;//required by assignment public: Model requiredModel; int sizex; int sizey; int maxdepth; FILE *inputfile ; //added variables camera cam; vector spheres; vector models; vector lights; vector triangles; char line[1000], command[1000]; char texfile[1000], objfile[1000]; vec3 globalLight; //face variables double ambient[3]; double diffuse[3]; double specular[3]; double shininess; double emission[3]; //extra face variables double dielectric; double reflectivity; double translucency; //lighting temp variables double attenuation[3]; ModelViewMatrix modelView; intersectionTest testScene(vec3 position, vec3 viewVec) { intersectionTest test; test.initializeTest(); intersectionTest boundingTest; boundingTest.initializeTest(); //go through our entire scene for each pixel bool intersected; for(int i = 0; i < spheres.size(); i++) { //for each sphere we need to do the transformations on the view vector Vec4 tempPosition = Vec4(position.x, position.y, position.z, 1); tempPosition = matrixByVector(spheres[i].inverseMatrix, tempPosition); Vec4 tempView = Vec4(viewVec.x, viewVec.y, viewVec.z, 0); tempView = matrixByVector(spheres[i].inverseMatrix, tempView); //lastly we do the translation //tempPosition = Vec4(position.x - spheres[i].matrix.m[0][3], position.y - spheres[i].matrix.m[1][3], position.z - spheres[i].matrix.m[2][3], 1); vec3 tempPos = vec3(tempPosition.v[0], tempPosition.v[1], tempPosition.v[2]); vec3 tempV = vec3(tempView.v[0], tempView.v[1], tempView.v[2]); intersected = test.raySphereIntersection(tempPos, tempV, vec3(spheres[i].center[0], spheres[i].center[1], spheres[i].center[2]), spheres[i].radius); if(intersected == true) { test.ambientColor = vec3(spheres[i].ambient[0], spheres[i].ambient[1], spheres[i].ambient[2]); test.diffuseColor = vec3(spheres[i].diffuse[0], spheres[i].diffuse[1], spheres[i].diffuse[2]); test.specularColor = vec3(spheres[i].specular[0], spheres[i].specular[1], spheres[i].specular[2]); test.shininess = spheres[i].shininess; test.emissionColor = vec3(spheres[i].emission[0], spheres[i].emission[1], spheres[i].emission[2]); test.translucency = spheres[i].translucency; test.reflectivity = spheres[i].reflectivity; test.dielectric = spheres[i].dielectric; //we now have a test that's found our point in inverse space, we need to get it back into regular space Vec4 realPosition = Vec4(test.intersectionPosition.x, test.intersectionPosition.y, test.intersectionPosition.z, 1); realPosition = matrixByVector(spheres[i].matrix, realPosition); //need to update the normal direction, this is tricky, can't transform the normal itself, have to find the tangent plane, transform that and then find the new normal //first need to find the tangent plane vec3 binormal; if(abs(test.normalDirection.dotProduct(vec3(0,1,0))) == 1) //if these two vectors are parallel binormal = test.normalDirection.crossProduct(vec3(1, 0, 0)); else binormal = test.normalDirection.crossProduct(vec3(0, 1, 0)); vec3 tangent = test.normalDirection.crossProduct(binormal); //transform the binormal and tangent by the matrix Vec4 binormal4 = matrixByVector(spheres[i].matrix, Vec4(binormal.x, binormal.y, binormal.z, 0)); Vec4 tangent4 = matrixByVector(spheres[i].matrix, Vec4(tangent.x, tangent.y, tangent.z, 0)); test.normalDirection = vec3(binormal4.v[0], binormal4.v[1], binormal4.v[2]).crossProduct( vec3(tangent4.v[0], tangent4.v[1], tangent4.v[2]) ); test.normalDirection.normalize(); // double realT = sqrt( pow(realPosition.v[0] - position.x, 2) + pow(realPosition.v[1] - position.y, 2) + pow(realPosition.v[2] - position.z, 2) ); test.intersectionPosition = vec3(realPosition.v[0], realPosition.v[1], realPosition.v[2]); // test.intersectionDistance = realT; test.viewVec = viewVec; test.texture = requiredModel.texture; } } // intersected = test.raySphereIntersection(position, viewVec, vec3(0, 0, 20), 10, 0, 0); test.texture = requiredModel.texture; bool modelIntersection = false; for(int i = 0; i < models.size(); i++) { boundingTest.initializeTest(); boundingTest.raySphereIntersection(position, viewVec, models[i].center, models[i].radius); //first want to test the ray intersection with the bounding sphere, if it comes back false then we do not need to check the faces in the model if(boundingTest.inBounds) //set the interesectionTest to "test" in order to render bounding spheres (or boundingTest to render normally) { for(int j = 0; j < models[i].obj.faces.size(); j++) { intersected = test.rayTriangleIntersection(position, viewVec, models[i].obj.faces[j].vert[0], models[i].obj.faces[j].vert[1], models[i].obj.faces[j].vert[2]); if(intersected == true) { test.ambientColor = vec3(models[i].obj.faces[j].ambient[0], models[i].obj.faces[j].ambient[1], models[i].obj.faces[j].ambient[2]); test.diffuseColor = vec3(models[i].obj.faces[j].diffuse[0], models[i].obj.faces[j].diffuse[1], models[i].obj.faces[j].diffuse[2]); test.specularColor = vec3(models[i].obj.faces[j].specular[0], models[i].obj.faces[j].specular[1], models[i].obj.faces[j].specular[2]); test.shininess = models[i].obj.faces[j].shininess; test.emissionColor = vec3(models[i].obj.faces[j].emission[0], models[i].obj.faces[j].emission[1], models[i].obj.faces[j].emission[2]); test.translucency = models[i].obj.faces[j].translucency; test.reflectivity = models[i].obj.faces[j].reflectivity; test.dielectric = models[i].obj.faces[j].dielectric; test.viewVec = viewVec; test.texture = models[i].texture; } } } } //all the regular polygons are stored in a special obj object, need to test that one special for(int j = 0; j < requiredModel.obj.faces.size(); j++) { //for each sphere we need to do the transformations on the view vector Vec4 tempPosition = Vec4(position.x, position.y, position.z, 1); tempPosition = matrixByVector(requiredModel.obj.faces[j].inverseMatrix, tempPosition); Vec4 tempView = Vec4(viewVec.x, viewVec.y, viewVec.z, 0); tempView = matrixByVector(requiredModel.obj.faces[j].inverseMatrix, tempView); vec3 tempPos = vec3(tempPosition.v[0], tempPosition.v[1], tempPosition.v[2]); vec3 tempV = vec3(tempView.v[0], tempView.v[1], tempView.v[2]); intersected = test.rayTriangleIntersection(tempPos, tempV, requiredModel.obj.faces[j].vert[0], requiredModel.obj.faces[j].vert[1], requiredModel.obj.faces[j].vert[2]); if(intersected == true) { test.ambientColor = vec3(requiredModel.obj.faces[j].ambient[0], requiredModel.obj.faces[j].ambient[1], requiredModel.obj.faces[j].ambient[2]); test.diffuseColor = vec3(requiredModel.obj.faces[j].diffuse[0], requiredModel.obj.faces[j].diffuse[1], requiredModel.obj.faces[j].diffuse[2]); test.specularColor = vec3(requiredModel.obj.faces[j].specular[0], requiredModel.obj.faces[j].specular[1], requiredModel.obj.faces[j].specular[2]); test.shininess = requiredModel.obj.faces[j].shininess; test.emissionColor = vec3(requiredModel.obj.faces[j].emission[0], requiredModel.obj.faces[j].emission[1], requiredModel.obj.faces[j].emission[2]); test.translucency = requiredModel.obj.faces[j].translucency; test.reflectivity = requiredModel.obj.faces[j].reflectivity; test.dielectric = requiredModel.obj.faces[j].dielectric; //we now have a test that's found our point in inverse space, we need to get it back into regular space Vec4 realPosition = Vec4(test.intersectionPosition.x, test.intersectionPosition.y, test.intersectionPosition.z, 1); realPosition = matrixByVector(requiredModel.obj.faces[j].matrix, realPosition); // double realT = sqrt( pow(realPosition.v[0] - position.x, 2) + pow(realPosition.v[1] - position.y, 2) + pow(realPosition.v[2] - position.z, 2) ); test.intersectionPosition = vec3(realPosition.v[0], realPosition.v[1], realPosition.v[2]); // test.intersectionDistance = realT; test.viewVec = viewVec; test.texture = requiredModel.texture; } } return test; } void initialparse (FILE *fp) { modelView.init(); maxverts = 0; maxvertnorms = 0; curvert = 0; curvertnorm = 0; attenuation[0] = 1.0; attenuation[1] = 0; attenuation[2] = 0; maxdepth = 5; lightnum = 0; ambient[0] = .2; ambient[1] = .2; ambient[2] = .2; for(int i = 0; i < 1000; i++){ texfile[i] = '\0'; objfile[i] = '\0'; command[i] = '\0'; line[i] = '\0'; } reflectivity = 0;//TODO: what constants to use? (to Eric) dielectric = 0; translucency = 0; while (1) { fgets(line,sizeof(line),fp) ; if (feof(fp)) { fprintf(stderr, "Nothing in file\n") ; exit(1); } ; if (line[0] == '#') continue ; // Comment lines int num = sscanf(line, "%s", command) ; if (num != 1) continue ; // Blank line etc. else break ; } /***** SET IMAGE SIZE ******/ // The first line should be the size command setting the image size assert(!strcmp(command, "size")) ; int num = sscanf(line, "%s %d %d", command, &sizex, &sizey) ; assert(num == 3) ; assert(!strcmp(command, "size")) ; } void parsefile (FILE *fp) { int sizeset = 0 ; while (!feof(fp)) { fgets(line,sizeof(line),fp) ; if (feof(fp)) break ; if (line[0] == '#') continue ; // Comment lines int num = sscanf(line, "%s", command) ; if (num != 1) continue ; // Blank line etc. // Now, we simply parse the file by looking at the first line for the // various commands /************** CAMERA LOCATION **********/ if (!strcmp(command, "camera")) { double lookfrom[3], lookat[3], up[3], fov ; int num = sscanf(line, "%s %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", command, &lookfrom[0], &lookfrom[1], &lookfrom[2], &lookat[0], &lookat[1], &lookat[2], &up[0], &up[1], &up[2], &fov) ; if (num != 11) { fprintf(stderr, "camera from[3] at[3] up[3] fov\n") ; exit(1) ; } assert(!strcmp(command,"camera")) ; vec3 lfvec = vec3(lookfrom[0], lookfrom[1], lookfrom[2]); vec3 lavec = vec3(lookat[0], lookat[1], lookat[2]); vec3 upvec = vec3(up[0], up[1], up[2]); cam.setCamera(lfvec,lavec,sizex,sizey,upvec,fov); } /************** Extra face values ******/ else if (!strcmp(command, "reflectivity")) { sscanf(line, "%s %lf", command, &reflectivity); } else if (!strcmp(command, "dielectric")){ sscanf(line, "%s %lf", command, &dielectric); } else if (!strcmp(command, "translucency")){ sscanf(line, "%s %lf", command, &translucency); } /****************************************/ /*********** GEOMETRY *******************/ else if (!strcmp(command, "sphere")) { printf("sphere\n"); Sphere s; s.dielectric = dielectric; s.translucency = translucency; s.reflectivity = reflectivity; s.matrix = modelView.matrix(); s.inverseMatrix = modelView.inverseMatrix(); double radius ; // Syntax is sphere x y z radius double pos[3] ; int num = sscanf(line, "%s %lf %lf %lf %lf", command, s.center, s.center+1, s.center+2, &s.radius) ; if (num != 5) { fprintf(stderr, "sphere x y z radius\n") ; exit(1) ; } for(int i = 0; i < 3; i++){ s.ambient[i] = ambient[i]; s.diffuse[i] = diffuse[i]; s.specular[i] = specular[i]; s.emission[i] = emission[i]; } s.shininess = shininess; s.dielectric = dielectric; s.reflectivity = reflectivity; s.translucency = translucency; spheres.push_back(s); } else if (!strcmp(command, "maxverts")) { printf("maxverts\n"); int num = sscanf(line, "%s %d", command, &maxverts) ; } else if (!strcmp(command, "maxvertnorms")) { printf("maxvertnorms\n"); int num = sscanf(line, "%s %d", command, &maxvertnorms) ; } else if (!strcmp(command, "vertex")) { // Add a vertex to the stack printf("vertex\n"); vertex v; int num = sscanf(line, "%s %f %f %f", command, &v.x, &v.y, &v.z) ; assert(num == 4) ; assert(!strcmp(command,"vertex")) ; requiredModel.obj.v.push_back(v); } else if (!strcmp(command, "vertexnormal")) { printf("vertexnormal\n"); vertex vn ; vn.init(); int num = sscanf(line, "%s %f %f %f %f %f %f", command, &vn.x, &vn.y, &vn.z, &vn.nx, &vn.ny, &vn.nz) ; assert(num == 7) ; assert(!strcmp(command,"vertexnormal")) ; requiredModel.obj.vn.push_back(vn); } else if (!strcmp(command, "tri")) { // Triangle from 3 vertices printf("tri\n"); int pts[3] ; int num = sscanf(line, "%s %d %d %d", command, pts, pts+1, pts+2) ; assert(num == 4) ; assert(!strcmp(command,"tri")) ; int i,j ; double vertex[3][3] ; double normal[3] ; for (i = 0 ; i < 3 ; i++){ vertex[i][0] = requiredModel.obj.v[pts[i]].x ; vertex[i][1] = requiredModel.obj.v[pts[i]].y ; vertex[i][2] = requiredModel.obj.v[pts[i]].z ; } // Calculate the face normal double vec1[3], vec2[3], vec3[3] ; for (i = 0 ; i < 3 ; i++) { vec1[i] = vertex[1][i] - vertex[0][i] ; vec2[i] = vertex[2][i] - vertex[0][i] ; } vec3[0] = vec1[1]*vec2[2] - vec1[2]*vec2[1] ; vec3[1] = vec1[2]*vec2[0] - vec1[0]*vec2[2] ; vec3[2] = vec1[0]*vec2[1] - vec1[1]*vec2[0] ; double norm = 0 ; for (i = 0 ; i < 3 ; i++) norm += vec3[i] * vec3[i] ; norm = sqrt(norm) ; if (norm == 0) {normal[0] = 0 ; normal[1] = 0 ; normal[2] = 1.0 ; } else { for (i = 0 ; i < 3 ; i++) normal[i] = vec3[i] / norm ; } Triangle t; for(int i = 0; i < 3; i++) { t.v[i].x = requiredModel.obj.v[pts[i]].x; t.v[i].y = requiredModel.obj.v[pts[i]].y; t.v[i].z = requiredModel.obj.v[pts[i]].z; t.v[i].nx = normal[0]; t.v[i].ny = normal[1]; t.v[i].nz = normal[2]; } Face f; f.matrix = modelView.matrix(); f.inverseMatrix = modelView.inverseMatrix(); for(int i = 0; i < 3; i++){ f.vert[i] = t.v[i]; } for(int i = 0; i < 3; i++){ f.ambient[i] = ambient[i]; f.diffuse[i] = diffuse[i]; f.emission[i] = emission[i]; f.specular[i] = specular[i]; } f.shininess = shininess; f.dielectric = dielectric; f.translucency = translucency; f.reflectivity = reflectivity; requiredModel.obj.faces.push_back(f); } else if (!strcmp(command, "trinormal")) { int pts[3] ; int num = sscanf(line, "%s %d %d %d", command, pts, pts+1, pts+2) ; Triangle t; for(int i = 0; i < 3; i++) { t.v[i].x = requiredModel.obj.vn[pts[i]].x; t.v[i].y = requiredModel.obj.vn[pts[i]].y; t.v[i].z = requiredModel.obj.vn[pts[i]].z; t.v[i].nx = requiredModel.obj.vn[pts[i]].nx; t.v[i].ny = requiredModel.obj.vn[pts[i]].ny; t.v[i].nz = requiredModel.obj.vn[pts[i]].nz; } triangles.push_back(t); Face f; f.matrix = modelView.matrix(); f.inverseMatrix = modelView.inverseMatrix(); for(int i = 0; i < 3; i++){ f.vert[i] = t.v[i]; } for(int i = 0; i < 3; i++){ f.ambient[i] = ambient[i]; f.diffuse[i] = diffuse[i]; f.emission[i] = emission[i]; f.specular[i] = specular[i]; } f.shininess = shininess; } /******************************************/ /**************** TRANSFORMATIONS *********/ else if (!strcmp(command, "translate")) { double x,y,z ; // Translate by x y z as in standard OpenGL int num = sscanf(line, "%s %lf %lf %lf",command, &x, &y, &z) ; if (num != 4) { fprintf(stderr, "translate x y z\n") ; exit(1) ; } modelView.translate(x,y,z); } else if (!strcmp(command, "rotate")) { double ang, x,y,z ; // Rotate by an angle about axis x y z as in standard OpenGL int num = sscanf(line, "%s %lf %lf %lf %lf",command, &x, &y, &z, &ang) ; if (num != 5) { fprintf(stderr, "rotate angle x y z\n") ; exit(1) ; } modelView.rotate(ang,x,y,z); } else if (!strcmp(command, "scale")) { double x,y,z ; // Scale by x y z as in standard OpenGL int num = sscanf(line, "%s %lf %lf %lf",command, &x, &y, &z) ; if (num != 4) { fprintf(stderr, "scale x y z\n") ; exit(1) ; } modelView.scale(x,y,z); } else if (!strcmp(command, "pushTransform")) { // Push the current matrix on the stack as in OpenGL modelView.pushMatrix(); } else if (!strcmp(command, "popTransform")) { // Pop the current matrix as in OpenGL modelView.popMatrix(); } /************************************************************/ /********* MISCELLANEOUS IGNORED FOR OPENGL *******************/ else if (!strcmp(command, "maxdepth")) { int num = sscanf(line, "%s %d", command, &maxdepth) ; assert(num == 2) ; assert(!strcmp(command, "maxdepth")) ; fprintf(stderr, "Maxdepth set to %d but irrelevant for OpenGL\n", maxdepth) ; } else if (!strcmp(command, "output")) { char out[300] ; int num = sscanf(line, "%s %s", command, out) ; assert(num == 2) ; assert(!strcmp(command, "output")) ; fprintf(stderr, "Output image file set to: result.ppm\n",out) ; } /*************************************************/ /*************** LIGHTS *******************/ else if (!strcmp(command, "directional")) { float direction[4], color[4] ; color[3] = 1.0 ; direction[3] = 0.0 ; int num = sscanf(line, "%s %f %f %f %f %f %f", command, direction, direction+1, direction+2, color, color+1, color+2) ; assert(num == 7) ; Light l; for(int i = 0; i<3; i++) l.attenuation[i] = attenuation[i]; //attenuation set before creating light. l.type = DIRECTIONAL; l.dir[0] = direction[0]; l.dir[1] = direction[1]; l.dir[2] = direction[2]; for(int i = 0; i < 3; i++) l.color[i] = color[i]; lights.push_back(l); } else if (!strcmp(command, "point")) { float direction[4], color[4] ; color[3] = 1.0 ; direction[3] = 1.0 ; int num = sscanf(line, "%s %f %f %f %f %f %f", command, direction, direction+1, direction+2, color, color+1, color+2) ; assert(num == 7) ; Light l; for(int i = 0; i<3; i++) l.attenuation[i] = attenuation[i]; //attenuation set before creating light. l.type = POINT; l.loc[0] = direction[0]; l.loc[1] = direction[1]; l.loc[2] = direction[2]; for(int i = 0; i < 3; i++) l.color[i] = color[i]; lights.push_back(l); } else if (!strcmp(command, "area")) { Light l; int num = sscanf(line, "%s %lf %lf %lf %lf", command, &l.loc[0], &l.loc[1], &l.loc[2], &l.radius) ; assert(num == 5) ; for(int i = 0; i<3; i++){ l.attenuation[i] = attenuation[i]; l.color[i] = 1; } l.type = AREA; lights.push_back(l); } else if (!strcmp(command, "attenuation")) { int num = sscanf(line, "%s %lf %lf %lf", command, attenuation,attenuation + 1, attenuation + 2) ; assert(num == 4) ; assert(!strcmp(command, "attenuation")) ; } else if (!strcmp(command, "ambient")) { int num = sscanf(line, "%s %lf %lf %lf", command, ambient, ambient+1, ambient+2) ; assert(num == 4) ; assert(!strcmp(command, "ambient")) ; } /*******************************************************/ /****************** MATERIALS ************************/ else if (!strcmp(command, "diffuse")) { int num = sscanf(line, "%s %lf %lf %lf", command, diffuse, diffuse+1, diffuse+2) ; assert(num == 4) ; assert (!strcmp(command, "diffuse")) ; } else if (!strcmp(command, "specular")) { int num = sscanf(line, "%s %lf %lf %lf", command, specular, specular+1, specular+2) ; assert(num == 4) ; assert (!strcmp(command, "specular")) ; } else if (!strcmp(command, "shininess")) { int num = sscanf(line, "%s %lf", command, &shininess) ; assert(num == 2) ; assert (!strcmp(command, "shininess")) ; } else if (!strcmp(command, "emission")) { int num = sscanf(line, "%s %lf %lf %lf", command, emission, emission+1, emission+2) ; assert(num == 4) ; assert (!strcmp(command, "emission")) ; } /*****************************************************/ /****************** Addition: MODELS ************************/ else if (!strcmp(command, "model")) { Model m; int num = sscanf(line, "%s %s %s", command, texfile, objfile) ; assert(num == 3) ; assert (!strcmp(command, "model")) ; ImageColor img; string texname; for(int i = 0; i < 1000; i ++){ if(texfile[i] == '\0') break; texname.push_back(texfile[i]); texfile[i] = '\0'; } setSizeColor(&img,512,512); readImageColor(&img,texname.c_str()); m.texture = img; string objname; for(int i = 0; i < 1000; i ++){ if(objfile[i] == '\0') break; objname.push_back(objfile[i]); objfile[i] = '\0'; } m.obj = loadObj(objname); m.obj.setMatrices(modelView.matrix(), modelView.inverseMatrix()); m.center.x = m.obj.center[0]; m.center.y = m.obj.center[1]; m.center.z = m.obj.center[2]; m.radius = m.obj.radius; for(int i = 0; i < m.obj.faces.size(); i++){ m.obj.faces[i].dielectric = dielectric; m.obj.faces[i].reflectivity = reflectivity; m.obj.faces[i].translucency = translucency; m.obj.faces[i].shininess = shininess; for(int j = 0; j< 3; j++){ m.obj.faces[i].ambient[j] = ambient[j]; m.obj.faces[i].diffuse[j] = diffuse[j]; m.obj.faces[i].emission[j] = emission[j]; m.obj.faces[i].ambient[j] = ambient[j]; m.obj.faces[i].specular[j] = specular[j]; } } models.push_back(m); } /*****************************************************/ else { fprintf(stderr, "Unimplemented command: %s\n", command) ; exit(1) ; } //reset defaults /* for(int i = 0; i < 3; i++) //ambient[i] = .2; attenuation[0] = 1; attenuation[1] = 0; attenuation[2] = 0; */ } } void load(char* filename) { FILE *fp ; fp = fopen(filename, "rt") ; inputfile = fp ; initialparse(fp) ; parsefile(inputfile); //set default texture ImageColor img; setSizeColor(&img,4,4); requiredModel.texture = img; //compute center and radius of default model double center[3] = {0}; double distance, maxDistance; maxDistance = 0; for(int i = 0; i