From 03e91fac28c62b0a3fa2433838f8eebc57003625 Mon Sep 17 00:00:00 2001 From: numzero Date: Sun, 17 Nov 2024 18:53:58 +0300 Subject: [PATCH] draw tube as translucent solid --- src/bin/wireframe/main.rs | 36 ++++++-- src/bin/wireframe/mesh.wgsl | 34 ++++++++ src/bin/wireframe/meshes.rs | 168 ++++++++++++++++++++++++++++++++++++ src/bin/wireframe/scene.rs | 99 ++++++++++++--------- 4 files changed, 287 insertions(+), 50 deletions(-) create mode 100644 src/bin/wireframe/mesh.wgsl create mode 100644 src/bin/wireframe/meshes.rs diff --git a/src/bin/wireframe/main.rs b/src/bin/wireframe/main.rs index 60f9a28..813879f 100644 --- a/src/bin/wireframe/main.rs +++ b/src/bin/wireframe/main.rs @@ -10,6 +10,7 @@ use winit::{ mod camera; mod lines; +mod meshes; mod scene; mod viewport; @@ -18,11 +19,17 @@ mod viewport; // * Y: left // * Z: up -fn prepare_scene(device: &wgpu::Device) -> Vec { - scene::build() +fn prepare_scene(device: &wgpu::Device) -> (Vec, Vec) { + let (meshes, lines) = scene::build(); + let meshes = meshes + .into_iter() + .map(|mesh| meshes::Mesh::new_list(device, meshes::Attrs { color: mesh.color }, mesh.tris)) + .collect(); + let lines = lines .into_iter() .map(|line| lines::Line::new_strip(device, lines::Attrs { color: line.color }, line.pts)) - .collect() + .collect(); + (meshes, lines) } #[cfg(any())] @@ -160,8 +167,10 @@ struct State<'a> { cam_loc: camctl::CameraLocation, cam_obj: camera::Camera, line_rend: lines::LineRenderer, + mesh_rend: meshes::Renderer, - scene: Vec, + lines: Vec, + meshes: Vec, window: &'a Window, } @@ -214,9 +223,16 @@ impl<'a> State<'a> { }; let cam_obj = camera::Camera::new(&device); - let line_rend = lines::LineRenderer::new(&device, cam_obj.bind_group_layout(), viewport.format(), depth, msaa); + let line_rend = lines::LineRenderer::new( + &device, + cam_obj.bind_group_layout(), + viewport.format(), + depth.clone(), + msaa, + ); + let mesh_rend = meshes::Renderer::new(&device, cam_obj.bind_group_layout(), viewport.format(), depth, msaa); - let scene = prepare_scene(&device); + let (meshes, lines) = prepare_scene(&device); let fps = fps::Counter::new(); @@ -225,12 +241,14 @@ impl<'a> State<'a> { queue, viewport, line_rend, + mesh_rend, kbd, fps, cam_loc, cam_obj, t1, - scene, + lines, + meshes, window, } } @@ -266,8 +284,10 @@ impl<'a> State<'a> { .set_title(&format!("Space Refraction ({:.1} FPS)", self.fps.get())); self.viewport .render_single_pass(&self.device, &self.queue, |mut render_pass| { + self.mesh_rend + .render(&mut render_pass, self.cam_obj.bind_group(), self.meshes.iter()); self.line_rend - .render(&mut render_pass, self.cam_obj.bind_group(), self.scene.iter()); + .render(&mut render_pass, self.cam_obj.bind_group(), self.lines.iter()); }) } } diff --git a/src/bin/wireframe/mesh.wgsl b/src/bin/wireframe/mesh.wgsl new file mode 100644 index 0000000..9e92181 --- /dev/null +++ b/src/bin/wireframe/mesh.wgsl @@ -0,0 +1,34 @@ +struct CameraUniform { + mvp: mat4x4, + scale: vec2, +} +@group(0) @binding(0) +var camera: CameraUniform; + +struct MeshUniform { + color: vec4, +} +var mesh: MeshUniform; + +struct VertexInput { + @location(0) position: vec3, + @location(1) normal: vec3, +} + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) vertex_color: vec4, +} + +@vertex +fn vs_main(ver: VertexInput) -> VertexOutput { + var out: VertexOutput; + out.vertex_color = mesh.color; + out.clip_position = camera.mvp * vec4(ver.position, 1.); + return out; +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return in.vertex_color; +} diff --git a/src/bin/wireframe/meshes.rs b/src/bin/wireframe/meshes.rs new file mode 100644 index 0000000..e0e1798 --- /dev/null +++ b/src/bin/wireframe/meshes.rs @@ -0,0 +1,168 @@ +use std::mem; + +use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; +use glam::{Vec3, Vec4}; +use wgpu::util::DeviceExt as _; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Vertex { + pub position: [f32; 3], + pub normal: [f32; 3], +} + +#[repr(C)] +#[derive(Copy, Clone, Pod, Zeroable)] +struct PushConsts { + pub color: [f32; 4], +} + +#[derive(Copy, Clone)] +pub struct Attrs { + pub color: Vec4, +} + +impl Attrs { + fn consts(&self) -> PushConsts { + PushConsts { + color: self.color.to_array(), + } + } +} + +pub struct Mesh { + consts: PushConsts, + npoints: u32, + buf: wgpu::Buffer, +} + +impl Mesh { + pub fn new_list(device: &wgpu::Device, attrs: Attrs, tris: Vec<(Vec3, Vec3, Vec3)>) -> Self { + let data: Vec = tris + .into_iter() + .flat_map(|(a, b, c)| { + let n = (b - a).cross(c - a).normalize(); + [ + Vertex { + position: a.into(), + normal: n.into(), + }, + Vertex { + position: b.into(), + normal: n.into(), + }, + Vertex { + position: c.into(), + normal: n.into(), + }, + ] + }) + .collect(); + let buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Mesh Vertex Buffer"), + contents: cast_slice(&data), + usage: wgpu::BufferUsages::VERTEX, + }); + Mesh { + consts: attrs.consts(), + npoints: data.len() as u32, + buf, + } + } +} + +pub struct Renderer { + pipeline: wgpu::RenderPipeline, +} + +static SHADER: &'static str = include_str!("mesh.wgsl"); + +impl Renderer { + pub fn new( + device: &wgpu::Device, + cam_layout: &wgpu::BindGroupLayout, + target_format: wgpu::TextureFormat, + depth_stencil: Option, + multisample: wgpu::MultisampleState, + ) -> Renderer { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Mesh Shader"), + source: wgpu::ShaderSource::Wgsl(SHADER.into()), + }); + + let consts_range = wgpu::PushConstantRange { + stages: wgpu::ShaderStages::VERTEX, + range: 0..mem::size_of::() as u32, + }; + + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Mesh RenderPipelineLayout"), + bind_group_layouts: &[cam_layout], + push_constant_ranges: &[consts_range], + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Mesh RenderPipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: mem::offset_of!(Vertex, position) as u64, + shader_location: 0, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::offset_of!(Vertex, normal) as u64, + shader_location: 1, + format: wgpu::VertexFormat::Float32x3, + }, + ], + }], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: target_format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::OVER, + alpha: wgpu::BlendComponent::OVER, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + ..Default::default() + }, + depth_stencil, + multisample, + multiview: None, + cache: None, + }); + + Renderer { pipeline } + } + + pub fn render<'a>( + &self, + pass: &mut wgpu::RenderPass, + cam_bind: &wgpu::BindGroup, + meshes: impl Iterator, + ) { + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, cam_bind, &[]); + for mesh in meshes { + pass.set_push_constants(wgpu::ShaderStages::VERTEX, 0, bytes_of(&mesh.consts)); + pass.set_vertex_buffer(0, mesh.buf.slice(..)); + pass.draw(0..mesh.npoints, 0..1); + } + } +} diff --git a/src/bin/wireframe/scene.rs b/src/bin/wireframe/scene.rs index f530deb..0529717 100644 --- a/src/bin/wireframe/scene.rs +++ b/src/bin/wireframe/scene.rs @@ -1,5 +1,5 @@ use glam::*; -use itertools::{chain, iproduct}; +use itertools::chain; use refraction::ifaces::{DebugTraceable, Traceable}; use refraction::tube::metric::Tube; @@ -17,6 +17,17 @@ pub struct FancyLine { pub pts: Vec, } +pub type Face = (Vec3, Vec3, Vec3); + +pub enum Mesh { + List(Vec), +} + +pub struct FancyMesh { + pub color: Vec4, + pub tris: Vec, +} + fn paint(onto: &mut Vec, color: Vec3, lines: Vec) { onto.extend(lines.into_iter().map(move |line| FancyLine { color, @@ -35,29 +46,6 @@ fn draw_line(a: Vec3, b: Vec3) -> Line { Line::Strip(vec![Ray { pos: a, dir }, Ray { pos: b, dir }]) } -fn draw_rect(center: Vec3, u: Vec3, v: Vec3) -> Vec { - let a = center - u - v; - let b = center + u - v; - let c = center + u + v; - let d = center - u + v; - vec![draw_line(a, b), draw_line(b, c), draw_line(c, d), draw_line(d, a)] -} - -fn draw_ellipse(center: Vec3, u: Vec3, v: Vec3) -> Line { - let segments = 47; - let step = 2. * std::f32::consts::PI / segments as f32; - Line::Loop( - (0..segments) - .map(|k| k as f32 * step) - .map(Vec2::from_angle) - .map(|d| Ray { - pos: center + d.x * u + d.y * v, - dir: -d.y * u + d.x * v, - }) - .collect(), - ) -} - fn draw_mark(pos: Vec3) -> Vec { [ vec3(1., 1., 1.), @@ -70,7 +58,7 @@ fn draw_mark(pos: Vec3) -> Vec { .collect() } -pub fn build() -> Vec { +pub fn build() -> (Vec, Vec) { let tube = Tube { inner_radius: 30.0, outer_radius: 50.0, @@ -110,7 +98,13 @@ pub fn build() -> Vec { ); let mut gc = vec![]; - paint(&mut gc, vec3(0.6, 0.6, 0.6), tube.render()); + gc.push(FancyMesh { + color: vec4(0.3, 0.5, 1.0, 1.0) * 0.2, + tris: tube.render(), + }); + let meshes = gc; + + let mut gc = vec![]; paint(&mut gc, vec3(0.0, 0.6, 1.0), draw_fan_2(&space, cam3, vec3(0., 1., 0.))); paint(&mut gc, vec3(0.2, 1.0, 0.0), draw_fan_2(&space, cam2, vec3(0., 1., 0.))); paint( @@ -119,7 +113,9 @@ pub fn build() -> Vec { draw_fan_2(&space, cam2l, vec3(0., 0., 1.)), ); paint(&mut gc, vec3(1.0, 0.2, 0.0), draw_fan_2(&space, cam1, vec3(0., 1., 0.))); - gc + let lines = gc; + + (meshes, lines) } fn draw_ray_2(gc: &mut Vec, space: &Space, camera: Location, dir: Vec3) { @@ -153,23 +149,42 @@ fn draw_fan_2(space: &Space, camera: Location, spread: Vec3) -> Vec { } trait Renderable { - fn render(&self) -> Vec; + fn render(&self) -> Vec; } impl Renderable for Tube { - fn render(&self) -> Vec { - let lines = 4; - let step = 2. * std::f32::consts::PI / lines as f32; - let r = 0.5 * (self.outer_radius + self.inner_radius); - let w = 0.5 * (self.outer_radius - self.inner_radius); - let l = vec3(0., self.external_halflength, 0.); - let along = (0..lines) - .map(|k| k as f32 * step) - .map(Vec2::from_angle) - .map(|d| vec3(d.x, 0., d.y)) - .flat_map(|d| draw_rect(r * d, w * d, l)); - let caps = iproduct!([self.inner_radius, self.outer_radius], [-l, l]) - .map(|(r, l)| draw_ellipse(l, vec3(r, 0., 0.), vec3(0., 0., r))); - chain!(along, caps).collect() + fn render(&self) -> Vec { + let sides = 42; + let step = 2. * std::f32::consts::PI / sides as f32; + let dir = |k| { + let d = Vec2::from_angle(k as f32 * step); + vec3(d.x, 0., d.y) + }; + let side = vec3(0., self.external_halflength, 0.); + let r1 = self.inner_radius; + let r2 = self.outer_radius; + let inner = (0..sides).flat_map(|k| { + let a = r1 * dir(k); + let b = r1 * dir(k + 1); + [(a - side, b - side, a + side), (b - side, b + side, a + side)] + }); + let outer = (0..sides).flat_map(|k| { + let a = r2 * dir(k); + let b = r2 * dir(k + 1); + [(a - side, b - side, a + side), (b - side, b + side, a + side)] + }); + let cap = (0..sides).flat_map(|k| { + let a = r1 * dir(k); + let b = r1 * dir(k + 1); + let c = r2 * dir(k + 1); + let d = r2 * dir(k); + [ + (a - side, b - side, c - side), + (a - side, c - side, d - side), + (a + side, b + side, c + side), + (a + side, c + side, d + side), + ] + }); + chain!(inner, outer, cap).collect() } }