Gonna take a break

This commit is contained in:
pingupingou 2025-11-19 21:59:15 +00:00
parent d58203e97a
commit 5937239a18
16 changed files with 503 additions and 170 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
*.lock
*.log
*.bak
flip.rs

View file

@ -4,8 +4,18 @@ version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.100"
bitflags = "2.10.0"
bumpalo = "3.19.0"
exr = "1.73.0"
half = "2.7.1"
image = "0.25.8"
num = "0.4.3"
num-integer = "0.1.46"
num-traits = "0.2.19"
once_cell = "1.21.3"
qoi = "0.4.1"
rand = "0.9.2"
rayon = "1.11.0"
smallvec = "1.15.1"
thiserror = "2.0.17"

View file

@ -219,6 +219,7 @@ pub enum Camera {
Spherical(SphericalCamera),
Realistic(RealisticCamera),
}
impl CameraTrait for Camera {
fn base(&self) -> &CameraBase {
match self {

View file

@ -4,7 +4,7 @@ use crate::core::medium::{Medium, MediumInterface};
use crate::core::pbrt::Float;
use crate::geometry::{Normal3f, Point2f, Point3f, Point3fi, Ray, Vector3f, VectorLike};
use crate::lights::Light;
use crate::shapes::ShapeTrait;
use crate::shapes::Shape;
use std::any::Any;
use std::sync::Arc;
@ -106,28 +106,14 @@ pub struct SurfaceInteraction {
pub dvdx: Float,
pub dudy: Float,
pub dvdy: Float,
pub shape: Option<Arc<dyn ShapeTrait>>,
pub shape: Arc<Shape>,
pub bsdf: Option<BSDF>,
}
impl SurfaceInteraction {
pub fn set_intersection_properties(
&mut self,
mtl: Arc<dyn MaterialTrait>,
area: Arc<Light>,
prim_medium_interface: Option<Arc<MediumInterface>>,
ray_medium: Arc<Medium>,
) {
self.material = Some(mtl);
self.area_light = Some(area);
if prim_medium_interface.as_ref().map_or(false, |mi| mi.is_medium_transition()) {
self.common.medium_interface = prim_medium_interface;
} else {
self.common.medium = Some(ray_medium);
}
}
}
#[derive(Default, Debug, Clone)]
pub struct PhaseFunction;
pub struct MediumInteraction {
@ -234,7 +220,7 @@ impl SurfaceInteraction {
dudy: 0.0,
dvdx: 0.0,
dvdy: 0.0,
shape: None,
shape: Arc::new(Shape::default()),
bsdf: None,
}
}
@ -288,8 +274,30 @@ impl SurfaceInteraction {
si.uv = uv;
si
}
pub fn set_intersection_properties(
&mut self,
mtl: Arc<dyn MaterialTrait>,
area: Arc<Light>,
prim_medium_interface: Option<Arc<MediumInterface>>,
ray_medium: Arc<Medium>,
) {
self.material = Some(mtl);
self.area_light = Some(area);
if prim_medium_interface.as_ref().map_or(false, |mi| mi.is_medium_transition()) {
self.common.medium_interface = prim_medium_interface;
} else {
self.common.medium = Some(ray_medium);
}
}
}
// pub enum InteractionEnum {
// Surface(SurfaceInteraction),
// Medium(MediumInteraction),
// }
impl Interaction for MediumInteraction {
fn get_common(&self) -> &InteractionData {
&self.common

View file

@ -1,12 +1,13 @@
use crate::core::interaction::Interaction;
use crate::core::interaction::{Interaction, SurfaceInteraction};
use crate::core::pbrt::Float;
use crate::geometry::{Bounds3f, Ray};
use crate::shapes::{ShapeIntersection, ShapeTrait};
use crate::shapes::{ShapeIntersection, Shape};
use crate::core::material::MaterialTrait;
use crate::core::medium::MediumInterface;
use crate::lights::Light;
use crate::core::texture::{FloatTextureTrait, TextureEvalContext};
use crate::utils::hash::hash_float;
use crate::utils::transform::{AnimatedTransform, Transform};
use std::sync::Arc;
@ -18,7 +19,7 @@ pub trait PrimitiveTrait: Send + Sync + std::fmt::Debug {
#[derive(Debug, Clone)]
pub struct GeometricPrimitive {
shape: Arc<dyn ShapeTrait>,
shape: Arc<Shape>,
material: Arc<dyn MaterialTrait>,
area_light: Arc<Light>,
medium_interface: Arc<MediumInterface>,
@ -45,7 +46,9 @@ impl PrimitiveTrait for GeometricPrimitive {
return Some(si_next)
}
}
si.intr_mut().set_intersection_properties(self.material.clone(), self.area_light.clone(), Some(self.medium_interface.clone()), r.medium.clone()?);
if let Some(si) = si.intr().downcast_mut::<SurfaceInteraction>() {
si.as_ref().set_intersection_properties(self.material.clone(), self.area_light.clone(), Some(self.medium_interface.clone()), r.medium.clone()?);
}
Some(si)
}
@ -59,13 +62,56 @@ impl PrimitiveTrait for GeometricPrimitive {
}
#[derive(Debug, Clone)]
pub struct SimplePrimitive {
shape: Arc<dyn ShapeTrait>,
pub struct SimplePrimitiv {
shape: Arc<Shape>,
material: Arc<dyn MaterialTrait>,
}
pub struct TransformedPrimitive;
pub struct AnimatedPrimitive;
#[derive(Debug, Clone)]
pub struct TransformedPrimitive {
primitive: Arc<dyn PrimitiveTrait>,
render_from_primitive: Transform<Float>,
}
impl PrimitiveTrait for TransformedPrimitive {
fn bounds(&self) -> Bounds3f {
self.render_from_primitive.apply_to_bounds(self.primitive.bounds())
}
fn intersect(&self, _r: &Ray, _t_max: Option<Float>) -> Option<ShapeIntersection> {
todo!()
}
fn intersect_p(&self, _r: &Ray, _t_max: Option<Float>) -> bool {
todo!()
}
}
#[derive(Debug, Clone)]
pub struct AnimatedPrimitive {
primitive: Arc<dyn PrimitiveTrait>,
render_from_primitive: AnimatedTransform
}
impl PrimitiveTrait for AnimatedPrimitive {
fn bounds(&self) -> Bounds3f {
self.render_from_primitive.motion_bounds(&self.primitive.bounds())
}
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let interp_render_from_primitive = self.render_from_primitive.interpolate(r.time);
let ray = interp_render_from_primitive.apply_inverse_ray(r, t_max);
let mut si = self.primitive.intersect(r, t_max) else { return None };
let new_render = interp_render_from_primitive.apply_to_interaction(si?.intr());
let transform = new_render.as_any().downcast_mut::<SurfaceInteraction>().expect(
"Failed to downcast Interaction to SurfaceInteraction. This should not happen."
);
*si?.intr = new_render;
si
}
}
pub struct BVHAggregatePrimitive;
pub struct KdTreeAggregate;
@ -77,6 +123,7 @@ pub enum Primitive {
KdTree(KdTreeAggregate),
}
impl Primitive {
// pub fn bounds(&self) -> Bounds3f {
// match self {

View file

@ -656,6 +656,16 @@ impl<const N: usize> From<Vector<Interval, N>> for Vector<Float, N> {
}
}
impl<const N: usize> From<Vector<Float, N>> for Vector<Interval, N> {
fn from(v: Vector<Float, N>) -> Self {
let mut arr = [Interval::default(); N];
for i in 0..N {
arr[i] = Interval::new(v[i]);
}
Self(arr)
}
}
impl<const N: usize> Mul<Vector<Interval, N>> for Interval {
type Output = Vector<Interval, N>;
fn mul(self, rhs: Vector<Interval, N>) -> Self::Output {

View file

@ -1,14 +1,14 @@
use crate::core::medium::MediumInterface;
use crate::core::pbrt::Float;
use crate::shapes::ShapeTrait;
use crate::shapes::Shape;
use crate::utils::spectrum::Spectrum;
use crate::utils::transform::Transform;
use std::sync::Arc;
pub struct DiffuseAreaLight<'a> {
pub struct DiffuseAreaLight {
pub l_emit: Spectrum,
pub shape: Arc<&'a dyn ShapeTrait>,
pub shape: Arc<Shape>,
pub two_sided: bool,
pub area: Float,
pub flags: u8,

View file

@ -1,7 +1,7 @@
use super::{
BilinearIntersection, BilinearPatchShape, Bounds3f, DirectionCone, Interaction, Normal3f,
Point2f, Point3f, Point3fi, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext,
ShapeTrait, SurfaceInteraction, Vector3f,
SurfaceInteraction, Vector3f,
};
use crate::core::pbrt::{Float, clamp_t, gamma, lerp};
use crate::geometry::{Tuple, VectorLike, spherical_quad_area};
@ -500,14 +500,12 @@ impl BilinearPatchShape {
}
n
}
}
impl ShapeTrait for BilinearPatchShape {
fn area(&self) -> Float {
pub fn area(&self) -> Float {
self.area
}
fn normal_bounds(&self) -> DirectionCone {
pub fn normal_bounds(&self) -> DirectionCone {
let data = self.get_data();
if data.p00 == data.p10
|| data.p10 == data.p11
@ -554,19 +552,19 @@ impl ShapeTrait for BilinearPatchShape {
DirectionCone::new(n_avg.into(), clamp_t(cos_theta, -1., 1.))
}
fn bounds(&self) -> Bounds3f {
pub fn bounds(&self) -> Bounds3f {
let data = self.get_data();
Bounds3f::from_points(data.p00, data.p01).union(Bounds3f::from_points(data.p10, data.p11))
}
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let t_max_val = t_max?;
let data = self.get_data();
if let Some(bilinear_hit) = self.intersect_bilinear_patch(ray, t_max_val, &data) {
let intr = self.interaction_from_intersection(&data, bilinear_hit.uv, ray.time, -ray.d);
Some(ShapeIntersection {
intr,
intr: Box::new(intr),
t_hit: bilinear_hit.t,
})
} else {
@ -574,14 +572,14 @@ impl ShapeTrait for BilinearPatchShape {
}
}
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
let t_max_val = t_max.unwrap_or(Float::INFINITY);
let data = self.get_data();
self.intersect_bilinear_patch(ray, t_max_val, &data)
.is_some()
}
fn sample(&self, u: Point2f) -> Option<ShapeSample> {
pub fn sample(&self, u: Point2f) -> Option<ShapeSample> {
let data = self.get_data();
// Sample bilinear patch parametric coordinate (u, v)
let (uv, pdf) = self.sample_parametric_coords(&data, u);
@ -613,7 +611,7 @@ impl ShapeTrait for BilinearPatchShape {
})
}
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
pub fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
let data = self.get_data();
let v00 = (data.p00 - ctx.p()).normalize();
let v10 = (data.p10 - ctx.p()).normalize();
@ -630,7 +628,7 @@ impl ShapeTrait for BilinearPatchShape {
}
}
fn pdf(&self, intr: Arc<&dyn Interaction>) -> Float {
pub fn pdf(&self, intr: &dyn Interaction) -> Float {
let Some(si) = intr.as_any().downcast_ref::<SurfaceInteraction>() else {
return 0.;
};
@ -660,7 +658,7 @@ impl ShapeTrait for BilinearPatchShape {
if cross == 0. { 0. } else { param_pdf / cross }
}
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
pub fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
let ray = ctx.spawn_ray(wi);
let Some(isect) = self.intersect(&ray, None) else {
return 0.;
@ -677,7 +675,7 @@ impl ShapeTrait for BilinearPatchShape {
|| data.mesh.image_distribution.is_some()
|| spherical_quad_area(v00, v10, v01, v11) <= Self::MIN_SPHERICAL_SAMPLE_AREA;
if use_area_sampling {
let isect_pdf = self.pdf(Arc::new(&isect.intr));
let isect_pdf = self.pdf(isect.intr.as_ref());
let distsq = ctx.p().distance_squared(isect.intr.p());
let absdot = Vector3f::from(isect.intr.n()).abs_dot(-wi);
if absdot == 0. {

View file

@ -8,18 +8,18 @@ use crate::utils::transform::look_at;
use super::{
Bounds3f, CurveCommon, CurveShape, CurveType, DirectionCone, Float, Interaction, Normal3f,
Point2f, Point3f, Point3fi, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext,
ShapeTrait, SurfaceInteraction, Transform, Vector2f, Vector3f, VectorLike,
SurfaceInteraction, Transform, Vector2f, Vector3f, VectorLike,
};
use std::sync::Arc;
struct IntersectionContext<'a> {
ray: &'a Ray,
object_from_ray: &'a Transform<Float>,
common: &'a CurveCommon<'a>,
struct IntersectionContext {
ray: Ray,
object_from_ray: Arc<Transform<Float>>,
common: CurveCommon,
}
impl<'a> CurveShape<'a> {
pub fn new(common: CurveCommon<'a>, u_min: Float, u_max: Float) -> Self {
impl CurveShape {
pub fn new(common: CurveCommon, u_min: Float, u_max: Float) -> Self {
Self {
common,
u_min,
@ -76,9 +76,9 @@ impl<'a> CurveShape<'a> {
};
let context = IntersectionContext {
ray: &ray,
object_from_ray: &ray_from_object.inverse(),
common: &self.common,
ray: ray,
object_from_ray: Arc::new(ray_from_object.inverse()),
common: self.common.clone(),
};
self.recursive_intersect(&context, t_max, &cp, self.u_min, self.u_max, max_depth)
@ -257,12 +257,10 @@ impl<'a> CurveShape<'a> {
flip_normal,
);
Some(ShapeIntersection { intr, t_hit })
Some(ShapeIntersection { intr: Box::new(intr), t_hit })
}
}
impl ShapeTrait for CurveShape<'_> {
fn bounds(&self) -> Bounds3f {
pub fn bounds(&self) -> Bounds3f {
let cs_span = self.common.cp_obj;
let obj_bounds = bound_cubic_bezier(&cs_span, self.u_min, self.u_max);
let width0 = lerp(self.u_min, self.common.width[0], self.common.width[1]);
@ -273,11 +271,11 @@ impl ShapeTrait for CurveShape<'_> {
.apply_to_bounds(obj_bounds_expand)
}
fn normal_bounds(&self) -> DirectionCone {
pub fn normal_bounds(&self) -> DirectionCone {
DirectionCone::entire_sphere()
}
fn area(&self) -> Float {
pub fn area(&self) -> Float {
let cp_obj = cubic_bezier_control_points(&self.common.cp_obj, self.u_min, self.u_max);
let width0 = lerp(self.u_min, self.common.width[0], self.common.width[1]);
let width1 = lerp(self.u_max, self.common.width[0], self.common.width[1]);
@ -289,28 +287,28 @@ impl ShapeTrait for CurveShape<'_> {
approx_length * avg_width
}
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
self.intersect_ray(ray, t_max.unwrap_or(Float::INFINITY))
.is_some()
}
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
self.intersect_ray(ray, t_max.unwrap_or(Float::INFINITY))
}
fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
pub fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
todo!()
}
fn pdf_from_context(&self, _ctx: &ShapeSampleContext, _wi: Vector3f) -> Float {
pub fn pdf_from_context(&self, _ctx: &ShapeSampleContext, _wi: Vector3f) -> Float {
todo!()
}
fn sample(&self, _u: Point2f) -> Option<ShapeSample> {
pub fn sample(&self, _u: Point2f) -> Option<ShapeSample> {
todo!()
}
fn sample_from_context(&self, _ctx: &ShapeSampleContext, _u: Point2f) -> Option<ShapeSample> {
pub fn sample_from_context(&self, _ctx: &ShapeSampleContext, _u: Point2f) -> Option<ShapeSample> {
todo!()
}
}

View file

@ -1,7 +1,7 @@
use super::{
Bounds3f, CylinderShape, DirectionCone, Float, Interaction, Normal3f, PI, Point2f, Point3f,
Point3fi, QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext,
ShapeTrait, SurfaceInteraction, Transform, Vector3f, Vector3fi,
SurfaceInteraction, Transform, Vector3f, Vector3fi,
};
use crate::core::pbrt::{gamma, lerp};
use crate::geometry::{Sqrt, Tuple, VectorLike};
@ -10,10 +10,10 @@ use crate::utils::math::{difference_of_products, square};
use std::mem;
use std::sync::Arc;
impl<'a> CylinderShape<'a> {
impl CylinderShape {
pub fn new(
render_from_object: &'a Transform<Float>,
object_from_render: &'a Transform<Float>,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
radius: Float,
z_min: Float,
@ -25,7 +25,7 @@ impl<'a> CylinderShape<'a> {
z_min,
z_max,
phi_max,
render_from_object,
render_from_object: render_from_object.clone(),
object_from_render,
reverse_orientation,
transform_swap_handedness: render_from_object.swaps_handedness(),
@ -168,14 +168,12 @@ impl<'a> CylinderShape<'a> {
);
surf_point
}
}
impl ShapeTrait for CylinderShape<'_> {
fn area(&self) -> Float {
pub fn area(&self) -> Float {
(self.z_max - self.z_min) * self.radius * self.phi_max
}
fn bounds(&self) -> Bounds3f {
pub fn bounds(&self) -> Bounds3f {
self.render_from_object
.apply_to_bounds(Bounds3f::from_points(
Point3f::new(-self.radius, -self.radius, self.z_min),
@ -183,11 +181,11 @@ impl ShapeTrait for CylinderShape<'_> {
))
}
fn normal_bounds(&self) -> DirectionCone {
pub fn normal_bounds(&self) -> DirectionCone {
DirectionCone::entire_sphere()
}
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let t = t_max.unwrap_or(Float::INFINITY);
if let Some(isect) = self.basic_intersect(ray, t) {
let intr = self.interaction_from_intersection(isect.clone(), -ray.d, ray.time);
@ -197,7 +195,7 @@ impl ShapeTrait for CylinderShape<'_> {
}
}
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
if let Some(t) = t_max {
self.basic_intersect(ray, t).is_some()
} else {
@ -205,11 +203,11 @@ impl ShapeTrait for CylinderShape<'_> {
}
}
fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
pub fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
1. / self.area()
}
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
pub fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
let ray = ctx.spawn_ray(wi);
if let Some(isect) = self.intersect(&ray, None) {
let n = isect.intr.n();
@ -224,7 +222,7 @@ impl ShapeTrait for CylinderShape<'_> {
}
}
fn sample(&self, u: Point2f) -> Option<ShapeSample> {
pub fn sample(&self, u: Point2f) -> Option<ShapeSample> {
let z = lerp(u[0], self.z_min, self.z_max);
let phi = u[1] * self.phi_max;
let mut p_obj = Point3f::new(self.radius * phi.cos(), self.radius * phi.sin(), z);
@ -253,7 +251,7 @@ impl ShapeTrait for CylinderShape<'_> {
})
}
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
pub fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
let mut ss = self.sample(u)?;
let intr = Arc::make_mut(&mut ss.intr);
intr.get_common_mut().time = ctx.time;

View file

@ -1,21 +1,21 @@
use super::{
Bounds3f, DirectionCone, DiskShape, Float, Interaction, Normal3f, PI, Point2f, Point3f,
Point3fi, QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext,
ShapeTrait, SurfaceInteraction, Transform, Vector3f,
SurfaceInteraction, Transform, Vector3f,
};
use crate::geometry::VectorLike;
use crate::utils::math::square;
use crate::utils::sampling::sample_uniform_disk_concentric;
use std::sync::Arc;
impl<'a> DiskShape<'a> {
impl DiskShape {
pub fn new(
radius: Float,
inner_radius: Float,
height: Float,
phi_max: Float,
render_from_object: &'a Transform<Float>,
object_from_render: &'a Transform<Float>,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
) -> Self {
Self {
@ -23,7 +23,7 @@ impl<'a> DiskShape<'a> {
inner_radius,
height,
phi_max,
render_from_object,
render_from_object: render_from_object.clone(),
object_from_render,
reverse_orientation,
transform_swap_handedness: render_from_object.swaps_handedness(),
@ -101,21 +101,19 @@ impl<'a> DiskShape<'a> {
);
surf_point
}
}
impl ShapeTrait for DiskShape<'_> {
fn area(&self) -> Float {
pub fn area(&self) -> Float {
self.phi_max * 0.5 * (square(self.radius) - square(self.inner_radius))
}
fn bounds(&self) -> Bounds3f {
pub fn bounds(&self) -> Bounds3f {
self.render_from_object
.apply_to_bounds(Bounds3f::from_points(
Point3f::new(-self.radius, -self.radius, self.height),
Point3f::new(self.radius, self.radius, self.height),
))
}
fn normal_bounds(&self) -> DirectionCone {
pub fn normal_bounds(&self) -> DirectionCone {
let mut n = self
.render_from_object
.apply_to_normal(Normal3f::new(0., 0., 1.));
@ -125,7 +123,7 @@ impl ShapeTrait for DiskShape<'_> {
DirectionCone::new_from_vector(Vector3f::from(n))
}
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let t = t_max.unwrap_or(Float::INFINITY);
if let Some(isect) = self.basic_intersect(ray, t) {
let intr = self.interaction_from_intersection(isect.clone(), -ray.d, ray.time);
@ -135,7 +133,7 @@ impl ShapeTrait for DiskShape<'_> {
}
}
fn sample(&self, u: Point2f) -> Option<ShapeSample> {
pub fn sample(&self, u: Point2f) -> Option<ShapeSample> {
let pd = sample_uniform_disk_concentric(u);
let p_obj = Point3f::new(pd.x() * self.radius, pd.y() * self.radius, self.height);
let pi = self
@ -163,7 +161,7 @@ impl ShapeTrait for DiskShape<'_> {
})
}
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
if let Some(t) = t_max {
self.basic_intersect(ray, t).is_some()
} else {
@ -171,7 +169,7 @@ impl ShapeTrait for DiskShape<'_> {
}
}
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
pub fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
let mut ss = self.sample(u)?;
let intr = Arc::make_mut(&mut ss.intr);
intr.get_common_mut().time = ctx.time;
@ -187,11 +185,11 @@ impl ShapeTrait for DiskShape<'_> {
return Some(ss);
}
fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
pub fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
1. / self.area()
}
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
pub fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
let ray = ctx.spawn_ray(wi);
if let Some(isect) = self.intersect(&ray, None) {
let n = isect.intr.n();

View file

@ -9,46 +9,46 @@ use crate::core::interaction::{Interaction, MediumInteraction, SurfaceInteractio
use crate::core::pbrt::{Float, PI};
use crate::geometry::{
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
Vector3fi, VectorLike,
Vector3fi, VectorLike
};
use crate::utils::math::{next_float_down, next_float_up};
use crate::utils::transform::Transform;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct SphereShape<'a> {
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: &'a Transform<Float>,
object_from_render: &'a Transform<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 CylinderShape<'a> {
pub struct CylinderShape {
radius: Float,
z_min: Float,
z_max: Float,
phi_max: Float,
render_from_object: &'a Transform<Float>,
object_from_render: &'a Transform<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<'a> {
pub struct DiskShape {
radius: Float,
inner_radius: Float,
height: Float,
phi_max: Float,
render_from_object: &'a Transform<Float>,
object_from_render: &'a Transform<Float>,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
transform_swap_handedness: bool,
}
@ -75,28 +75,28 @@ pub enum CurveType {
}
#[derive(Debug, Clone)]
pub struct CurveCommon<'a> {
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: &'a Transform<Float>,
object_from_render: &'a Transform<Float>,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
transform_swap_handedness: bool,
}
impl<'a> CurveCommon<'a> {
impl CurveCommon {
pub fn new(
c: &[Point3f],
w0: Float,
w1: Float,
curve_type: CurveType,
norm: &[Vector3f],
render_from_object: &'a Transform<Float>,
object_from_render: &'a Transform<Float>,
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();
@ -133,8 +133,8 @@ impl<'a> CurveCommon<'a> {
}
#[derive(Debug, Clone)]
pub struct CurveShape<'a> {
common: CurveCommon<'a>,
pub struct CurveShape {
common: CurveCommon,
u_min: Float,
u_max: Float,
}
@ -142,21 +142,23 @@ pub struct CurveShape<'a> {
// Define Intersection objects. This only varies for
#[derive(Debug, Clone)]
pub struct ShapeIntersection {
intr: SurfaceInteraction,
t_hit: Float,
pub intr: Box<SurfaceInteraction>,
pub t_hit: Float,
}
impl ShapeIntersection {
pub fn new(intr: SurfaceInteraction, t_hit: Float) -> Self {
Self { intr, t_hit }
Self { intr: Box::new(intr), t_hit }
}
pub fn intr(&self) -> &SurfaceInteraction {
&self.intr
pub fn as_ref(&self) -> &dyn Interaction { &*self.intr }
pub fn intr(&self) -> &dyn Interaction {
&*self.intr
}
pub fn intr_mut(&mut self) -> &mut SurfaceInteraction {
&mut self.intr
&mut *self.intr
}
pub fn t_hit(&self) -> Float {
@ -266,24 +268,124 @@ impl ShapeSampleContext {
}
}
#[derive(Debug, Clone)]
pub enum Shape<'a> {
Sphere(SphereShape<'a>),
Cylinder(CylinderShape<'a>),
Disk(DiskShape<'a>),
#[derive(Default, Debug, Clone)]
pub enum Shape {
#[default]
None,
Sphere(SphereShape),
Cylinder(CylinderShape),
Disk(DiskShape),
Triangle(TriangleShape),
BilinearPatch(BilinearPatchShape),
Curve(CurveShape<'a>),
Curve(CurveShape),
}
pub trait ShapeTrait: Send + Sync + std::fmt::Debug {
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 sample(&self, u: Point2f) -> Option<ShapeSample>;
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample>;
fn pdf(&self, interaction: Arc<&dyn Interaction>) -> Float;
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float;
impl<'a> Shape {
pub fn bounds(&self) -> Bounds3f {
match self {
Shape::None => Bounds3f::default(),
Shape::Sphere(s) => s.bounds(),
Shape::Cylinder(c) => c.bounds(),
Shape::Disk(d) => d.bounds(),
Shape::Triangle(t) => t.bounds(),
Shape::BilinearPatch(b) => b.bounds(),
Shape::Curve(c) => c.bounds(),
}
}
pub fn normal_bounds(&self) -> DirectionCone {
match self {
Shape::None => DirectionCone::entire_sphere(),
Shape::Sphere(s) => s.normal_bounds(),
Shape::Cylinder(c) => c.normal_bounds(),
Shape::Disk(d) => d.normal_bounds(),
Shape::Triangle(t) => t.normal_bounds(),
Shape::BilinearPatch(b) => b.normal_bounds(),
Shape::Curve(c) => c.normal_bounds(),
}
}
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
match self {
Shape::None => None,
Shape::Sphere(s) => s.intersect(ray, t_max),
Shape::Cylinder(c) => c.intersect(ray, t_max),
Shape::Disk(d) => d.intersect(ray, t_max),
Shape::Triangle(t) => t.intersect(ray, t_max),
Shape::BilinearPatch(b) => b.intersect(ray, t_max),
Shape::Curve(c) => c.intersect(ray, t_max),
}
}
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
match self {
Shape::None => false,
Shape::Sphere(s) => s.intersect_p(ray, t_max),
Shape::Cylinder(c) => c.intersect_p(ray, t_max),
Shape::Disk(d) => d.intersect_p(ray, t_max),
Shape::Triangle(t) => t.intersect_p(ray, t_max),
Shape::BilinearPatch(b) => b.intersect_p(ray, t_max),
Shape::Curve(c) => c.intersect_p(ray, t_max),
}
}
pub fn area(&self) -> Float {
match self {
Shape::None => 0.,
Shape::Sphere(s) => s.area(),
Shape::Cylinder(c) => c.area(),
Shape::Disk(d) => d.area(),
Shape::Triangle(t) => t.area(),
Shape::BilinearPatch(b) => b.area(),
Shape::Curve(c) => c.area(),
}
}
pub fn pdf(&self, interaction: Arc<&dyn Interaction>) -> Float {
match self {
Shape::None => 0.,
Shape::Sphere(s) => s.pdf(interaction),
Shape::Cylinder(c) => c.pdf(interaction),
Shape::Disk(d) => d.pdf(interaction),
Shape::Triangle(t) => t.pdf(interaction),
Shape::BilinearPatch(b) => b.pdf(interaction),
Shape::Curve(c) => c.pdf(interaction),
}
}
pub fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
match self {
Shape::None => 0.,
Shape::Sphere(s) => s.pdf_from_context(ctx, wi),
Shape::Cylinder(c) => c.pdf_from_context(ctx, wi),
Shape::Disk(d) => d.pdf_from_context(ctx, wi),
Shape::Triangle(t) => t.pdf_from_context(ctx, wi),
Shape::BilinearPatch(b) => b.pdf_from_context(ctx, wi),
Shape::Curve(c) => c.pdf_from_context(ctx, wi),
}
}
pub fn sample(&self, u: Point2f) -> Option<ShapeSample> {
match self {
Shape::None => None,
Shape::Sphere(s) => s.sample(u),
Shape::Cylinder(c) => c.sample(u),
Shape::Disk(d) => d.sample(u),
Shape::Triangle(t) => t.sample(u),
Shape::BilinearPatch(b) => b.sample(u),
Shape::Curve(c) => c.sample(u),
}
}
pub fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
match self {
Shape::None => None,
Shape::Sphere(s) => s.sample_from_context(ctx, u),
Shape::Cylinder(c) => c.sample_from_context(ctx, u),
Shape::Disk(d) => d.sample_from_context(ctx, u),
Shape::Triangle(t) => t.sample_from_context(ctx, u),
Shape::BilinearPatch(b) => b.sample_from_context(ctx, u),
Shape::Curve(c) => c.sample_from_context(ctx, u),
}
}
}

View file

@ -1,6 +1,6 @@
use super::{
Bounds3f, DirectionCone, Float, Interaction, Normal3f, PI, Point2f, Point3f, Point3fi,
QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait,
QuadricIntersection, Ray, ShapeIntersection, ShapeSample, ShapeSampleContext,
SphereShape, SurfaceInteraction, Transform, Vector3f, Vector3fi,
};
use crate::core::pbrt::{clamp_t, gamma};
@ -12,10 +12,10 @@ use crate::utils::sampling::sample_uniform_sphere;
use std::mem;
use std::sync::Arc;
impl<'a> SphereShape<'a> {
impl SphereShape {
pub fn new(
render_from_object: &'a Transform<Float>,
object_from_render: &'a Transform<Float>,
render_from_object: Arc<Transform<Float>>,
object_from_render: Arc<Transform<Float>>,
reverse_orientation: bool,
radius: Float,
z_min: Float,
@ -26,8 +26,8 @@ impl<'a> SphereShape<'a> {
let theta_z_max = clamp_t(z_max.min(z_max) / radius, -1., 1.).acos();
let phi_max = radians(clamp_t(phi_max, 0., 360.0));
Self {
render_from_object,
object_from_render,
render_from_object: render_from_object.clone(),
object_from_render: object_from_render.clone(),
radius,
z_min: clamp_t(z_min.min(z_max), -radius, radius),
z_max: clamp_t(z_min.max(z_max), -radius, radius),
@ -193,10 +193,8 @@ impl<'a> SphereShape<'a> {
// self.render_from_object.apply_to_point(surf_point)
surf_point
}
}
impl ShapeTrait for SphereShape<'_> {
fn bounds(&self) -> Bounds3f {
pub fn bounds(&self) -> Bounds3f {
self.render_from_object
.apply_to_bounds(Bounds3f::from_points(
Point3f::new(-self.radius, -self.radius, self.z_min),
@ -204,19 +202,19 @@ impl ShapeTrait for SphereShape<'_> {
))
}
fn normal_bounds(&self) -> DirectionCone {
pub fn normal_bounds(&self) -> DirectionCone {
DirectionCone::entire_sphere()
}
fn area(&self) -> Float {
pub fn area(&self) -> Float {
self.phi_max * self.radius * (self.z_max - self.z_min)
}
fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
pub fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
1. / self.area()
}
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
let t = t_max.unwrap_or(Float::INFINITY);
if let Some(isect) = self.basic_intersect(ray, t) {
let intr = self.interaction_from_intersection(isect.clone(), -ray.d, ray.time);
@ -226,7 +224,7 @@ impl ShapeTrait for SphereShape<'_> {
}
}
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
if let Some(t) = t_max {
self.basic_intersect(ray, t).is_some()
} else {
@ -234,7 +232,7 @@ impl ShapeTrait for SphereShape<'_> {
}
}
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
pub fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
let p_center = self
.object_from_render
.apply_to_point(Point3f::new(0., 0., 0.));
@ -262,7 +260,7 @@ impl ShapeTrait for SphereShape<'_> {
1. / (2. * PI * one_minus_cos_theta_max)
}
fn sample(&self, u: Point2f) -> Option<ShapeSample> {
pub fn sample(&self, u: Point2f) -> Option<ShapeSample> {
let p_obj = Point3f::new(0., 0., 0.) + self.radius * sample_uniform_sphere(u);
let mut p_obj_vec = Vector3f::from(p_obj);
p_obj_vec *= self.radius / p_obj.distance(Point3f::zero());
@ -294,7 +292,7 @@ impl ShapeTrait for SphereShape<'_> {
})
}
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
pub fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
let p_center = self.render_from_object.apply_to_point(Point3f::zero());
let p_origin = ctx.offset_ray_origin_from_point(p_center);
if p_origin.distance_squared(p_center) <= square(self.radius) {

View file

@ -1,6 +1,6 @@
use super::{
Bounds3f, DirectionCone, Float, Interaction, Normal3f, Point2f, Point3f, Point3fi, Ray,
ShapeIntersection, ShapeSample, ShapeSampleContext, ShapeTrait, SurfaceInteraction,
ShapeIntersection, ShapeSample, ShapeSampleContext, SurfaceInteraction,
TriangleIntersection, TriangleShape, Vector2f, Vector3f,
};
use crate::core::pbrt::gamma;
@ -323,15 +323,13 @@ impl TriangleShape {
isect.dndu = dndu;
isect.dndv = dndv;
}
}
impl ShapeTrait for TriangleShape {
fn bounds(&self) -> Bounds3f {
pub fn bounds(&self) -> Bounds3f {
let [p0, p1, p2] = self.get_data().vertices;
Bounds3f::from_points(p0, p1).union_point(p2)
}
fn normal_bounds(&self) -> DirectionCone {
pub fn normal_bounds(&self) -> DirectionCone {
let data = self.get_data();
let mut n = data.normal;
if let Some([n0, n1, n2]) = data.normals {
@ -342,15 +340,15 @@ impl ShapeTrait for TriangleShape {
DirectionCone::new_from_vector(Vector3f::from(n))
}
fn area(&self) -> Float {
pub fn area(&self) -> Float {
self.get_data().area
}
fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
pub fn pdf(&self, _interaction: Arc<&dyn Interaction>) -> Float {
1. / self.area()
}
fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
pub fn pdf_from_context(&self, ctx: &ShapeSampleContext, wi: Vector3f) -> Float {
let solid_angle = self.solid_angle(ctx.p());
if solid_angle < Self::MIN_SPHERICAL_SAMPLE_AREA
|| solid_angle > Self::MAX_SPHERICAL_SAMPLE_AREA
@ -387,7 +385,7 @@ impl ShapeTrait for TriangleShape {
pdf
}
fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
pub fn sample_from_context(&self, ctx: &ShapeSampleContext, u: Point2f) -> Option<ShapeSample> {
let data = self.get_data();
let [p0, p1, p2] = data.vertices;
let solid_angle = self.solid_angle(ctx.p());
@ -472,7 +470,7 @@ impl ShapeTrait for TriangleShape {
})
}
fn sample(&self, u: Point2f) -> Option<ShapeSample> {
pub fn sample(&self, u: Point2f) -> Option<ShapeSample> {
let data = self.get_data();
let [p0, p1, p2] = data.vertices;
let [uv0, uv1, uv2] = data.uvs;
@ -503,15 +501,15 @@ impl ShapeTrait for TriangleShape {
})
}
fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
pub fn intersect(&self, ray: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY))
.map(|ti| {
let intr = self.interaction_from_intersection(ti, ray.time, -ray.d);
ShapeIntersection { intr, t_hit: ti.t }
ShapeIntersection { intr: Box::new(intr), t_hit: ti.t }
})
}
fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
pub fn intersect_p(&self, ray: &Ray, t_max: Option<Float>) -> bool {
self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY))
.is_some()
}

View file

@ -44,6 +44,21 @@ impl Interval {
pub fn is_empty(&self) -> bool {
self.low > self.high
}
pub fn abs(&self) -> Self {
if self.low >= 0.0 {
return *self;
}
if self.high < 0.0 {
return -(*self);
}
Self {
low: 0.0,
high: next_float_up((-self.low).max(self.high)),
}
}
}
impl Default for Interval {
@ -65,6 +80,20 @@ impl Add for Interval {
}
}
impl Add<Interval> for Float {
type Output = Interval;
fn add(self, rhs: Interval) -> Self::Output {
Interval::new(self) + rhs
}
}
impl Sub<Interval> for Float {
type Output = Interval;
fn sub(self, rhs: Interval) -> Self::Output {
Interval::new(self) - rhs
}
}
impl Sub for Interval {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {

View file

@ -2,10 +2,12 @@ use num_traits::Float as NumFloat;
use std::error::Error;
use std::fmt::{self, Display};
use std::ops::{Add, Div, Index, IndexMut, Mul};
use std::sync::Arc;
use super::color::{RGB, XYZ};
use super::math::{SquareMatrix, safe_acos};
use super::quaternion::Quaternion;
use crate::core::interaction::{SurfaceInteraction, MediumInteraction, Interaction, InteractionData};
use crate::core::pbrt::{Float, gamma};
use crate::geometry::{
Bounds3f, Normal, Normal3f, Point, Point3f, Point3fi, Ray, Vector, Vector3f, Vector3fi,
@ -51,8 +53,13 @@ impl<T: NumFloat> Transform<T> {
}
pub fn apply_inverse_vector(&self, v: Vector<T, 3>) -> Vector<T, 3> {
self.clone() * v
}
let x = v.x();
let y = v.y();
let z = v.z();
Vector::<T, 3>::new(self.m_inv[0][0] * x + self.m_inv[0][1] * y + self.m_inv[0][2] * z,
self.m_inv[1][0] * x + self.m_inv[1][1] * y + self.m_inv[1][2] * z,
self.m_inv[2][0] * x + self.m_inv[2][1] * y + self.m_inv[2][2] * z)
}
pub fn apply_inverse_normal(&self, n: Normal<T, 3>) -> Normal<T, 3> {
self.clone() * n
@ -208,6 +215,120 @@ impl Transform<Float> {
}
}
pub fn apply_to_interaction(&self, inter: &dyn Interaction) -> Box<dyn Interaction> {
if let Some(si) = inter.as_any().downcast_ref::<SurfaceInteraction>() {
let mut ret = si.clone();
ret.common.pi = self.apply_to_interval(&si.common.pi);
let n = self.apply_to_normal(si.common.n);
ret.common.wo = self.apply_to_vector(si.common.wo).normalize();
ret.dpdu = self.apply_to_vector(si.dpdu);
ret.dpdv = self.apply_to_vector(si.dpdv);
ret.dndu = self.apply_to_normal(si.dndu);
ret.dndv = self.apply_to_normal(si.dndv);
ret.dpdx = self.apply_to_vector(si.dpdx);
ret.dpdy = self.apply_to_vector(si.dpdy);
let shading_n = self.apply_to_normal(si.shading.n);
ret.shading.n = shading_n.normalize();
ret.shading.dpdu = self.apply_to_vector(si.shading.dpdu);
ret.shading.dpdv = self.apply_to_vector(si.shading.dpdv);
ret.shading.dndu = self.apply_to_normal(si.shading.dndu);
ret.shading.dndv = self.apply_to_normal(si.shading.dndv);
ret.common.n = n.normalize().face_forward(shading_n.into());
Box::new(ret)
} else if let Some(mi) = inter.as_any().downcast_ref::<MediumInteraction>() {
let ret = MediumInteraction {
common: InteractionData {
pi: self.apply_to_interval(&mi.common.pi),
n: self.apply_to_normal(mi.common.n).normalize(),
wo: self.apply_to_vector(mi.common.wo).normalize(),
time: mi.common.time,
medium_interface: mi.common.medium_interface.clone(),
medium: mi.common.medium.clone(),
},
medium: mi.medium.clone(),
phase: mi.phase.clone(),
};
Box::new(ret)
} else {
panic!("Unhandled Interaction type in Transform::apply_to_interaction");
}
}
pub fn apply_inverse_interval(&self, p: &Point3fi) -> Point3fi {
let x = Float::from(p.x());
let y = Float::from(p.y());
let z = Float::from(p.z());
let m_inv = &self.m_inv;
// Compute transformed coordinates from point
let xp = (m_inv[0][0] * x + m_inv[0][1] * y) + (m_inv[0][2] * z + m_inv[0][3]);
let yp = (m_inv[1][0] * x + m_inv[1][1] * y) + (m_inv[1][2] * z + m_inv[1][3]);
let zp = (m_inv[2][0] * x + m_inv[2][1] * y) + (m_inv[2][2] * z + m_inv[2][3]);
let wp = (m_inv[3][0] * x + m_inv[3][1] * y) + (m_inv[3][2] * z + m_inv[3][3]);
// Compute absolute error for transformed point
let p_out_error: Vector3f;
let g3 = gamma(3);
if p.is_exact() {
p_out_error = Vector3f::new(
g3 * (m_inv[0][0] * x).abs() + (m_inv[0][1] * y).abs() + (m_inv[0][2] * z).abs(),
g3 * (m_inv[1][0] * x).abs() + (m_inv[1][1] * y).abs() + (m_inv[1][2] * z).abs(),
g3 * (m_inv[2][0] * x).abs() + (m_inv[2][1] * y).abs() + (m_inv[2][2] * z).abs(),
);
} else {
let p_in_error = p.error();
let g3_plus_1 = g3 + 1.0;
p_out_error = Vector3f::new(
g3_plus_1 * (m_inv[0][0].abs() * p_in_error.x() +
m_inv[0][1].abs() * p_in_error.y() +
m_inv[0][2].abs() * p_in_error.z()) +
g3 * ((m_inv[0][0] * x).abs() + (m_inv[0][1] * y).abs() +
(m_inv[0][2] * z).abs() + m_inv[0][3].abs()),
g3_plus_1 * (m_inv[1][0].abs() * p_in_error.x() +
m_inv[1][1].abs() * p_in_error.y() +
m_inv[1][2].abs() * p_in_error.z()) +
g3 * ((m_inv[1][0] * x).abs() + (m_inv[1][1] * y).abs() +
(m_inv[1][2] * z).abs() + m_inv[1][3].abs()),
g3_plus_1 * (m_inv[2][0].abs() * p_in_error.x() +
m_inv[2][1].abs() * p_in_error.y() +
m_inv[2][2].abs() * p_in_error.z()) +
g3 * ((m_inv[2][0] * x).abs() + (m_inv[2][1] * y).abs() +
(m_inv[2][2] * z).abs() + m_inv[2][3].abs()),
);
}
if wp == 1.0 {
Point3fi::new_with_error(Point3f::new(xp, yp, zp), p_out_error)
} else {
Point3fi::new_with_error(Point3f::new(xp/wp, yp/wp, zp/wp), p_out_error)
}
}
pub fn apply_inverse_ray(&self, r: &Ray, t_max: Option<Float>) -> (Ray, Float) {
let mut o = self.apply_inverse_interval(&Point3fi::new_from_point(r.o));
let d = self.apply_inverse_vector(r.d);
// Offset ray origin to edge of error bounds and compute _tMax_
let mut t = 0.;
let length_squared = d.norm_squared();
if length_squared > 0. {
let o_error = Vector3f::new(o.x().width() / 2., o.y().width() / 2., o.z().width() / 2.);
let dt = d.abs().dot(o_error) / length_squared;
o = o + Vector3fi::from(d * dt);
if let Some(t_max) = t_max {
t = t_max - dt;
}
}
(Ray::new(Point3f::from(o), d, Some(r.time), r.medium.clone()), t)
}
pub fn to_quaternion(&self) -> Quaternion {
let trace = self.m.trace();
let mut quat = Quaternion::default();
@ -580,6 +701,7 @@ impl DerivativeTerm {
}
}
#[derive(Debug, Clone)]
pub struct AnimatedTransform {
pub start_transform: Transform<Float>,
pub end_transform: Transform<Float>,
@ -1800,6 +1922,14 @@ impl AnimatedTransform {
t.apply_to_ray(r, t_max)
}
pub fn apply_interaction(&self, si: &dyn Interaction) -> Box<dyn Interaction> {
if !self.actually_animated {
return self.start_transform.apply_to_interaction(si)
}
let t = self.interpolate(si.time());
t.apply_to_interaction(si)
}
pub fn interpolate(&self, time: Float) -> Transform<Float> {
if !self.actually_animated || time <= self.start_time {
return self.start_transform;
@ -1840,6 +1970,13 @@ impl AnimatedTransform {
}
return self.interpolate(time).apply_inverse_normal(n);
}
pub fn motion_bounds(&self, b: &Bounds3f) -> Bounds3f {
if !self.actually_animated {
return self.start_transform.apply_to_bounds(*b)
}
return self.start_transform.apply_to_bounds(*b).union(self.end_transform.apply_to_bounds(*b))
}
}
pub fn look_at(