Switched from in-tree float4 lib to glam.

This commit is contained in:
Nathan Vegdahl 2019-07-22 22:30:37 +09:00
parent 5c5a01ecee
commit 88e7365bc4
21 changed files with 301 additions and 2033 deletions

29
Cargo.lock generated
View File

@ -8,6 +8,14 @@ dependencies = [
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "approx"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.11" version = "0.2.11"
@ -110,10 +118,6 @@ name = "crossbeam"
version = "0.3.2" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "float4"
version = "0.1.0"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.6" version = "1.0.6"
@ -124,6 +128,14 @@ name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "glam"
version = "0.7.1"
source = "git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99#0f314f990710ff9357e5896de2b55ec82fe88e0d"
dependencies = [
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "half" name = "half"
version = "1.3.0" version = "1.3.0"
@ -147,7 +159,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "math3d" name = "math3d"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"float4 0.1.0", "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)",
] ]
[[package]] [[package]]
@ -246,7 +259,7 @@ dependencies = [
"color 0.1.0", "color 0.1.0",
"copy_in_place 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "copy_in_place 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"float4 0.1.0", "glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)",
"half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"halton 0.1.0", "halton 0.1.0",
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -449,7 +462,7 @@ version = "0.1.0"
name = "spectral_upsampling" name = "spectral_upsampling"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"float4 0.1.0", "glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)",
] ]
[[package]] [[package]]
@ -551,6 +564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
@ -567,6 +581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" "checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum glam 0.7.1 (git+https://github.com/bitshifter/glam-rs.git?rev=0f314f99)" = "<none>"
"checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836" "checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836"
"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
"checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880" "checksum libc 0.2.55 (registry+https://github.com/rust-lang/crates.io-index)" = "42914d39aad277d9e176efbdad68acb1d5443ab65afe0e0e4f0d49352a950880"

View File

@ -2,7 +2,6 @@
members = [ members = [
"sub_crates/bvh_order", "sub_crates/bvh_order",
"sub_crates/color", "sub_crates/color",
"sub_crates/float4",
"sub_crates/halton", "sub_crates/halton",
"sub_crates/math3d", "sub_crates/math3d",
"sub_crates/mem_arena", "sub_crates/mem_arena",
@ -36,6 +35,7 @@ png_encode_mini = "0.1.2"
rustc-serialize = "0.3" rustc-serialize = "0.3"
scoped_threadpool = "0.1" scoped_threadpool = "0.1"
time = "0.1" time = "0.1"
glam = {git="https://github.com/bitshifter/glam-rs.git", rev="0f314f99", default-features=false, features=["approx"]}
# Local crate dependencies # Local crate dependencies
[dependencies.bvh_order] [dependencies.bvh_order]
@ -44,9 +44,6 @@ path = "sub_crates/bvh_order"
[dependencies.color] [dependencies.color]
path = "sub_crates/color" path = "sub_crates/color"
[dependencies.float4]
path = "sub_crates/float4"
[dependencies.halton] [dependencies.halton]
path = "sub_crates/halton" path = "sub_crates/halton"

View File

@ -6,6 +6,8 @@
use std::mem::{transmute, MaybeUninit}; use std::mem::{transmute, MaybeUninit};
use glam::Vec4Mask;
use mem_arena::MemArena; use mem_arena::MemArena;
use crate::{ use crate::{
@ -23,7 +25,6 @@ use super::{
}; };
use bvh_order::{calc_traversal_code, SplitAxes, TRAVERSAL_TABLE}; use bvh_order::{calc_traversal_code, SplitAxes, TRAVERSAL_TABLE};
use float4::Bool4;
pub fn ray_code(dir: Vector) -> usize { pub fn ray_code(dir: Vector) -> usize {
let ray_sign_is_neg = [dir.x() < 0.0, dir.y() < 0.0, dir.z() < 0.0]; let ray_sign_is_neg = [dir.x() < 0.0, dir.y() < 0.0, dir.z() < 0.0];
@ -122,12 +123,12 @@ impl<'a> BVH4<'a> {
traversal_code, traversal_code,
} => { } => {
node_tests += ray_stack.ray_count_in_next_task() as u64; node_tests += ray_stack.ray_count_in_next_task() as u64;
let mut all_hits = Bool4::new_false(); let mut all_hits = Vec4Mask::default();
// Ray testing // Ray testing
ray_stack.pop_do_next_task_and_push_rays(children.len(), |ray_idx| { ray_stack.pop_do_next_task_and_push_rays(children.len(), |ray_idx| {
if rays.is_done(ray_idx) { if rays.is_done(ray_idx) {
Bool4::new_false() Vec4Mask::default()
} else { } else {
let hits = if bounds.len() == 1 { let hits = if bounds.len() == 1 {
bounds[0].intersect_ray( bounds[0].intersect_ray(
@ -148,7 +149,7 @@ impl<'a> BVH4<'a> {
}); });
// If there were any intersections, create tasks. // If there were any intersections, create tasks.
if !all_hits.is_all_false() { if all_hits.any() {
let order_code = traversal_table[traversal_code as usize]; let order_code = traversal_table[traversal_code as usize];
let mut lane_count = 0; let mut lane_count = 0;
let mut i = children.len() as u8; let mut i = children.len() as u8;

View File

@ -45,12 +45,12 @@ impl BBox {
let t2 = (self.max.co - orig.co) * dir_inv.co; let t2 = (self.max.co - orig.co) * dir_inv.co;
// Find the far and near intersection // Find the far and near intersection
let mut far_t = t1.v_max(t2); let mut far_t = t1.max(t2);
let mut near_t = t1.v_min(t2); let mut near_t = t1.min(t2);
far_t.set_3(std::f32::INFINITY); far_t.set_w(std::f32::INFINITY);
near_t.set_3(0.0); near_t.set_w(0.0);
let far_hit_t = fast_minf32(far_t.h_min() * BBOX_MAXT_ADJUST, max_t); let far_hit_t = fast_minf32(far_t.min_element() * BBOX_MAXT_ADJUST, max_t);
let near_hit_t = near_t.h_max(); let near_hit_t = near_t.max_element();
// Did we hit? // Did we hit?
near_hit_t <= far_hit_t near_hit_t <= far_hit_t
@ -106,10 +106,10 @@ impl BitOr for BBox {
fn bitor(self, rhs: BBox) -> BBox { fn bitor(self, rhs: BBox) -> BBox {
BBox::from_points( BBox::from_points(
Point { Point {
co: self.min.co.v_min(rhs.min.co), co: self.min.co.min(rhs.min.co),
}, },
Point { Point {
co: self.max.co.v_max(rhs.max.co), co: self.max.co.max(rhs.max.co),
}, },
) )
} }
@ -128,10 +128,10 @@ impl BitOr<Point> for BBox {
fn bitor(self, rhs: Point) -> BBox { fn bitor(self, rhs: Point) -> BBox {
BBox::from_points( BBox::from_points(
Point { Point {
co: self.min.co.v_min(rhs.co), co: self.min.co.min(rhs.co),
}, },
Point { Point {
co: self.max.co.v_max(rhs.co), co: self.max.co.max(rhs.co),
}, },
) )
} }

View File

@ -9,16 +9,16 @@ use crate::{
math::{Point, Vector}, math::{Point, Vector},
}; };
use float4::{Bool4, Float4}; use glam::{Vec4, Vec4Mask};
const BBOX_MAXT_ADJUST: f32 = 1.00000024; const BBOX_MAXT_ADJUST: f32 = 1.00000024;
/// A SIMD set of 4 3D axis-aligned bounding boxes. /// A SIMD set of 4 3D axis-aligned bounding boxes.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct BBox4 { pub struct BBox4 {
pub x: (Float4, Float4), // (min, max) pub x: (Vec4, Vec4), // (min, max)
pub y: (Float4, Float4), // (min, max) pub y: (Vec4, Vec4), // (min, max)
pub z: (Float4, Float4), // (min, max) pub z: (Vec4, Vec4), // (min, max)
} }
impl BBox4 { impl BBox4 {
@ -26,16 +26,16 @@ impl BBox4 {
pub fn new() -> BBox4 { pub fn new() -> BBox4 {
BBox4 { BBox4 {
x: ( x: (
Float4::splat(std::f32::INFINITY), Vec4::splat(std::f32::INFINITY),
Float4::splat(std::f32::NEG_INFINITY), Vec4::splat(std::f32::NEG_INFINITY),
), ),
y: ( y: (
Float4::splat(std::f32::INFINITY), Vec4::splat(std::f32::INFINITY),
Float4::splat(std::f32::NEG_INFINITY), Vec4::splat(std::f32::NEG_INFINITY),
), ),
z: ( z: (
Float4::splat(std::f32::INFINITY), Vec4::splat(std::f32::INFINITY),
Float4::splat(std::f32::NEG_INFINITY), Vec4::splat(std::f32::NEG_INFINITY),
), ),
} }
} }
@ -45,30 +45,30 @@ impl BBox4 {
pub fn from_bboxes(b1: BBox, b2: BBox, b3: BBox, b4: BBox) -> BBox4 { pub fn from_bboxes(b1: BBox, b2: BBox, b3: BBox, b4: BBox) -> BBox4 {
BBox4 { BBox4 {
x: ( x: (
Float4::new(b1.min.x(), b2.min.x(), b3.min.x(), b4.min.x()), Vec4::new(b1.min.x(), b2.min.x(), b3.min.x(), b4.min.x()),
Float4::new(b1.max.x(), b2.max.x(), b3.max.x(), b4.max.x()), Vec4::new(b1.max.x(), b2.max.x(), b3.max.x(), b4.max.x()),
), ),
y: ( y: (
Float4::new(b1.min.y(), b2.min.y(), b3.min.y(), b4.min.y()), Vec4::new(b1.min.y(), b2.min.y(), b3.min.y(), b4.min.y()),
Float4::new(b1.max.y(), b2.max.y(), b3.max.y(), b4.max.y()), Vec4::new(b1.max.y(), b2.max.y(), b3.max.y(), b4.max.y()),
), ),
z: ( z: (
Float4::new(b1.min.z(), b2.min.z(), b3.min.z(), b4.min.z()), Vec4::new(b1.min.z(), b2.min.z(), b3.min.z(), b4.min.z()),
Float4::new(b1.max.z(), b2.max.z(), b3.max.z(), b4.max.z()), Vec4::new(b1.max.z(), b2.max.z(), b3.max.z(), b4.max.z()),
), ),
} }
} }
// Returns whether the given ray intersects with the bboxes. // Returns whether the given ray intersects with the bboxes.
pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> Bool4 { pub fn intersect_ray(&self, orig: Point, dir_inv: Vector, max_t: f32) -> Vec4Mask {
// Get the ray data into SIMD format. // Get the ray data into SIMD format.
let ro_x = orig.co.all_0(); let ro_x = Vec4::splat(orig.co.x());
let ro_y = orig.co.all_1(); let ro_y = Vec4::splat(orig.co.y());
let ro_z = orig.co.all_2(); let ro_z = Vec4::splat(orig.co.z());
let rdi_x = dir_inv.co.all_0(); let rdi_x = Vec4::splat(dir_inv.co.x());
let rdi_y = dir_inv.co.all_1(); let rdi_y = Vec4::splat(dir_inv.co.y());
let rdi_z = dir_inv.co.all_2(); let rdi_z = Vec4::splat(dir_inv.co.z());
let max_t = Float4::splat(max_t); let max_t = Vec4::splat(max_t);
// Slab tests // Slab tests
let t1_x = (self.x.0 - ro_x) * rdi_x; let t1_x = (self.x.0 - ro_x) * rdi_x;
@ -79,24 +79,21 @@ impl BBox4 {
let t2_z = (self.z.1 - ro_z) * rdi_z; let t2_z = (self.z.1 - ro_z) * rdi_z;
// Get the far and near t hits for each axis. // Get the far and near t hits for each axis.
let t_far_x = t1_x.v_max(t2_x); let t_far_x = t1_x.max(t2_x);
let t_far_y = t1_y.v_max(t2_y); let t_far_y = t1_y.max(t2_y);
let t_far_z = t1_z.v_max(t2_z); let t_far_z = t1_z.max(t2_z);
let t_near_x = t1_x.v_min(t2_x); let t_near_x = t1_x.min(t2_x);
let t_near_y = t1_y.v_min(t2_y); let t_near_y = t1_y.min(t2_y);
let t_near_z = t1_z.v_min(t2_z); let t_near_z = t1_z.min(t2_z);
// Calculate over-all far t hit. // Calculate over-all far t hit.
let far_t = let far_t = (t_far_x.min(t_far_y.min(t_far_z)) * Vec4::splat(BBOX_MAXT_ADJUST)).min(max_t);
(t_far_x.v_min(t_far_y.v_min(t_far_z)) * Float4::splat(BBOX_MAXT_ADJUST)).v_min(max_t);
// Calculate over-all near t hit. // Calculate over-all near t hit.
let near_t = t_near_x let near_t = t_near_x.max(t_near_y).max(t_near_z.max(Vec4::splat(0.0)));
.v_max(t_near_y)
.v_max(t_near_z.v_max(Float4::splat(0.0)));
// Hit results // Hit results
near_t.lt(far_t) near_t.cmplt(far_t)
} }
} }
@ -106,9 +103,9 @@ impl BitOr for BBox4 {
fn bitor(self, rhs: BBox4) -> BBox4 { fn bitor(self, rhs: BBox4) -> BBox4 {
BBox4 { BBox4 {
x: (self.x.0.v_min(rhs.x.0), self.x.1.v_max(rhs.x.1)), x: (self.x.0.min(rhs.x.0), self.x.1.max(rhs.x.1)),
y: (self.y.0.v_min(rhs.y.0), self.y.1.v_max(rhs.y.1)), y: (self.y.0.min(rhs.y.0), self.y.1.max(rhs.y.1)),
z: (self.z.0.v_min(rhs.z.0), self.z.1.v_max(rhs.z.1)), z: (self.z.0.min(rhs.z.0), self.z.1.max(rhs.z.1)),
} }
} }
} }

View File

@ -4,7 +4,7 @@ pub use color::{
rec709_e_to_xyz, rec709_to_xyz, xyz_to_aces_ap0, xyz_to_aces_ap0_e, xyz_to_rec709, rec709_e_to_xyz, rec709_to_xyz, xyz_to_aces_ap0, xyz_to_aces_ap0_e, xyz_to_rec709,
xyz_to_rec709_e, xyz_to_rec709_e,
}; };
use float4::Float4; use glam::Vec4;
use half::f16; use half::f16;
use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE}; use spectral_upsampling::meng::{spectrum_xyz_to_p_4, EQUAL_ENERGY_REFLECTANCE};
use trifloat::signed48; use trifloat::signed48;
@ -31,10 +31,10 @@ fn nth_wavelength(hero_wavelength: f32, n: usize) -> f32 {
} }
} }
/// Returns all wavelengths of a hero wavelength set as a Float4 /// Returns all wavelengths of a hero wavelength set as a Vec4
#[inline(always)] #[inline(always)]
fn wavelengths(hero_wavelength: f32) -> Float4 { fn wavelengths(hero_wavelength: f32) -> Vec4 {
Float4::new( Vec4::new(
nth_wavelength(hero_wavelength, 0), nth_wavelength(hero_wavelength, 0),
nth_wavelength(hero_wavelength, 1), nth_wavelength(hero_wavelength, 1),
nth_wavelength(hero_wavelength, 2), nth_wavelength(hero_wavelength, 2),
@ -94,11 +94,11 @@ impl Color {
} => { } => {
SpectralSample::from_parts( SpectralSample::from_parts(
// TODO: make this SIMD // TODO: make this SIMD
Float4::new( Vec4::new(
plancks_law(temperature, wls.get_0()) * factor, plancks_law(temperature, wls.x()) * factor,
plancks_law(temperature, wls.get_1()) * factor, plancks_law(temperature, wls.y()) * factor,
plancks_law(temperature, wls.get_2()) * factor, plancks_law(temperature, wls.z()) * factor,
plancks_law(temperature, wls.get_3()) * factor, plancks_law(temperature, wls.w()) * factor,
), ),
hero_wavelength, hero_wavelength,
) )
@ -109,11 +109,11 @@ impl Color {
} => { } => {
SpectralSample::from_parts( SpectralSample::from_parts(
// TODO: make this SIMD // TODO: make this SIMD
Float4::new( Vec4::new(
plancks_law_normalized(temperature, wls.get_0()) * factor, plancks_law_normalized(temperature, wls.x()) * factor,
plancks_law_normalized(temperature, wls.get_1()) * factor, plancks_law_normalized(temperature, wls.y()) * factor,
plancks_law_normalized(temperature, wls.get_2()) * factor, plancks_law_normalized(temperature, wls.z()) * factor,
plancks_law_normalized(temperature, wls.get_3()) * factor, plancks_law_normalized(temperature, wls.w()) * factor,
), ),
hero_wavelength, hero_wavelength,
) )
@ -388,7 +388,7 @@ fn plancks_law_normalized(temperature: f32, wavelength: f32) -> f32 {
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct SpectralSample { pub struct SpectralSample {
pub e: Float4, pub e: Vec4,
hero_wavelength: f32, hero_wavelength: f32,
} }
@ -396,7 +396,7 @@ impl SpectralSample {
pub fn new(wavelength: f32) -> SpectralSample { pub fn new(wavelength: f32) -> SpectralSample {
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX); debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
SpectralSample { SpectralSample {
e: Float4::splat(0.0), e: Vec4::splat(0.0),
hero_wavelength: wavelength, hero_wavelength: wavelength,
} }
} }
@ -405,12 +405,12 @@ impl SpectralSample {
pub fn from_value(value: f32, wavelength: f32) -> SpectralSample { pub fn from_value(value: f32, wavelength: f32) -> SpectralSample {
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX); debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
SpectralSample { SpectralSample {
e: Float4::splat(value), e: Vec4::splat(value),
hero_wavelength: wavelength, hero_wavelength: wavelength,
} }
} }
pub fn from_parts(e: Float4, wavelength: f32) -> SpectralSample { pub fn from_parts(e: Vec4, wavelength: f32) -> SpectralSample {
debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX); debug_assert!(wavelength >= WL_MIN && wavelength <= WL_MAX);
SpectralSample { SpectralSample {
e: e, e: e,
@ -520,10 +520,10 @@ impl XYZ {
} }
pub fn from_spectral_sample(ss: &SpectralSample) -> XYZ { pub fn from_spectral_sample(ss: &SpectralSample) -> XYZ {
let xyz0 = XYZ::from_wavelength(ss.wl_n(0), ss.e.get_0()); let xyz0 = XYZ::from_wavelength(ss.wl_n(0), ss.e.x());
let xyz1 = XYZ::from_wavelength(ss.wl_n(1), ss.e.get_1()); let xyz1 = XYZ::from_wavelength(ss.wl_n(1), ss.e.y());
let xyz2 = XYZ::from_wavelength(ss.wl_n(2), ss.e.get_2()); let xyz2 = XYZ::from_wavelength(ss.wl_n(2), ss.e.z());
let xyz3 = XYZ::from_wavelength(ss.wl_n(3), ss.e.get_3()); let xyz3 = XYZ::from_wavelength(ss.wl_n(3), ss.e.w());
(xyz0 + xyz1 + xyz2 + xyz3) * 0.75 (xyz0 + xyz1 + xyz2 + xyz3) * 0.75
} }
@ -601,8 +601,8 @@ impl DivAssign<f32> for XYZ {
/// the method in the paper "Physically Meaningful Rendering using Tristimulus /// the method in the paper "Physically Meaningful Rendering using Tristimulus
/// Colours" by Meng et al. /// Colours" by Meng et al.
#[inline(always)] #[inline(always)]
fn xyz_to_spectrum_4(xyz: (f32, f32, f32), wavelengths: Float4) -> Float4 { fn xyz_to_spectrum_4(xyz: (f32, f32, f32), wavelengths: Vec4) -> Vec4 {
spectrum_xyz_to_p_4(wavelengths, xyz) * Float4::splat(1.0 / EQUAL_ENERGY_REFLECTANCE) spectrum_xyz_to_p_4(wavelengths, xyz) * Vec4::splat(1.0 / EQUAL_ENERGY_REFLECTANCE)
// aces_to_spectrum_p4(wavelengths, xyz_to_aces_ap0_e(xyz)) // aces_to_spectrum_p4(wavelengths, xyz_to_aces_ap0_e(xyz))
} }

View File

@ -73,23 +73,15 @@ impl<T: Lerp> Lerp for (T, T) {
} }
} }
impl Lerp for float4::Float4 { impl Lerp for glam::Vec4 {
fn lerp(self, other: float4::Float4, alpha: f32) -> float4::Float4 { fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 {
(self * (1.0 - alpha)) + (other * alpha) (self * (1.0 - alpha)) + (other * alpha)
} }
} }
impl Lerp for Matrix4x4 { impl Lerp for Matrix4x4 {
fn lerp(self, other: Matrix4x4, alpha: f32) -> Matrix4x4 { fn lerp(self, other: Matrix4x4, alpha: f32) -> Matrix4x4 {
let alpha_minus = 1.0 - alpha; (self * (1.0 - alpha)) + (other * alpha)
Matrix4x4 {
values: [
(self[0] * alpha_minus) + (other[0] * alpha),
(self[1] * alpha_minus) + (other[1] * alpha),
(self[2] * alpha_minus) + (other[2] * alpha),
(self[3] * alpha_minus) + (other[3] * alpha),
],
}
} }
} }

View File

@ -1,6 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
use float4::{Bool4, Float4}; use glam::{Vec4, Vec4Mask};
use crate::math::{Matrix4x4, Point, Vector}; use crate::math::{Matrix4x4, Point, Vector};
@ -86,7 +86,7 @@ impl RayBatch {
pub fn set_from_ray(&mut self, ray: &Ray, is_occlusion: bool, idx: usize) { pub fn set_from_ray(&mut self, ray: &Ray, is_occlusion: bool, idx: usize) {
self.hot[idx].orig_local = ray.orig; self.hot[idx].orig_local = ray.orig;
self.hot[idx].dir_inv_local = Vector { self.hot[idx].dir_inv_local = Vector {
co: Float4::splat(1.0) / ray.dir.co, co: Vec4::splat(1.0) / ray.dir.co,
}; };
self.hot[idx].max_t = ray.max_t; self.hot[idx].max_t = ray.max_t;
self.hot[idx].time = ray.time; self.hot[idx].time = ray.time;
@ -122,7 +122,7 @@ impl RayBatch {
pub fn update_local(&mut self, idx: usize, xform: &Matrix4x4) { pub fn update_local(&mut self, idx: usize, xform: &Matrix4x4) {
self.hot[idx].orig_local = self.cold[idx].orig * *xform; self.hot[idx].orig_local = self.cold[idx].orig * *xform;
self.hot[idx].dir_inv_local = Vector { self.hot[idx].dir_inv_local = Vector {
co: Float4::splat(1.0) / (self.cold[idx].dir * *xform).co, co: Vec4::splat(1.0) / (self.cold[idx].dir * *xform).co,
}; };
} }
@ -349,7 +349,7 @@ impl RayStack {
/// indicated lanes. /// indicated lanes.
pub fn pop_do_next_task_and_push_rays<F>(&mut self, output_lane_count: usize, mut handle_ray: F) pub fn pop_do_next_task_and_push_rays<F>(&mut self, output_lane_count: usize, mut handle_ray: F)
where where
F: FnMut(usize) -> Bool4, F: FnMut(usize) -> Vec4Mask,
{ {
// Pop the task and do necessary bookkeeping. // Pop the task and do necessary bookkeeping.
let task = self.tasks.pop().unwrap(); let task = self.tasks.pop().unwrap();
@ -372,9 +372,9 @@ impl RayStack {
// Execute task. // Execute task.
for i in task_range.0..task_range.1 { for i in task_range.0..task_range.1 {
let ray_idx = *unsafe { self.lanes[task.lane].idxs.get_unchecked(i) }; let ray_idx = *unsafe { self.lanes[task.lane].idxs.get_unchecked(i) };
let push_mask = handle_ray(ray_idx as usize); let push_mask = handle_ray(ray_idx as usize).bitmask();
for l in 0..output_lane_count { for l in 0..output_lane_count {
if push_mask.get_n(l) { if (push_mask & (1 << l)) != 0 {
self.lanes[l as usize].idxs.push(ray_idx); self.lanes[l as usize].idxs.push(ray_idx);
} }
} }

View File

@ -9,7 +9,7 @@ use std::{
use crossbeam::sync::MsQueue; use crossbeam::sync::MsQueue;
use scoped_threadpool::Pool; use scoped_threadpool::Pool;
use float4::Float4; use glam::Vec4;
use crate::{ use crate::{
accel::ACCEL_NODE_RAY_TESTS, accel::ACCEL_NODE_RAY_TESTS,
@ -374,12 +374,12 @@ pub struct LightPath {
wavelength: f32, wavelength: f32,
next_bounce_ray: Option<Ray>, next_bounce_ray: Option<Ray>,
next_attenuation_fac: Float4, next_attenuation_fac: Vec4,
closure_sample_pdf: f32, closure_sample_pdf: f32,
light_attenuation: Float4, light_attenuation: Vec4,
pending_color_addition: Float4, pending_color_addition: Vec4,
color: Float4, color: Vec4,
} }
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
@ -405,12 +405,12 @@ impl LightPath {
wavelength: wavelength, wavelength: wavelength,
next_bounce_ray: None, next_bounce_ray: None,
next_attenuation_fac: Float4::splat(1.0), next_attenuation_fac: Vec4::splat(1.0),
closure_sample_pdf: 1.0, closure_sample_pdf: 1.0,
light_attenuation: Float4::splat(1.0), light_attenuation: Vec4::splat(1.0),
pending_color_addition: Float4::splat(0.0), pending_color_addition: Vec4::splat(0.0),
color: Float4::splat(0.0), color: Vec4::splat(0.0),
}, },
scene.camera.generate_ray( scene.camera.generate_ray(
image_plane_co.0, image_plane_co.0,
@ -565,7 +565,7 @@ impl LightPath {
// If there's any possible contribution, set up for a // If there's any possible contribution, set up for a
// light ray. // light ray.
if attenuation.e.h_max() <= 0.0 { if attenuation.e.max_element() <= 0.0 {
false false
} else { } else {
// Calculate and store the light that will be contributed // Calculate and store the light that will be contributed
@ -599,7 +599,7 @@ impl LightPath {
}; };
// Check if pdf is zero, to avoid NaN's. // Check if pdf is zero, to avoid NaN's.
if (pdf > 0.0) && (filter.e.h_max() > 0.0) { if (pdf > 0.0) && (filter.e.max_element() > 0.0) {
// Account for the additional light attenuation from // Account for the additional light attenuation from
// this bounce // this bounce
self.next_attenuation_fac = filter.e; self.next_attenuation_fac = filter.e;

View File

@ -2,7 +2,7 @@
use std::f32::consts::PI as PI_32; use std::f32::consts::PI as PI_32;
use float4::Float4; use glam::Vec4;
use crate::{ use crate::{
color::{Color, SpectralSample}, color::{Color, SpectralSample},
@ -492,27 +492,27 @@ mod ggx_closure {
let spectrum_sample = col.to_spectral_sample(wavelength); let spectrum_sample = col.to_spectral_sample(wavelength);
let rev_fresnel = 1.0 - fresnel; let rev_fresnel = 1.0 - fresnel;
let c0 = lerp( let c0 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_0(), hb), schlick_fresnel_from_fac(spectrum_sample.e.x(), hb),
spectrum_sample.e.get_0(), spectrum_sample.e.x(),
rev_fresnel, rev_fresnel,
); );
let c1 = lerp( let c1 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_1(), hb), schlick_fresnel_from_fac(spectrum_sample.e.y(), hb),
spectrum_sample.e.get_1(), spectrum_sample.e.y(),
rev_fresnel, rev_fresnel,
); );
let c2 = lerp( let c2 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_2(), hb), schlick_fresnel_from_fac(spectrum_sample.e.z(), hb),
spectrum_sample.e.get_2(), spectrum_sample.e.z(),
rev_fresnel, rev_fresnel,
); );
let c3 = lerp( let c3 = lerp(
schlick_fresnel_from_fac(spectrum_sample.e.get_3(), hb), schlick_fresnel_from_fac(spectrum_sample.e.w(), hb),
spectrum_sample.e.get_3(), spectrum_sample.e.w(),
rev_fresnel, rev_fresnel,
); );
SpectralSample::from_parts(Float4::new(c0, c1, c2, c3), wavelength) SpectralSample::from_parts(Vec4::new(c0, c1, c2, c3), wavelength)
}; };
// Calculate everything else // Calculate everything else

View File

@ -163,7 +163,7 @@ pub fn surface_point(tri: (Point, Point, Point), bary: (f32, f32, f32)) -> (Poin
+ (tri.2.into_vector().abs() * bary.2)) + (tri.2.into_vector().abs() * bary.2))
* fp_gamma(7)) * fp_gamma(7))
.co .co
.h_max(); .max_element();
(pos, pos_err) (pos, pos_err)
} }

View File

@ -1,10 +0,0 @@
[package]
name = "float4"
version = "0.1.0"
authors = ["Nathan Vegdahl <cessen@cessen.com>"]
edition = "2018"
license = "MIT"
[lib]
name = "float4"
path = "src/lib.rs"

File diff suppressed because it is too large Load Diff

View File

@ -10,5 +10,6 @@ name = "math3d"
path = "src/lib.rs" path = "src/lib.rs"
# Local crate dependencies # Local crate dependencies
[dependencies.float4] [dependencies]
path = "../float4" glam = {git="https://github.com/bitshifter/glam-rs.git", rev="0f314f99", default-features=false, features=["approx"]}
approx = "0.3"

View File

@ -1,29 +1,21 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::ops::{Index, IndexMut, Mul}; use std::ops::{Add, Mul};
use float4::{invert, transpose, Float4}; use approx::RelativeEq;
use glam::{Mat4, Vec4};
use super::Point; use super::Point;
/// A 4x4 matrix, used for transforms /// A 4x4 matrix, used for transforms
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct Matrix4x4 { pub struct Matrix4x4(pub Mat4);
pub values: [Float4; 4],
}
impl Matrix4x4 { impl Matrix4x4 {
/// Creates a new identity matrix /// Creates a new identity matrix
#[inline] #[inline]
pub fn new() -> Matrix4x4 { pub fn new() -> Matrix4x4 {
Matrix4x4 { Matrix4x4(Mat4::identity())
values: [
Float4::new(1.0, 0.0, 0.0, 0.0),
Float4::new(0.0, 1.0, 0.0, 0.0),
Float4::new(0.0, 0.0, 1.0, 0.0),
Float4::new(0.0, 0.0, 0.0, 1.0),
],
}
} }
/// Creates a new matrix with the specified values: /// Creates a new matrix with the specified values:
@ -52,108 +44,37 @@ impl Matrix4x4 {
o: f32, o: f32,
p: f32, p: f32,
) -> Matrix4x4 { ) -> Matrix4x4 {
Matrix4x4 { Matrix4x4(Mat4::new(
values: [ Vec4::new(a, e, i, m),
Float4::new(a, b, c, d), Vec4::new(b, f, j, n),
Float4::new(e, f, g, h), Vec4::new(c, g, k, o),
Float4::new(i, j, k, l), Vec4::new(d, h, l, p),
Float4::new(m, n, o, p), ))
],
}
} }
#[inline] #[inline]
pub fn from_location(loc: Point) -> Matrix4x4 { pub fn from_location(loc: Point) -> Matrix4x4 {
Matrix4x4 { Matrix4x4(Mat4::from_translation(loc.co.truncate()))
values: [
Float4::new(1.0, 0.0, 0.0, loc.x()),
Float4::new(0.0, 1.0, 0.0, loc.y()),
Float4::new(0.0, 0.0, 1.0, loc.z()),
Float4::new(0.0, 0.0, 0.0, 1.0),
],
}
} }
/// Returns whether the matrices are approximately equal to each other. /// Returns whether the matrices are approximately equal to each other.
/// Each corresponding element in the matrices cannot have a relative error /// Each corresponding element in the matrices cannot have a relative
/// exceeding `epsilon`. /// error exceeding epsilon.
#[inline] #[inline]
pub fn aprx_eq(&self, other: Matrix4x4, epsilon: f32) -> bool { pub fn aprx_eq(&self, other: Matrix4x4, epsilon: f32) -> bool {
let mut result = true; self.0.relative_eq(&other.0, std::f32::EPSILON, epsilon)
for y in 0..4 {
for x in 0..4 {
// All of this stuff is just an approximate comparison
// of floating point numbers. See:
// http://floating-point-gui.de/errors/comparison/
// It might be worth breaking this out into a separate funcion,
// but I'm not entirely sure where to put it.
let a = self[y].get_n(x);
let b = other[y].get_n(x);
let aabs = a.abs();
let babs = b.abs();
let diff = (a - b).abs();
if a == b {
} else if (aabs <= std::f32::EPSILON) || (babs <= std::f32::EPSILON) {
result = result && (diff < std::f32::EPSILON);
} else {
let rel = 2.0 * diff / (aabs + babs);
println!("{}", rel);
result = result && (rel < epsilon);
}
}
}
result
} }
/// Returns the transpose of the matrix /// Returns the transpose of the matrix
#[inline] #[inline]
pub fn transposed(&self) -> Matrix4x4 { pub fn transposed(&self) -> Matrix4x4 {
let mut m = *self; Matrix4x4(self.0.transpose())
transpose(&mut m.values);
m
} }
/// Returns the inverse of the Matrix /// Returns the inverse of the Matrix
#[inline] #[inline]
#[allow(clippy::float_cmp)]
pub fn inverse(&self) -> Matrix4x4 { pub fn inverse(&self) -> Matrix4x4 {
let mut m = *self; Matrix4x4(self.0.inverse())
let det = invert(&mut m.values);
debug_assert_ne!(det, 0.0);
m
}
}
impl Index<usize> for Matrix4x4 {
type Output = Float4;
#[inline(always)]
fn index(&self, _index: usize) -> &Float4 {
&self.values[_index]
}
}
impl IndexMut<usize> for Matrix4x4 {
#[inline(always)]
fn index_mut(&mut self, _index: usize) -> &mut Float4 {
&mut self.values[_index]
}
}
impl PartialEq for Matrix4x4 {
#[inline]
fn eq(&self, other: &Matrix4x4) -> bool {
let mut result = true;
for y in 0..4 {
for x in 0..4 {
result = result && (self[y].get_n(x) == other[y].get_n(x));
}
}
result
} }
} }
@ -164,40 +85,32 @@ impl Default for Matrix4x4 {
} }
/// Multiply two matrices together /// Multiply two matrices together
impl Mul<Matrix4x4> for Matrix4x4 { impl Mul for Matrix4x4 {
type Output = Matrix4x4; type Output = Self;
#[inline] #[inline]
fn mul(self, other: Matrix4x4) -> Matrix4x4 { fn mul(self, other: Self) -> Self {
let m = self.transposed(); Self(other.0.mul_mat4(&self.0))
Matrix4x4 {
values: [
Float4::new(
(m[0] * other[0]).h_sum(),
(m[1] * other[0]).h_sum(),
(m[2] * other[0]).h_sum(),
(m[3] * other[0]).h_sum(),
),
Float4::new(
(m[0] * other[1]).h_sum(),
(m[1] * other[1]).h_sum(),
(m[2] * other[1]).h_sum(),
(m[3] * other[1]).h_sum(),
),
Float4::new(
(m[0] * other[2]).h_sum(),
(m[1] * other[2]).h_sum(),
(m[2] * other[2]).h_sum(),
(m[3] * other[2]).h_sum(),
),
Float4::new(
(m[0] * other[3]).h_sum(),
(m[1] * other[3]).h_sum(),
(m[2] * other[3]).h_sum(),
(m[3] * other[3]).h_sum(),
),
],
} }
}
/// Multiply a matrix by a f32
impl Mul<f32> for Matrix4x4 {
type Output = Self;
#[inline]
fn mul(self, other: f32) -> Self {
Self(self.0 * other)
}
}
/// Add two matrices together
impl Add for Matrix4x4 {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Self(self.0 + other.0)
} }
} }
@ -218,22 +131,24 @@ mod tests {
} }
#[test] #[test]
fn aproximate_equality_test() { fn approximate_equality_test() {
let a = Matrix4x4::new(); let a = Matrix4x4::new();
let b = Matrix4x4::new_from_values( let b = Matrix4x4::new_from_values(
1.001, 0.0, 0.0, 0.0, 0.0, 1.001, 0.0, 0.0, 0.0, 0.0, 1.001, 0.0, 0.0, 0.0, 0.0, 1.001, 1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0, 0.0, 1.000001, 0.0, 0.0, 0.0,
0.0, 1.000001,
); );
let c = Matrix4x4::new_from_values( let c = Matrix4x4::new_from_values(
1.003, 0.0, 0.0, 0.0, 0.0, 1.003, 0.0, 0.0, 0.0, 0.0, 1.003, 0.0, 0.0, 0.0, 0.0, 1.003, 1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0, 0.0, 1.000003, 0.0, 0.0, 0.0,
0.0, 1.000003,
); );
let d = Matrix4x4::new_from_values( let d = Matrix4x4::new_from_values(
-1.001, 0.0, 0.0, 0.0, 0.0, -1.001, 0.0, 0.0, 0.0, 0.0, -1.001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0, 0.0, -1.000001, 0.0, 0.0, 0.0,
-1.001, 0.0, -1.000001,
); );
assert!(a.aprx_eq(b, 0.002)); assert!(a.aprx_eq(b, 0.000001));
assert!(!a.aprx_eq(c, 0.002)); assert!(!a.aprx_eq(c, 0.000001));
assert!(!a.aprx_eq(d, 0.002)); assert!(!a.aprx_eq(d, 0.000001));
} }
#[test] #[test]
@ -260,7 +175,7 @@ mod tests {
let b = a.inverse(); let b = a.inverse();
let c = Matrix4x4::new(); let c = Matrix4x4::new();
assert!((a * b).aprx_eq(c, 0.00001)); assert!((dbg!(a * b)).aprx_eq(dbg!(c), 0.0000001));
} }
#[test] #[test]

View File

@ -5,42 +5,44 @@ use std::{
ops::{Add, Div, Mul, Neg, Sub}, ops::{Add, Div, Mul, Neg, Sub},
}; };
use float4::Float4; use glam::Vec4;
use super::{CrossProduct, DotProduct, Matrix4x4, Vector}; use super::{CrossProduct, DotProduct, Matrix4x4, Vector};
/// A surface normal in 3d homogeneous space. /// A surface normal in 3d homogeneous space.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Normal { pub struct Normal {
pub co: Float4, pub co: Vec4,
} }
impl Normal { impl Normal {
#[inline(always)] #[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Normal { pub fn new(x: f32, y: f32, z: f32) -> Normal {
Normal { Normal {
co: Float4::new(x, y, z, 0.0), co: Vec4::new(x, y, z, 0.0),
} }
} }
#[inline(always)] #[inline(always)]
pub fn length(&self) -> f32 { pub fn length(&self) -> f32 {
(self.co * self.co).h_sum().sqrt() self.co.length()
} }
#[inline(always)] #[inline(always)]
pub fn length2(&self) -> f32 { pub fn length2(&self) -> f32 {
(self.co * self.co).h_sum() self.co.length_squared()
} }
#[inline(always)] #[inline(always)]
pub fn normalized(&self) -> Normal { pub fn normalized(&self) -> Normal {
*self / self.length() Normal {
co: self.co.normalize(),
}
} }
#[inline(always)] #[inline(always)]
pub fn into_vector(self) -> Vector { pub fn into_vector(self) -> Vector {
Vector::new(self.co.get_0(), self.co.get_1(), self.co.get_2()) Vector { co: self.co }
} }
#[inline(always)] #[inline(always)]
@ -55,32 +57,32 @@ impl Normal {
#[inline(always)] #[inline(always)]
pub fn x(&self) -> f32 { pub fn x(&self) -> f32 {
self.co.get_0() self.co.x()
} }
#[inline(always)] #[inline(always)]
pub fn y(&self) -> f32 { pub fn y(&self) -> f32 {
self.co.get_1() self.co.y()
} }
#[inline(always)] #[inline(always)]
pub fn z(&self) -> f32 { pub fn z(&self) -> f32 {
self.co.get_2() self.co.z()
} }
#[inline(always)] #[inline(always)]
pub fn set_x(&mut self, x: f32) { pub fn set_x(&mut self, x: f32) {
self.co.set_0(x); self.co.set_x(x);
} }
#[inline(always)] #[inline(always)]
pub fn set_y(&mut self, y: f32) { pub fn set_y(&mut self, y: f32) {
self.co.set_1(y); self.co.set_y(y);
} }
#[inline(always)] #[inline(always)]
pub fn set_z(&mut self, z: f32) { pub fn set_z(&mut self, z: f32) {
self.co.set_2(z); self.co.set_z(z);
} }
} }
@ -129,15 +131,10 @@ impl Mul<Matrix4x4> for Normal {
#[inline] #[inline]
fn mul(self, other: Matrix4x4) -> Normal { fn mul(self, other: Matrix4x4) -> Normal {
let mat = other.inverse().transposed(); let mat = other.0.inverse().transpose();
Normal { let mut co = mat.mul_vec4(self.co);
co: Float4::new( co.set_w(0.0);
(self.co * mat.values[0]).h_sum(), Normal { co: co }
(self.co * mat.values[1]).h_sum(),
(self.co * mat.values[2]).h_sum(),
0.0,
),
}
} }
} }
@ -164,7 +161,7 @@ impl Neg for Normal {
impl DotProduct for Normal { impl DotProduct for Normal {
#[inline(always)] #[inline(always)]
fn dot(self, other: Normal) -> f32 { fn dot(self, other: Normal) -> f32 {
(self.co * other.co).h_sum() self.co.dot(other.co)
} }
} }
@ -172,12 +169,7 @@ impl CrossProduct for Normal {
#[inline] #[inline]
fn cross(self, other: Normal) -> Normal { fn cross(self, other: Normal) -> Normal {
Normal { Normal {
co: Float4::new( co: self.co.truncate().cross(other.co.truncate()).extend(0.0),
(self.co.get_1() * other.co.get_2()) - (self.co.get_2() * other.co.get_1()),
(self.co.get_2() * other.co.get_0()) - (self.co.get_0() * other.co.get_2()),
(self.co.get_0() * other.co.get_1()) - (self.co.get_1() * other.co.get_0()),
0.0,
),
} }
} }
} }
@ -186,6 +178,7 @@ impl CrossProduct for Normal {
mod tests { mod tests {
use super::super::{CrossProduct, DotProduct, Matrix4x4}; use super::super::{CrossProduct, DotProduct, Matrix4x4};
use super::*; use super::*;
use approx::UlpsEq;
#[test] #[test]
fn add() { fn add() {
@ -220,8 +213,10 @@ mod tests {
let m = Matrix4x4::new_from_values( let m = Matrix4x4::new_from_values(
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0, 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
); );
let nm = Normal::new(-19.258825, 5.717648, -1.770588); let mut nm = n * m;
assert!(((n * m) - nm).length2() < 0.00001); nm.co.set_w(0.0);
let nm2 = Normal::new(-19.258825, 5.717648, -1.770588);
assert!(nm.co.ulps_eq(&nm2.co, 0.0, 4));
} }
#[test] #[test]

View File

@ -5,21 +5,21 @@ use std::{
ops::{Add, Mul, Sub}, ops::{Add, Mul, Sub},
}; };
use float4::Float4; use glam::Vec4;
use super::{Matrix4x4, Vector}; use super::{Matrix4x4, Vector};
/// A position in 3d homogeneous space. /// A position in 3d homogeneous space.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Point { pub struct Point {
pub co: Float4, pub co: Vec4,
} }
impl Point { impl Point {
#[inline(always)] #[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Point { pub fn new(x: f32, y: f32, z: f32) -> Point {
Point { Point {
co: Float4::new(x, y, z, 1.0), co: Vec4::new(x, y, z, 1.0),
} }
} }
@ -28,7 +28,7 @@ impl Point {
#[inline(always)] #[inline(always)]
pub fn norm(&self) -> Point { pub fn norm(&self) -> Point {
Point { Point {
co: self.co / self.co.get_3(), co: self.co / self.co.w(),
} }
} }
@ -38,7 +38,7 @@ impl Point {
let n2 = other.norm(); let n2 = other.norm();
Point { Point {
co: n1.co.v_min(n2.co), co: n1.co.min(n2.co),
} }
} }
@ -48,13 +48,15 @@ impl Point {
let n2 = other.norm(); let n2 = other.norm();
Point { Point {
co: n1.co.v_max(n2.co), co: n1.co.max(n2.co),
} }
} }
#[inline(always)] #[inline(always)]
pub fn into_vector(self) -> Vector { pub fn into_vector(self) -> Vector {
Vector::new(self.co.get_0(), self.co.get_1(), self.co.get_2()) let mut v = Vector { co: self.co };
v.co.set_w(0.0);
v
} }
#[inline(always)] #[inline(always)]
@ -69,32 +71,32 @@ impl Point {
#[inline(always)] #[inline(always)]
pub fn x(&self) -> f32 { pub fn x(&self) -> f32 {
self.co.get_0() self.co.x()
} }
#[inline(always)] #[inline(always)]
pub fn y(&self) -> f32 { pub fn y(&self) -> f32 {
self.co.get_1() self.co.y()
} }
#[inline(always)] #[inline(always)]
pub fn z(&self) -> f32 { pub fn z(&self) -> f32 {
self.co.get_2() self.co.z()
} }
#[inline(always)] #[inline(always)]
pub fn set_x(&mut self, x: f32) { pub fn set_x(&mut self, x: f32) {
self.co.set_0(x); self.co.set_x(x);
} }
#[inline(always)] #[inline(always)]
pub fn set_y(&mut self, y: f32) { pub fn set_y(&mut self, y: f32) {
self.co.set_1(y); self.co.set_y(y);
} }
#[inline(always)] #[inline(always)]
pub fn set_z(&mut self, z: f32) { pub fn set_z(&mut self, z: f32) {
self.co.set_2(z); self.co.set_z(z);
} }
} }
@ -144,12 +146,7 @@ impl Mul<Matrix4x4> for Point {
#[inline] #[inline]
fn mul(self, other: Matrix4x4) -> Point { fn mul(self, other: Matrix4x4) -> Point {
Point { Point {
co: Float4::new( co: other.0.mul_vec4(self.co),
(self.co * other.values[0]).h_sum(),
(self.co * other.values[1]).h_sum(),
(self.co * other.values[2]).h_sum(),
(self.co * other.values[3]).h_sum(),
),
} }
} }
} }
@ -163,7 +160,7 @@ mod tests {
fn norm() { fn norm() {
let mut p1 = Point::new(1.0, 2.0, 3.0); let mut p1 = Point::new(1.0, 2.0, 3.0);
let p2 = Point::new(2.0, 4.0, 6.0); let p2 = Point::new(2.0, 4.0, 6.0);
p1.co.set_3(0.5); p1.co.set_w(0.5);
assert_eq!(p2, p1.norm()); assert_eq!(p2, p1.norm());
} }
@ -203,7 +200,7 @@ mod tests {
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 2.0, 3.0, 1.0, 5.0, 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 2.0, 3.0, 1.0, 5.0,
); );
let mut pm = Point::new(15.5, 54.0, 70.0); let mut pm = Point::new(15.5, 54.0, 70.0);
pm.co.set_3(18.5); pm.co.set_w(18.5);
assert_eq!(p * m, pm); assert_eq!(p * m, pm);
} }

View File

@ -5,37 +5,39 @@ use std::{
ops::{Add, Div, Mul, Neg, Sub}, ops::{Add, Div, Mul, Neg, Sub},
}; };
use float4::Float4; use glam::Vec4;
use super::{CrossProduct, DotProduct, Matrix4x4, Normal, Point}; use super::{CrossProduct, DotProduct, Matrix4x4, Normal, Point};
/// A direction vector in 3d homogeneous space. /// A direction vector in 3d homogeneous space.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct Vector { pub struct Vector {
pub co: Float4, pub co: Vec4,
} }
impl Vector { impl Vector {
#[inline(always)] #[inline(always)]
pub fn new(x: f32, y: f32, z: f32) -> Vector { pub fn new(x: f32, y: f32, z: f32) -> Vector {
Vector { Vector {
co: Float4::new(x, y, z, 0.0), co: Vec4::new(x, y, z, 0.0),
} }
} }
#[inline(always)] #[inline(always)]
pub fn length(&self) -> f32 { pub fn length(&self) -> f32 {
(self.co * self.co).h_sum().sqrt() self.co.length()
} }
#[inline(always)] #[inline(always)]
pub fn length2(&self) -> f32 { pub fn length2(&self) -> f32 {
(self.co * self.co).h_sum() self.co.length_squared()
} }
#[inline(always)] #[inline(always)]
pub fn normalized(&self) -> Vector { pub fn normalized(&self) -> Vector {
*self / self.length() Vector {
co: self.co.normalize(),
}
} }
#[inline(always)] #[inline(always)]
@ -65,32 +67,32 @@ impl Vector {
#[inline(always)] #[inline(always)]
pub fn x(&self) -> f32 { pub fn x(&self) -> f32 {
self.co.get_0() self.co.x()
} }
#[inline(always)] #[inline(always)]
pub fn y(&self) -> f32 { pub fn y(&self) -> f32 {
self.co.get_1() self.co.y()
} }
#[inline(always)] #[inline(always)]
pub fn z(&self) -> f32 { pub fn z(&self) -> f32 {
self.co.get_2() self.co.z()
} }
#[inline(always)] #[inline(always)]
pub fn set_x(&mut self, x: f32) { pub fn set_x(&mut self, x: f32) {
self.co.set_0(x); self.co.set_x(x);
} }
#[inline(always)] #[inline(always)]
pub fn set_y(&mut self, y: f32) { pub fn set_y(&mut self, y: f32) {
self.co.set_1(y); self.co.set_y(y);
} }
#[inline(always)] #[inline(always)]
pub fn set_z(&mut self, z: f32) { pub fn set_z(&mut self, z: f32) {
self.co.set_2(z); self.co.set_z(z);
} }
} }
@ -140,12 +142,7 @@ impl Mul<Matrix4x4> for Vector {
#[inline] #[inline]
fn mul(self, other: Matrix4x4) -> Vector { fn mul(self, other: Matrix4x4) -> Vector {
Vector { Vector {
co: Float4::new( co: other.0.mul_vec4(self.co),
(self.co * other.values[0]).h_sum(),
(self.co * other.values[1]).h_sum(),
(self.co * other.values[2]).h_sum(),
(self.co * other.values[3]).h_sum(),
),
} }
} }
} }
@ -173,7 +170,7 @@ impl Neg for Vector {
impl DotProduct for Vector { impl DotProduct for Vector {
#[inline(always)] #[inline(always)]
fn dot(self, other: Vector) -> f32 { fn dot(self, other: Vector) -> f32 {
(self.co * other.co).h_sum() self.co.dot(other.co)
} }
} }
@ -181,12 +178,7 @@ impl CrossProduct for Vector {
#[inline] #[inline]
fn cross(self, other: Vector) -> Vector { fn cross(self, other: Vector) -> Vector {
Vector { Vector {
co: Float4::new( co: self.co.truncate().cross(other.co.truncate()).extend(0.0),
(self.co.get_1() * other.co.get_2()) - (self.co.get_2() * other.co.get_1()),
(self.co.get_2() * other.co.get_0()) - (self.co.get_0() * other.co.get_2()),
(self.co.get_0() * other.co.get_1()) - (self.co.get_1() * other.co.get_0()),
0.0,
),
} }
} }
} }
@ -230,7 +222,7 @@ mod tests {
1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0, 1.0, 2.0, 2.0, 1.5, 3.0, 6.0, 7.0, 8.0, 9.0, 2.0, 11.0, 12.0, 13.0, 7.0, 15.0, 3.0,
); );
let mut vm = Vector::new(14.0, 46.0, 58.0); let mut vm = Vector::new(14.0, 46.0, 58.0);
vm.co.set_3(90.5); vm.co.set_w(90.5);
assert_eq!(v * m, vm); assert_eq!(v * m, vm);
} }

View File

@ -9,6 +9,5 @@ license = "MIT"
name = "spectral_upsampling" name = "spectral_upsampling"
path = "src/lib.rs" path = "src/lib.rs"
# Local crate dependencies [dependencies]
[dependencies.float4] glam = {git="https://github.com/bitshifter/glam-rs.git", rev="0f314f99", default-features=false, features=["approx"]}
path = "../float4"

View File

@ -6,7 +6,7 @@
/// The provides similar color matching as full Jakob, at the expense of /// The provides similar color matching as full Jakob, at the expense of
/// somewhat lower quality spectrums, and the inability to precalculate /// somewhat lower quality spectrums, and the inability to precalculate
/// the coefficents for even more efficient evaluation later on. /// the coefficents for even more efficient evaluation later on.
use float4::Float4; use glam::Vec4;
/// How many polynomial coefficients? /// How many polynomial coefficients?
const RGB2SPEC_N_COEFFS: usize = 3; const RGB2SPEC_N_COEFFS: usize = 3;
@ -15,7 +15,7 @@ const RGB2SPEC_N_COEFFS: usize = 3;
include!(concat!(env!("OUT_DIR"), "/jakob_table_inc.rs")); include!(concat!(env!("OUT_DIR"), "/jakob_table_inc.rs"));
#[inline] #[inline]
pub fn rec709_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 { pub fn rec709_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
small_rgb_to_spectrum_p4( small_rgb_to_spectrum_p4(
REC709_TABLE, REC709_TABLE,
REC709_TABLE_RES, REC709_TABLE_RES,
@ -26,7 +26,7 @@ pub fn rec709_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
} }
#[inline] #[inline]
pub fn rec2020_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 { pub fn rec2020_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
small_rgb_to_spectrum_p4( small_rgb_to_spectrum_p4(
REC2020_TABLE, REC2020_TABLE,
REC2020_TABLE_RES, REC2020_TABLE_RES,
@ -37,7 +37,7 @@ pub fn rec2020_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 {
} }
#[inline] #[inline]
pub fn aces_to_spectrum_p4(lambdas: Float4, rgb: (f32, f32, f32)) -> Float4 { pub fn aces_to_spectrum_p4(lambdas: Vec4, rgb: (f32, f32, f32)) -> Vec4 {
small_rgb_to_spectrum_p4( small_rgb_to_spectrum_p4(
ACES_TABLE, ACES_TABLE,
ACES_TABLE_RES, ACES_TABLE_RES,
@ -55,9 +55,9 @@ fn small_rgb_to_spectrum_p4(
table: &[[(f32, f32, f32); 2]], table: &[[(f32, f32, f32); 2]],
table_res: usize, table_res: usize,
table_mid_value: f32, table_mid_value: f32,
lambdas: Float4, lambdas: Vec4,
rgb: (f32, f32, f32), rgb: (f32, f32, f32),
) -> Float4 { ) -> Vec4 {
// Determine largest RGB component, and calculate the other two // Determine largest RGB component, and calculate the other two
// components scaled for lookups. // components scaled for lookups.
let (i, max_val, x, y) = if rgb.0 > rgb.1 && rgb.0 > rgb.2 { let (i, max_val, x, y) = if rgb.0 > rgb.1 && rgb.0 > rgb.2 {
@ -70,7 +70,7 @@ fn small_rgb_to_spectrum_p4(
if max_val == 0.0 { if max_val == 0.0 {
// If max_val is zero, just return zero. This avoids NaN's from // If max_val is zero, just return zero. This avoids NaN's from
// divide by zero. This is also correct, since it's black. // divide by zero. This is also correct, since it's black.
return Float4::splat(0.0); return Vec4::splat(0.0);
} }
let x = x * 63.0 / max_val; let x = x * 63.0 / max_val;
let y = y * 63.0 / max_val; let y = y * 63.0 / max_val;
@ -90,20 +90,20 @@ fn small_rgb_to_spectrum_p4(
// Convert to SIMD format for faster interpolation. // Convert to SIMD format for faster interpolation.
let a0 = [ let a0 = [
Float4::new(a0[0].0, a0[0].1, a0[0].2, 0.0), Vec4::new(a0[0].0, a0[0].1, a0[0].2, 0.0),
Float4::new(a0[1].0, a0[1].1, a0[1].2, 0.0), Vec4::new(a0[1].0, a0[1].1, a0[1].2, 0.0),
]; ];
let a1 = [ let a1 = [
Float4::new(a1[0].0, a1[0].1, a1[0].2, 0.0), Vec4::new(a1[0].0, a1[0].1, a1[0].2, 0.0),
Float4::new(a1[1].0, a1[1].1, a1[1].2, 0.0), Vec4::new(a1[1].0, a1[1].1, a1[1].2, 0.0),
]; ];
let a2 = [ let a2 = [
Float4::new(a2[0].0, a2[0].1, a2[0].2, 0.0), Vec4::new(a2[0].0, a2[0].1, a2[0].2, 0.0),
Float4::new(a2[1].0, a2[1].1, a2[1].2, 0.0), Vec4::new(a2[1].0, a2[1].1, a2[1].2, 0.0),
]; ];
let a3 = [ let a3 = [
Float4::new(a3[0].0, a3[0].1, a3[0].2, 0.0), Vec4::new(a3[0].0, a3[0].1, a3[0].2, 0.0),
Float4::new(a3[1].0, a3[1].1, a3[1].2, 0.0), Vec4::new(a3[1].0, a3[1].1, a3[1].2, 0.0),
]; ];
// Do interpolation. // Do interpolation.
@ -117,16 +117,14 @@ fn small_rgb_to_spectrum_p4(
// Evaluate the spectral function and return the result. // Evaluate the spectral function and return the result.
if max_val <= table_mid_value { if max_val <= table_mid_value {
rgb2spec_eval_4([c[0].get_0(), c[0].get_1(), c[0].get_2()], lambdas) rgb2spec_eval_4([c[0].x(), c[0].y(), c[0].z()], lambdas) * (1.0 / table_mid_value) * max_val
* (1.0 / table_mid_value)
* max_val
} else if max_val < 1.0 { } else if max_val < 1.0 {
let n = (max_val - table_mid_value) / (1.0 - table_mid_value); let n = (max_val - table_mid_value) / (1.0 - table_mid_value);
let s0 = rgb2spec_eval_4([c[0].get_0(), c[0].get_1(), c[0].get_2()], lambdas); let s0 = rgb2spec_eval_4([c[0].x(), c[0].y(), c[0].z()], lambdas);
let s1 = rgb2spec_eval_4([c[1].get_0(), c[1].get_1(), c[1].get_2()], lambdas); let s1 = rgb2spec_eval_4([c[1].x(), c[1].y(), c[1].z()], lambdas);
(s0 * (1.0 - n)) + (s1 * n) (s0 * (1.0 - n)) + (s1 * n)
} else { } else {
rgb2spec_eval_4([c[1].get_0(), c[1].get_1(), c[1].get_2()], lambdas) * max_val rgb2spec_eval_4([c[1].x(), c[1].y(), c[1].z()], lambdas) * max_val
} }
} }
@ -134,18 +132,22 @@ fn small_rgb_to_spectrum_p4(
// Coefficient -> eval functions // Coefficient -> eval functions
#[inline(always)] #[inline(always)]
fn rgb2spec_fma_4(a: Float4, b: Float4, c: Float4) -> Float4 { fn rgb2spec_fma_4(a: Vec4, b: Vec4, c: Vec4) -> Vec4 {
a.fmadd(b, c) (a * b) + c
} }
fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Float4) -> Float4 { fn rgb2spec_eval_4(coeff: [f32; RGB2SPEC_N_COEFFS], lambda: Vec4) -> Vec4 {
let co0 = Float4::splat(coeff[0]); let co0 = Vec4::splat(coeff[0]);
let co1 = Float4::splat(coeff[1]); let co1 = Vec4::splat(coeff[1]);
let co2 = Float4::splat(coeff[2]); let co2 = Vec4::splat(coeff[2]);
let x = rgb2spec_fma_4(rgb2spec_fma_4(co0, lambda, co1), lambda, co2); let x = rgb2spec_fma_4(rgb2spec_fma_4(co0, lambda, co1), lambda, co2);
let y = Float4::splat(1.0) / (rgb2spec_fma_4(x, x, Float4::splat(1.0))).sqrt(); let y = {
// TODO: replace this with a SIMD sqrt op.
let (x, y, z, w) = rgb2spec_fma_4(x, x, Vec4::splat(1.0)).into();
Vec4::new(x.sqrt(), y.sqrt(), z.sqrt(), w.sqrt()).reciprocal()
};
rgb2spec_fma_4(Float4::splat(0.5) * x, y, Float4::splat(0.5)) rgb2spec_fma_4(Vec4::splat(0.5) * x, y, Vec4::splat(0.5))
} }

View File

@ -6,7 +6,7 @@
use std::f32; use std::f32;
use float4::Float4; use glam::Vec4;
mod meng_spectra_tables; mod meng_spectra_tables;
@ -174,9 +174,9 @@ pub fn spectrum_xyz_to_p(lambda: f32, xyz: (f32, f32, f32)) -> f32 {
/// ///
/// Works on 4 wavelengths at once via SIMD. /// Works on 4 wavelengths at once via SIMD.
#[inline] #[inline]
pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 { pub fn spectrum_xyz_to_p_4(lambdas: Vec4, xyz: (f32, f32, f32)) -> Vec4 {
assert!(lambdas.h_min() >= SPECTRUM_SAMPLE_MIN); assert!(lambdas.min_element() >= SPECTRUM_SAMPLE_MIN);
assert!(lambdas.h_max() <= SPECTRUM_SAMPLE_MAX); assert!(lambdas.max_element() <= SPECTRUM_SAMPLE_MAX);
let inv_norm = xyz.0 + xyz.1 + xyz.2; let inv_norm = xyz.0 + xyz.1 + xyz.2;
let norm = { let norm = {
@ -184,7 +184,7 @@ pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
if norm < f32::MAX { if norm < f32::MAX {
norm norm
} else { } else {
return Float4::splat(0.0); return Vec4::splat(0.0);
} }
}; };
@ -197,7 +197,7 @@ pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
|| uv.1 < 0.0 || uv.1 < 0.0
|| uv.1 >= SPECTRUM_GRID_HEIGHT as f32 || uv.1 >= SPECTRUM_GRID_HEIGHT as f32
{ {
return Float4::splat(0.0); return Vec4::splat(0.0);
} }
let uvi = (uv.0 as i32, uv.1 as i32); let uvi = (uv.0 as i32, uv.1 as i32);
@ -214,53 +214,48 @@ pub fn spectrum_xyz_to_p_4(lambdas: Float4, xyz: (f32, f32, f32)) -> Float4 {
// If the cell has no points, nothing we can do, so return 0.0 // If the cell has no points, nothing we can do, so return 0.0
if num == 0 { if num == 0 {
return Float4::splat(0.0); return Vec4::splat(0.0);
} }
// Normalize lambda to spectrum table index range. // Normalize lambda to spectrum table index range.
let sb: Float4 = (lambdas - Float4::splat(SPECTRUM_SAMPLE_MIN)) let sb: Vec4 = (lambdas - Vec4::splat(SPECTRUM_SAMPLE_MIN))
/ (SPECTRUM_SAMPLE_MAX - SPECTRUM_SAMPLE_MIN) / (SPECTRUM_SAMPLE_MAX - SPECTRUM_SAMPLE_MIN)
* (SPECTRUM_NUM_SAMPLES as f32 - 1.0); * (SPECTRUM_NUM_SAMPLES as f32 - 1.0);
debug_assert!(sb.h_min() >= 0.0); debug_assert!(sb.min_element() >= 0.0);
debug_assert!(sb.h_max() <= SPECTRUM_NUM_SAMPLES as f32); debug_assert!(sb.max_element() <= SPECTRUM_NUM_SAMPLES as f32);
// Get the spectral values for the vertices of the grid cell. // Get the spectral values for the vertices of the grid cell.
// TODO: use integer SIMD intrinsics to make this part faster. // TODO: use integer SIMD intrinsics to make this part faster.
let mut p = [Float4::splat(0.0); 6]; let mut p = [Vec4::splat(0.0); 6];
let sb0: [i32; 4] = [ let sb0: [i32; 4] = [sb.x() as i32, sb.y() as i32, sb.z() as i32, sb.w() as i32];
sb.get_0() as i32,
sb.get_1() as i32,
sb.get_2() as i32,
sb.get_3() as i32,
];
assert!(sb0[0].max(sb0[1]).max(sb0[2].max(sb0[3])) < SPECTRUM_NUM_SAMPLES); assert!(sb0[0].max(sb0[1]).max(sb0[2].max(sb0[3])) < SPECTRUM_NUM_SAMPLES);
let sb1: [i32; 4] = [ let sb1: [i32; 4] = [
(sb.get_0() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1), (sb.x() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.get_1() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1), (sb.y() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.get_2() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1), (sb.z() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
(sb.get_3() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1), (sb.w() as i32 + 1).min(SPECTRUM_NUM_SAMPLES - 1),
]; ];
let sbf = sb - Float4::new(sb0[0] as f32, sb0[1] as f32, sb0[2] as f32, sb0[3] as f32); let sbf = sb - Vec4::new(sb0[0] as f32, sb0[1] as f32, sb0[2] as f32, sb0[3] as f32);
for i in 0..(num as usize) { for i in 0..(num as usize) {
debug_assert!(idx[i] >= 0); debug_assert!(idx[i] >= 0);
let spectrum = &SPECTRUM_DATA_POINTS[idx[i] as usize].spectrum; let spectrum = &SPECTRUM_DATA_POINTS[idx[i] as usize].spectrum;
let p0 = Float4::new( let p0 = Vec4::new(
spectrum[sb0[0] as usize], spectrum[sb0[0] as usize],
spectrum[sb0[1] as usize], spectrum[sb0[1] as usize],
spectrum[sb0[2] as usize], spectrum[sb0[2] as usize],
spectrum[sb0[3] as usize], spectrum[sb0[3] as usize],
); );
let p1 = Float4::new( let p1 = Vec4::new(
spectrum[sb1[0] as usize], spectrum[sb1[0] as usize],
spectrum[sb1[1] as usize], spectrum[sb1[1] as usize],
spectrum[sb1[2] as usize], spectrum[sb1[2] as usize],
spectrum[sb1[3] as usize], spectrum[sb1[3] as usize],
); );
p[i] = p0 * (Float4::splat(1.0) - sbf) + p1 * sbf; p[i] = p0 * (Vec4::splat(1.0) - sbf) + p1 * sbf;
} }
// Linearly interpolate the spectral power of the cell vertices. // Linearly interpolate the spectral power of the cell vertices.
let mut interpolated_p = Float4::splat(0.0); let mut interpolated_p = Vec4::splat(0.0);
if inside { if inside {
// Fast path for normal inner quads: // Fast path for normal inner quads:
let uv2 = (uv.0 - uvi.0 as f32, uv.1 - uvi.1 as f32); let uv2 = (uv.0 - uvi.0 as f32, uv.1 - uvi.1 as f32);