pbrt/src/shapes/mod.rs

319 lines
7.7 KiB
Rust

pub mod bilinear;
pub mod curves;
pub mod cylinder;
pub mod disk;
pub mod sphere;
pub mod triangle;
use crate::core::interaction::{
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
};
use crate::core::material::Material;
use crate::core::medium::{Medium, MediumInterface};
use crate::core::pbrt::{Float, PI};
use crate::geometry::{
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
Vector3fi, VectorLike,
};
use crate::lights::Light;
use crate::utils::math::{next_float_down, next_float_up};
use crate::utils::transform::Transform;
use enum_dispatch::enum_dispatch;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct SphereShape {
radius: Float,
z_min: Float,
z_max: Float,
theta_z_min: Float,
theta_z_max: Float,
phi_max: Float,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
transform_swap_handedness: bool,
}
impl Default for SphereShape {
fn default() -> Self {
Self::new(
Transform::default().into(),
Transform::default().into(),
false,
1.0,
-1.0,
1.0,
360.0,
)
}
}
#[derive(Debug, Clone)]
pub struct CylinderShape {
radius: Float,
z_min: Float,
z_max: Float,
phi_max: Float,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
transform_swap_handedness: bool,
}
#[derive(Debug, Clone)]
pub struct DiskShape {
radius: Float,
inner_radius: Float,
height: Float,
phi_max: Float,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
transform_swap_handedness: bool,
}
#[derive(Debug, Clone)]
pub struct TriangleShape {
pub mesh_ind: usize,
pub tri_index: usize,
}
#[derive(Debug, Clone)]
pub struct BilinearPatchShape {
mesh_index: usize,
blp_index: usize,
area: Float,
rectangle: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CurveType {
Flat,
Cylinder,
Ribbon,
}
#[derive(Debug, Clone)]
pub struct CurveCommon {
curve_type: CurveType,
cp_obj: [Point3f; 4],
width: [Float; 2],
n: [Normal3f; 2],
normal_angle: Float,
inv_sin_normal_angle: Float,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
transform_swap_handedness: bool,
}
impl CurveCommon {
#[allow(clippy::too_many_arguments)]
pub fn new(
c: &[Point3f],
w0: Float,
w1: Float,
curve_type: CurveType,
norm: &[Vector3f],
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
) -> Self {
let transform_swap_handedness = render_from_object.swaps_handedness();
let width = [w0, w1];
assert_eq!(c.len(), 4);
let cp_obj: [Point3f; 4] = c[..4].try_into().unwrap();
let mut n = [Normal3f::default(); 2];
let mut normal_angle: Float = 0.;
let mut inv_sin_normal_angle: Float = 0.;
if norm.len() == 2 {
n[0] = norm[0].normalize().into();
n[1] = norm[1].normalize().into();
normal_angle = n[0].angle_between(n[1]);
inv_sin_normal_angle = 1. / normal_angle.sin();
}
Self {
curve_type,
cp_obj,
width,
n,
normal_angle,
inv_sin_normal_angle,
render_from_object,
object_from_render,
reverse_orientation,
transform_swap_handedness,
}
}
}
#[derive(Debug, Clone)]
pub struct CurveShape {
common: CurveCommon,
u_min: Float,
u_max: Float,
}
// Define Intersection objects. This only varies for
#[derive(Debug, Clone)]
pub struct ShapeIntersection {
pub intr: SurfaceInteraction,
pub t_hit: Float,
}
impl ShapeIntersection {
pub fn new(intr: SurfaceInteraction, t_hit: Float) -> Self {
Self { intr, t_hit }
}
pub fn t_hit(&self) -> Float {
self.t_hit
}
pub fn set_t_hit(&mut self, new_t: Float) {
self.t_hit = new_t;
}
pub fn set_intersection_properties(
&mut self,
mtl: Arc<Material>,
area: Arc<Light>,
prim_medium_interface: Option<MediumInterface>,
ray_medium: Option<Arc<Medium>>,
) {
let ray_medium = ray_medium.expect("Ray medium must be defined for intersection");
self.intr
.set_intersection_properties(mtl, area, prim_medium_interface, ray_medium);
}
}
#[derive(Debug, Clone)]
pub struct QuadricIntersection {
t_hit: Float,
p_obj: Point3f,
phi: Float,
}
impl QuadricIntersection {
pub fn new(t_hit: Float, p_obj: Point3f, phi: Float) -> Self {
Self { t_hit, p_obj, phi }
}
}
#[derive(Debug, Clone, Copy)]
pub struct TriangleIntersection {
b0: Float,
b1: Float,
b2: Float,
t: Float,
}
impl TriangleIntersection {
pub fn new(b0: Float, b1: Float, b2: Float, t: Float) -> Self {
Self { b0, b1, b2, t }
}
}
#[derive(Debug, Clone)]
pub struct BilinearIntersection {
uv: Point2f,
t: Float,
}
impl BilinearIntersection {
pub fn new(uv: Point2f, t: Float) -> Self {
Self { uv, t }
}
}
#[derive(Clone)]
pub struct ShapeSample {
pub intr: Arc<SurfaceInteraction>,
pub pdf: Float,
}
#[derive(Clone, Debug)]
pub struct ShapeSampleContext {
pub pi: Point3fi,
pub n: Normal3f,
pub ns: Normal3f,
pub time: Float,
}
impl ShapeSampleContext {
pub fn new(pi: Point3fi, n: Normal3f, ns: Normal3f, time: Float) -> Self {
Self { pi, n, ns, time }
}
pub fn new_from_interaction(si: &SurfaceInteraction) -> Self {
Self {
pi: si.pi(),
n: si.n(),
ns: si.shading.n,
time: si.time(),
}
}
pub fn p(&self) -> Point3f {
Point3f::from(self.pi)
}
pub fn offset_ray_origin(&self, w: Vector3f) -> Point3f {
let d = self.n.abs().dot(self.pi.error().into());
let mut offset = d * Vector3f::from(self.n);
if w.dot(self.n.into()) < 0.0 {
offset = -offset;
}
let mut po = Point3f::from(self.pi) + offset;
for i in 0..3 {
if offset[i] > 0.0 {
po[i] = next_float_up(po[i]);
} else {
po[i] = next_float_down(po[i]);
}
}
po
}
pub fn offset_ray_origin_from_point(&self, pt: Point3f) -> Point3f {
self.offset_ray_origin(pt - self.p())
}
pub fn spawn_ray(&self, w: Vector3f) -> Ray {
Ray::new(self.offset_ray_origin(w), w, Some(self.time), None)
}
}
#[enum_dispatch]
pub trait ShapeTrait {
fn bounds(&self) -> Bounds3f;
fn normal_bounds(&self) -> DirectionCone;
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection>;
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool;
fn area(&self) -> Float;
fn pdf(&self, interaction: &Interaction) -> Float;
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float;
fn sample(&self, u: Point2f) -> Option<ShapeSample>;
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample>;
}
#[derive(Debug, Clone)]
#[enum_dispatch(ShapeTrait)]
pub enum Shape {
Sphere(SphereShape),
Cylinder(CylinderShape),
Disk(DiskShape),
Triangle(TriangleShape),
BilinearPatch(BilinearPatchShape),
Curve(CurveShape),
}
impl Default for Shape {
fn default() -> Self {
Shape::Sphere(SphereShape::default())
}
}