pbrt/shared/src/core/shape.rs
2026-06-08 16:32:59 +01:00

155 lines
4 KiB
Rust

use crate::core::geometry::{
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
Vector3fi, VectorLike, ray,
};
use crate::core::interaction::{
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
};
use crate::core::{MaterialIdx, LightIdx};
use crate::core::light::Light;
use crate::core::material::Material;
use crate::core::medium::{Medium, MediumInterface};
use crate::shapes::*;
use crate::utils::math::{next_float_down, next_float_up};
use crate::utils::{Ptr, Transform};
use crate::{Float, PI};
use enum_dispatch::enum_dispatch;
// Define Intersection objects. This only varies for
#[repr(C)]
#[derive(Debug, Clone, Copy)]
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: MaterialIdx,
area: LightIdx,
prim_medium_interface: MediumInterface,
ray_medium: Ptr<Medium>,
) {
self.intr
.set_intersection_properties(mtl, area, ray_medium, prim_medium_interface);
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct QuadricIntersection {
pub t_hit: Float,
pub p_obj: Point3f,
pub phi: Float,
}
impl QuadricIntersection {
pub fn new(t_hit: Float, p_obj: Point3f, phi: Float) -> Self {
Self { t_hit, p_obj, phi }
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ShapeSample {
pub intr: Interaction,
pub pdf: Float,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
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), Ptr::null())
}
}
#[enum_dispatch]
pub trait ShapeTrait {
fn bounds(&self) -> Bounds3f;
fn normal_bounds(&self) -> DirectionCone;
fn area(&self) -> Float;
fn sample(&self, u: Point2f) -> Option<ShapeSample>;
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample>;
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection>;
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool;
fn pdf(&self, interaction: &Interaction) -> Float;
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float;
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
#[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())
}
}