diff --git a/src/crown_gear.rs b/src/crown_gear.rs index 364adc1..66c6ba9 100644 --- a/src/crown_gear.rs +++ b/src/crown_gear.rs @@ -4,7 +4,7 @@ use glam::{Mat3A, Vec3, vec3}; use crate::{ profile::Profile, - render::mesh::{CpuMeshIndexed, Vertex}, + render::mesh::{MeshIndexed, Vertex}, }; #[derive(Debug, Clone, Copy)] @@ -20,17 +20,17 @@ pub struct CrownGearVisual { pub base_thickness: f32, } -pub fn make_mesh( +pub fn make_mesh_smooth( desc: &CrownGearDesc, visual: &CrownGearVisual, sides: usize, color: Vec3, -) -> CpuMeshIndexed { +) -> MeshIndexed { let teeth = 2.0 * desc.pitch_radius; let inner_scale = visual.inner_radius / desc.pitch_radius; let outer_scale = visual.outer_radius / desc.pitch_radius; - let mut mesh = CpuMeshIndexed::new(); + let mut mesh = MeshIndexed::new(); mesh.vertices.reserve_exact(8 * sides); mesh.faces.reserve_exact(8 * sides); for k in 0..sides { @@ -81,3 +81,43 @@ pub fn make_mesh( } mesh } + +pub fn make_mesh_simple( + desc: &CrownGearDesc, + visual: &CrownGearVisual, + sides: usize, +) -> MeshIndexed { + let teeth = 2.0 * desc.pitch_radius; + let inner_scale = visual.inner_radius / desc.pitch_radius; + let outer_scale = visual.outer_radius / desc.pitch_radius; + + let mut mesh = MeshIndexed::new(); + mesh.vertices.reserve_exact(4 * sides); + mesh.faces.reserve_exact(8 * sides); + for k in 0..sides { + let t = k as f32 / sides as f32; + let rot = Mat3A::from_rotation_z(2.0 * PI * t); + + let tooth = t * teeth; + let y = desc.profile.y_from_tooth(tooth); + + let vertex = |x, z| rot * vec3(x, 0.0, z); + mesh.vertices.extend([ + vertex(visual.inner_radius, -visual.base_thickness), + vertex(visual.outer_radius, -visual.base_thickness), + vertex(visual.outer_radius, outer_scale * y), + vertex(visual.inner_radius, inner_scale * y), + ]); + + let base1 = 4 * k; + let base2 = 4 * ((k + 1) % sides); + for j1 in 0..4 { + let j2 = (j1 + 1) % 4; + mesh.faces + .push([base1 + j1, base2 + j1, base1 + j2].map(|index| index.try_into().unwrap())); + mesh.faces + .push([base2 + j2, base1 + j2, base2 + j1].map(|index| index.try_into().unwrap())); + } + } + mesh +} diff --git a/src/lib.rs b/src/lib.rs index 11b45d5..7c85053 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,7 +80,7 @@ impl Core { let depth = Self::create_depth_buffer(&device, pixel_size); let mesh_pipeline = render::mesh::Pipeline::new(&device); - let crown_gear_mesh = crown_gear::make_mesh( + let crown_gear_mesh = crown_gear::make_mesh_simple( &crown_gear::CrownGearDesc { profile: Default::default(), pitch_radius: 24.0, @@ -91,8 +91,10 @@ impl Core { base_thickness: 2.0, }, 600, - vec3(0.5, 0.5, 0.5), - ); + ) + .to_mesh() + .to_standard_mesh(vec3(0.5, 0.5, 0.5)) + .into_indexed(); let mesh_gray = render::mesh::GpuMeshIndexed::new(&device, &crown_gear_mesh); Self::configure_surface(&surface, &device, pixel_size); diff --git a/src/render/mesh.rs b/src/render/mesh.rs index 6b1507e..06e3cdd 100644 --- a/src/render/mesh.rs +++ b/src/render/mesh.rs @@ -4,6 +4,10 @@ use bytemuck::{Pod, Zeroable, bytes_of, cast_slice}; use glam::{Mat4, Vec3}; use wgpu::util::DeviceExt; +pub use cpu::*; + +mod cpu; + static SHADER: &str = include_str!("mesh.wgsl"); #[derive(Debug, Clone, Copy, Zeroable, Pod)] @@ -22,23 +26,6 @@ pub struct Params { pub _zero: f32, } -#[derive(Debug, Clone, Default)] -pub struct CpuMesh { - pub faces: Vec<[Vertex; 3]>, -} - -#[derive(Debug, Clone, Default)] -pub struct CpuMeshIndexed { - pub vertices: Vec, - pub faces: Vec<[u16; 3]>, -} - -impl CpuMeshIndexed { - pub fn new() -> Self { - Self::default() - } -} - pub struct GpuMesh { vertices: wgpu::Buffer, vertex_count: u32, diff --git a/src/render/mesh/cpu.rs b/src/render/mesh/cpu.rs new file mode 100644 index 0000000..4d369b6 --- /dev/null +++ b/src/render/mesh/cpu.rs @@ -0,0 +1,102 @@ +use glam::Vec3; + +use super::Vertex; + +#[derive(Debug, Clone)] +pub struct Mesh { + pub faces: Vec<[T; 3]>, +} + +#[derive(Debug, Clone)] +pub struct MeshIndexed { + pub vertices: Vec, + pub faces: Vec<[u16; 3]>, +} + +pub type CpuMesh = Mesh; +pub type CpuMeshIndexed = MeshIndexed; + +impl Mesh { + pub fn new() -> Self { + Self { faces: Vec::new() } + } + + pub fn concat(meshes: impl IntoIterator) -> Self { + Self { + faces: meshes + .into_iter() + .map(|mesh| mesh.faces) + .flatten() + .collect(), + } + } + + pub fn into_indexed(self) -> MeshIndexed { + if self.faces.len() >= (1 << 16) / 3 { + panic!("too many faces"); + } + let n_faces = self.faces.len() as u16; + MeshIndexed { + vertices: self.faces.into_iter().flatten().collect(), + faces: (0..n_faces) + .map(|face| [3 * face, 3 * face + 1, 3 * face + 2]) + .collect(), + } + } +} + +impl Mesh { + pub fn to_standard_mesh(&self, color: Vec3) -> CpuMesh { + CpuMesh { + faces: self + .faces + .iter() + .map(|face| { + let [a, b, c] = face; + let normal = (b - a).cross(c - a).normalize(); + face.map(|position| Vertex { + position, + color, + normal, + }) + }) + .collect(), + } + } +} + +impl MeshIndexed { + pub fn new() -> Self { + Self { + vertices: Vec::new(), + faces: Vec::new(), + } + } + + pub fn concat(meshes: impl IntoIterator) -> Self { + let mut ret = Self::new(); + for mesh in meshes { + let base = ret.vertices.len() as u16; + ret.vertices.extend(mesh.vertices); + if ret.vertices.len() > 1 << 16 { + panic!("too many vertices"); + } + ret.faces.extend( + mesh.faces + .into_iter() + .map(|face| face.map(|index| base + index)), + ); + } + ret + } + + pub fn to_mesh(&self) -> Mesh { + Mesh { + faces: self + .faces + .iter() + .map(|face| face.map(|index| self.vertices[index as usize])) + .collect(), + } + } +}