From 232720a78a8e9f16d3709e80a0156fbe16a6eb7b Mon Sep 17 00:00:00 2001 From: numzero Date: Sat, 17 Oct 2026 22:34:50 +0300 Subject: [PATCH] add crown gear --- src/crown_gear.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 41 ++++++---------------- src/render/mesh.rs | 6 ++++ 3 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 src/crown_gear.rs diff --git a/src/crown_gear.rs b/src/crown_gear.rs new file mode 100644 index 0000000..0ee3be1 --- /dev/null +++ b/src/crown_gear.rs @@ -0,0 +1,84 @@ +use std::f32::consts::PI; + +use glam::{Vec3, vec3}; + +use crate::{ + profile::Profile, + render::mesh::{CpuMeshIndexed, Vertex}, +}; + +pub struct CrownGear { + pub profile: Profile, + pub pitch_radius: f32, + + pub inner_radius: f32, + pub outer_radius: f32, + pub base_thickness: f32, +} + +impl CrownGear { + pub fn make_mesh(&self, sides: usize, color: Vec3) -> CpuMeshIndexed { + let teeth = 2.0 * self.pitch_radius; + let inner_scale = self.inner_radius / self.pitch_radius; + let outer_scale = self.outer_radius / self.pitch_radius; + + let mut mesh = CpuMeshIndexed::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 phi = 2.0 * PI * t; + let (s, c) = phi.sin_cos(); + let tooth = t * teeth; + let y = self.profile.y_from_tooth(tooth); + mesh.vertices.push(Vertex { + position: vec3( + c * self.inner_radius, + s * self.inner_radius, + -self.base_thickness, + ), + color, + normal: Vec3::ZERO, // FIXME calculate the normal + }); + mesh.vertices.push(Vertex { + position: vec3( + c * self.outer_radius, + s * self.outer_radius, + -self.base_thickness, + ), + color, + normal: Vec3::ZERO, // FIXME calculate the normal + }); + mesh.vertices.push(Vertex { + position: vec3( + c * self.outer_radius, + s * self.outer_radius, + outer_scale * y, + ), + color, + normal: Vec3::ZERO, // FIXME calculate the normal + }); + mesh.vertices.push(Vertex { + position: vec3( + c * self.inner_radius, + s * self.inner_radius, + inner_scale * y, + ), + color, + normal: Vec3::ZERO, // FIXME calculate the normal + }); + 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 d1617eb..da85cfd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::error::Error; use glam::{Mat4, UVec2, Vec3, Vec4, vec3, vec4}; mod camera; +mod crown_gear; mod profile; mod render; @@ -40,38 +41,10 @@ pub struct Core { surface: wgpu::Surface<'static>, mesh_pipeline: render::mesh::Pipeline, - mesh_color: render::mesh::GpuMeshIndexed, mesh_gray: render::mesh::GpuMeshIndexed, depth: wgpu::Texture, } -fn cube(a: f32, b: f32) -> render::mesh::CpuMeshIndexed { - let gray_level = (b + a) / 2.; - let gray = Vec3::splat(gray_level); - let color_level = (b - a) / 2.; - let mut mesh = render::mesh::CpuMeshIndexed::default(); - for (u, v, w) in [ - (Vec3::X, Vec3::Y, Vec3::Z), - (Vec3::Y, Vec3::Z, Vec3::X), - (Vec3::Z, Vec3::X, Vec3::Y), - ] { - for m in [1., -1.] { - let base = mesh.vertices.len() as u16; - for offset in [m * v + u, m * v - u, -m * v - u, -m * v + u] { - let vertex = render::mesh::Vertex { - position: (m * w + offset), - color: gray + color_level * m * w, - normal: m * w, - }; - mesh.vertices.push(vertex); - } - mesh.faces.push([base, base + 1, base + 2]); - mesh.faces.push([base, base + 2, base + 3]); - } - } - mesh -} - fn srgb_to_linear(color: Vec3) -> Vec3 { color.map(|x| { if x > 0.04045 { @@ -107,8 +80,15 @@ impl Core { let depth = Self::create_depth_buffer(&device, pixel_size); let mesh_pipeline = render::mesh::Pipeline::new(&device); - let mesh_color = render::mesh::GpuMeshIndexed::new(&device, &cube(0.2, 0.8)); - let mesh_gray = render::mesh::GpuMeshIndexed::new(&device, &cube(0.7, 0.7)); + let crown_gear_mesh = crown_gear::CrownGear { + profile: Default::default(), + pitch_radius: 24.0, + inner_radius: 16.0, + outer_radius: 30.0, + base_thickness: 2.0, + } + .make_mesh(600, vec3(0.5, 0.5, 0.5)); + let mesh_gray = render::mesh::GpuMeshIndexed::new(&device, &crown_gear_mesh); Self::configure_surface(&surface, &device, pixel_size); @@ -117,7 +97,6 @@ impl Core { queue, surface, mesh_pipeline, - mesh_color, mesh_gray, depth, } diff --git a/src/render/mesh.rs b/src/render/mesh.rs index f1d0530..6b1507e 100644 --- a/src/render/mesh.rs +++ b/src/render/mesh.rs @@ -33,6 +33,12 @@ pub struct CpuMeshIndexed { pub faces: Vec<[u16; 3]>, } +impl CpuMeshIndexed { + pub fn new() -> Self { + Self::default() + } +} + pub struct GpuMesh { vertices: wgpu::Buffer, vertex_count: u32,