Make bilinear patch splitting a little smarter.

This way it doesn't just keep splitting on the same axis
repeatedly, creating narrow, long patches..
This commit is contained in:
Nathan Vegdahl 2019-08-01 13:49:44 +09:00
parent 95e7d6bdea
commit ecbdf5d609
2 changed files with 99 additions and 42 deletions

View File

@ -3,7 +3,7 @@
use math3d::{Matrix4x4, Normal, Point, Vector}; use math3d::{Matrix4x4, Normal, Point, Vector};
/// Trait for allowing a type to be linearly interpolated. /// Trait for allowing a type to be linearly interpolated.
pub trait Lerp { pub trait Lerp: Copy {
fn lerp(self, other: Self, alpha: f32) -> Self; fn lerp(self, other: Self, alpha: f32) -> Self;
} }
@ -17,7 +17,7 @@ pub fn lerp<T: Lerp>(a: T, b: T, alpha: f32) -> T {
/// Interpolates a slice of data as if each adjecent pair of elements /// Interpolates a slice of data as if each adjecent pair of elements
/// represent a linear segment. /// represent a linear segment.
pub fn lerp_slice<T: Lerp + Copy>(s: &[T], alpha: f32) -> T { pub fn lerp_slice<T: Lerp>(s: &[T], alpha: f32) -> T {
debug_assert!(!s.is_empty()); debug_assert!(!s.is_empty());
debug_assert!(alpha >= 0.0); debug_assert!(alpha >= 0.0);
debug_assert!(alpha <= 1.0); debug_assert!(alpha <= 1.0);
@ -73,6 +73,33 @@ impl<T: Lerp> Lerp for (T, T) {
} }
} }
impl<T: Lerp> Lerp for [T; 2] {
fn lerp(self, other: Self, alpha: f32) -> Self {
[self[0].lerp(other[0], alpha), self[1].lerp(other[1], alpha)]
}
}
impl<T: Lerp> Lerp for [T; 3] {
fn lerp(self, other: Self, alpha: f32) -> Self {
[
self[0].lerp(other[0], alpha),
self[1].lerp(other[1], alpha),
self[2].lerp(other[2], alpha),
]
}
}
impl<T: Lerp> Lerp for [T; 4] {
fn lerp(self, other: Self, alpha: f32) -> Self {
[
self[0].lerp(other[0], alpha),
self[1].lerp(other[1], alpha),
self[2].lerp(other[2], alpha),
self[3].lerp(other[3], alpha),
]
}
}
impl Lerp for glam::Vec4 { impl Lerp for glam::Vec4 {
fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 { fn lerp(self, other: glam::Vec4, alpha: f32) -> glam::Vec4 {
(self * (1.0 - alpha)) + (other * alpha) (self * (1.0 - alpha)) + (other * alpha)

View File

@ -1,5 +1,8 @@
use super::{point_order, PointOrder, Splitable, MAX_EDGE_DICE}; use super::{point_order, PointOrder, Splitable, MAX_EDGE_DICE};
use crate::{lerp::lerp, math::Point}; use crate::{
lerp::{lerp, lerp_slice},
math::Point,
};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct BilinearPatch<'a> { pub struct BilinearPatch<'a> {
@ -41,8 +44,8 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
where where
F: Fn(Point, Point) -> f32, F: Fn(Point, Point) -> f32,
{ {
// Get the points of the sub-patch. // Get the points of the sub-patch at time 0.5.
let patch = self.original.control_points[0]; let patch = lerp_slice(self.original.control_points, 0.5);
let points = [ let points = [
bilerp_point(patch, self.clip[0]), bilerp_point(patch, self.clip[0]),
bilerp_point(patch, self.clip[1]), bilerp_point(patch, self.clip[1]),
@ -58,52 +61,79 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
metric(points[3], points[0]), metric(points[3], points[0]),
]; ];
// Do the split, if needed. // Find an edge to split, if any.
for i in 0..4 { let split_edge_index = {
if self.must_split[i] || edge_metric[i] > MAX_EDGE_DICE as f32 { // Find the "longest" edge in terms of the metric.
let edge_1 = (i, (i + 1) % 4); let (edge_i, m) = edge_metric
let edge_2 = ((i + 2) % 4, (i + 3) % 4); .iter()
let new_must_split = { .enumerate()
let mut new_must_split = self.must_split; .max_by(|a, b| {
new_must_split[edge_1.0] = false; if a.1 > b.1 {
new_must_split[edge_2.0] = false; std::cmp::Ordering::Greater
new_must_split } else {
}; std::cmp::Ordering::Less
}
})
.unwrap();
let midpoint_1 = lerp(self.clip[edge_1.0], self.clip[edge_1.1], 0.5); // Return an edge to split, if a split is needed.
let midpoint_2 = { if *m > MAX_EDGE_DICE as f32 {
let alpha = if self.must_split[edge_2.0] // Split needed because of over-long edge.
|| edge_metric[edge_2.0] > MAX_EDGE_DICE as f32 Some(edge_i)
{ } else {
// Return the the first edge with "must_split" set, if any.
// Otherwise returns `None`.
self.must_split
.iter()
.enumerate()
.find(|a| *a.1)
.map(|a| a.0)
}
};
// Do the split if needed
if let Some(i) = split_edge_index {
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 0.5
} else { } else {
let edge_2_dice_rate = edge_metric[edge_2.0].ceil(); let edge_2_dice_rate = edge_metric[edge_2.0].ceil();
(edge_2_dice_rate * 0.5).floor() / edge_2_dice_rate (edge_2_dice_rate * 0.5).floor() / edge_2_dice_rate
}; };
match point_order(points[edge_2.0], points[edge_2.1]) { 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::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), PointOrder::Flip => lerp(self.clip[edge_2.1], self.clip[edge_2.0], alpha),
} }
}; };
// Build the new sub-patches // Build the new sub-patches
let mut patch_1 = BilinearSubPatch { let mut patch_1 = BilinearSubPatch {
original: self.original, original: self.original,
clip: self.clip, clip: self.clip,
must_split: new_must_split, must_split: new_must_split,
}; };
let mut patch_2 = patch_1; let mut patch_2 = patch_1;
patch_1.clip[edge_1.1] = midpoint_1; patch_1.clip[edge_1.1] = midpoint_1;
patch_1.clip[edge_2.0] = midpoint_2; patch_1.clip[edge_2.0] = midpoint_2;
patch_2.clip[edge_1.0] = midpoint_1; patch_2.clip[edge_1.0] = midpoint_1;
patch_2.clip[edge_2.1] = midpoint_2; patch_2.clip[edge_2.1] = midpoint_2;
return Some((patch_1, patch_2)); Some((patch_1, patch_2))
} } else {
// No split
None
} }
// No splitting needed to be done.
None
} }
} }