diff --git a/src/bevel_gear.rs b/src/bevel_gear.rs new file mode 100644 index 0000000..3ba5ecd --- /dev/null +++ b/src/bevel_gear.rs @@ -0,0 +1,110 @@ +use std::f32::consts::PI; + +use glam::{Mat3A, Vec2, Vec3, vec3}; + +use crate::{profile::Profile, render::mesh::MeshIndexed}; + +#[derive(Debug, Clone, Copy)] +pub struct BevelGearDesc { + pub profile: Profile, + pub teeth: u32, + pub cone_halfangle: f32, +} + +impl BevelGearDesc { + pub fn cutter(&self) -> crate::crown_gear::CrownGearDesc { + crate::crown_gear::CrownGearDesc { + profile: Profile { + addendum: self.profile.dedendum, + dedendum: self.profile.addendum, + pressure_angle: self.profile.pressure_angle, + }, + pitch_radius: self.pitch_radius() / self.cone_halfangle.sin(), + } + } + + fn pitch_radius(&self) -> f32 { + 0.5 * self.teeth as f32 + } + + fn pitch_distance(&self) -> f32 { + self.pitch_radius() / self.cone_halfangle.tan() + } +} + +#[derive(Debug, Clone)] +pub struct BevelGearShape { + points: Vec, +} + +impl BevelGearShape { + fn new_raw(r: f32, n_points: usize) -> Self { + BevelGearShape { + points: vec![r; n_points], + } + } + + pub fn new_blank(desc: &BevelGearDesc, n_points: usize) -> Self { + let r = 0.5 * desc.teeth as f32 + desc.profile.addendum + 0.1; + Self::new_raw(r, n_points) + } + + pub fn to_points(&self) -> impl Iterator { + let angle = 2.0 * PI / self.points.len() as f32; + self.points + .iter() + .enumerate() + .map(move |(k, r)| Vec2::from_angle(k as f32 * angle) * r) + } +} + +#[derive(Debug, Clone, Copy)] +pub enum BevelGearCap { + Flat, + Conical, + Spherical, +} + +#[derive(Debug, Clone, Copy)] +pub struct BevelGearVisual { + pub inner_distance: f32, + pub outer_distance: f32, +} + +pub fn make_mesh( + desc: &BevelGearDesc, + visual: &BevelGearVisual, + shape: &BevelGearShape, +) -> MeshIndexed { + let inner_scale = visual.inner_distance / desc.pitch_distance(); + let outer_scale = visual.outer_distance / desc.pitch_distance(); + + let sides = shape.points.len(); + let mut mesh = MeshIndexed::new(); + mesh.vertices.reserve_exact(2 * sides + 2); + mesh.faces.reserve_exact(4 * sides); + for pt in shape.to_points() { + let pt = vec3(pt.x, pt.y, desc.pitch_distance()); + mesh.vertices.extend([inner_scale * pt, outer_scale * pt]); + } + let caps = mesh.vertices.len(); + mesh.vertices.extend([ + vec3(0., 0., visual.inner_distance), + vec3(0., 0., visual.outer_distance), + ]); + for k in 0..sides { + let base1 = 2 * k; + let base2 = 2 * ((k + 1) % sides); + mesh.faces.extend( + [ + [base1, caps, base2], + [base1, base2, base1 + 1], + [base2 + 1, base1 + 1, base2], + [base2 + 1, caps + 1, base1 + 1], + ] + .map(|face| face.map(|index| u16::try_from(index).unwrap())), + ); + } + + mesh +} diff --git a/src/lib.rs b/src/lib.rs index 7c85053..56aba16 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use std::error::Error; use glam::{Mat4, UVec2, Vec3, Vec4, vec3, vec4}; +mod bevel_gear; mod camera; mod crown_gear; mod profile; @@ -80,6 +81,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_simple( &crown_gear::CrownGearDesc { profile: Default::default(), @@ -95,7 +97,25 @@ impl Core { .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); + + let bevel_gear_desc = bevel_gear::BevelGearDesc { + profile: Default::default(), + teeth: 24, + cone_halfangle: 0.5, + }; + let bevel_gear_mesh = bevel_gear::make_mesh( + &bevel_gear_desc, + &bevel_gear::BevelGearVisual { + inner_distance: 8.0, + outer_distance: 12.0, + }, + &bevel_gear::BevelGearShape::new_blank(&bevel_gear_desc, 60), + ) + .to_mesh() + .to_standard_mesh(vec3(0.5, 0.5, 0.5)) + .into_indexed(); + + let mesh_gray = render::mesh::GpuMeshIndexed::new(&device, &bevel_gear_mesh); Self::configure_surface(&surface, &device, pixel_size);