From 0c99ce8227ea99f7a8eea20f9799d31c64e74478 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Wed, 31 Jul 2019 22:46:06 +0900 Subject: [PATCH] Playing around with surface patch implementation. Trying out ideas. --- src/surface/bilinear_patch.rs | 82 ++++++++++++++++++++++++++++++++--- src/surface/mod.rs | 37 +++++++++++++++- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/src/surface/bilinear_patch.rs b/src/surface/bilinear_patch.rs index dcacdf9..c85fbaf 100644 --- a/src/surface/bilinear_patch.rs +++ b/src/surface/bilinear_patch.rs @@ -1,10 +1,8 @@ -use super::Splitable; -use crate::math::Point; +use super::{point_order, PointOrder, Splitable, MAX_EDGE_DICE}; +use crate::{lerp::lerp, math::Point}; #[derive(Debug, Copy, Clone)] pub struct BilinearPatch<'a> { - time_sample_count: usize, - // The control points are stored in clockwise order, like this: // u -----> // v 0 1 @@ -25,6 +23,12 @@ pub struct BilinearPatch<'a> { must_split: [bool; 4], } +fn bilerp_point(patch: [Point; 4], uv: (f32, f32)) -> Point { + let a = lerp(patch[0], patch[1], uv.0); + let b = lerp(patch[3], patch[2], uv.0); + lerp(a, b, uv.1) +} + #[derive(Debug, Copy, Clone)] pub struct BilinearSubPatch<'a> { original: &'a BilinearPatch<'a>, @@ -33,7 +37,73 @@ pub struct BilinearSubPatch<'a> { } impl<'a> Splitable for BilinearSubPatch<'a> { - fn split(&self /* TODO: splitting criteria. */) -> Option<(Self, Self)> { - unimplemented!() + fn split(&self, metric: F) -> Option<(Self, Self)> + where + F: Fn(Point, Point) -> f32, + { + // Get the points of the sub-patch. + let patch = self.original.control_points[0]; + let points = [ + bilerp_point(patch, self.clip[0]), + bilerp_point(patch, self.clip[1]), + bilerp_point(patch, self.clip[2]), + bilerp_point(patch, self.clip[3]), + ]; + + // Calculate edge metrics. + let edge_metric = [ + metric(points[0], points[1]), + metric(points[1], points[2]), + metric(points[2], points[3]), + metric(points[3], points[0]), + ]; + + // Do the split, if needed. + for i in 0..4 { + if self.must_split[i] || edge_metric[i] > MAX_EDGE_DICE as f32 { + let edge_1 = (i, (i + 1) % 4); + let edge_2 = ((i + 2) % 4, (i + 3) % 4); + let new_must_split = { + let mut new_must_split = self.must_split; + new_must_split[edge_1.0] = false; + new_must_split[edge_2.0] = false; + new_must_split + }; + + let midpoint_1 = lerp(self.clip[edge_1.0], self.clip[edge_1.1], 0.5); + let midpoint_2 = { + let alpha = if self.must_split[edge_2.0] + || edge_metric[edge_2.0] > MAX_EDGE_DICE as f32 + { + 0.5 + } else { + let edge_2_dice_rate = edge_metric[edge_2.0].ceil(); + (edge_2_dice_rate * 0.5).floor() / edge_2_dice_rate + }; + + match point_order(points[edge_2.0], points[edge_2.1]) { + PointOrder::AsIs => lerp(self.clip[edge_2.0], self.clip[edge_2.1], alpha), + PointOrder::Flip => lerp(self.clip[edge_2.1], self.clip[edge_2.0], alpha), + } + }; + + // Build the new sub-patches + let mut patch_1 = BilinearSubPatch { + original: self.original, + clip: self.clip, + must_split: new_must_split, + }; + let mut patch_2 = patch_1; + patch_1.clip[edge_1.1] = midpoint_1; + patch_1.clip[edge_2.0] = midpoint_2; + patch_2.clip[edge_1.0] = midpoint_1; + patch_2.clip[edge_2.1] = midpoint_2; + + return Some((patch_1, patch_2)); + } + } + + // No splitting needed to be done. + None } } diff --git a/src/surface/mod.rs b/src/surface/mod.rs index 65d9114..9c6f18d 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -16,6 +16,8 @@ use crate::{ shading::SurfaceShader, }; +const MAX_EDGE_DICE: u32 = 128; + pub trait Surface: Boundable + Debug + Sync { fn intersect_rays( &self, @@ -29,7 +31,40 @@ pub trait Surface: Boundable + Debug + Sync { pub trait Splitable: Copy { /// Splits the surface into two pieces if necessary. - fn split(&self /* TODO: splitting criteria. */) -> Option<(Self, Self)>; + fn split(&self, metric: F) -> Option<(Self, Self)> + where + F: Fn(Point, Point) -> f32; +} + +#[derive(Debug, Copy, Clone)] +pub enum PointOrder { + AsIs, + Flip, +} + +pub fn point_order(p1: Point, p2: Point) -> PointOrder { + let max_diff = { + let v = p2 - p1; + let v_abs = v.abs(); + + let mut diff = v.x(); + let mut diff_abs = v_abs.x(); + if v_abs.y() > diff_abs { + diff = v.y(); + diff_abs = v_abs.y(); + } + if v_abs.z() > diff_abs { + diff = v.z(); + } + + diff + }; + + if max_diff <= 0.0 { + PointOrder::AsIs + } else { + PointOrder::Flip + } } #[derive(Debug, Copy, Clone)]