Files
raytracer_challenge/raytracing/src/shapes/cylinder.cpp
NADAL Jean-Baptiste b3490b146e [FEAT] Add cone
2024-03-21 19:10:01 +01:00

200 lines
5.9 KiB
C++

/*!
* cylinder.cpp
*
* Copyright (c) 2024, NADAL Jean-Baptiste. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* @Author: NADAL Jean-Baptiste
* @Date: 18/03/2024
*
*/
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
/* ------------------------------------------------------------------------- */
#include <cmath>
#include "core/common.h"
#include "core/intersections.h"
#include "cylinder.h"
using namespace Raytracer;
/* ------------------------------------------------------------------------- */
Intersections Cylinder::local_intersect(const Ray &a_ray)
{
double the_a, the_b, the_c, the_discriminant;
double the_t0, the_t1;
double the_y0, the_y1;
Intersections the_intersections;
const Tuple &the_ray_direction = a_ray.direction();
const Tuple &the_ray_origin = a_ray.origin();
the_a = std::pow(the_ray_direction.x(), 2) + std::pow(the_ray_direction.z(), 2);
// Ray is parallel to the y axis
if (double_equal(the_a, 0) == false)
{
the_b = 2 * the_ray_origin.x() * the_ray_direction.x() +
2 * the_ray_origin.z() * the_ray_direction.z();
the_c = std::pow(the_ray_origin.x(), 2) + std::pow(the_ray_origin.z(), 2) - 1;
the_discriminant = std::pow(the_b, 2) - 4 * the_a * the_c;
if (the_discriminant < 0)
{
return the_intersections;
}
the_t0 = (-the_b - std::sqrt(the_discriminant)) / (2 * the_a);
the_t1 = (-the_b + std::sqrt(the_discriminant)) / (2 * the_a);
if (the_t0 > the_t1)
{
std::swap(the_t0, the_t1);
}
the_y0 = the_ray_origin.y() + the_t0 * the_ray_direction.y();
if ((m_minimum < the_y0) && (the_y0 < m_maximum))
{
the_intersections.add(Intersection(the_t0, this));
}
the_y1 = the_ray_origin.y() + the_t1 * the_ray_direction.y();
if ((m_minimum < the_y1) && (the_y1 < m_maximum))
{
the_intersections.add(Intersection(the_t1, this));
}
}
// Caps
intersect_caps(a_ray, the_intersections);
return the_intersections;
}
/* ------------------------------------------------------------------------- */
Tuple Cylinder::local_normal_at(const Tuple &a_local_point) const
{
double the_distance;
// Compute the square of the distance from the y axis
the_distance = std::pow(a_local_point.x(), 2) + std::pow(a_local_point.z(), 2);
if ((the_distance < 1) && (a_local_point.y() >= m_maximum - kEpsilon))
{
return Tuple::Vector(0, 1, 0);
}
else if ((the_distance < 1) && (a_local_point.y() <= m_minimum + kEpsilon))
{
return Tuple::Vector(0, -1, 0);
}
return Tuple::Vector(a_local_point.x(), 0, a_local_point.z());
}
/* ------------------------------------------------------------------------- */
double Cylinder::minimum(void)
{
return m_minimum;
}
/* ------------------------------------------------------------------------- */
void Cylinder::set_minimum(double a_value)
{
m_minimum = a_value;
}
/* ------------------------------------------------------------------------- */
double Cylinder::maximum(void)
{
return m_maximum;
}
/* ------------------------------------------------------------------------- */
void Cylinder::set_maximum(double a_value)
{
m_maximum = a_value;
}
/* ------------------------------------------------------------------------- */
bool Cylinder::closed(void)
{
return m_closed;
}
/* ------------------------------------------------------------------------- */
void Cylinder::set_closed(bool a_state)
{
m_closed = a_state;
}
/* ------------------------------------------------------------------------- */
bool Cylinder::check_cap(const Ray &a_ray, double a_distance_t)
{
double the_x, the_z;
const Tuple &the_ray_direction = a_ray.direction();
const Tuple &the_ray_origin = a_ray.origin();
the_x = the_ray_origin.x() + a_distance_t * the_ray_direction.x();
the_z = the_ray_origin.z() + a_distance_t * the_ray_direction.z();
return (std::pow(the_x, 2) + std::pow(the_z, 2)) <= 1;
}
/* ------------------------------------------------------------------------- */
void Cylinder::intersect_caps(const Ray &a_ray, Intersections &an_xs)
{
double the_distance_t;
const Tuple &the_ray_direction = a_ray.direction();
// Caps only matter if the cylinder is closed. and might possibility be intersected the ray.
if ((m_closed == false) or (double_equal(the_ray_direction.y(), 0)))
{
return;
}
// Check for an intersection with the lower end cap by intersecting
// the ray with the plane at y = cyl.minimum
the_distance_t = (m_minimum - a_ray.origin().y()) / the_ray_direction.y();
if (check_cap(a_ray, the_distance_t))
{
an_xs.add(Intersection(the_distance_t, this));
}
// Check for an intersection with the upper end cap by intersecting
// the ray with the plane at y = cyl.maximum
the_distance_t = (m_maximum - a_ray.origin().y()) / the_ray_direction.y();
if (check_cap(a_ray, the_distance_t))
{
an_xs.add(Intersection(the_distance_t, this));
}
}