diff --git a/core/src/geom/mesh.rs b/core/src/geom/mesh.rs index f04a50cb..a4fc1d72 100644 --- a/core/src/geom/mesh.rs +++ b/core/src/geom/mesh.rs @@ -202,7 +202,7 @@ impl Builder { /// 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 { + pub fn with_vertex_normals(self) -> Builder> { let Mesh { verts, faces } = self.mesh; // Compute weighted face normals... @@ -210,7 +210,7 @@ impl Builder { // TODO If n-gonal faces are supported some day, the cross // product is not proportional to area anymore let [a, b, c] = tri.map(|i| verts[i].pos).0; - (b - a).cross(&(c - a)).to() + (b - a).cross(&(c - a)) }); // ...initialize vertex normals to zero... let mut verts: Vec<_> = verts diff --git a/core/src/geom/prim.rs b/core/src/geom/prim.rs index 623dff04..ed8395f6 100644 --- a/core/src/geom/prim.rs +++ b/core/src/geom/prim.rs @@ -66,7 +66,8 @@ pub struct Sphere(pub Point3, pub f32); /// A surface normal in 3D. // TODO Use distinct type rather than alias -pub type Normal3 = Vec3; +pub type Normal3 = Vec3; + /// A surface normal in 2D. pub type Normal2 = Vec2; @@ -248,10 +249,9 @@ impl Tri> { /// ]); /// assert_approx_eq!(tri.normal(), vec3(0.0, FRAC_1_SQRT_2, -FRAC_1_SQRT_2)); /// ``` - pub fn normal(&self) -> Normal3 { + pub fn normal(&self) -> Normal3 { let [t, u] = self.tangents(); - // TODO add basis parameter to normals - t.cross(&u).normalize_or_zero().to() + t.cross(&u).normalize_or_zero() } /// Returns the plane that `self` lies on. @@ -372,7 +372,7 @@ impl Plane3 { /// /// ``` pub fn from_points(a: Point3, b: Point3, c: Point3) -> Self { - let n = (b - a).cross(&(c - a)).to(); + let n = (b - a).cross(&(c - a)); Self::from_point_and_normal(a, n) } @@ -392,10 +392,9 @@ impl Plane3 { /// assert_eq!(p.offset(), 3.0); /// /// ``` - pub fn from_point_and_normal(pt: Point3, n: Normal3) -> Self { + pub fn from_point_and_normal(pt: Point3, n: Normal3) -> Self { let n = n.normalize(); - let d = dot(&pt.0, &n.0); - Plane::new(n.x(), n.y(), n.z(), d) + Plane::new(n.x(), n.y(), n.z(), dot(&pt.0, &n.0)) } /// Returns the normal vector of `self`. @@ -409,7 +408,7 @@ impl Plane3 { /// assert_eq!(::XY.normal(), Vec3::Z); /// assert_eq!(::YZ.normal(), Vec3::X); #[inline] - pub fn normal(&self) -> Normal3 { + pub fn normal(&self) -> Normal3 { let [a, b, c, _] = self.0.0; let n = vec3(a, b, c); debug_assert!(n.len_sqr().approx_eq(&1.0)); diff --git a/core/src/render/debug.rs b/core/src/render/debug.rs index b1b6646a..36b40bc4 100644 --- a/core/src/render/debug.rs +++ b/core/src/render/debug.rs @@ -90,7 +90,7 @@ pub fn ray<'a, B>(o: Point3, dir: Vec3) -> DbgBatch { pub fn face_normal( tri: Tri>, ) -> DbgBatch { - ray(tri.centroid(), tri.normal().to()) + ray(tri.centroid(), tri.normal()) } /// Draws a visualization of an affine basis. diff --git a/core/src/render/tex.rs b/core/src/render/tex.rs index 5a95c203..f341b3dd 100644 --- a/core/src/render/tex.rs +++ b/core/src/render/tex.rs @@ -80,7 +80,7 @@ pub const fn uv(u: f32, v: f32) -> TexCoord { /// 1 +------+------+------+ /// /// ``` -pub fn cube_map(pos: Vec3, dir: Normal3) -> TexCoord { +pub fn cube_map(pos: Vec3, dir: Normal3<()>) -> TexCoord { // -1.0..1.0 -> 0.0..1.0 let [x, y, z] = (0.5 * pos + splat(0.5)) .clamp(&splat(0.0), &splat(1.0)) diff --git a/demos/src/bin/crates.rs b/demos/src/bin/crates.rs index 555659b3..71d92c25 100644 --- a/demos/src/bin/crates.rs +++ b/demos/src/bin/crates.rs @@ -36,10 +36,10 @@ fn main() { }, ); let crate_shader = shader::new( - |v: Vertex3<(Normal3, TexCoord)>, mvp: &ProjMat3<_>| { + |v: Vertex3<(Normal3<_>, TexCoord)>, mvp: &ProjMat3<_>| { vertex(mvp.apply(&v.pos), v.attrib) }, - |frag: Frag<(Normal3, TexCoord)>| { + |frag: Frag<(Normal3<_>, TexCoord)>| { let (n, uv) = frag.var; let kd = lerp(n.dot(&light_dir).max(0.0), 0.4, 1.0); let col = SamplerClamp.sample(&tex, uv); @@ -137,7 +137,7 @@ fn main() { .expect("should run"); } -fn crates() -> Vec> { +fn crates() -> Vec, TexCoord)>> { let obj = Obj::new(Cube { side_len: 2.0 }.build()); let mut res = vec![]; diff --git a/demos/src/bin/curses.rs b/demos/src/bin/curses.rs index 05112783..e2b1df16 100644 --- a/demos/src/bin/curses.rs +++ b/demos/src/bin/curses.rs @@ -50,7 +50,7 @@ fn main() { |v: Vertex3<_>, mvp: &ProjMat3| { vertex(mvp.apply(&v.pos), v.attrib) }, - |frag: Frag| { + |frag: Frag>| { let [x, y, z] = (frag.var / 2.0 + splat(0.5)).0; rgb(x, y, z).to_color4() }, diff --git a/demos/src/bin/solids.rs b/demos/src/bin/solids.rs index 59d63504..c640f51c 100644 --- a/demos/src/bin/solids.rs +++ b/demos/src/bin/solids.rs @@ -7,8 +7,8 @@ use re::prelude::*; use re::core::{ geom::Polyline, math::{ProjMat3, ProjVec3, color::gray}, - render::cam::Fov, - render::{Model, ModelToWorld, shader}, + render::cam::{Fov, Transform}, + render::{Model, ModelToWorld, View, shader}, }; use re::front::{Frame, minifb::Window}; use re::geom::{io::read_obj, solids::*}; @@ -63,15 +63,17 @@ fn main() { .perspective(Fov::Equiv35mm(28.0), 0.1..1000.0) .viewport(pt2(10, h - 10)..pt2(w - 10, 10)); - type VertexIn = Vertex3; + type VertexIn = Vertex3>; type VertexOut = Vertex; - type Uniform<'a> = (&'a ProjMat3, &'a Mat4); - fn vtx_shader(v: VertexIn, (mvp, spin): Uniform) -> VertexOut { + type NormalMat = Mat3; + type Uniform<'a> = (&'a ProjMat3, &'a NormalMat); + + fn vtx_shader(v: VertexIn, (mvp, n): Uniform) -> VertexOut { // Transform vertex normal - let norm = spin.apply(&v.attrib); + let norm = n.apply(&v.attrib); // Calculate diffuse shading - let diffuse = (norm.z() + 0.2).max(0.2) * 0.8; + let diffuse = (-norm.z() + 0.2).max(0.2) * 0.8; // Visualize normal by mapping to RGB values let [r, g, b] = (0.45 * (v.attrib + splat(1.1))).0; let col = diffuse * rgb(r, g, b); @@ -108,12 +110,17 @@ fn main() { .to::() .then(&cam.world_to_project()); + let normal: NormalMat = spin + .to() + .linear() + .then(&cam.transform.world_to_view().linear()); + let object = &objects[carousel.idx % objects.len()]; Batch { prims: object.faces.clone(), verts: object.verts.clone(), - uniform: (&model_view_project, &spin), + uniform: (&model_view_project, &normal), shader: shader, viewport: cam.viewport, target: frame.buf, @@ -127,7 +134,7 @@ fn main() { // Creates the 14 objects exhibited. #[rustfmt::skip] -fn objects_n(res: u32) -> [Mesh; 14] { +fn objects_n(res: u32) -> [Mesh>; 14] { let segments = res; let sectors = 2 * res; @@ -160,7 +167,7 @@ fn objects_n(res: u32) -> [Mesh; 14] { } // Creates a Lathe mesh. -fn lathe(secs: u32) -> Mesh { +fn lathe(secs: u32) -> Mesh> { let pts = [ (pt2(0.75, -0.5), vec2(1.0, 1.0)), (pt2(0.55, -0.25), vec2(1.0, 0.5)), @@ -176,10 +183,10 @@ fn lathe(secs: u32) -> Mesh { } // Loads the Utah teapot model. -fn teapot() -> Mesh { +fn teapot() -> Mesh> { static TEAPOT: &[u8] = include_bytes!("../../assets/teapot.obj"); read_obj(TEAPOT) - .unwrap() + .expect("valid .obj, included in binary") .transform( &scale(splat(0.4)) .then(&translate(-0.5 * Vec3::Y)) @@ -189,20 +196,20 @@ fn teapot() -> Mesh { } // Loads the Stanford bunny model. -fn bunny() -> Mesh { +fn bunny() -> Mesh> { static BUNNY: &[u8] = include_bytes!("../../assets/bunny.obj"); read_obj::<()>(BUNNY) - .unwrap() + .expect("valid .obj, included in binary") .transform(&scale(splat(0.12)).then(&translate(-Vec3::Y)).to()) .with_vertex_normals() .build() } // Loads the Stanford dragon model. -fn dragon() -> Mesh { +fn dragon() -> Mesh> { static DRAGON: &[u8] = include_bytes!("../../assets/dragon.obj"); read_obj::<()>(DRAGON) - .unwrap() + .expect("valid .obj, included in binary") .with_vertex_normals() .transform( &scale(splat(0.18)) diff --git a/geom/src/io.rs b/geom/src/io.rs index 885f9e6d..1c1a287d 100644 --- a/geom/src/io.rs +++ b/geom/src/io.rs @@ -79,7 +79,7 @@ pub enum Error { pub struct Obj { faces: Vec>, coords: Vec>, - norms: Vec, + norms: Vec>, texcs: Vec, } @@ -235,7 +235,7 @@ impl TryFrom for Builder<()> { o.try_into_with(|_| Some(())) } } -impl TryFrom for Builder { +impl TryFrom for Builder> { type Error = Error; fn try_from(o: Obj) -> Result { @@ -255,7 +255,7 @@ impl TryFrom for Builder { o.try_into_with(|i| i.uv.map(|ti| o.texcs[ti])) } } -impl TryFrom for Builder<(Normal3, TexCoord)> { +impl TryFrom for Builder<(Normal3, TexCoord)> { type Error = Error; fn try_from(o: Obj) -> Result { @@ -335,8 +335,10 @@ fn parse_vector<'a>( Ok(vec3(x, y, z)) } -fn parse_normal<'a>(i: &mut impl Iterator) -> Result { - Ok(parse_vector(i)?.to()) +fn parse_normal<'a>( + i: &mut impl Iterator, +) -> Result> { + Ok(parse_vector(i)?) } fn parse_point<'a>( @@ -506,7 +508,7 @@ v 0.0 -2.0 0.0 v 1.0 2.0 3.0 vn 0.0 0.0 -1.0"; - let m: Mesh = parse_obj(input).unwrap().build(); + let m: Mesh> = parse_obj(input).unwrap().build(); assert_eq!(m.faces.len(), 2); assert_eq!(m.verts.len(), 5); @@ -585,7 +587,7 @@ v 0.0 -2.0 0.0 vt 0.0 -1.0 vn 0.0 0.0 1.0"; - let m = &parse_obj::<(Normal3, TexCoord)>(input) + let m = &parse_obj::<(Normal3<_>, TexCoord)>(input) .unwrap() .build(); assert_eq!(m.faces.len(), 2); diff --git a/geom/src/solids.rs b/geom/src/solids.rs index 52c311bd..f39abdf4 100644 --- a/geom/src/solids.rs +++ b/geom/src/solids.rs @@ -13,6 +13,7 @@ use retrofire_core::math::{Lerp, Vec3}; pub use lathe::*; pub use platonic::*; +use retrofire_core::render::Model; pub trait Build: Sized { fn build(self) -> Mesh; @@ -24,8 +25,8 @@ pub trait Build: Sized { pub struct Icosphere(pub f32, pub u8); -impl Build for Icosphere { - fn build(self) -> Mesh { +impl Build> for Icosphere { + fn build(self) -> Mesh> { #[derive(Default)] struct Tessellator { coords: Vec, @@ -70,7 +71,7 @@ impl Build for Icosphere { } let verts = recurser.coords.iter().map(|&p| { - vertex((p.normalize() * self.0).to().to_pt(), p.normalize()) + vertex((p.normalize() * self.0).to().to_pt(), p.normalize().to()) }); Mesh::new(recurser.faces, verts) diff --git a/geom/src/solids/lathe.rs b/geom/src/solids/lathe.rs index 534488c5..b508cd57 100644 --- a/geom/src/solids/lathe.rs +++ b/geom/src/solids/lathe.rs @@ -11,7 +11,7 @@ use retrofire_core::math::{ Angle, Lerp, Parametric, Point3, Vary, Vec3, polar, pt2, pt3, rotate2, turns, vec2, vec3, }; -use retrofire_core::render::{TexCoord, uv}; +use retrofire_core::render::{Model, TexCoord, uv}; use super::Build; @@ -97,7 +97,7 @@ impl>> Lathe

{ #[inline(never)] pub fn build_with( self, - f: &mut dyn FnMut(Point3, Normal3, TexCoord) -> Vertex3, + f: &mut dyn FnMut(Point3, Normal3, TexCoord) -> Vertex3, ) -> Mesh { let secs = self.sectors as usize; let segs = self.segments as usize; @@ -158,7 +158,7 @@ fn create_faces(secs: usize, verts_per_sec: usize, out: &mut Vec>) { #[inline(never)] fn create_verts( - f: &mut dyn FnMut(Point3, Normal3, TexCoord) -> Vertex3, + f: &mut dyn FnMut(Point3, Normal3, TexCoord) -> Vertex3, pts: &dyn Parametric>, secs: usize, verts_per_sec: usize, @@ -192,9 +192,9 @@ fn create_verts( #[inline(never)] fn make_cap( m: &mut Mesh, - f: &mut dyn FnMut(Point3, Normal3, TexCoord) -> Vertex3, + f: &mut dyn FnMut(Point3, Normal3, TexCoord) -> Vertex3, rg: Range, - n: Normal3, + n: Normal3, ) { let verts = &mut m.verts; let secs = rg.len(); @@ -218,8 +218,8 @@ fn make_cap( // Local trait impls // -impl>> Build for Lathe

{ - fn build(self) -> Mesh { +impl>> Build> for Lathe

{ + fn build(self) -> Mesh> { self.build_with(&mut |p, n, _| vertex(p.to(), n)) } } @@ -228,10 +228,10 @@ impl>> Build for Lathe

{ self.build_with(&mut |p, _, tc| vertex(p.to(), tc)) } } -impl>> Build<(Normal3, TexCoord)> +impl>> Build<(Normal3, TexCoord)> for Lathe

{ - fn build(self) -> Mesh<(Normal3, TexCoord)> { + fn build(self) -> Mesh<(Normal3, TexCoord)> { self.build_with(&mut |p, n, tc| vertex(p.to(), (n, tc))) } } @@ -248,9 +248,9 @@ impl Sphere { } } -impl Build for Sphere { +impl Build> for Sphere { /// Builds a spherical mesh with normals. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { self.lathe().build() } } @@ -271,9 +271,9 @@ impl Torus { Lathe::new(pts, self.major_sectors, self.minor_sectors) } } -impl Build for Torus { +impl Build> for Torus { /// Builds the toroidal mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { self.lathe().build() } } @@ -284,9 +284,9 @@ impl Build for Torus { } } -impl Build for Cylinder { +impl Build> for Cylinder { /// Builds the cylindrical mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { #[rustfmt::skip] let Self { sectors, segments, capped, radius } = self; Cone { @@ -330,9 +330,9 @@ impl Cone { Lathe::new(pts, self.sectors, self.segments).capped(self.capped) } } -impl Build for Cone { +impl Build> for Cone { /// Builds the conical mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { self.lathe().build() } } @@ -381,9 +381,9 @@ impl Capsule { } } -impl Build for Capsule { +impl Build> for Capsule { /// Builds the capsule mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { self.lathe().build() } } @@ -398,7 +398,7 @@ impl Build for Capsule { mod tests { use super::*; - type Mesh = super::Mesh; + type Mesh = super::Mesh>; #[test] fn sphere_verts_faces() { diff --git a/geom/src/solids/platonic.rs b/geom/src/solids/platonic.rs index dda3279e..48648238 100644 --- a/geom/src/solids/platonic.rs +++ b/geom/src/solids/platonic.rs @@ -115,13 +115,13 @@ impl Tetrahedron { ]; /// Builds the tetrahedral mesh. - pub fn build(self) -> Mesh { + pub fn build(self) -> Mesh> { let mut b = Mesh::builder(); for (vs, i) in zip(Self::FACES, 0..) { b.push_face(i * 3, i * 3 + 1, i * 3 + 2); let n = -Self::NORMS[i]; // already unit length for v in vs { - b.push_vert(Self::COORDS[v].to_pt(), n); + b.push_vert(Self::COORDS[v].to_pt(), n.to()); } } b.build() @@ -141,7 +141,7 @@ impl Box { vec3(1.0, 1.0, 0.0), // 0b110 vec3(1.0, 1.0, 1.0), // 0b111 ]; - const NORMS: [Normal3; 6] = [ + const NORMS: [Normal3; 6] = [ vec3(-1.0, 0.0, 0.0), vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0), @@ -190,7 +190,7 @@ impl Box { /// Builds the cuboid mesh. pub fn build_with( self, - mut f: impl FnMut(Point3, Normal3, TexCoord) -> Vertex3, + mut f: impl FnMut(Point3, Normal3, TexCoord) -> Vertex3, ) -> Mesh { let mut b = Mesh::builder(); b.push_faces(Self::FACES); @@ -209,8 +209,8 @@ impl Box { } } -impl Build for Box { - fn build(self) -> Mesh { +impl Build> for Box { + fn build(self) -> Mesh> { self.build_with(|p, n, _| vertex(p, n)) } } @@ -219,8 +219,8 @@ impl Build for Box { self.build_with(|p, _, uv| vertex(p, uv)) } } -impl Build<(Normal3, TexCoord)> for Box { - fn build(self) -> Mesh<(Normal3, TexCoord)> { +impl Build<(Normal3, TexCoord)> for Box { + fn build(self) -> Mesh<(Normal3, TexCoord)> { self.build_with(|p, n, uv| vertex(p, (n, uv))) } } @@ -251,7 +251,7 @@ impl Octahedron { pt3(0.0, 0.0, 1.0), pt3(1.0, 0.0, 0.0), ]; - const NORMS: [Normal3; 8] = [ + const NORMS: [Normal3<()>; 8] = [ vec3(-1.0, -1.0, -1.0), vec3(-1.0, 1.0, -1.0), vec3(-1.0, 1.0, 1.0), @@ -284,16 +284,16 @@ impl Octahedron { ]; } -impl Build for Octahedron { +impl Build> for Octahedron { /// Builds the octahedral mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { let mut b = Mesh::builder(); for (vs, i) in zip(&Self::FACES, 0..) { b.push_face(i * 3, i * 3 + 1, i * 3 + 2); for &vi in vs { let pos = Self::COORDS[Self::VERTS[vi].0]; let n = Self::NORMS[i].normalize(); - b.push_vert(pos, n); + b.push_vert(pos, n.to()); } } b.build() @@ -346,9 +346,9 @@ impl Dodecahedron { const NORMALS: [Vec3; 12] = Icosahedron::COORDS; } -impl Build for Dodecahedron { +impl Build> for Dodecahedron { /// Builds the dodecahedral mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { let mut b = Mesh::builder(); for (face, i) in zip(&Self::FACES, 0..) { @@ -360,7 +360,7 @@ impl Build for Dodecahedron { b.push_face(i5, i5 + 3, i5 + 4); for &j in face { let pos = Self::COORDS[j].normalize().to_pt(); - b.push_vert(pos, n); + b.push_vert(pos, n.to()); } } b.build() @@ -399,16 +399,16 @@ impl Icosahedron { const NORMALS: [Vec3; 20] = Dodecahedron::COORDS; } -impl Build for Icosahedron { +impl Build> for Icosahedron { /// Builds the icosahedral mesh. - fn build(self) -> Mesh { + fn build(self) -> Mesh> { let mut b = Mesh::builder(); for (vs, i) in zip(&Self::FACES, 0..) { let n = Self::NORMALS[i].normalize(); b.push_face(i * 3, i * 3 + 1, i * 3 + 2); for &vi in vs { let pos = Self::COORDS[vi].normalize().to_pt(); - b.push_vert(pos, n); + b.push_vert(pos, n.to()); } } b.build()