319 lines
7.7 KiB
Rust
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())
|
|
}
|
|
}
|