#ifndef HITTABLE_H #define HITTABLE_H #include "rayTracer.h" #include "aabb.h" class material; class hitRecord { public: point3 p; vec3 normal; shared_ptr mat; double t; double u; double v; bool frontFace; void setFaceNormal(const ray& r, const vec3& outwardNormal) { /* * Sets the hit record normal vector. * NOTE: the parameter `outward_normal` is assumed to have unit length. */ frontFace = dot(r.direction(), outwardNormal) < 0; normal = frontFace ? outwardNormal : -outwardNormal; } }; class hittable { public: virtual ~hittable() = default; virtual bool hit(const ray& r, interval rayT, hitRecord& rec) const = 0; virtual aabb boundingBox() const = 0; }; class translate : public hittable { public: translate(shared_ptr object, const vec3& offset) : object(object), offset(offset) {} bool hit(const ray& r, interval rayT, hitRecord& rec) const override { // Move the ray backwards by the offset. ray offsetR(r.origin() - offset, r.direction(), r.time()); // Determine whether an intersection exists along the offset ray (and if so, where) if (!object->hit(offsetR, rayT, rec)) return false; // Move the intersection point forwards by the offset rec.p += offset; return true; } aabb boundingBox() const override { return bBox; } private: shared_ptr object; vec3 offset; aabb bBox; }; class rotateY : public hittable { public: rotateY(shared_ptr object, double angle) : object(object) { auto radians = degreesToRadians(angle); sinTheta = sin(radians); cosTheta = cos(radians); bBox = object->boundingBox(); point3 min(infinity, infinity, infinity); point3 max(-infinity, -infinity, -infinity); for (int i = 0; i < 2; i ++) { for (int j = 0; j < 2; j++) { for (int k = 0; k < 2; k++) { auto x = i * bBox.x.max + (1 - i) * bBox.x.min; auto y = j * bBox.y.max + (1 - j) * bBox.y.min; auto z = k * bBox.z.max + (1 - k) * bBox.z.min; auto newX = cosTheta * x + sinTheta * z; auto newZ = -sinTheta * x + cosTheta * z; vec3 tester(newX, y, newZ); for (int c = 0; c < 3; c++) { min[c] = fmin(min[c], tester[c]); max[c] = fmax(max[c], tester[c]); } } } } bBox = aabb(min, max); } bool hit(const ray& r, interval rayT, hitRecord& rec) const override { // Change the ray from world space to object space. auto origin = r.origin(); auto direction = r.direction(); origin[0] = cosTheta * r.origin()[0] - sinTheta * r.origin()[2]; origin[2] = sinTheta * r.origin()[0] + cosTheta * r.origin()[2]; direction[0] = cosTheta * r.direction()[0] - sinTheta * r.direction()[2]; direction[2] = sinTheta * r.direction()[0] + cosTheta * r.direction()[2]; ray rotatedR(origin, direction, r.time()); // Determine whether an intersection exists in object space (and if so, where). if (!object->hit(rotatedR, rayT, rec)) return false; // Change the intersection point from object space to world space. auto p = rec.p; p[0] = cosTheta * rec.p[0] + sinTheta * rec.p[2]; p[2] = -sinTheta * rec.p[0] + cosTheta * rec.normal[2]; // Change the normal from object space to world space. auto normal = rec.normal; normal[0] = cosTheta * rec.normal[0] + sinTheta * rec.normal[2]; normal[2] = -sinTheta * rec.normal[0] + cosTheta * rec.normal[2]; rec.p = p; rec.normal = normal; return true; } private: shared_ptr object; double sinTheta; double cosTheta; aabb bBox; }; #endif