Initial commit

This commit is contained in:
pingupingou 2025-10-29 17:29:36 +00:00
commit 137ddfbd28
15 changed files with 890 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
*.lock

7
Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "pbrt"
version = "0.1.0"
edition = "2024"
[dependencies]
num-traits = "0.2.19"

396
src/core/geometry.rs Normal file
View file

@ -0,0 +1,396 @@
use std::ops::{Sub, SubAssign, Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Index, IndexMut};
use crate::core::pbrt::Float;
use num_traits::{Num, Bounded};
pub trait Tuple<const N: usize, T> : Index<usize, Output = T> + IndexMut<usize> + Copy {
fn get(&self, i: usize) -> T;
fn set(&mut self, i: usize, val: T);
}
#[derive(Copy, Clone, PartialEq)]
pub struct Vector2<T> { pub x: T, pub y: T }
#[derive(Copy, Clone, PartialEq)]
pub struct Point2<T> { pub x: T, pub y: T }
#[derive(Copy, Clone, PartialEq)]
pub struct Vector3<T> { pub x: T, pub y: T, pub z: T }
#[derive(Copy, Clone, PartialEq)]
pub struct Normal3<T> { pub x: T, pub y: T, pub z: T }
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Point3<T> { pub x: T, pub y: T, pub z : T }
// Using macros to make this more concise
// This is pretty useful, gotta use it more often
macro_rules! impl_tuple_core {
($Struct:ident, [$($field:ident), +]) => {
impl<T> $Struct<T> {
pub fn new($($field: T), +) -> Self { Self { $($field), + }}
pub fn has_nan(&self) -> bool where T: num_traits::Float {
$(self.$field.is_nan())||+
}
}
impl<T: Num + Copy> $Struct<T> {
pub fn dot(self, other: Self) -> T {
let mut sum = T::zero();
$( sum = sum + self.$field * other.$field; )+
sum
}
pub fn abs_dot(self, other: Self) -> T {
self.dot(other).abs()
}
pub fn length_squared(self) -> T {
self.dot(self)
}
}
impl<T: num_traits::Float> $Struct<T> {
pub fn length(self) -> T {
self.length_squared().sqrt()
}
pub fn normalize(self) -> Self {
self / self.length()
}
}
impl<T: Neg<Output=T>> Neg for $Struct<T> {
type Output = Self;
fn neg(self) -> Self { Self::new($(-self.$field),+) }
}
impl<T: Mul<Output=T> + Copy> Mul<T> for $Struct<T> {
type Output = Self;
fn mul(self, s: T) -> Self { Self::new($(self.$field * s),+) }}
impl<T: MulAssign + Copy> MulAssign<T> for $Struct<T> {
fn mul_assign(&mut self, s: T) { $(self.$field *= s;)+ }
}
impl<T: Div<Output=T> + Copy> Div<T> for $Struct<T> {
type Output = Self;
fn div(self, s: T) -> Self { Self::new($(self.$field / s),+) }
}
impl<T: DivAssign + Copy> DivAssign<T> for $Struct<T> {
fn div_assign(&mut self, s: T) { $(self.$field /= s;)+ }
}
// Indexing
impl<T> Index<usize> for $Struct<T> {
type Output = T;
fn index(&self, i: usize) -> &T {
let mut idx = 0;
$( if i == idx { return &self.$field; } idx += 1; )+
panic!("Index out of bounds");
}
}
impl<T> IndexMut<usize> for $Struct<T> {
fn index_mut(&mut self, i: usize) -> &mut T {
let mut idx = 0;
$( if i == idx { return &mut self.$field; } idx += 1; )+
panic!("Index out of bounds");
}
}
};
}
macro_rules! impl_tuple_ops {
($Struct:ident) => {
impl<T: Add<Output=T>> Add for $Struct<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self { Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) }
}
impl<T: AddAssign + Copy> AddAssign for $Struct<T> {
fn add_assign(&mut self, rhs: Self)
{ self.x += rhs.x; self.y += rhs.y; self.z += rhs.z; }
}
impl<T: Sub<Output=T>> Sub for $Struct<T> {
type Output = Self;
fn sub(self, rhs: Self) -> Self { Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) }
}
impl<T: SubAssign + Copy> SubAssign for $Struct<T> {
fn sub_assign(&mut self, rhs: Self) { self.x -= rhs.x; self.y -= rhs.y; self.z -= rhs.z; }
}
};
}
impl_tuple_core!(Vector3, [x, y, z]);
impl_tuple_core!(Normal3, [x, y, z]);
impl_tuple_core!(Point2, [x, y]);
impl_tuple_core!(Point3, [x, y, z]);
impl_tuple_ops!(Vector3);
impl_tuple_ops!(Normal3);
// Point - Point -> Vector
impl<T: Sub<Output = T>> Sub<Point3<T>> for Point3<T> {
type Output = Vector3<T>;
fn sub(self, rhs: Point3<T>) -> Vector3<T> {
Vector3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
// Point + Vector -> Point
impl<T: Add<Output = T>> Add<Vector3<T>> for Point3<T> {
type Output = Point3<T>;
fn add(self, rhs: Vector3<T>) -> Point3<T> {
Point3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
}
}
impl<T: AddAssign + Copy> AddAssign<Vector3<T>> for Point3<T> {
fn add_assign(&mut self, rhs: Vector3<T>) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
// Point - Vector -> Point
impl<T: Sub<Output = T>> Sub<Vector3<T>> for Point3<T> {
type Output = Point3<T>;
fn sub(self, rhs: Vector3<T>) -> Point3<T> {
Point3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
impl<T: SubAssign + Copy> SubAssign<Vector3<T>> for Point3<T> {
fn sub_assign(&mut self, rhs: Vector3<T>) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl<T: Num + Copy> Vector3<T> {
pub fn cross(self, other: Vector3<T>) -> Vector3<T> {
Vector3 {
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
}
pub trait Norm {
type Scalar;
fn norm(self) -> Self::Scalar;
fn norm_squared(self) -> Self::Scalar;
}
impl<T: num_traits::Float> Norm for Vector2<T> {
type Scalar = T;
fn norm(self) -> T { self.length_squared().sqrt() }
fn norm_squared(self) -> T { self.length_squared }
}
pub fn distance<T, const N: usize>(p1: Point2<T>, p2: Point2<T>) -> T
where
T: num_traits::Float,
Point2<T>: Sub<Output = Vector2<T>>,
Vector2<T>: Copy + Mul<Output = T>,
{
(p1 - p2).length()
}
pub fn distance_squared<T>(p1: Point2<T>) {
}
pub type Point3f = Point3<Float>;
pub type Point3i = Point3<i32>;
pub type Vector3f = Vector3<Float>;
pub type Vector3i = Vector3<i32>;
pub type Normal3f = Normal3<Float>;
pub type Normal3i = Normal3<i32>;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Bounds3<T> {
pub p_min: Point3<T>,
pub p_max: Point3<T>,
}
impl<T> Default for Bounds3<T>
where
T: Num + Bounded
{
fn default() -> Self {
Self {
p_min: Point3::new(T::max_value(), T::max_value(), T::max_value()),
p_max: Point3::new(T::min_value(), T::min_value(), T::min_value()),
}
}
}
impl<T> Bounds3<T>
where
T: Num + PartialOrd + Copy,
{
pub fn from_point(p: Point3<T>) -> Self {
Self { p_min: p, p_max: p }
}
pub fn from_points(p1: Point3<T>, p2: Point3<T>) -> Self {
Self {
p_min: Point3::new(
if p1.x < p2.x { p1.x } else { p2.x },
if p1.y < p2.y { p1.y } else { p2.y },
if p1.z < p2.z { p1.z } else { p2.z },
),
p_max: Point3::new(
if p1.x > p2.x { p1.x } else { p2.x },
if p1.y > p2.y { p1.y } else { p2.y },
if p1.z > p2.z { p1.z } else { p2.z },
),
}
}
pub fn diagonal(&self) -> Vector3<T> {
self.p_max - self.p_min
}
pub fn surface_area(&self) -> T {
let d = self.diagonal();
let two = T::one() + T::one();
two * (d.x * d.y + d.x * d.z + d.y * d.z)
}
pub fn volume(&self) -> T {
let d = self.diagonal();
d.x * d.y * d.z
}
pub fn is_empty(&self) -> bool {
self.p_min.x >= self.p_max.x || self.p_min.y >= self.p_max.y || self.p_min.z >= self.p_max.z
}
pub fn max_dimension(&self) -> usize {
let d = self.diagonal();
if d.x > d.y && d.x > d.z {
0
} else if d.y > d.z {
1
} else {
2
}
}
pub fn offset(p: Point3<T>) {}
}
fn min<T: PartialOrd>(a: T, b: T) -> T { if a < b { a } else { b }}
fn max<T: PartialOrd>(a: T, b: T) -> T { if a > b { a } else { b }}
pub fn union_bounds_point<T: PartialOrd + Copy>(b: Bounds3<T>, p: Point3<T>) -> Bounds3<T> {
Bounds3 {
p_min: Point3::new(min(b.p_min.x, p.x), min(b.p_min.y, p.y), min(b.p_min.z, p.z)),
p_max: Point3::new(max(b.p_max.x, p.x), max(b.p_max.y, p.y), max(b.p_max.z, p.z)),
}
}
pub fn union_bounds<T: PartialOrd + Copy>(b1: Bounds3<T>, b2: Bounds3<T>) -> Bounds3<T> {
Bounds3 {
p_min: Point3::new(min(b1.p_min.x, b2.p_min.x), min(b1.p_min.y, b2.p_min.y), min(b1.p_min.z, b2.p_min.z)),
p_max: Point3::new(max(b1.p_max.x, b2.p_max.x), max(b1.p_max.y, b2.p_max.y), max(b1.p_max.z, b2.p_max.z)),
}
}
pub fn face_forward_v(v: Vector3f, n: Normal3f) -> Vector3f {
if v.dot(Vector3::new(n.x, n.y, n.z)) < 0.0 { -v } else { v }
}
pub fn face_forward_n(n1: Normal3f, n2: Normal3f) -> Normal3f {
if n1.dot(n2) < 0.0 { -n1 } else { n1 }
}
pub fn coordinate_system(v1: Vector3f) -> (Vector3f, Vector3f) {
let v2 = if v1.x.abs() > v1.y.abs() {
Vector3::new(-v1.z, 0.0, v1.x) / (v1.x * v1.x + v1.z * v1.z).sqrt()
} else {
Vector3::new(0.0, v1.z, -v1.y) / (v1.y * v1.y + v1.z * v1.z).sqrt()
};
let v3 = v1.cross(v2);
(v2, v3)
}
#[derive(Copy, Clone, Default, PartialEq)]
pub struct Frame {
pub x: Vector3f,
pub y: Vector3f,
pub z: Vector3f,
}
impl Frame {
pub fn from_z(z: Vector3f) -> Self {
let (x, y) = coordinate_system(z.normalize());
Self { x, y, z: z.normalize() }
}
pub fn to_local(&self, v: Vector3f) -> Vector3f {
Vector3f::new(v.dot(self.x), v.dot(self.y), v.dot(self.z))
}
pub fn from_local(&self, v: Vector3f) -> Vector3f {
self.x * v.x + self.y * v.y + self.z * v.z
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct Quaternion {
pub v: Vector3f,
pub w: Float,
}
impl Default for Quaternion {
fn default() -> Self {
Self { v: Vector3f::default(), w: 1.0 }
}
}
impl Add for Quaternion {
type Output = Self;
fn add(self, rhs: Quaternion) -> Self {
Self { v: self.v + rhs.v, w: self.w + rhs.w }
}
}
impl AddAssign for Quaternion {
fn add_assign(&mut self, rhs: Self) { self.v += rhs.v; self.w += rhs.w; }
}
impl Sub for Quaternion {
type Output = Self;
fn sub(self, rhs: Self) -> Self { Self { v: self.v - rhs.v, w: self.w - rhs.w }}
}
impl SubAssign for Quaternion {
fn sub_assign(&mut self, rhs: Self) { self.v -= rhs.v; self.w -= rhs.w; }
}
impl Mul<Float> for Quaternion {
type Output = Self;
fn mul(self, rhs: Float) -> Self { Self { v: self.v * rhs, w: self.w * rhs }}
}
impl MulAssign<Float> for Quaternion {
fn mul_assign(&mut self, rhs: Float) { self.v *= rhs; self.w *= rhs; }
}
impl Div<Float> for Quaternion {
type Output = Self;
fn div(self, rhs: Float) -> Self { Self { v: self.v / rhs, w: self.w / rhs }}
}
impl DivAssign<Float> for Quaternion {
fn div_assign(&mut self, rhs: Float) { self.v /= rhs; self.w /= rhs; }
}
impl Neg for Quaternion {
type Output = Self;
fn neg(self) -> Self { Self { v: -self.v, w: -self.w }}
}

8
src/core/integrator.rs Normal file
View file

@ -0,0 +1,8 @@
use std::sync::Arc;
use crate::core::primitive::Primitive;
pub struct Integrator {
aggregate: Arc<Primitive>,
lights: Vec<Arc<Light>>,
}

0
src/core/light.rs Normal file
View file

44
src/core/material.rs Normal file
View file

@ -0,0 +1,44 @@
pub struct CoatedDiffuseMaterial;
pub struct CoatedConductorMaterial;
pub struct ConductorMaterial;
pub struct DielectricMaterial;
pub struct DiffuseMaterial;
pub struct DiffuseTransmissionMaterial;
pub struct HairMaterial;
pub struct MeasuredMaterial;
pub struct SubsurfaceMaterial;
pub struct ThinDielectricMaterial;
pub struct MixMaterial;
pub enum Material {
CoatedDiffuse(CoatedDiffuseMaterial),
CoatedConductor(CoatedConductorMaterial),
Conductor(ConductorMaterial),
Dielectric(DielectricMaterial),
Diffuse(DiffuseMaterial),
DiffuseTransmission(DiffuseTransmissionMaterial),
Hair(HairMaterial),
Measured(MeasuredMaterial),
Subsurface(SubsurfaceMaterial),
ThinDielectric(ThinDielectricMaterial),
Mix(MixMaterial),
}
impl Material {
pub fn get_bsdf<T: TextureEvaluator>(
&self, tex_eval: T, ctx: MaterialEvalContext, lambda: &mut SampledWavelengths, buf: &mut ScratchBuffer) -> BSDF {
match self {
Material::CoatedDiffuse(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::CoatedConductor(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Conductor(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Dielectric(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Diffuse(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::DiffuseTransmission(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Hair(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Measured(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Subsurface(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::ThinDielectric(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
Material::Mix(m) => m.get_bsdf(tex_eval, ctx, lambda, buf),
}
}
}

8
src/core/mod.rs Normal file
View file

@ -0,0 +1,8 @@
pub mod geometry;
pub mod integrator;
pub mod light;
pub mod material;
pub mod pbrt;
pub mod primitive;
pub mod shape;
pub mod texture;

25
src/core/pbrt.rs Normal file
View file

@ -0,0 +1,25 @@
pub type Float = f32;
pub const MACHINE_EPSILON: Float = std::f32::EPSILON * 0.5;
pub const SHADOW_EPSILON: Float = 0.0001;
#[inline]
pub fn lerp<T>(x: T, a: f64, b: f64) -> f64 {
(T::onw() - x) * a + x * b
}
pub fn linear_pdf(x: f64, a: f64, b: f64) -> f64 {
if x < 0.0 || x > 1.0 {
return 0.0;
}
2.0 * lerp(x, a, b) / (a + b)
}
pub fn sample_linear(u: f64, a: f64, b: f64) -> f64 {
if u == 0.0 && a == 0.0 {
return 0.0;
}
u * (a + b)
}

45
src/core/primitive.rs Normal file
View file

@ -0,0 +1,45 @@
// use crate::core::medium::MediumInterface;
// use crate::core::light::Light;
use crate::utils::types::Bounds3f;
//
//
pub struct GeometricPrimitive;
pub struct TransformedPrimitive;
pub struct AnimatedPrimitive;
pub struct BVHAggregatePrimitive;
pub struct KdTreeAggregate;
pub enum Primitive {
Geometric(GeometricPrimitive),
Transformed(TransformedPrimitive),
Animated(AnimatedPrimitive),
BVH(BVHAggregatePrimitive),
KdTree(KdTreeAggregate)
}
impl Primitive {
pub fn bounds(&self) -> Bounds3f {
match self {
Primitive::Geometric(primitive) => primitive.bounds(),
Primitive::Transformed(primitive) => primitive.bounds(),
Primitive::Animated(primitive) => primitive.bounds(),
Primitive::BVH(primitive) => primitive.bounds(),
Primitive::KdTree(primitive) => primitive.bounds(),
}
}
pub fn
}
// struct GeometricPrimitive {
// shape: Shape,
// material: Material,
// area_light: Light,
// medium_interface: MediumInterface,
// alpha: Texture<f64>,
// }
//
//
// impl GeometricPrimitive {
// fn new(shape: Shape, material: Material, medium_interface: MediumInterface, alpha: Texture<f64>)
// }

0
src/core/shape.rs Normal file
View file

107
src/core/texture.rs Normal file
View file

@ -0,0 +1,107 @@
pub struct TextureEvalContext;
pub struct FloatImageTexture;
pub struct GPUFloatImageTexture;
pub struct FloatMixTexture;
pub struct FloatDirectionMixTexture;
pub struct FloatScaledTexture;
pub struct FloatConstantTexture;
pub struct FloatBilerpTexture;
pub struct FloatCheckerboardTexture;
pub struct FloatDotsTexture;
pub struct FBmTexture;
pub struct FloatPtexTexture;
pub struct GPUFloatPtex;
pub struct WindyTexture;
pub struct WrinkledTexture;
pub enum FloatTexture {
FloatImage(FloatImageTexture),
GPUFloatImage(GPUFloatImageTexture),
FloatMix(FloatMixTexture),
FloatDirectionMix(FloatDirectionMixTexture),
FloatScaled(FloatScaledTexture),
FloatConstant(FloatConstantTexture),
FloatBilerp(FloatBilerpTexture),
FloatCheckerboard(FloatCheckerboardTexture),
FloatDots(FloatDotsTexture),
FBm(FBmTexture),
FloatPtex(FloatPtexTexture),
GPUFloatPtex(GPUFloatPtex),
Windy(WindyTexture),
Wrinkled(WrinkledTexture),
}
impl FloatTexture {
pub fn evaluate(&self, ctx: TextureEvalContext) -> f32 {
match self {
FloatTexture::FloatImage(texture) => texture.evaluate(ctx),
FloatTexture::GPUFloatImage(texture) => texture.evaluate(ctx),
FloatTexture::FloatMix(texture) => texture.evaluate(ctx),
FloatTexture::FloatDirectionMix(texture) => texture.evaluate(ctx),
FloatTexture::FloatScaled(texture) => texture.evaluate(ctx),
FloatTexture::FloatConstant(texture) => texture.evaluate(ctx),
FloatTexture::FloatBilerp(texture) => texture.evaluate(ctx),
FloatTexture::FloatCheckerboard(texture) => texture.evaluate(ctx),
FloatTexture::FloatDots(texture) => texture.evaluate(ctx),
FloatTexture::FBm(texture) => texture.evaluate(ctx),
FloatTexture::FloatPtex(texture) => texture.evaluate(ctx),
FloatTexture::GPUFloatPtex(texture) => texture.evaluate(ctx),
FloatTexture::Windy(texture) => texture.evaluate(ctx),
FloatTexture::Wrinkled(texture) => texture.evaluate(ctx),
}
}
}
pub struct RGBConstantTexture;
pub struct RGBReflectanceConstantTexture;
pub struct SpectrumConstantTexture;
pub struct SpectrumBilerpTexture;
pub struct SpectrumCheckerboardTexture;
pub struct SpectrumImageTexture;
pub struct GPUSpectrumImageTexture;
pub struct MarbleTexture;
pub struct SpectrumMixTexture;
pub struct SpectrumDirectionMixTexture;
pub struct SpectrumDotsTexture;
pub struct SpectrumPtexTexture;
pub struct GPUSpectrumPtexTexture;
pub struct SpectrumScaledTexture;
pub enum SpectrumTexture {
RGBConstant(RGBConstantTexture),
RGBReflectanceConstant(RGBReflectanceConstantTexture),
SpectrumConstant(SpectrumConstantTexture),
SpectrumBilerp(SpectrumBilerpTexture),
SpectrumCheckerboard(SpectrumCheckerboardTexture),
SpectrumImage(SpectrumImageTexture),
GPUSpectrumImage(GPUSpectrumImageTexture),
Marble(MarbleTexture),
SpectrumMix(SpectrumMixTexture),
SpectrumDirectionMix(SpectrumDirectionMixTexture),
SpectrumDots(SpectrumDotsTexture),
SpectrumPtex(SpectrumPtexTexture),
GPUSpectrumPtex(GPUSpectrumPtexTexture),
SpectrumScaled(SpectrumScaledTexture),
}
impl SpectrumTexture {
pub fn evaluate(&self, ctx: TextureEvalContext) -> f32 {
match self {
SpectrumTexture::FloatImage(texture) => texture.evaluate(ctx),
SpectrumTexture::GPUFloatImage(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatMix(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatDirectionMix(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatScaled(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatConstant(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatBilerp(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatCheckerboard(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatDots(texture) => texture.evaluate(ctx),
SpectrumTexture::FBm(texture) => texture.evaluate(ctx),
SpectrumTexture::FloatPtex(texture) => texture.evaluate(ctx),
SpectrumTexture::GPUFloatPtex(texture) => texture.evaluate(ctx),
SpectrumTexture::Windy(texture) => texture.evaluate(ctx),
SpectrumTexture::Wrinkled(texture) => texture.evaluate(ctx),
}
}
}

2
src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
mod core;
mod utils;

0
src/mod.rs Normal file
View file

1
src/utils/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod types;

245
src/utils/types.rs Normal file
View file

@ -0,0 +1,245 @@
use std::ops::{Sub, Add, Div, Mul, Neg, Index, IndexMut};
use num_traits::{Num, Float, Bounded};
pub trait Tuple<const N: usize, T> : Index<usize, Output = T> + IndexMut<usize> + Copy {
fn get(&self, i: usize) -> T;
fn set(&mut self, i: usize, val: T);
fn has_nan(&self) -> bool where T: Float {
for i in 0..N {
if self.get(i).is_nan() {
return true
}
}
false
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct Vector2<T> { pub x: T, pub y: T }
#[derive(Copy, Clone, PartialEq)]
pub struct Point2<T> { pub x: T, pub y: T }
#[derive(Copy, Clone, PartialEq)]
pub struct Vector3<T> { pub x: T, pub y: T, pub z: T }
#[derive(Copy, Clone, PartialEq)]
pub struct Normal3<T> { pub x: T, pub y: T, pub z: T }
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Point3<T> { pub x: T, pub y: T, pub z : T }
// Using macros to make this more concise
macro_rules! impl_tuple3 {
($Struct:ident, [$($field:ident), +]) => {
impl<T> $Struct<T> {
pub fn new($($field: T), +) -> Self { Self { $($field), + }}
}
impl<T: Num + Copy> $Struct<T> {
pub fn dot(&self, other: Self) -> T {
self.x * other.x + self.y * other.y + self.z * other.z
}
pub fn length_squared(self) -> T {
self.dot(self)
}
}
impl<T: Float> $Struct<T> {
pub fn length(self) -> T {
self.length_squared().sqrt()
}
pub fn normalize(self) -> Self {
self / self.length()
}
}
// Operators
impl<T: Add<Output=T>> Add for $Struct<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self { Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z )}
}
impl<T: Mul<Output=T> + Copy > Mul<T> for $Struct<T> {
type Output = Self;
fn mul(self, rhs: T) -> Self { Self::new(self.x * rhs, self.y * rhs, self.z * rhs )}
}
impl<T: Div<Output=T> + Copy > Div<T> for $Struct<T> {
type Output = Self;
fn div(self, rhs: T) -> Self { Self::new(self.x / rhs, self.y / rhs, self.z / rhs )}
}
impl<T: Neg<Output=T>> Neg for $Struct<T> {
type Output = Self;
fn neg(self) -> Self { Self::new(-self.x, -self.y, -self.z)}
}
impl<T> Index<usize> for $Struct<T> {
type Output = T;
fn index(&self, i: usize) -> &T {
match i { 0 => &self.x, 1 => &self.y, 2 => &self.z, _ => panic!("Index out of bonds") }
}
}
impl<T> IndexMut<usize> for $Struct<T> {
fn index_mut(&mut self, i: usize) -> &mut T {
match i { 0 => &mut self.x, 1 => &mut self.y, 2 => &mut self.z, _ => panic!("Index out of bonds") }
}
}
};
}
macro_rules! impl_component_ops {
($Struct:ident) => {
impl<T: Sub<Output=T>> Sub for $Struct<T> {
type Output = Self;
fn sub(self, rhs: Self) -> Self { Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) }
}
}
}
impl_tuple3!(Vector3, [x, y, z]);
impl_tuple3!(Point3, [x, y, z]);
impl_tuple3!(Normal3, [x, y, z]);
impl_component_ops!(Vector3);
impl_component_ops!(Normal3);
// Point3 - Point3 -> Vector3
impl<T: Sub<Output = T>> Sub<Point3<T>> for Point3<T> {
type Output = Vector3<T>;
fn sub(self, rhs: Point3<T>) -> Vector3<T> {
Vector3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
// Point3 + Vector3 -> Point3
impl<T: Add<Output = T>> Add<Vector3<T>> for Point3<T> {
type Output = Point3<T>;
fn add(self, rhs: Vector3<T>) -> Point3<T> {
Point3::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
}
}
// Point3 - Vector3 -> Point3
impl<T: Sub<Output = T>> Sub<Vector3<T>> for Point3<T> {
type Output = Point3<T>;
fn sub(self, rhs: Vector3<T>) -> Point3<T> {
Point3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
}
}
// Cross Product for Vector3
impl<T: Num + Copy> Vector3<T> {
pub fn cross(self, other: Vector3<T>) -> Vector3<T> {
Vector3 {
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
}
pub type Point3f = Point3<f32>;
pub type Point3i = Point3<i32>;
pub type Vector3f = Vector3<f32>;
pub type Vector3i = Vector3<i32>;
pub type Normal3f = Normal3<f32>;
pub type Normal3i = Normal3<i32>;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Bounds3<T> {
pub p_min: Point3<T>,
pub p_max: Point3<T>,
}
impl<T> Bounds3<T>
where
T: Num + Bounded + PartialOrd + Copy,
{
pub fn new() -> Self {
Self {
p_min: Point3::new(T::max_value(), T::max_value(), T::max_value()),
p_max: Point3::new(T::min_value(), T::min_value(), T::min_value()),
}
}
pub fn from_point(p: Point3<T>) -> Self {
Self { p_min: p, p_max: p }
}
pub fn from_points(p1: Point3<T>, p2: Point3<T>) -> Self {
Self {
p_min: Point3::new(
if p1.x < p2.x { p1.x } else { p2.x },
if p1.y < p2.y { p1.y } else { p2.y },
if p1.z < p2.z { p1.z } else { p2.z },
),
p_max: Point3::new(
if p1.x > p2.x { p1.x } else { p2.x },
if p1.y > p2.y { p1.y } else { p2.y },
if p1.z > p2.z { p1.z } else { p2.z },
),
}
}
pub fn diagonal(&self) -> Vector3<T> {
self.p_max - self.p_min
}
pub fn surface_area(&self) -> T {
let d = self.diagonal();
let two = T::one() + T::one();
two * (d.x * d.y + d.x * d.z + d.y * d.z)
}
pub fn volume(&self) -> T {
let d = self.diagonal();
d.x * d.y * d.z
}
pub fn is_empty(&self) -> bool {
self.p_min.x >= self.p_max.x || self.p_min.y >= self.p_max.y || self.p_min.z >= self.p_max.z
}
pub fn max_dimension(&self) -> usize {
let d = self.diagonal();
if d.x > d.y && d.x > d.z {
return 0;
} else if d.y > d.z {
return 1;
}
2
}
}
pub fn union_bounds<T: PartialOrd + Copy>(b1: Bounds3<T>, b2: Bounds3<T>) -> Bounds3<T> {
Bounds3 {
p_min: Point3::new(
if b1.p_min.x < b2.p_min.x { b1.p_min.x } else { b2.p_min.x },
if b1.p_min.y < b2.p_min.y { b1.p_min.y } else { b2.p_min.y },
if b1.p_min.z < b2.p_min.z { b1.p_min.z } else { b2.p_min.z },
),
p_max: Point3::new(
if b1.p_max.x > b2.p_max.x { b1.p_max.x } else { b2.p_max.x },
if b1.p_max.y > b2.p_max.y { b1.p_max.y } else { b2.p_max.y },
if b1.p_max.z > b2.p_max.z { b1.p_max.z } else { b2.p_max.z },
),
}
}
pub fn union_bounds_point<T: PartialOrd + Copy>(b: Bounds3<T>, p: Point3<T>) -> Bounds3<T> {
Bounds3 {
p_min: Point3::new(
if b.p_min.x < p.x { b.p_min.x } else { p.x },
if b.p_min.y < p.y { b.p_min.y } else { p.y },
if b.p_min.z < p.z { b.p_min.z } else { p.z },
),
p_max: Point3::new(
if b.p_max.x > p.x { b.p_max.x } else { p.x },
if b.p_max.y > p.y { b.p_max.y } else { p.y },
if b.p_max.z > p.z { b.p_max.z } else { p.z },
),
}
}