Initial commit
This commit is contained in:
commit
137ddfbd28
15 changed files with 890 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
*.lock
|
||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal 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
396
src/core/geometry.rs
Normal 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
8
src/core/integrator.rs
Normal 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
0
src/core/light.rs
Normal file
44
src/core/material.rs
Normal file
44
src/core/material.rs
Normal 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
8
src/core/mod.rs
Normal 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
25
src/core/pbrt.rs
Normal 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
45
src/core/primitive.rs
Normal 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
0
src/core/shape.rs
Normal file
107
src/core/texture.rs
Normal file
107
src/core/texture.rs
Normal 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
2
src/lib.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
mod core;
|
||||
mod utils;
|
||||
0
src/mod.rs
Normal file
0
src/mod.rs
Normal file
1
src/utils/mod.rs
Normal file
1
src/utils/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod types;
|
||||
245
src/utils/types.rs
Normal file
245
src/utils/types.rs
Normal 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 },
|
||||
),
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue