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,9 +61,38 @@ 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_i, m) = edge_metric
.iter()
.enumerate()
.max_by(|a, b| {
if a.1 > b.1 {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Less
}
})
.unwrap();
// Return an edge to split, if a split is needed.
if *m > MAX_EDGE_DICE as f32 {
// Split needed because of over-long edge.
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_1 = (i, (i + 1) % 4);
let edge_2 = ((i + 2) % 4, (i + 3) % 4); let edge_2 = ((i + 2) % 4, (i + 3) % 4);
let new_must_split = { let new_must_split = {
@ -72,9 +104,8 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
let midpoint_1 = lerp(self.clip[edge_1.0], self.clip[edge_1.1], 0.5); let midpoint_1 = lerp(self.clip[edge_1.0], self.clip[edge_1.1], 0.5);
let midpoint_2 = { let midpoint_2 = {
let alpha = if self.must_split[edge_2.0] let alpha =
|| edge_metric[edge_2.0] > MAX_EDGE_DICE as f32 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();
@ -99,11 +130,10 @@ impl<'a> Splitable for BilinearSubPatch<'a> {
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
// No splitting needed to be done.
None None
} }
} }
}