Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions core/src/geom/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use core::{
};

use crate::{
math::{Linear, Mat4, Point3},
math::{Apply, Linear, Point3},
render::Model,
};

Expand Down Expand Up @@ -111,20 +111,20 @@ fn assert_indices_in_bounds(faces: &[Tri<usize>], len: usize) {
}
}

impl<A> Mesh<A> {
impl<A, B> Mesh<A, B> {
/// Returns a new mesh builder.
pub fn builder() -> Builder<A> {
pub fn builder() -> Builder<A, B> {
Builder::default()
}

/// Consumes `self` and returns a mesh builder with the faces and vertices
/// of `self`.
pub fn into_builder(self) -> Builder<A> {
pub fn into_builder(self) -> Builder<A, B> {
Builder { mesh: self }
}
}

impl<A> Builder<A> {
impl<A, B> Builder<A, B> {
/// Appends a face with the given vertex indices.
///
/// Invalid indices (referring to vertices not yet added) are permitted,
Expand Down Expand Up @@ -164,27 +164,38 @@ impl<A> Builder<A> {
///
/// # Panics
/// If any of the vertex indices in `faces` ≥ `verts.len()`.
pub fn build(self) -> Mesh<A> {
pub fn build(self) -> Mesh<A, B> {
// Sanity checks done by new()
Mesh::new(self.mesh.faces, self.mesh.verts)
}
}

impl<A> Builder<A> {
impl<A, B> Builder<A, B> {
/// Applies the given transform to the position of each vertex.
///
/// This is an eager operation, that is, only vertices *currently*
/// added to the builder are transformed.
pub fn transform(self, tf: &Mat4<Model, Model>) -> Self {
self.warp(|v| vertex(tf.apply(&v.pos), v.attrib))
pub fn transform<C, Tf>(self, tf: &Tf) -> Builder<A, C>
where
Tf: Apply<Point3<B>, Output = Point3<C>>,
{
let Mesh { faces, verts } = self.mesh;
let verts = verts
.into_iter()
.map(|v| vertex(tf.apply(&v.pos), v.attrib))
.collect();
Mesh { faces, verts }.into_builder()
}

/// Applies an arbitrary mapping to each vertex.
///
/// This method can be used for various nonlinear transformations such as
/// twisting or dilation. This is an eager operation, that is, only vertices
/// *currently* added to the builder are transformed.
pub fn warp(mut self, f: impl FnMut(Vertex3<A>) -> Vertex3<A>) -> Self {
pub fn warp(
mut self,
f: impl FnMut(Vertex3<A, B>) -> Vertex3<A, B>,
) -> Self {
self.mesh.verts = self.mesh.verts.into_iter().map(f).collect();
self
}
Expand All @@ -202,7 +213,7 @@ impl<A> Builder<A> {
/// This is an eager operation, that is, only vertices *currently* added
/// to the builder are transformed. The attribute type of the result is
/// `Normal3`; the vertex type it accepts is changed accordingly.
pub fn with_vertex_normals(self) -> Builder<Normal3> {
pub fn with_vertex_normals(self) -> Builder<Normal3, B> {
let Mesh { verts, faces } = self.mesh;

// Compute weighted face normals...
Expand All @@ -225,7 +236,7 @@ impl<A> Builder<A> {
}
// ...and normalize to unit length.
for v in &mut verts {
v.attrib = v.attrib.normalize();
v.attrib = v.attrib.normalize_or_zero();
}

// No need to sanity check again
Expand All @@ -237,7 +248,7 @@ impl<A> Builder<A> {
// Foreign trait impls
//

impl<A: Debug, S: Debug + Default> Debug for Mesh<A, S> {
impl<A: Debug, B: Debug + Default> Debug for Mesh<A, B> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Mesh")
.field("faces", &self.faces)
Expand All @@ -246,7 +257,7 @@ impl<A: Debug, S: Debug + Default> Debug for Mesh<A, S> {
}
}

impl<A: Debug, S: Debug + Default> Debug for Builder<A, S> {
impl<A: Debug, B: Debug + Default> Debug for Builder<A, B> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Builder")
.field("faces", &self.mesh.faces)
Expand All @@ -255,14 +266,14 @@ impl<A: Debug, S: Debug + Default> Debug for Builder<A, S> {
}
}

impl<A, S> Default for Mesh<A, S> {
impl<A, B> Default for Mesh<A, B> {
/// Returns an empty mesh.
fn default() -> Self {
Self { faces: vec![], verts: vec![] }
}
}

impl<A> Default for Builder<A> {
impl<A, B> Default for Builder<A, B> {
/// Returns an empty builder.
fn default() -> Self {
Self { mesh: Mesh::default() }
Expand Down
35 changes: 35 additions & 0 deletions core/src/math/param.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::marker::PhantomData;
use core::ops::Range;

use super::Lerp;
Expand All @@ -13,6 +14,28 @@ pub trait Parametric<T> {
/// the unit interval as well.
#[allow(unused)]
fn eval(&self, t: f32) -> T;

fn iter(&self, step: f32) -> Iter<'_, T, Self> {
Iter {
param: self,
t: 0.0,
step,
end: 1.0,
_pd: PhantomData,
}
}

fn iter_n(&self, n: u32) -> Iter<'_, T, Self> {
self.iter(1.0 / n as f32)
}
}

pub struct Iter<'a, T, P: ?Sized> {
pub(crate) param: &'a P,
pub(crate) t: f32,
pub(crate) step: f32,
pub(crate) end: f32,
pub(crate) _pd: PhantomData<T>,
}

impl<F: Fn(f32) -> T, T> Parametric<T> for F {
Expand Down Expand Up @@ -44,3 +67,15 @@ impl<T: Lerp> Parametric<T> for Range<T> {
self.start.lerp(&self.end, t)
}
}

impl<T, P: Parametric<T>> Iterator for Iter<'_, T, P> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let Self { param, t, step, end, .. } = self;
(*t <= *end + 0.5 * *step).then(|| {
let res = param.eval(*t);
*t += *step;
res
})
}
}
112 changes: 107 additions & 5 deletions core/src/math/spline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
use alloc::vec::Vec;
use core::{array::from_fn, fmt::Debug, marker::PhantomData};

use crate::geom::{Polyline, Ray};
use crate::mat;

use super::{
Affine, Lerp, Linear, Mat4, Parametric, Point, Vary, Vector, inv_lerp,
space::Real,
Affine, Lerp, Linear, Mat4, Parametric, Point, Point3, Vary, Vec3, Vector,
inv_lerp, param, space::Real,
};
use crate::geom::{Polyline, Ray};
use crate::mat;
use crate::math::param::Iter;

/// A cubic Bézier curve, defined by four control points.
///
Expand Down Expand Up @@ -497,6 +497,94 @@ where
}
}

impl<B> BezierSpline<Point3<B>> {
pub fn frame_iter(&self, step: f32) -> impl Iterator<Item = Mat4<B>> {
FrameIter {
iter: self.iter(step),
mat: Mat4::identity(),
_pd: PhantomData,
}
}
}

impl<B> CatmullRomSpline<Point3<B>> {
pub fn frame_iter(&self, step: f32) -> impl Iterator<Item = Mat4<B>> {
FrameIter {
iter: self.iter(step),
mat: Mat4::identity(),
_pd: PhantomData,
}
}
}
impl<B> BSpline<Point3<B>> {
pub fn frame_iter(&self, step: f32) -> impl Iterator<Item = Mat4<B>> {
FrameIter {
iter: self.iter(step),
mat: Mat4::identity(),
_pd: PhantomData,
}
}
}
struct FrameIter<'a, T, S> {
iter: param::Iter<'a, T, S>,
mat: Mat4,
_pd: PhantomData<T>,
}

impl<'a, B, S: Parametric<Point3<B>>> FrameIter<'a, Point3<B>, S> {
fn next_frame(&mut self, fwd: Vec3<B>) -> Option<Mat4<B>> {
let Some(pt) = self.iter.next() else {
return None;
};
let up = 1.0 * self.mat.linear().col_vec(1).to() + 0.0 * Vec3::Y;
let right = up.cross(&fwd).normalize_or_zero();
let up = fwd.cross(&right);

let mat = Mat4::from_affine(right, up, fwd, pt);
self.mat = mat.to().clone();
Some(mat)
}
}
impl<B> Iterator for FrameIter<'_, Point3<B>, BezierSpline<Point3<B>>> {
type Item = Mat4<B>;

fn next(&mut self) -> Option<Mat4<B>> {
let t = self.iter.t;
let fwd = self.iter.param.velocity(t).normalize_or_zero();
self.next_frame(fwd)
}
}

impl<B> Iterator for FrameIter<'_, Point3<B>, HermiteSpline<Point3<B>>> {
type Item = Mat4<B>;

fn next(&mut self) -> Option<Mat4<B>> {
let t = self.iter.t;
let fwd = self.iter.param.velocity(t).normalize_or_zero();
self.next_frame(fwd)
}
}

impl<B> Iterator for FrameIter<'_, Point3<B>, CatmullRomSpline<Point3<B>>> {
type Item = Mat4<B>;

fn next(&mut self) -> Option<Mat4<B>> {
let t = self.iter.t;
let fwd = self.iter.param.velocity(t).normalize_or_zero();
self.next_frame(fwd)
}
}

impl<B> Iterator for FrameIter<'_, Point3<B>, BSpline<Point3<B>>> {
type Item = Mat4<B>;

fn next(&mut self) -> Option<Mat4<B>> {
let t = self.iter.t;
let fwd = self.iter.param.velocity(t).normalize_or_zero();
self.next_frame(fwd)
}
}

impl<T> HermiteSpline<T>
where
T: Affine<Diff: Linear<Scalar = f32> + Clone> + Clone,
Expand Down Expand Up @@ -837,6 +925,20 @@ where
fn eval(&self, s: f32) -> T {
self.eval(s)
}

fn iter(&self, step: f32) -> Iter<'_, T, Self> {
Iter {
param: self,
t: 0.0,
step,
end: self.len(),
_pd: PhantomData,
}
}

fn iter_n(&self, n: u32) -> Iter<'_, T, Self> {
self.iter(self.len() / n as f32)
}
}

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions core/src/render/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ impl<Prim, Vtx, Uni, Shd, Tgt, Ctx> Batch<Prim, Vtx, Uni, Shd, Tgt, Ctx> {
}

/// Clones faces and vertices from a mesh to this batch.
pub fn mesh<A: Clone>(
pub fn mesh<A: Clone, B>(
self,
mesh: &Mesh<A>,
) -> Batch<Tri<usize>, Vertex3<A>, Uni, Shd, Tgt, Ctx> {
mesh: &Mesh<A, B>,
) -> Batch<Tri<usize>, Vertex3<A, B>, Uni, Shd, Tgt, Ctx> {
let prims = mesh.faces.clone();
let verts = mesh.verts.clone();
update!(verts prims; self uniform shader viewport target ctx)
Expand Down
2 changes: 1 addition & 1 deletion core/src/render/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::math::{Vary, polar, turns, vec3};

use super::{Context, Frag, FragmentShader, VertexShader, scene::BBox};

#[derive(Default)]
#[derive(Copy, Clone, Default)]
pub struct Shader;

impl<'a, B> VertexShader<Vertex3<Color4f, B>, &'a ProjMat3<B>> for Shader {
Expand Down
Loading
Loading