nuclear@0: #include nuclear@0: #include nuclear@0: #include "camera.h" nuclear@0: nuclear@0: static void calc_sample_pos_rec(int sidx, float xsz, float ysz, float *pos); nuclear@0: nuclear@0: Camera::Camera() nuclear@0: { nuclear@0: vfov = M_PI / 4.0; nuclear@0: cached_matrix_valid = false; nuclear@0: nuclear@0: rdir_cache_width = rdir_cache_height = 0; nuclear@0: rdir_cache = 0; nuclear@0: } nuclear@0: nuclear@0: Camera::Camera(const Vector3 &p) nuclear@0: : pos(p) nuclear@0: { nuclear@0: vfov = M_PI / 4.0; nuclear@0: cached_matrix_valid = false; nuclear@0: nuclear@0: rdir_cache_width = rdir_cache_height = 0; nuclear@0: rdir_cache = 0; nuclear@0: } nuclear@0: nuclear@0: Camera::~Camera() nuclear@0: { nuclear@0: delete [] rdir_cache; nuclear@0: } nuclear@0: nuclear@0: void Camera::set_fov(float vfov) nuclear@0: { nuclear@0: this->vfov = vfov; nuclear@0: nuclear@0: // invalidate the dir cache nuclear@0: delete [] rdir_cache; nuclear@0: } nuclear@0: nuclear@0: float Camera::get_fov() const nuclear@0: { nuclear@0: return vfov; nuclear@0: } nuclear@0: nuclear@0: void Camera::set_position(const Vector3 &pos) nuclear@0: { nuclear@0: this->pos = pos; nuclear@0: cached_matrix_valid = false; // invalidate the cached matrix nuclear@0: } nuclear@0: nuclear@0: const Vector3 &Camera::get_position() const nuclear@0: { nuclear@0: return pos; nuclear@0: } nuclear@0: nuclear@0: const Matrix4x4 &Camera::get_matrix() const nuclear@0: { nuclear@0: if(!cached_matrix_valid) { nuclear@0: calc_matrix(&cached_matrix); nuclear@0: cached_matrix_valid = true; nuclear@0: } nuclear@0: return cached_matrix; nuclear@0: } nuclear@0: nuclear@0: Vector2 Camera::calc_sample_pos(int x, int y, int xsz, int ysz, int sample) const nuclear@0: { nuclear@0: float ppos[2]; nuclear@0: float aspect = (float)xsz / (float)ysz; nuclear@0: nuclear@0: float pwidth = 2.0 * aspect / (float)xsz; nuclear@0: float pheight = 2.0 / (float)ysz; nuclear@0: nuclear@0: ppos[0] = (float)x * pwidth - aspect; nuclear@0: ppos[1] = 1.0 - (float)y * pheight; nuclear@0: nuclear@0: calc_sample_pos_rec(sample, pwidth, pheight, ppos); nuclear@0: return Vector2(ppos[0], ppos[1]); nuclear@0: } nuclear@0: nuclear@0: Ray Camera::get_primary_ray(int x, int y, int xsz, int ysz, int sample) const nuclear@0: { nuclear@0: #pragma omp single nuclear@0: { nuclear@0: if(!rdir_cache || rdir_cache_width != xsz || rdir_cache_height != ysz) { nuclear@0: printf("calculating primary ray direction cache\n"); nuclear@0: nuclear@0: delete [] rdir_cache; nuclear@0: rdir_cache = new Vector3[xsz * ysz]; nuclear@0: nuclear@0: #pragma omp parallel for nuclear@0: for(int i=0; ix = ppos.x; nuclear@0: rdir->y = ppos.y; nuclear@0: rdir->z = 1.0 / tan(vfov / 2.0); nuclear@0: rdir->normalize(); nuclear@0: nuclear@0: rdir++; nuclear@0: } nuclear@0: } nuclear@0: rdir_cache_width = xsz; nuclear@0: rdir_cache_height = ysz; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: Ray ray; nuclear@0: ray.origin = pos; nuclear@0: ray.dir = rdir_cache[y * xsz + x]; nuclear@0: nuclear@0: // transform the ray direction with the camera matrix nuclear@0: Matrix4x4 mat = get_matrix(); nuclear@0: mat.m[0][3] = mat.m[1][3] = mat.m[2][3] = mat.m[3][0] = mat.m[3][1] = mat.m[3][2] = 0.0; nuclear@0: mat.m[3][3] = 1.0; nuclear@0: nuclear@0: ray.dir = ray.dir.transformed(mat); nuclear@0: return ray; nuclear@0: } nuclear@0: nuclear@0: TargetCamera::TargetCamera() {} nuclear@0: nuclear@0: TargetCamera::TargetCamera(const Vector3 &pos, const Vector3 &targ) nuclear@0: : Camera(pos), target(targ) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: void TargetCamera::set_target(const Vector3 &targ) nuclear@0: { nuclear@0: target = targ; nuclear@0: cached_matrix_valid = false; // invalidate the cached matrix nuclear@0: } nuclear@0: nuclear@0: const Vector3 &TargetCamera::get_target() const nuclear@0: { nuclear@0: return target; nuclear@0: } nuclear@0: nuclear@0: void TargetCamera::calc_matrix(Matrix4x4 *mat) const nuclear@0: { nuclear@0: Vector3 up(0, 1, 0); nuclear@0: Vector3 dir = (target - pos).normalized(); nuclear@0: Vector3 right = cross_product(up, dir); nuclear@0: up = cross_product(dir, right); nuclear@0: nuclear@0: *mat = Matrix4x4( nuclear@0: right.x, up.x, dir.x, pos.x, nuclear@0: right.y, up.y, dir.y, pos.y, nuclear@0: right.z, up.z, dir.z, pos.z, nuclear@0: 0.0, 0.0, 0.0, 1.0); nuclear@0: } nuclear@0: nuclear@0: void FlyCamera::input_move(float x, float y, float z) nuclear@0: { nuclear@0: static const Vector3 vfwd(0, 0, 1), vright(1, 0, 0); nuclear@0: nuclear@0: Vector3 k = vfwd.transformed(rot); nuclear@0: Vector3 i = vright.transformed(rot); nuclear@0: Vector3 j = cross_product(k, i); nuclear@0: nuclear@0: pos += i * x + j * y + k * z; nuclear@0: cached_matrix_valid = false; nuclear@0: } nuclear@0: nuclear@0: void FlyCamera::input_rotate(float x, float y, float z) nuclear@0: { nuclear@0: Vector3 axis(x, y, z); nuclear@0: float axis_len = axis.length(); nuclear@0: if(fabs(axis_len) < 1e-5) { nuclear@0: return; nuclear@0: } nuclear@0: rot.rotate(axis / axis_len, -axis_len); nuclear@0: rot.normalize(); nuclear@0: nuclear@0: cached_matrix_valid = false; nuclear@0: } nuclear@0: nuclear@0: void FlyCamera::calc_matrix(Matrix4x4 *mat) const nuclear@0: { nuclear@0: Matrix4x4 tmat; nuclear@0: tmat.set_translation(pos); nuclear@0: nuclear@0: Matrix3x3 rmat = rot.get_rotation_matrix(); nuclear@0: nuclear@0: *mat = tmat * Matrix4x4(rmat); nuclear@0: } nuclear@0: nuclear@0: /* generates a sample position for sample number sidx, in the unit square nuclear@0: * by recursive subdivision and jittering nuclear@0: */ nuclear@0: static void calc_sample_pos_rec(int sidx, float xsz, float ysz, float *pos) nuclear@0: { nuclear@0: static const float subpt[4][2] = { nuclear@0: {-0.25, -0.25}, {0.25, -0.25}, {-0.25, 0.25}, {0.25, 0.25} nuclear@0: }; nuclear@0: nuclear@0: if(!sidx) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: /* determine which quadrant to recurse into */ nuclear@0: int quadrant = ((sidx - 1) % 4); nuclear@0: pos[0] += subpt[quadrant][0] * xsz; nuclear@0: pos[1] += subpt[quadrant][1] * ysz; nuclear@0: nuclear@0: calc_sample_pos_rec((sidx - 1) / 4, xsz / 2, ysz / 2, pos); nuclear@0: }