can create a stripped/basic version of cornell box
1. Refactored the project 2. Changed src/shapes/rectangle.rs to make it easy to create rectangles aligned with any(X/Y/Z) Axis.
This commit is contained in:
parent
298e0a2301
commit
9d82129891
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -331,6 +331,7 @@ name = "rtnw"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"image",
|
"image",
|
||||||
|
"num-traits",
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
"sdl2",
|
"sdl2",
|
||||||
|
|
|
@ -3,14 +3,14 @@ authors = ["ishanjain28 <ishanjain28@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
name = "rtnw"
|
name = "rtnw"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
num-traits = "*"
|
||||||
rayon = "1.5.0"
|
rayon = "1.5.0"
|
||||||
|
|
||||||
[dependencies.image]
|
[dependencies.image]
|
||||||
|
default-features = false
|
||||||
features = ["jpeg", "png"]
|
features = ["jpeg", "png"]
|
||||||
version = "0.23.14"
|
version = "0.23.14"
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.rand]
|
[dependencies.rand]
|
||||||
features = ["small_rng"]
|
features = ["small_rng"]
|
||||||
|
|
19
src/aabb.rs
19
src/aabb.rs
|
@ -13,9 +13,9 @@ impl Aabb {
|
||||||
|
|
||||||
pub fn hit(&self, ray: &Ray, mut t_min: f64, mut t_max: f64) -> bool {
|
pub fn hit(&self, ray: &Ray, mut t_min: f64, mut t_max: f64) -> bool {
|
||||||
for i in 0..=2 {
|
for i in 0..=2 {
|
||||||
let inverse_dir = 1.0 / ray.direction()[i];
|
let inverse_dir = 1.0 / ray.direction[i];
|
||||||
let mut t0 = (self.min[i] - ray.origin()[i]) * inverse_dir;
|
let mut t0 = (self.min[i] - ray.origin[i]) * inverse_dir;
|
||||||
let mut t1 = (self.max[i] - ray.origin()[i]) * inverse_dir;
|
let mut t1 = (self.max[i] - ray.origin[i]) * inverse_dir;
|
||||||
if inverse_dir < 0.0 {
|
if inverse_dir < 0.0 {
|
||||||
std::mem::swap(&mut t0, &mut t1);
|
std::mem::swap(&mut t0, &mut t1);
|
||||||
}
|
}
|
||||||
|
@ -31,17 +31,8 @@ impl Aabb {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn surrounding_box(box0: Aabb, box1: Aabb) -> Self {
|
pub fn surrounding_box(box0: Aabb, box1: Aabb) -> Self {
|
||||||
let smol_box = Vec3::new(
|
let smol_box = Vec3::min(box0.min, box1.min);
|
||||||
box0.min.x().min(box1.min.x()),
|
let big_box = Vec3::max(box0.max, box1.max);
|
||||||
box0.min.y().min(box1.min.y()),
|
|
||||||
box0.min.z().min(box1.min.z()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let big_box = Vec3::new(
|
|
||||||
box0.max.x().max(box1.max.x()),
|
|
||||||
box0.max.y().max(box1.max.y()),
|
|
||||||
box0.max.z().max(box1.max.z()),
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
min: smol_box,
|
min: smol_box,
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Demo, ParallelHit},
|
demos::{Demo, ParallelHit},
|
||||||
materials::{Dielectric, Lambertian, Metal},
|
hitable::{
|
||||||
shapes::{MovingSphere, Sphere},
|
shapes::{MovingSphere, Sphere},
|
||||||
|
BvhNode,
|
||||||
|
},
|
||||||
|
materials::{Dielectric, Lambertian, Metal},
|
||||||
texture::{Checker, Solid},
|
texture::{Checker, Solid},
|
||||||
types::Vec3,
|
types::Vec3,
|
||||||
BvhNode, Camera,
|
Camera,
|
||||||
};
|
};
|
||||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -18,6 +21,10 @@ impl Demo for CheckeredMotionBlur {
|
||||||
"checkered_motion_blur"
|
"checkered_motion_blur"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_background(&self) -> Vec3 {
|
||||||
|
Vec3::new(0.7, 0.8, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
fn world(&self) -> Self::DemoT {
|
fn world(&self) -> Self::DemoT {
|
||||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
|
||||||
|
|
||||||
|
|
110
src/demos/cornell_box.rs
Normal file
110
src/demos/cornell_box.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
demos::{Demo, ParallelHit},
|
||||||
|
hitable::shapes::{Cuboid, RectBuilder},
|
||||||
|
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
||||||
|
texture::Solid,
|
||||||
|
types::Vec3,
|
||||||
|
BvhNode, Camera,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CornellBox {}
|
||||||
|
|
||||||
|
impl Demo for CornellBox {
|
||||||
|
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"cornell_box"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn world(&self) -> Self::DemoT {
|
||||||
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(8);
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
|
||||||
|
|
||||||
|
let red = Lambertian::new(Solid::new(Vec3::new(0.65, 0.05, 0.05)));
|
||||||
|
let white = Lambertian::new(Solid::new(Vec3::splat(0.73)));
|
||||||
|
let green = Lambertian::new(Solid::new(Vec3::new(0.12, 0.45, 0.15)));
|
||||||
|
let light = DiffuseLight::new(Solid::new(Vec3::splat(15.0)));
|
||||||
|
|
||||||
|
world.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.y(0.0..=555.0)
|
||||||
|
.z(0.0..=555.0)
|
||||||
|
.x(555.0)
|
||||||
|
.material(green),
|
||||||
|
));
|
||||||
|
world.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.y(0.0..=555.0)
|
||||||
|
.z(0.0..=555.0)
|
||||||
|
.x(0.0)
|
||||||
|
.material(red),
|
||||||
|
));
|
||||||
|
world.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(213.0..=343.0)
|
||||||
|
.z(227.0..=332.0)
|
||||||
|
.y(554.0)
|
||||||
|
.material(light),
|
||||||
|
));
|
||||||
|
|
||||||
|
world.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(0.0..=555.0)
|
||||||
|
.z(0.0..=555.0)
|
||||||
|
.y(0.0)
|
||||||
|
.material(white),
|
||||||
|
));
|
||||||
|
world.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(0.0..=555.0)
|
||||||
|
.z(0.0..=555.0)
|
||||||
|
.y(555.0)
|
||||||
|
.material(white),
|
||||||
|
));
|
||||||
|
world.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(0.0..=555.0)
|
||||||
|
.y(0.0..=555.0)
|
||||||
|
.z(555.0)
|
||||||
|
.material(white),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Add the two boxes
|
||||||
|
world.push(Arc::new(Cuboid::new(
|
||||||
|
Vec3::new(136.0, 0.0, 65.0),
|
||||||
|
Vec3::new(295.0, 165.0, 230.0),
|
||||||
|
white,
|
||||||
|
)));
|
||||||
|
world.push(Arc::new(Cuboid::new(
|
||||||
|
Vec3::new(265.0, 0.0, 295.0),
|
||||||
|
Vec3::new(430.0, 330.0, 460.0),
|
||||||
|
white,
|
||||||
|
)));
|
||||||
|
|
||||||
|
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn camera(&self, aspect_ratio: f64) -> Camera {
|
||||||
|
let lookfrom = Vec3::new(278.0, 278.0, -800.0);
|
||||||
|
let lookat = Vec3::new(278.0, 278.0, 0.0);
|
||||||
|
let aperture = 0.1;
|
||||||
|
let focus_distance = 40.0;
|
||||||
|
Camera::new(
|
||||||
|
lookfrom,
|
||||||
|
lookat,
|
||||||
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
40.0,
|
||||||
|
aspect_ratio,
|
||||||
|
aperture,
|
||||||
|
focus_distance,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,8 @@ use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Demo, ParallelHit},
|
demos::{Demo, ParallelHit},
|
||||||
|
hitable::shapes::Sphere,
|
||||||
materials::Lambertian,
|
materials::Lambertian,
|
||||||
shapes::Sphere,
|
|
||||||
texture::ImageTexture,
|
texture::ImageTexture,
|
||||||
types::Vec3,
|
types::Vec3,
|
||||||
BvhNode, Camera,
|
BvhNode, Camera,
|
||||||
|
@ -20,6 +20,10 @@ impl Demo for ImageTextureDemo {
|
||||||
"image_texture"
|
"image_texture"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_background(&self) -> Vec3 {
|
||||||
|
Vec3::new(0.7, 0.8, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
fn world(&self) -> Self::DemoT {
|
fn world(&self) -> Self::DemoT {
|
||||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(1);
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(1);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::{types::Vec3, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION};
|
use crate::{
|
||||||
|
hitable::Hitable,
|
||||||
|
types::{Color, Vec3},
|
||||||
|
Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
||||||
|
};
|
||||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -9,12 +13,14 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod checkered_motion_blur;
|
mod checkered_motion_blur;
|
||||||
|
mod cornell_box;
|
||||||
mod image_texture;
|
mod image_texture;
|
||||||
mod perlin_noise_ball;
|
mod perlin_noise_ball;
|
||||||
mod simple_light;
|
mod simple_light;
|
||||||
mod two_spheres;
|
mod two_spheres;
|
||||||
|
|
||||||
pub use checkered_motion_blur::CheckeredMotionBlur;
|
pub use checkered_motion_blur::CheckeredMotionBlur;
|
||||||
|
pub use cornell_box::CornellBox;
|
||||||
pub use image_texture::ImageTextureDemo;
|
pub use image_texture::ImageTextureDemo;
|
||||||
pub use perlin_noise_ball::PerlinNoiseBall;
|
pub use perlin_noise_ball::PerlinNoiseBall;
|
||||||
pub use simple_light::SimpleLight;
|
pub use simple_light::SimpleLight;
|
||||||
|
@ -55,7 +61,7 @@ pub trait Demo: Send + Sync {
|
||||||
fn camera(&self, aspect_ratio: f64) -> Camera;
|
fn camera(&self, aspect_ratio: f64) -> Camera;
|
||||||
|
|
||||||
fn get_background(&self) -> Vec3 {
|
fn get_background(&self) -> Vec3 {
|
||||||
Vec3::new(0.7, 0.8, 1.0)
|
Vec3::new(0.0, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &Self::DemoT, samples: u16) {
|
fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &Self::DemoT, samples: u16) {
|
||||||
|
@ -156,14 +162,16 @@ pub trait Demo: Send + Sync {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn update_rgb(&self, buffer: &mut [u8], color: Vec3, offset: usize) {
|
fn update_rgb(&self, buffer: &mut [u8], color: Vec3, offset: usize) {
|
||||||
|
let color: Color = color.into();
|
||||||
|
|
||||||
if let Some(pos) = buffer.get_mut(offset) {
|
if let Some(pos) = buffer.get_mut(offset) {
|
||||||
*pos = (255.99 * color.r().sqrt()) as u8;
|
*pos = color.0;
|
||||||
}
|
}
|
||||||
if let Some(pos) = buffer.get_mut(offset + 1) {
|
if let Some(pos) = buffer.get_mut(offset + 1) {
|
||||||
*pos = (255.99 * color.g().sqrt()) as u8;
|
*pos = color.1
|
||||||
}
|
}
|
||||||
if let Some(pos) = buffer.get_mut(offset + 2) {
|
if let Some(pos) = buffer.get_mut(offset + 2) {
|
||||||
*pos = (255.99 * color.b().sqrt()) as u8;
|
*pos = color.2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@ use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Demo, ParallelHit},
|
demos::{Demo, ParallelHit},
|
||||||
|
hitable::{shapes::Sphere, BvhNode},
|
||||||
materials::Lambertian,
|
materials::Lambertian,
|
||||||
shapes::Sphere,
|
|
||||||
texture::PerlinNoise,
|
texture::PerlinNoise,
|
||||||
types::Vec3,
|
types::Vec3,
|
||||||
BvhNode, Camera,
|
Camera,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PerlinNoiseBall {}
|
pub struct PerlinNoiseBall {}
|
||||||
|
@ -20,6 +20,10 @@ impl Demo for PerlinNoiseBall {
|
||||||
"perlin_noise"
|
"perlin_noise"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_background(&self) -> Vec3 {
|
||||||
|
Vec3::new(0.7, 0.8, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
fn world(&self) -> Self::DemoT {
|
fn world(&self) -> Self::DemoT {
|
||||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(2);
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(2);
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,14 @@ use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Demo, ParallelHit},
|
demos::{Demo, ParallelHit},
|
||||||
materials::{DiffuseLight, Lambertian},
|
hitable::{
|
||||||
shapes::{Sphere, XyRectangle},
|
shapes::{RectBuilder, Sphere},
|
||||||
|
BvhNode,
|
||||||
|
},
|
||||||
|
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
||||||
texture::{PerlinNoise, Solid},
|
texture::{PerlinNoise, Solid},
|
||||||
types::Vec3,
|
types::Vec3,
|
||||||
BvhNode, Camera,
|
Camera,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SimpleLight {}
|
pub struct SimpleLight {}
|
||||||
|
@ -20,10 +23,6 @@ impl Demo for SimpleLight {
|
||||||
"simple_light"
|
"simple_light"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_background(&self) -> Vec3 {
|
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn world(&self) -> Self::DemoT {
|
fn world(&self) -> Self::DemoT {
|
||||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(5);
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(5);
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
@ -39,14 +38,14 @@ impl Demo for SimpleLight {
|
||||||
2.0,
|
2.0,
|
||||||
Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)),
|
Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)),
|
||||||
)));
|
)));
|
||||||
world.push(Arc::new(XyRectangle::new(
|
|
||||||
3.0,
|
world.push(Arc::new(
|
||||||
5.0,
|
RectBuilder
|
||||||
1.0,
|
.x(3.0..=5.0)
|
||||||
3.0,
|
.y(1.0..=3.0)
|
||||||
-2.0,
|
.z(-2.0)
|
||||||
DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0))),
|
.material(DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0)))),
|
||||||
)));
|
));
|
||||||
world.push(Arc::new(Sphere::new(
|
world.push(Arc::new(Sphere::new(
|
||||||
Vec3::new(0.0, 7.0, 0.0),
|
Vec3::new(0.0, 7.0, 0.0),
|
||||||
2.0,
|
2.0,
|
||||||
|
|
|
@ -4,11 +4,11 @@ use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Demo, ParallelHit},
|
demos::{Demo, ParallelHit},
|
||||||
|
hitable::{shapes::Sphere, BvhNode},
|
||||||
materials::Lambertian,
|
materials::Lambertian,
|
||||||
shapes::Sphere,
|
|
||||||
texture::{Checker, Solid},
|
texture::{Checker, Solid},
|
||||||
types::Vec3,
|
types::Vec3,
|
||||||
BvhNode, Camera,
|
Camera,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TwoSpheres {}
|
pub struct TwoSpheres {}
|
||||||
|
@ -20,6 +20,10 @@ impl Demo for TwoSpheres {
|
||||||
"two_checkered_sphere"
|
"two_checkered_sphere"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_background(&self) -> Vec3 {
|
||||||
|
Vec3::new(0.7, 0.8, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
fn world(&self) -> Self::DemoT {
|
fn world(&self) -> Self::DemoT {
|
||||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(2);
|
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(2);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,11 @@ use std::cmp::Ordering;
|
||||||
|
|
||||||
use rand::{prelude::SliceRandom, Rng};
|
use rand::{prelude::SliceRandom, Rng};
|
||||||
|
|
||||||
use crate::{types::Ray, Aabb, HitRecord, Hitable};
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
|
types::Ray,
|
||||||
|
Aabb,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct BvhNode<T: Hitable> {
|
pub struct BvhNode<T: Hitable> {
|
||||||
bounding_box: Aabb,
|
bounding_box: Aabb,
|
|
@ -28,6 +28,8 @@ pub struct HitRecord<'a> {
|
||||||
/// texture coordinates for an object
|
/// texture coordinates for an object
|
||||||
pub u: f64,
|
pub u: f64,
|
||||||
pub v: f64,
|
pub v: f64,
|
||||||
|
|
||||||
|
pub front_face: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HitRecord<'a> {
|
impl<'a> HitRecord<'a> {
|
||||||
|
@ -45,6 +47,17 @@ impl<'a> HitRecord<'a> {
|
||||||
material,
|
material,
|
||||||
u,
|
u,
|
||||||
v,
|
v,
|
||||||
|
front_face: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_face_normal(&mut self, ray: &Ray) {
|
||||||
|
self.front_face = ray.direction.dot(&self.normal) < 0.0;
|
||||||
|
|
||||||
|
self.normal = if self.front_face {
|
||||||
|
self.normal
|
||||||
|
} else {
|
||||||
|
-self.normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,3 +76,37 @@ impl<T: Hitable + ?Sized> Hitable for Arc<T> {
|
||||||
self.as_ref().bounding_box(t0, t1)
|
self.as_ref().bounding_box(t0, t1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Translate<T> {
|
||||||
|
object: T,
|
||||||
|
offset: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Translate<T> {
|
||||||
|
pub fn new(object: T, offset: Vec3) -> Self {
|
||||||
|
Self { object, offset }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Hitable> Hitable for Translate<T> {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
let moved_ray = Ray::new(ray.origin - self.offset, ray.direction, ray.time());
|
||||||
|
|
||||||
|
if let Some(mut hit) = self.object.hit(&moved_ray, t_min, t_max) {
|
||||||
|
hit.p += self.offset;
|
||||||
|
hit.set_face_normal(&moved_ray);
|
||||||
|
|
||||||
|
Some(hit)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
|
||||||
|
if let Some(bbox) = self.object.bounding_box(t0, t1) {
|
||||||
|
Some(Aabb::new(bbox.min + self.offset, bbox.max + self.offset))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{demos::ParallelHit, types::Ray, Aabb, HitRecord, Hitable};
|
use crate::{
|
||||||
|
demos::ParallelHit,
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
|
types::Ray,
|
||||||
|
Aabb,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct HitableList {
|
pub struct HitableList {
|
||||||
pub list: Vec<Arc<dyn ParallelHit>>,
|
pub list: Vec<Arc<dyn ParallelHit>>,
|
7
src/hitable/mod.rs
Normal file
7
src/hitable/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
pub mod bvh;
|
||||||
|
pub mod hitable;
|
||||||
|
pub mod hitable_list;
|
||||||
|
pub mod shapes;
|
||||||
|
|
||||||
|
pub use bvh::*;
|
||||||
|
pub use hitable::*;
|
87
src/hitable/shapes/cuboid.rs
Normal file
87
src/hitable/shapes/cuboid.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
hitable::{hitable_list::HitableList, shapes::RectBuilder, HitRecord, Hitable},
|
||||||
|
materials::{Material, MaterialBuilder},
|
||||||
|
types::{Ray, Vec3},
|
||||||
|
Aabb,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Cuboid {
|
||||||
|
min: Vec3,
|
||||||
|
max: Vec3,
|
||||||
|
sides: HitableList,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cuboid {
|
||||||
|
pub fn new(p0: Vec3, p1: Vec3, mat: impl Material + Clone + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
min: p0,
|
||||||
|
max: p1,
|
||||||
|
sides: Self::build_cuboid(p0, p1, mat),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_cuboid(p0: Vec3, p1: Vec3, mat: impl Material + Clone + 'static) -> HitableList {
|
||||||
|
let mut sides = HitableList {
|
||||||
|
list: Vec::with_capacity(6),
|
||||||
|
};
|
||||||
|
|
||||||
|
sides.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(p0.x()..=p1.x())
|
||||||
|
.y(p0.y()..=p1.y())
|
||||||
|
.z(p1.z())
|
||||||
|
.material(mat.clone()),
|
||||||
|
));
|
||||||
|
sides.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(p0.x()..=p1.x())
|
||||||
|
.y(p0.y()..=p1.y())
|
||||||
|
.z(p0.z())
|
||||||
|
.material(mat.clone()),
|
||||||
|
));
|
||||||
|
|
||||||
|
sides.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(p0.x()..=p1.x())
|
||||||
|
.z(p0.z()..=p1.z())
|
||||||
|
.y(p1.y())
|
||||||
|
.material(mat.clone()),
|
||||||
|
));
|
||||||
|
sides.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.x(p0.x()..=p1.x())
|
||||||
|
.z(p0.z()..=p1.z())
|
||||||
|
.y(p0.y())
|
||||||
|
.material(mat.clone()),
|
||||||
|
));
|
||||||
|
|
||||||
|
sides.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.y(p0.y()..=p1.y())
|
||||||
|
.z(p0.z()..=p1.z())
|
||||||
|
.x(p1.x())
|
||||||
|
.material(mat.clone()),
|
||||||
|
));
|
||||||
|
sides.push(Arc::new(
|
||||||
|
RectBuilder
|
||||||
|
.y(p0.y()..=p1.y())
|
||||||
|
.z(p0.z()..=p1.z())
|
||||||
|
.x(p0.x())
|
||||||
|
.material(mat),
|
||||||
|
));
|
||||||
|
|
||||||
|
sides
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hitable for Cuboid {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
self.sides.hit(ray, t_min, t_max)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||||
|
Some(Aabb::new(self.min, self.max))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
|
mod cuboid;
|
||||||
mod moving_sphere;
|
mod moving_sphere;
|
||||||
|
mod rectangle;
|
||||||
mod sphere;
|
mod sphere;
|
||||||
mod xy_rectangle;
|
|
||||||
|
|
||||||
|
pub use cuboid::Cuboid;
|
||||||
pub use moving_sphere::MovingSphere;
|
pub use moving_sphere::MovingSphere;
|
||||||
|
pub use rectangle::RectBuilder;
|
||||||
pub use sphere::Sphere;
|
pub use sphere::Sphere;
|
||||||
pub use xy_rectangle::XyRectangle;
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
types::{Ray, Vec3},
|
types::{Ray, Vec3},
|
||||||
Aabb, HitRecord, Hitable, Material,
|
Aabb, Material,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct MovingSphere<T: Material + Sized> {
|
pub struct MovingSphere<T: Material + Sized> {
|
||||||
|
@ -53,9 +54,9 @@ impl<T: Material + Sized> MovingSphere<T> {
|
||||||
|
|
||||||
impl<T: Material + Sized> Hitable for MovingSphere<T> {
|
impl<T: Material + Sized> Hitable for MovingSphere<T> {
|
||||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
let oc = ray.origin() - self.center(ray.time());
|
let oc = ray.origin - self.center(ray.time());
|
||||||
let a = ray.direction().dot(&ray.direction());
|
let a = ray.direction.dot(&ray.direction);
|
||||||
let b = oc.dot(&ray.direction());
|
let b = oc.dot(&ray.direction);
|
||||||
let c = oc.dot(&oc) - self.radius * self.radius;
|
let c = oc.dot(&oc) - self.radius * self.radius;
|
||||||
|
|
||||||
let discriminant = b * b - a * c;
|
let discriminant = b * b - a * c;
|
175
src/hitable/shapes/rectangle.rs
Normal file
175
src/hitable/shapes/rectangle.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
use std::{marker::PhantomData, ops::RangeInclusive};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
|
materials::MaterialBuilder,
|
||||||
|
types::{Ray, Vec3},
|
||||||
|
Aabb, Dimension, Material, X, Y, Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
type DimRange = RangeInclusive<f64>;
|
||||||
|
|
||||||
|
pub struct Rectangle<D1, D2, D3, T> {
|
||||||
|
d1_range: DimRange,
|
||||||
|
d2_range: DimRange,
|
||||||
|
d3: f64,
|
||||||
|
material: T,
|
||||||
|
tag: PhantomData<(D1, D2, D3)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D1, D2, D3, T> Hitable for Rectangle<D1, D2, D3, T>
|
||||||
|
where
|
||||||
|
T: Material,
|
||||||
|
D1: Dimension,
|
||||||
|
D2: Dimension,
|
||||||
|
D3: Dimension,
|
||||||
|
{
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
let t = (self.d3 - ray.origin.get::<D3>()) / ray.direction.get::<D3>();
|
||||||
|
|
||||||
|
if t < t_min || t > t_max {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let d1 = ray.origin.get::<D1>() + t * ray.direction.get::<D1>();
|
||||||
|
let d2 = ray.origin.get::<D2>() + t * ray.direction.get::<D2>();
|
||||||
|
|
||||||
|
if !self.d1_range.contains(&d1) || !self.d2_range.contains(&d2) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let u = (d1 - self.d1_range.start()) / (self.d1_range.end() - self.d1_range.start());
|
||||||
|
let v = (d2 - self.d2_range.start()) / (self.d2_range.end() - self.d2_range.start());
|
||||||
|
|
||||||
|
let mut hit_rec = HitRecord::new(
|
||||||
|
t,
|
||||||
|
ray.point_at_parameter(t),
|
||||||
|
Vec3::splat(0.0).set::<D3>(1.0),
|
||||||
|
&self.material,
|
||||||
|
(u, v),
|
||||||
|
);
|
||||||
|
|
||||||
|
hit_rec.set_face_normal(ray);
|
||||||
|
|
||||||
|
Some(hit_rec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||||
|
// Since this is a axis aligned Rectangle and we are using AABB BVH, Gap between the rectangle and
|
||||||
|
// the bounding box will be infinitely small
|
||||||
|
|
||||||
|
let (&d1_0, &d1_1) = (self.d1_range.start(), self.d1_range.end());
|
||||||
|
let (&d2_0, &d2_1) = (self.d2_range.start(), self.d2_range.end());
|
||||||
|
|
||||||
|
let min = Vec3::splat(self.d3 - 0.0001)
|
||||||
|
.set::<D1>(d1_0)
|
||||||
|
.set::<D2>(d2_0);
|
||||||
|
let max = Vec3::splat(self.d3 + 0.0001)
|
||||||
|
.set::<D1>(d1_1)
|
||||||
|
.set::<D2>(d2_1);
|
||||||
|
|
||||||
|
Some(Aabb::new(min, max))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// taken from, https://github.com/Globidev/toy-rt/blob/master/trt-core/src/hit/rect.rs#L74
|
||||||
|
// because it's amazing!
|
||||||
|
|
||||||
|
pub struct RectBuilder;
|
||||||
|
|
||||||
|
macro_rules! builder {
|
||||||
|
($name:ident, $dim:ty) => {
|
||||||
|
pub fn $name(self, range: RangeInclusive<f64>) -> OneBoundedRectBuilder<$dim> {
|
||||||
|
OneBoundedRectBuilder {
|
||||||
|
range,
|
||||||
|
tag: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OneBoundedRectBuilder<D> {
|
||||||
|
range: DimRange,
|
||||||
|
tag: PhantomData<D>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RectBuilder {
|
||||||
|
builder!(x, X);
|
||||||
|
builder!(y, Y);
|
||||||
|
builder!(z, Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! one_bounded_rect_builder {
|
||||||
|
($name:ident, $dim1: ty, $dim2: ty) => {
|
||||||
|
pub fn $name(self, d2_range: DimRange) -> TwoBoundedRectBuilder<$dim1, $dim2> {
|
||||||
|
TwoBoundedRectBuilder {
|
||||||
|
d1_range: self.range,
|
||||||
|
d2_range,
|
||||||
|
tag: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OneBoundedRectBuilder<X> {
|
||||||
|
one_bounded_rect_builder!(y, X, Y);
|
||||||
|
one_bounded_rect_builder!(z, X, Z);
|
||||||
|
}
|
||||||
|
impl OneBoundedRectBuilder<Y> {
|
||||||
|
one_bounded_rect_builder!(x, Y, X);
|
||||||
|
one_bounded_rect_builder!(z, Y, Z);
|
||||||
|
}
|
||||||
|
impl OneBoundedRectBuilder<Z> {
|
||||||
|
one_bounded_rect_builder!(x, Z, X);
|
||||||
|
one_bounded_rect_builder!(y, Z, Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TwoBoundedRectBuilder<D1, D2> {
|
||||||
|
d1_range: DimRange,
|
||||||
|
d2_range: DimRange,
|
||||||
|
tag: PhantomData<(D1, D2)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! two_bounded_rect_builder {
|
||||||
|
($name:ident, $dim1: ty, $dim2: ty, $dim3: ty) => {
|
||||||
|
pub fn $name(self, $name: f64) -> ThreeBoundedRectBuilder<$dim1, $dim2, $dim3> {
|
||||||
|
ThreeBoundedRectBuilder {
|
||||||
|
d1_range: self.d1_range,
|
||||||
|
d2_range: self.d2_range,
|
||||||
|
d3: $name,
|
||||||
|
tag: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TwoBoundedRectBuilder<X, Y> {
|
||||||
|
two_bounded_rect_builder!(z, X, Y, Z);
|
||||||
|
}
|
||||||
|
impl TwoBoundedRectBuilder<X, Z> {
|
||||||
|
two_bounded_rect_builder!(y, X, Z, Y);
|
||||||
|
}
|
||||||
|
impl TwoBoundedRectBuilder<Y, Z> {
|
||||||
|
two_bounded_rect_builder!(x, Y, Z, X);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ThreeBoundedRectBuilder<D1, D2, D3> {
|
||||||
|
d1_range: DimRange,
|
||||||
|
d2_range: DimRange,
|
||||||
|
d3: f64,
|
||||||
|
tag: PhantomData<(D1, D2, D3)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D1, D2, D3, T> MaterialBuilder<T> for ThreeBoundedRectBuilder<D1, D2, D3> {
|
||||||
|
type Finished = Rectangle<D1, D2, D3, T>;
|
||||||
|
|
||||||
|
fn material(self, material: T) -> Self::Finished {
|
||||||
|
Rectangle {
|
||||||
|
d1_range: self.d1_range,
|
||||||
|
d2_range: self.d2_range,
|
||||||
|
d3: self.d3,
|
||||||
|
material,
|
||||||
|
tag: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
types::{Ray, Vec3},
|
types::{Ray, Vec3},
|
||||||
Aabb, HitRecord, Hitable, Material,
|
Aabb, Material,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Sphere<T: Material + Sized> {
|
pub struct Sphere<T: Material + Sized> {
|
||||||
|
@ -34,9 +35,9 @@ impl<T: Material + Sized> Sphere<T> {
|
||||||
|
|
||||||
impl<T: Material + Sized> Hitable for Sphere<T> {
|
impl<T: Material + Sized> Hitable for Sphere<T> {
|
||||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
let oc = ray.origin() - self.center;
|
let oc = ray.origin - self.center;
|
||||||
let a = ray.direction().dot(&ray.direction());
|
let a = ray.direction.dot(&ray.direction);
|
||||||
let b = oc.dot(&ray.direction());
|
let b = oc.dot(&ray.direction);
|
||||||
let c = oc.dot(&oc) - self.radius * self.radius;
|
let c = oc.dot(&oc) - self.radius * self.radius;
|
||||||
|
|
||||||
// The discriminant is calculated using b^2 - 4 * a * c
|
// The discriminant is calculated using b^2 - 4 * a * c
|
23
src/main.rs
23
src/main.rs
|
@ -1,33 +1,32 @@
|
||||||
#![allow(clippy::suspicious_arithmetic_impl)]
|
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||||
|
|
||||||
mod aabb;
|
mod aabb;
|
||||||
mod bvh;
|
|
||||||
mod camera;
|
mod camera;
|
||||||
mod demos;
|
mod demos;
|
||||||
mod hitable;
|
mod hitable;
|
||||||
mod hitable_list;
|
|
||||||
mod materials;
|
mod materials;
|
||||||
mod shapes;
|
|
||||||
mod texture;
|
mod texture;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
pub use aabb::Aabb;
|
pub use aabb::Aabb;
|
||||||
pub use bvh::BvhNode;
|
|
||||||
pub use camera::Camera;
|
pub use camera::Camera;
|
||||||
pub use hitable::{HitRecord, Hitable};
|
|
||||||
pub use hitable_list::HitableList;
|
|
||||||
pub use materials::Material;
|
pub use materials::Material;
|
||||||
pub use texture::Texture;
|
pub use texture::Texture;
|
||||||
|
pub use types::{Dimension, X, Y, Z};
|
||||||
|
|
||||||
|
use crate::hitable::BvhNode;
|
||||||
use demos::Demo;
|
use demos::Demo;
|
||||||
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
const NUM_SAMPLES: u16 = 1000;
|
pub trait Asf64: num_traits::AsPrimitive<f64> {}
|
||||||
|
impl<T: num_traits::AsPrimitive<f64>> Asf64 for T {}
|
||||||
|
|
||||||
|
const NUM_SAMPLES: u16 = 100;
|
||||||
const VERTICAL_PARTITION: usize = 12;
|
const VERTICAL_PARTITION: usize = 12;
|
||||||
const HORIZONTAL_PARTITION: usize = 12;
|
const HORIZONTAL_PARTITION: usize = 12;
|
||||||
const WIDTH: usize = 2560;
|
const WIDTH: usize = 1500;
|
||||||
const HEIGHT: usize = 1440;
|
const HEIGHT: usize = 1500;
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
fn main() -> Result<(), String> {
|
||||||
run(WIDTH, HEIGHT)
|
run(WIDTH, HEIGHT)
|
||||||
|
@ -67,7 +66,7 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
||||||
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::SimpleLight {};
|
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::CornellBox {};
|
||||||
let mut should_update = true;
|
let mut should_update = true;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -104,6 +103,10 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
||||||
active_demo = &demos::SimpleLight {};
|
active_demo = &demos::SimpleLight {};
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
|
Some(Keycode::Num6) => {
|
||||||
|
active_demo = &demos::CornellBox {};
|
||||||
|
should_update = true;
|
||||||
|
}
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
use rand::{prelude::SmallRng, Rng};
|
use rand::{prelude::SmallRng, Rng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hitable::HitRecord,
|
||||||
materials::{reflect, refract, schlick},
|
materials::{reflect, refract, schlick},
|
||||||
types::{Ray, Vec3},
|
types::{Ray, Vec3},
|
||||||
HitRecord, Material,
|
Material,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Dielectric {
|
pub struct Dielectric {
|
||||||
reflection_index: f64,
|
refraction_index: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dielectric {
|
impl Dielectric {
|
||||||
pub fn new(reflection_index: f64) -> Self {
|
pub fn new(refraction_index: f64) -> Self {
|
||||||
Self { reflection_index }
|
Self { refraction_index }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,44 +24,37 @@ impl Material for Dielectric {
|
||||||
hit_rec: &HitRecord,
|
hit_rec: &HitRecord,
|
||||||
rng: &mut SmallRng,
|
rng: &mut SmallRng,
|
||||||
) -> (Vec3, Option<Ray>) {
|
) -> (Vec3, Option<Ray>) {
|
||||||
let reflected_ray = reflect(ray_in.direction(), hit_rec.normal);
|
|
||||||
// Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this
|
// Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this
|
||||||
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(&hit_rec.normal) > 0.0
|
let refraction_ratio = if hit_rec.front_face {
|
||||||
{
|
1.0 / self.refraction_index
|
||||||
(
|
|
||||||
-hit_rec.normal,
|
|
||||||
self.reflection_index,
|
|
||||||
(ray_in.direction().dot(&hit_rec.normal) * self.reflection_index)
|
|
||||||
/ ray_in.direction().length(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(
|
self.refraction_index
|
||||||
hit_rec.normal,
|
|
||||||
1.0 / self.reflection_index,
|
|
||||||
(-ray_in.direction().dot(&hit_rec.normal)) / ray_in.direction().length(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(refracted_ray) = refract(ray_in.direction(), outward_normal, ni_over_nt) {
|
let unit_direction = ray_in.direction.unit_vector();
|
||||||
let reflect_prob = schlick(cosine, self.reflection_index);
|
let cosine = (-unit_direction).dot(&hit_rec.normal).min(1.0);
|
||||||
|
let sin_theta = (1.0 - cosine * cosine).sqrt();
|
||||||
|
|
||||||
if rng.gen::<f64>() < reflect_prob {
|
let cannot_refract = refraction_ratio * sin_theta > 1.0;
|
||||||
|
|
||||||
|
if cannot_refract || schlick(cosine, refraction_ratio) > rng.gen::<f64>() {
|
||||||
|
let direction = reflect(unit_direction, hit_rec.normal);
|
||||||
(
|
(
|
||||||
attenuation,
|
attenuation,
|
||||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
Some(Ray::new(hit_rec.p, direction, ray_in.time())),
|
||||||
|
)
|
||||||
|
} else if let Some(direction) = refract(unit_direction, hit_rec.normal, refraction_ratio) {
|
||||||
|
(
|
||||||
|
attenuation,
|
||||||
|
Some(Ray::new(hit_rec.p, direction, ray_in.time())),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
let direction = reflect(unit_direction, hit_rec.normal);
|
||||||
(
|
(
|
||||||
attenuation,
|
attenuation,
|
||||||
Some(Ray::new(hit_rec.p, refracted_ray, ray_in.time())),
|
Some(Ray::new(hit_rec.p, direction, ray_in.time())),
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
attenuation,
|
|
||||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use rand::prelude::SmallRng;
|
use rand::prelude::SmallRng;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hitable::HitRecord,
|
||||||
materials::random_point_in_unit_sphere,
|
materials::random_point_in_unit_sphere,
|
||||||
types::{Ray, Vec3},
|
types::{Ray, Vec3},
|
||||||
HitRecord, Material, Texture,
|
Material, Texture,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct Lambertian<T: Texture> {
|
pub struct Lambertian<T: Texture> {
|
||||||
albedo: T,
|
albedo: T,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use rand::prelude::SmallRng;
|
use rand::prelude::SmallRng;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hitable::HitRecord,
|
||||||
materials::{random_point_in_unit_sphere, reflect},
|
materials::{random_point_in_unit_sphere, reflect},
|
||||||
types::{Ray, Vec3},
|
types::{Ray, Vec3},
|
||||||
HitRecord, Material,
|
Material,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Metal {
|
pub struct Metal {
|
||||||
|
@ -28,14 +29,14 @@ impl Material for Metal {
|
||||||
hit_rec: &HitRecord,
|
hit_rec: &HitRecord,
|
||||||
rng: &mut SmallRng,
|
rng: &mut SmallRng,
|
||||||
) -> (Vec3, Option<Ray>) {
|
) -> (Vec3, Option<Ray>) {
|
||||||
let reflected_ray = reflect(ray_in.direction().unit_vector(), hit_rec.normal);
|
let reflected_ray = reflect(ray_in.direction.unit_vector(), hit_rec.normal);
|
||||||
let scattered_ray = Ray::new(
|
let scattered_ray = Ray::new(
|
||||||
hit_rec.p,
|
hit_rec.p,
|
||||||
reflected_ray + random_point_in_unit_sphere(rng) * self.fuzz,
|
reflected_ray + random_point_in_unit_sphere(rng) * self.fuzz,
|
||||||
ray_in.time(),
|
ray_in.time(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 {
|
if scattered_ray.direction.dot(&hit_rec.normal) > 0.0 {
|
||||||
(self.albedo, Some(scattered_ray))
|
(self.albedo, Some(scattered_ray))
|
||||||
} else {
|
} else {
|
||||||
(self.albedo, None)
|
(self.albedo, None)
|
||||||
|
|
|
@ -10,8 +10,8 @@ pub use metal::Metal;
|
||||||
use rand::{prelude::SmallRng, Rng};
|
use rand::{prelude::SmallRng, Rng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hitable::HitRecord,
|
||||||
types::{Ray, Vec3},
|
types::{Ray, Vec3},
|
||||||
HitRecord,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Material: Send + Sync {
|
pub trait Material: Send + Sync {
|
||||||
|
@ -23,11 +23,11 @@ pub trait Material: Send + Sync {
|
||||||
_hit_rec: &HitRecord,
|
_hit_rec: &HitRecord,
|
||||||
_rng: &mut SmallRng,
|
_rng: &mut SmallRng,
|
||||||
) -> (Vec3, Option<Ray>) {
|
) -> (Vec3, Option<Ray>) {
|
||||||
(Vec3::new(0.0, 0.0, 0.0), None)
|
(Vec3::splat(0.0), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 {
|
fn emit(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::splat(0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +56,15 @@ fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> Vec3 {
|
fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> Vec3 {
|
||||||
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
let mut point = Vec3::random(rng) * 2.0 - Vec3::splat(1.0);
|
||||||
- Vec3::new(1.0, 1.0, 1.0);
|
|
||||||
while point.sq_len() >= 1.0 {
|
while point.sq_len() >= 1.0 {
|
||||||
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
point = Vec3::random(rng) * 2.0 - Vec3::splat(1.0);
|
||||||
- Vec3::new(1.0, 1.0, 1.0);
|
|
||||||
}
|
}
|
||||||
point
|
point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait MaterialBuilder<T> {
|
||||||
|
type Finished;
|
||||||
|
|
||||||
|
fn material(self, material: T) -> Self::Finished;
|
||||||
|
}
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
use crate::{
|
|
||||||
types::{Ray, Vec3},
|
|
||||||
Aabb, HitRecord, Hitable, Material,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct XyRectangle<T: Material> {
|
|
||||||
x0: f64,
|
|
||||||
x1: f64,
|
|
||||||
y0: f64,
|
|
||||||
y1: f64,
|
|
||||||
k: f64,
|
|
||||||
material: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Material> XyRectangle<T> {
|
|
||||||
pub fn new(x0: f64, x1: f64, y0: f64, y1: f64, k: f64, material: T) -> Self {
|
|
||||||
Self {
|
|
||||||
x0,
|
|
||||||
x1,
|
|
||||||
y0,
|
|
||||||
y1,
|
|
||||||
k,
|
|
||||||
material,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Material> Hitable for XyRectangle<T> {
|
|
||||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
|
||||||
let t = (self.k - ray.origin().z()) / ray.direction().z();
|
|
||||||
|
|
||||||
if t < t_min || t > t_max {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let x = ray.origin().x() + t * ray.direction().x();
|
|
||||||
let y = ray.origin().y() + t * ray.direction().y();
|
|
||||||
|
|
||||||
if x < self.x0 || x > self.x1 || y < self.y0 || y > self.y1 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let u = (x - self.x0) / (self.x1 - self.x0);
|
|
||||||
let v = (y - self.y0) / (self.y1 - self.y0);
|
|
||||||
|
|
||||||
Some(HitRecord::new(
|
|
||||||
t,
|
|
||||||
ray.point_at_parameter(t),
|
|
||||||
Vec3::new(0.0, 0.0, 1.0),
|
|
||||||
&self.material,
|
|
||||||
(u, v),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
|
||||||
// Since this is a axis aligned Rectangle and we are using AABB BVH, Gap between the rectangle and
|
|
||||||
// the bounding box will be infinitely small
|
|
||||||
|
|
||||||
Some(Aabb::new(
|
|
||||||
Vec3::new(self.x0, self.y0, self.k - 0.0001),
|
|
||||||
Vec3::new(self.x1, self.y1, self.k + 0.0001),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::{types::Vec3, Texture};
|
use crate::{types::Vec3, Texture};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct Solid {
|
pub struct Solid {
|
||||||
color: Vec3,
|
color: Vec3,
|
||||||
}
|
}
|
||||||
|
|
12
src/types/color.rs
Normal file
12
src/types/color.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
use crate::types::Vec3;
|
||||||
|
|
||||||
|
pub struct Color(pub u8, pub u8, pub u8);
|
||||||
|
|
||||||
|
impl From<Vec3> for Color {
|
||||||
|
fn from(v: Vec3) -> Self {
|
||||||
|
let v = v.sqrt() * 255.99;
|
||||||
|
let (r, g, b) = (v.x(), v.y(), v.z());
|
||||||
|
|
||||||
|
Self(r as u8, g as u8, b as u8)
|
||||||
|
}
|
||||||
|
}
|
21
src/types/dimension.rs
Normal file
21
src/types/dimension.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Taken from, https://github.com/Globidev/toy-rt/tree/master/trt-core/src/dimension.rs
|
||||||
|
|
||||||
|
pub trait Dimension {
|
||||||
|
const INDEX: usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct X;
|
||||||
|
pub struct Y;
|
||||||
|
pub struct Z;
|
||||||
|
|
||||||
|
impl Dimension for X {
|
||||||
|
const INDEX: usize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dimension for Y {
|
||||||
|
const INDEX: usize = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dimension for Z {
|
||||||
|
const INDEX: usize = 2;
|
||||||
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
|
mod color;
|
||||||
|
mod dimension;
|
||||||
mod ray;
|
mod ray;
|
||||||
mod vec3;
|
mod vec3;
|
||||||
|
|
||||||
|
pub use color::Color;
|
||||||
|
pub use dimension::{Dimension, X, Y, Z};
|
||||||
pub use ray::Ray;
|
pub use ray::Ray;
|
||||||
pub use vec3::Vec3;
|
pub use vec3::Vec3;
|
||||||
|
|
|
@ -1,28 +1,25 @@
|
||||||
use rand::prelude::SmallRng;
|
use rand::prelude::SmallRng;
|
||||||
|
|
||||||
use crate::{types::Vec3, Hitable};
|
use crate::{hitable::Hitable, types::Vec3};
|
||||||
|
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
a: Vec3,
|
pub origin: Vec3,
|
||||||
b: Vec3,
|
pub direction: Vec3,
|
||||||
time: f64,
|
time: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ray {
|
impl Ray {
|
||||||
pub fn new(a: Vec3, b: Vec3, time: f64) -> Ray {
|
pub fn new(origin: Vec3, direction: Vec3, time: f64) -> Ray {
|
||||||
Ray { a, b, time }
|
Ray {
|
||||||
|
origin,
|
||||||
|
direction,
|
||||||
|
time,
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub const fn origin(&self) -> Vec3 {
|
|
||||||
self.a
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn direction(&self) -> Vec3 {
|
|
||||||
self.b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn point_at_parameter(&self, t: f64) -> Vec3 {
|
pub fn point_at_parameter(&self, t: f64) -> Vec3 {
|
||||||
self.a + self.b * t
|
self.origin + self.direction * t
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn time(&self) -> f64 {
|
pub const fn time(&self) -> f64 {
|
||||||
|
@ -38,7 +35,7 @@ impl Ray {
|
||||||
) -> Vec3 {
|
) -> Vec3 {
|
||||||
if let Some(hit_rec) = world.hit(self, 0.001, std::f64::MAX) {
|
if let Some(hit_rec) = world.hit(self, 0.001, std::f64::MAX) {
|
||||||
if depth >= 50 {
|
if depth >= 50 {
|
||||||
Vec3::new(0.0, 0.0, 0.0)
|
Vec3::splat(0.0f64)
|
||||||
} else {
|
} else {
|
||||||
let material = hit_rec.material;
|
let material = hit_rec.material;
|
||||||
let emitted_color = hit_rec.material.emit(hit_rec.u, hit_rec.v, hit_rec.p);
|
let emitted_color = hit_rec.material.emit(hit_rec.u, hit_rec.v, hit_rec.p);
|
||||||
|
|
|
@ -5,37 +5,45 @@ use std::{
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
use crate::{Asf64, Dimension, X, Y, Z};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone)]
|
||||||
pub struct Vec3([f64; 3]);
|
pub struct Vec3([f64; 3]);
|
||||||
|
|
||||||
impl Vec3 {
|
impl Vec3 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new(a: f64, b: f64, c: f64) -> Vec3 {
|
pub fn new(a: impl Asf64, b: impl Asf64, c: impl Asf64) -> Vec3 {
|
||||||
Vec3([a, b, c])
|
Vec3([a.as_(), b.as_(), c.as_()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn splat(xyz: impl Asf64) -> Self {
|
||||||
|
Self::new(xyz, xyz, xyz)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
||||||
|
Self(rng.gen())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn x(&self) -> f64 {
|
pub fn x(&self) -> f64 {
|
||||||
self[0]
|
self.get::<X>()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn y(&self) -> f64 {
|
pub fn y(&self) -> f64 {
|
||||||
self[1]
|
self.get::<Y>()
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn z(&self) -> f64 {
|
pub fn z(&self) -> f64 {
|
||||||
self[2]
|
self.get::<Z>()
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn r(&self) -> f64 {
|
pub fn get<D: Dimension>(&self) -> f64 {
|
||||||
self[0]
|
self.0[D::INDEX]
|
||||||
}
|
}
|
||||||
#[inline]
|
|
||||||
pub fn g(&self) -> f64 {
|
pub fn set<D: Dimension>(mut self, value: f64) -> Self {
|
||||||
self[1]
|
self.0[D::INDEX] = value;
|
||||||
}
|
self
|
||||||
#[inline]
|
|
||||||
pub fn b(&self) -> f64 {
|
|
||||||
self[2]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -45,40 +53,55 @@ impl Vec3 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn sq_len(&self) -> f64 {
|
pub fn sq_len(&self) -> f64 {
|
||||||
self[0] * self[0] + self[1] * self[1] + self[2] * self[2]
|
self.x() * self.x() + self.y() * self.y() + self.z() * self.z()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn dot(&self, v: &Vec3) -> f64 {
|
pub fn dot(&self, v: &Vec3) -> f64 {
|
||||||
self[0] * v[0] + self[1] * v[1] + self[2] * v[2]
|
self.x() * v.x() + self.y() * v.y() + self.z() * v.z()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cross(&self, v: &Vec3) -> Vec3 {
|
pub fn cross(&self, v: &Vec3) -> Vec3 {
|
||||||
Vec3([
|
Vec3([
|
||||||
self[1] * v[2] - self[2] * v[1],
|
self.y() * v.z() - self.z() * v.y(),
|
||||||
self[2] * v[0] - self[0] * v[2],
|
self.z() * v.x() - self.x() * v.z(),
|
||||||
self[0] * v[1] - self[1] * v[0],
|
self.x() * v.y() - self.y() * v.x(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn make_unit_vector(&mut self) {
|
pub fn unit_vector(self) -> Vec3 {
|
||||||
let k = 1.0f64 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
|
self / self.length()
|
||||||
self[0] *= k;
|
}
|
||||||
self[1] *= k;
|
|
||||||
self[2] *= k;
|
pub fn min(self, other: Self) -> Vec3 {
|
||||||
|
Self([
|
||||||
|
self.x().min(other.x()),
|
||||||
|
self.y().min(other.y()),
|
||||||
|
self.z().min(other.z()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max(self, other: Self) -> Vec3 {
|
||||||
|
Self([
|
||||||
|
self.x().max(other.x()),
|
||||||
|
self.y().max(other.y()),
|
||||||
|
self.z().max(other.z()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min_element(self) -> f64 {
|
||||||
|
self.x().min(self.y()).min(self.z())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_element(self) -> f64 {
|
||||||
|
self.x().max(self.y()).max(self.z())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unit_vector(&self) -> Vec3 {
|
pub fn sqrt(self) -> Self {
|
||||||
let length = self.length();
|
Vec3::new(self.x().sqrt(), self.y().sqrt(), self.z().sqrt())
|
||||||
Vec3([self[0] / length, self[1] / length, self[2] / length])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Vec3 {
|
|
||||||
Vec3([rng.gen(), rng.gen(), rng.gen()])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +109,7 @@ impl Add for Vec3 {
|
||||||
type Output = Vec3;
|
type Output = Vec3;
|
||||||
|
|
||||||
fn add(self, o: Vec3) -> Vec3 {
|
fn add(self, o: Vec3) -> Vec3 {
|
||||||
Vec3([self[0] + o[0], self[1] + o[1], self[2] + o[2]])
|
Vec3([self.x() + o.x(), self.y() + o.y(), self.z() + o.z()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,15 +125,15 @@ impl Sub for Vec3 {
|
||||||
type Output = Vec3;
|
type Output = Vec3;
|
||||||
|
|
||||||
fn sub(self, o: Vec3) -> Vec3 {
|
fn sub(self, o: Vec3) -> Vec3 {
|
||||||
Vec3([self[0] - o[0], self[1] - o[1], self[2] - o[2]])
|
Vec3([self.x() - o.x(), self.y() - o.y(), self.z() - o.z()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubAssign for Vec3 {
|
impl SubAssign for Vec3 {
|
||||||
fn sub_assign(&mut self, o: Vec3) {
|
fn sub_assign(&mut self, o: Vec3) {
|
||||||
self[0] -= o[0];
|
self.0[0] -= o.0[0];
|
||||||
self[1] -= o[1];
|
self.0[1] -= o.0[1];
|
||||||
self[2] -= o[2];
|
self.0[2] -= o.0[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,37 +141,37 @@ impl Neg for Vec3 {
|
||||||
type Output = Vec3;
|
type Output = Vec3;
|
||||||
|
|
||||||
fn neg(self) -> Vec3 {
|
fn neg(self) -> Vec3 {
|
||||||
Vec3([-self[0], -self[1], -self[2]])
|
Vec3([-self.x(), -self.y(), -self.z()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MulAssign<Vec3> for Vec3 {
|
impl MulAssign<Vec3> for Vec3 {
|
||||||
fn mul_assign(&mut self, o: Vec3) {
|
fn mul_assign(&mut self, o: Vec3) {
|
||||||
self[0] *= o[0];
|
self.0[0] *= o.0[0];
|
||||||
self[1] *= o[1];
|
self.0[1] *= o.0[1];
|
||||||
self[2] *= o[2];
|
self.0[2] *= o.0[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MulAssign<f64> for Vec3 {
|
impl MulAssign<f64> for Vec3 {
|
||||||
fn mul_assign(&mut self, o: f64) {
|
fn mul_assign(&mut self, o: f64) {
|
||||||
self[0] *= o;
|
self.0[0] *= o;
|
||||||
self[1] *= o;
|
self.0[1] *= o;
|
||||||
self[2] *= o;
|
self.0[2] *= o;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<f64> for Vec3 {
|
impl Mul<f64> for Vec3 {
|
||||||
type Output = Vec3;
|
type Output = Vec3;
|
||||||
fn mul(self, o: f64) -> Vec3 {
|
fn mul(self, o: f64) -> Vec3 {
|
||||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
Vec3([self.x() * o, self.y() * o, self.z() * o])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Vec3> for Vec3 {
|
impl Mul<Vec3> for Vec3 {
|
||||||
type Output = Vec3;
|
type Output = Vec3;
|
||||||
fn mul(self, o: Vec3) -> Vec3 {
|
fn mul(self, o: Vec3) -> Vec3 {
|
||||||
Vec3([self[0] * o[0], self[1] * o[1], self[2] * o[2]])
|
Vec3([self.x() * o.x(), self.y() * o.y(), self.z() * o.z()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +179,7 @@ impl Div<Vec3> for Vec3 {
|
||||||
type Output = Vec3;
|
type Output = Vec3;
|
||||||
|
|
||||||
fn div(self, o: Vec3) -> Vec3 {
|
fn div(self, o: Vec3) -> Vec3 {
|
||||||
Vec3([self[0] / o[0], self[1] / o[1], self[2] / o[2]])
|
Vec3([self.x() / o.x(), self.y() / o.y(), self.z() / o.z()])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +188,7 @@ impl Div<f64> for Vec3 {
|
||||||
|
|
||||||
fn div(self, o: f64) -> Vec3 {
|
fn div(self, o: f64) -> Vec3 {
|
||||||
let o = 1.0 / o;
|
let o = 1.0 / o;
|
||||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
Vec3([self.x() * o, self.y() * o, self.z() * o])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,6 +217,17 @@ impl IndexMut<usize> for Vec3 {
|
||||||
|
|
||||||
impl Display for Vec3 {
|
impl Display for Vec3 {
|
||||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||||
f.write_fmt(format_args!("{} {} {}", self[0], self[1], self[2]))
|
f.write_fmt(format_args!(
|
||||||
|
"{} {} {}",
|
||||||
|
self.get::<X>(),
|
||||||
|
self.get::<Y>(),
|
||||||
|
self.get::<Z>()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: Asf64, B: Asf64, C: Asf64> From<(A, B, C)> for Vec3 {
|
||||||
|
fn from((x, y, z): (A, B, C)) -> Self {
|
||||||
|
Self::new(x, y, z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user