generate a faceted mesh

This commit is contained in:
numzero 2026-10-18 03:40:06 +03:00
parent 8c0ae03add
commit cfbd5a41c3
4 changed files with 155 additions and 24 deletions

View File

@ -4,7 +4,7 @@ use glam::{Mat3A, Vec3, vec3};
use crate::{ use crate::{
profile::Profile, profile::Profile,
render::mesh::{CpuMeshIndexed, Vertex}, render::mesh::{MeshIndexed, Vertex},
}; };
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -20,17 +20,17 @@ pub struct CrownGearVisual {
pub base_thickness: f32, pub base_thickness: f32,
} }
pub fn make_mesh( pub fn make_mesh_smooth(
desc: &CrownGearDesc, desc: &CrownGearDesc,
visual: &CrownGearVisual, visual: &CrownGearVisual,
sides: usize, sides: usize,
color: Vec3, color: Vec3,
) -> CpuMeshIndexed { ) -> MeshIndexed {
let teeth = 2.0 * desc.pitch_radius; let teeth = 2.0 * desc.pitch_radius;
let inner_scale = visual.inner_radius / desc.pitch_radius; let inner_scale = visual.inner_radius / desc.pitch_radius;
let outer_scale = visual.outer_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.vertices.reserve_exact(8 * sides);
mesh.faces.reserve_exact(8 * sides); mesh.faces.reserve_exact(8 * sides);
for k in 0..sides { for k in 0..sides {
@ -81,3 +81,43 @@ pub fn make_mesh(
} }
mesh mesh
} }
pub fn make_mesh_simple(
desc: &CrownGearDesc,
visual: &CrownGearVisual,
sides: usize,
) -> MeshIndexed<Vec3> {
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
}

View File

@ -80,7 +80,7 @@ impl Core {
let depth = Self::create_depth_buffer(&device, pixel_size); let depth = Self::create_depth_buffer(&device, pixel_size);
let mesh_pipeline = render::mesh::Pipeline::new(&device); 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 { &crown_gear::CrownGearDesc {
profile: Default::default(), profile: Default::default(),
pitch_radius: 24.0, pitch_radius: 24.0,
@ -91,8 +91,10 @@ impl Core {
base_thickness: 2.0, base_thickness: 2.0,
}, },
600, 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); let mesh_gray = render::mesh::GpuMeshIndexed::new(&device, &crown_gear_mesh);
Self::configure_surface(&surface, &device, pixel_size); Self::configure_surface(&surface, &device, pixel_size);

View File

@ -4,6 +4,10 @@ use bytemuck::{Pod, Zeroable, bytes_of, cast_slice};
use glam::{Mat4, Vec3}; use glam::{Mat4, Vec3};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
pub use cpu::*;
mod cpu;
static SHADER: &str = include_str!("mesh.wgsl"); static SHADER: &str = include_str!("mesh.wgsl");
#[derive(Debug, Clone, Copy, Zeroable, Pod)] #[derive(Debug, Clone, Copy, Zeroable, Pod)]
@ -22,23 +26,6 @@ pub struct Params {
pub _zero: f32, 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<Vertex>,
pub faces: Vec<[u16; 3]>,
}
impl CpuMeshIndexed {
pub fn new() -> Self {
Self::default()
}
}
pub struct GpuMesh { pub struct GpuMesh {
vertices: wgpu::Buffer, vertices: wgpu::Buffer,
vertex_count: u32, vertex_count: u32,

102
src/render/mesh/cpu.rs Normal file
View File

@ -0,0 +1,102 @@
use glam::Vec3;
use super::Vertex;
#[derive(Debug, Clone)]
pub struct Mesh<T: Copy = Vertex> {
pub faces: Vec<[T; 3]>,
}
#[derive(Debug, Clone)]
pub struct MeshIndexed<T: Copy = Vertex> {
pub vertices: Vec<T>,
pub faces: Vec<[u16; 3]>,
}
pub type CpuMesh = Mesh<Vertex>;
pub type CpuMeshIndexed = MeshIndexed<Vertex>;
impl<T: Copy> Mesh<T> {
pub fn new() -> Self {
Self { faces: Vec::new() }
}
pub fn concat(meshes: impl IntoIterator<Item = Self>) -> Self {
Self {
faces: meshes
.into_iter()
.map(|mesh| mesh.faces)
.flatten()
.collect(),
}
}
pub fn into_indexed(self) -> MeshIndexed<T> {
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<Vec3> {
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<T: Copy> MeshIndexed<T> {
pub fn new() -> Self {
Self {
vertices: Vec::new(),
faces: Vec::new(),
}
}
pub fn concat(meshes: impl IntoIterator<Item = Self>) -> 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<T> {
Mesh {
faces: self
.faces
.iter()
.map(|face| face.map(|index| self.vertices[index as usize]))
.collect(),
}
}
}