/* * CS5600 University of Utah * Charles McGarvey * mcgarvey@eng.utah.edu */ #include #include #include "contact.hh" #include "light.hh" #include "plane.hh" #include "sphere.hh" #include "triangle.hh" #include "vec.hh" #include "scene.hh" static int _scene_add_light(scene_t* s, FILE* file); static int _scene_add_sphere(scene_t* s, FILE* file); static int _scene_add_plane(scene_t* s, FILE* file); static int _scene_add_triangle(scene_t* s, FILE* file); struct scene { int w, h; vec_t eye; vec_t screenCenter; vec_t screenU; vec_t screenV; list_t* lights; list_t* objects; color_t ambient; }; scene_t* scene_alloc(FILE* file) { int w, h; double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ; double fovy, aspect; double aR, aG, aB; if (fscanf(file, "U5 %d %d %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", &w, &h, &eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ, &fovy, &aspect, &aR, &aG, &aB) != 16) { fprintf(stderr, "Cannot read scene header.\n"); return NULL; } vec_t eye = vec_new(eyeX, eyeY, eyeZ); vec_t spot = vec_new(spotX, spotY, spotZ); vec_t up = vec_new(upX, upY, upZ); scal_t d = S(1.0) / scal_tan((scal_t)fovy * S(0.5)); vec_t look = vec_normalize(vec_sub(spot, eye)); vec_t screenCenter = vec_scale(look, d); vec_t screenU = vec_normalize(vec_cross(look, up)); vec_t screenV = vec_cross(screenU, look); screenU = vec_scale(screenU, (scal_t)aspect); scene_t* s = (scene_t*)mem_alloc(sizeof(scene_t)); s->w = w; s->h = h; s->eye = eye; s->screenCenter = screenCenter; s->screenU = screenU; s->screenV = screenV; s->lights = NULL; s->objects = NULL; s->ambient = color_new((scal_t)aR, (scal_t)aG, (scal_t)aB, S(1.0)); char type; while (fscanf(file, " %c", &type) == 1) { switch (type) { case 'l': if (_scene_add_light(s, file) != 0) { goto fail; } break; case 's': if (_scene_add_sphere(s, file) != 0) { goto fail; } break; case 'p': if (_scene_add_plane(s, file) != 0) { goto fail; } break; case 't': if (_scene_add_triangle(s, file) != 0) { goto fail; } break; case 'X': goto done; default: fprintf(stderr, "Unknown identifier: %c\n", type); } } done: if (s->objects) list_reverse(&s->objects); return s; fail: scene_destroy(s); return NULL; } void scene_destroy(scene_t* s) { if (s->lights) list_destroy(&s->lights); if (s->objects) list_destroy(&s->objects); mem_free(s); } /* * Add a light to the scene. */ static int _scene_add_light(scene_t* s, FILE* file) { double lx, ly, lz, r, g, b; if (fscanf(file, " %lf %lf %lf %lf %lf %lf", &lx, &ly, &lz, &r, &g, &b) != 6) { fprintf(stderr, "Cannot read light values from scene.\n"); return -1; } light_t* l = light_alloc( vec_new(lx, ly, lz), color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0)) ); list_push2(&s->lights, l, mem_free); return 0; } static int _scene_add_sphere(scene_t* s, FILE* file) { double x, y, z, radius, r, g, b; if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf", &x, &y, &z, &radius, &r, &g, &b) != 7) { fprintf(stderr, "Cannot read sphere values from scene.\n"); return -1; } rt::element* sphere = new rt::sphere( vec_new((scal_t)x, (scal_t)y, (scal_t)z), (scal_t)radius ); sphere->material(color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0))); list_push2(&s->objects, sphere, DTOR(rt::sphere_destroy)); return 0; } static int _scene_add_plane(scene_t* s, FILE* file) { double x, y, z, nx, ny, nz, r, g, b; if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf", &x, &y, &z, &nx, &ny, &nz, &r, &g, &b) != 9) { fprintf(stderr, "Cannot read plane values from scene.\n"); return -1; } rt::element* plane = new rt::plane( vec_new((scal_t)x, (scal_t)y, (scal_t)z), vec_new((scal_t)nx, (scal_t)ny, (scal_t)nz) ); plane->material(color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0))); list_push2(&s->objects, plane, DTOR(rt::plane_destroy)); return 0; } static int _scene_add_triangle(scene_t* s, FILE* file) { double x1, y1, z1, x2, y2, z2, x3, y3, z3, r, g, b; if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf", &x1, &y1, &z1, &x2, &y2, &z2, &x3, &y3, &z3, &r, &g, &b) != 12) { fprintf(stderr, "Cannot read triangle values from scene.\n"); return -1; } rt::element* triangle = new rt::triangle( vec_new((scal_t)x1, (scal_t)y1, (scal_t)z1), vec_new((scal_t)x2, (scal_t)y2, (scal_t)z2), vec_new((scal_t)x3, (scal_t)y3, (scal_t)z3) ); triangle->material(color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0))); list_push2(&s->objects, triangle, DTOR(rt::triangle_destroy)); return 0; } list_t* scene_elements(const scene_t* s) { return s->objects; } list_t* scene_lights(const scene_t* s) { return s->lights; } color_t scene_ambient(const scene_t* s) { return s->ambient; } raster_t* scene_render(const scene_t* s) { #if VERBOSITY >= 3 timer_start(); #endif raster_t* p = raster_alloc(s->w, s->h, COLOR_BLACK); /*for (list_t* i = s->lights; i; i = i->link) {*/ /*raster_light(p, *(light_t*)i->val);*/ /*}*/ raster_t* texture = raster_import("texture.bmp"); if (texture == NULL) { texture = raster_import("texture.ppm"); } if (s->objects && texture != NULL) { rt::element* obj = (rt::element*)s->objects->val; obj->texture(texture); #if VERBOSITY >= 3 printf("Loaded texture file; will texture first scene object.\n"); #endif } #if VERBOSITY >= 3 printf("rendering scene...\n"); #endif scal_t wh = S(2.0) / (scal_t)s->w; scal_t hh = S(2.0) / (scal_t)s->h; for (int y = 0; y < s->h; ++y) { for (int x = 0; x < s->w; ++x) { color_t* pixel = raster_color(p, x, y); scal_t u = (scal_t)x * wh - S(1.0); scal_t v = (scal_t)y * hh - S(1.0); vec_t rayDirection = vec_add2(s->screenCenter, vec_scale(s->screenU, u), vec_scale(s->screenV, v)); ray_t ray = ray_normalize(ray_new(s->eye, rayDirection)); rt::element* nearestObj = 0; contact_t nearestHit; // find the nearest object along the ray for (list_t* i = s->objects; i; i = i->link) { rt::element* obj = (rt::element*)i->val; contact_t hit; if (obj->intersect(ray, hit) && (!nearestObj || hit.d < nearestHit.d)) { nearestObj = obj; nearestHit = hit; } } if (nearestObj) { *pixel = nearestObj->cast(nearestHit, s); } } } #if VERBOSITY >= 3 long dt = timer_stop(); printf("render complete!\ntime\t%.3fms\n", (float)dt / 1000.0f); #endif if (texture) { raster_destroy(texture); } return p; }