From ff6d879dc6f2039ad2fdbcd35c020aaca6f44064 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sat, 23 Jul 2022 12:40:44 -0700 Subject: [PATCH] Put hilbert and morton code into one module. --- src/hilbert.rs | 77 ---------------------- src/main.rs | 3 +- src/morton.rs | 81 ----------------------- src/renderer.rs | 7 +- src/space_fill.rs | 162 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 164 deletions(-) delete mode 100644 src/hilbert.rs delete mode 100644 src/morton.rs create mode 100644 src/space_fill.rs diff --git a/src/hilbert.rs b/src/hilbert.rs deleted file mode 100644 index 4ea1817..0000000 --- a/src/hilbert.rs +++ /dev/null @@ -1,77 +0,0 @@ -#![allow(dead_code)] - -const N: u32 = 1 << 16; - -// Utility function used by the functions below. -fn hil_rot(n: u32, rx: u32, ry: u32, x: &mut u32, y: &mut u32) { - use std::mem; - if ry == 0 { - if rx == 1 { - *x = (n - 1).wrapping_sub(*x); - *y = (n - 1).wrapping_sub(*y); - } - mem::swap(x, y); - } -} - -/// Convert (x,y) to hilbert curve index. -/// -/// x: The x coordinate. Must be a positive integer no greater than 2^16-1. -/// y: The y coordinate. Must be a positive integer no greater than 2^16-1. -/// -/// Returns the hilbert curve index corresponding to the (x,y) coordinates given. -pub fn xy2i(x: u32, y: u32) -> u32 { - assert!(x < N); - assert!(y < N); - - let (mut x, mut y) = (x, y); - let mut d = 0; - let mut s = N >> 1; - while s > 0 { - let rx = if (x & s) > 0 { 1 } else { 0 }; - let ry = if (y & s) > 0 { 1 } else { 0 }; - d += s * s * ((3 * rx) ^ ry); - hil_rot(s, rx, ry, &mut x, &mut y); - - s >>= 1 - } - - d -} - -/// Convert hilbert curve index to (x,y). -/// -/// d: The hilbert curve index. -/// -/// Returns the (x, y) coords at the given index. -pub fn i2xy(d: u32) -> (u32, u32) { - let (mut x, mut y) = (0, 0); - let mut s = 1; - let mut t = d; - while s < N { - let rx = 1 & (t >> 1); - let ry = 1 & (t ^ rx); - hil_rot(s, rx, ry, &mut x, &mut y); - x += s * rx; - y += s * ry; - t >>= 2; - - s <<= 1; - } - - (x, y) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn reversible() { - let d = 54; - let (x, y) = i2xy(d); - let d2 = xy2i(x, y); - - assert_eq!(d, d2); - } -} diff --git a/src/main.rs b/src/main.rs index ebf8468..1627931 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,13 +23,11 @@ mod camera; mod color; mod fp_utils; mod hash; -mod hilbert; mod image; mod lerp; mod light; mod math; mod mis; -mod morton; mod parse; mod ray; mod renderer; @@ -37,6 +35,7 @@ mod sampling; mod scene; mod scramble; mod shading; +mod space_fill; mod surface; mod timer; mod tracer; diff --git a/src/morton.rs b/src/morton.rs deleted file mode 100644 index 9882db1..0000000 --- a/src/morton.rs +++ /dev/null @@ -1,81 +0,0 @@ -#![allow(dead_code)] - -const N: u32 = 1 << 16; - -#[inline(always)] -fn part_1_by_1(mut x: u32) -> u32 { - x &= 0x0000ffff; - x = (x ^ (x << 8)) & 0x00ff00ff; - x = (x ^ (x << 4)) & 0x0f0f0f0f; - x = (x ^ (x << 2)) & 0x33333333; - x = (x ^ (x << 1)) & 0x55555555; - x -} - -#[inline(always)] -fn part_1_by_2(mut x: u32) -> u32 { - x &= 0x000003ff; - x = (x ^ (x << 16)) & 0xff0000ff; - x = (x ^ (x << 8)) & 0x0300f00f; - x = (x ^ (x << 4)) & 0x030c30c3; - x = (x ^ (x << 2)) & 0x09249249; - x -} - -#[inline(always)] -fn compact_1_by_1(mut x: u32) -> u32 { - x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 - x = (x ^ (x >> 1)) & 0x33333333; - x = (x ^ (x >> 2)) & 0x0f0f0f0f; - x = (x ^ (x >> 4)) & 0x00ff00ff; - x = (x ^ (x >> 8)) & 0x0000ffff; - x -} - -#[inline(always)] -fn compact_1_by_2(mut x: u32) -> u32 { - x &= 0x09249249; - x = (x ^ (x >> 2)) & 0x030c30c3; - x = (x ^ (x >> 4)) & 0x0300f00f; - x = (x ^ (x >> 8)) & 0xff0000ff; - x = (x ^ (x >> 16)) & 0x000003ff; - x -} - -/// Convert (x,y) to morton curve index. -/// -/// x: The x coordinate. Must be a positive integer no greater than 2^16-1. -/// y: The y coordinate. Must be a positive integer no greater than 2^16-1. -/// -/// Returns the morton curve index corresponding to the (x,y) coordinates given. -pub fn xy2i(x: u32, y: u32) -> u32 { - debug_assert!(x < N); - debug_assert!(y < N); - - part_1_by_1(x) | (part_1_by_1(y) << 1) -} - -/// Convert morton curve index to (x,y). -/// -/// i: The morton curve index. -/// -/// Returns the (x, y) coords at the given index. -pub fn i2xy(i: u32) -> (u32, u32) { - (compact_1_by_1(i), compact_1_by_1(i >> 1)) -} - -// TODO: 3D morton curves. - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn reversible() { - let d = 54; - let (x, y) = i2xy(d); - let d2 = xy2i(x, y); - - assert_eq!(d, d2); - } -} diff --git a/src/renderer.rs b/src/renderer.rs index 7666ea9..d2d9313 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -13,14 +13,13 @@ use crate::{ accel::ACCEL_NODE_RAY_TESTS, color::{map_0_1_to_wavelength, SpectralSample, XYZ}, fp_utils::robust_ray_origin, - hilbert, image::Image, math::{probit, upper_power_of_two, Float4}, mis::power_heuristic, - morton, ray::{Ray, RayBatch}, scene::{Scene, SceneLightSample}, scramble::owen4, + space_fill::{hilbert, morton}, surface, timer::Timer, tracer::Tracer, @@ -154,7 +153,7 @@ impl<'a> Renderer<'a> { pow2 * pow2 }; for hilbert_d in 0..bucket_n { - let (bx, by) = hilbert::i2xy(hilbert_d); + let (bx, by) = hilbert::decode(hilbert_d); let x = bx as usize * bucket_w; let y = by as usize * bucket_h; @@ -249,7 +248,7 @@ impl<'a> Renderer<'a> { // and golden ratio sampling, we do this up-front here rather than in // the samplers themselves. let si_offset = - owen4(morton::xy2i(x, y), self.seed).wrapping_mul(self.spp as u32); + owen4(morton::encode(x, y), self.seed).wrapping_mul(self.spp as u32); for si in 0..self.spp { let si = (si as u32).wrapping_add(si_offset); diff --git a/src/space_fill.rs b/src/space_fill.rs new file mode 100644 index 0000000..cb6c76a --- /dev/null +++ b/src/space_fill.rs @@ -0,0 +1,162 @@ +//! Space-filling curves and other related functionality. + +#![allow(dead_code)] + +const N: u32 = 1 << 16; + +pub mod hilbert { + use super::hilbert_rotate; + + /// Convert (x,y) to hilbert curve index. + /// + /// x: The x coordinate. Must be no greater than 2^16-1. + /// y: The y coordinate. Must be no greater than 2^16-1. + /// + /// Returns the hilbert curve index corresponding to the (x,y) coordinates given. + pub fn encode(x: u32, y: u32) -> u32 { + assert!(x < super::N); + assert!(y < super::N); + + let (mut x, mut y) = (x, y); + let mut d = 0; + let mut s = super::N >> 1; + while s > 0 { + let rx = if (x & s) > 0 { 1 } else { 0 }; + let ry = if (y & s) > 0 { 1 } else { 0 }; + d += s * s * ((3 * rx) ^ ry); + hilbert_rotate(s, rx, ry, &mut x, &mut y); + + s >>= 1 + } + + d + } + + /// Convert hilbert curve index to (x,y). + /// + /// d: The hilbert curve index. + /// + /// Returns the (x, y) coords at the given index. + pub fn decode(d: u32) -> (u32, u32) { + let (mut x, mut y) = (0, 0); + let mut s = 1; + let mut t = d; + while s < super::N { + let rx = 1 & (t >> 1); + let ry = 1 & (t ^ rx); + hilbert_rotate(s, rx, ry, &mut x, &mut y); + x += s * rx; + y += s * ry; + t >>= 2; + + s <<= 1; + } + + (x, y) + } +} + +pub mod morton { + use super::{compact_1_by_1, part_1_by_1}; + + /// Convert (x,y) to morton curve index. + /// + /// x: The x coordinate. Should be no greater than 2^16-1. + /// y: The y coordinate. Should be no greater than 2^16-1. + /// + /// Returns the morton curve index corresponding to the (x,y) coordinates given. + pub fn encode(x: u32, y: u32) -> u32 { + debug_assert!(x < super::N); + debug_assert!(y < super::N); + + part_1_by_1(x) | (part_1_by_1(y) << 1) + } + + /// Convert morton curve index to (x,y). + /// + /// i: The morton curve index. + /// + /// Returns the (x, y) coords at the given index. + pub fn decode(i: u32) -> (u32, u32) { + (compact_1_by_1(i), compact_1_by_1(i >> 1)) + } +} + +//----------------------------------------------------------- +// Utility functions used in the sub-modules above. + +fn hilbert_rotate(n: u32, rx: u32, ry: u32, x: &mut u32, y: &mut u32) { + use std::mem; + if ry == 0 { + if rx == 1 { + *x = (n - 1).wrapping_sub(*x); + *y = (n - 1).wrapping_sub(*y); + } + mem::swap(x, y); + } +} + +#[inline(always)] +fn part_1_by_1(mut x: u32) -> u32 { + x &= 0x0000ffff; + x = (x ^ (x << 8)) & 0x00ff00ff; + x = (x ^ (x << 4)) & 0x0f0f0f0f; + x = (x ^ (x << 2)) & 0x33333333; + x = (x ^ (x << 1)) & 0x55555555; + x +} + +#[inline(always)] +fn compact_1_by_1(mut x: u32) -> u32 { + x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 + x = (x ^ (x >> 1)) & 0x33333333; + x = (x ^ (x >> 2)) & 0x0f0f0f0f; + x = (x ^ (x >> 4)) & 0x00ff00ff; + x = (x ^ (x >> 8)) & 0x0000ffff; + x +} + +#[inline(always)] +fn part_1_by_2(mut x: u32) -> u32 { + x &= 0x000003ff; + x = (x ^ (x << 16)) & 0xff0000ff; + x = (x ^ (x << 8)) & 0x0300f00f; + x = (x ^ (x << 4)) & 0x030c30c3; + x = (x ^ (x << 2)) & 0x09249249; + x +} + +#[inline(always)] +fn compact_1_by_2(mut x: u32) -> u32 { + x &= 0x09249249; + x = (x ^ (x >> 2)) & 0x030c30c3; + x = (x ^ (x >> 4)) & 0x0300f00f; + x = (x ^ (x >> 8)) & 0xff0000ff; + x = (x ^ (x >> 16)) & 0x000003ff; + x +} + +//------------------------------------------------------------- + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hilbert_reversible() { + let i = 0x4c8587a2; + let (x, y) = hilbert::decode(i); + let i2 = hilbert::encode(x, y); + + assert_eq!(i, i2); + } + + #[test] + fn morton_reversible() { + let i = 0x4c8587a2; + let (x, y) = morton::decode(i); + let i2 = morton::encode(x, y); + + assert_eq!(i, i2); + } +}