Add support for displaying faces

This commit is contained in:
Hanno Braun 2025-05-16 12:39:21 +02:00
parent f910ebf6a5
commit d56459da92
7 changed files with 99 additions and 7 deletions

View File

@ -9,6 +9,9 @@ use super::{
#[derive(Debug)] #[derive(Debug)]
pub enum Pipelines { pub enum Pipelines {
ForFace {
lines: Pipeline,
},
ForModel { ForModel {
model: Pipeline, model: Pipeline,
mesh: Option<Pipeline>, mesh: Option<Pipeline>,
@ -16,6 +19,24 @@ pub enum Pipelines {
} }
impl Pipelines { impl Pipelines {
pub fn for_face(
device: &wgpu::Device,
shaders: &Shaders,
pipeline_layout: &wgpu::PipelineLayout,
color_format: wgpu::TextureFormat,
) -> Self {
let lines = Pipeline::new(
device,
pipeline_layout,
shaders.face(),
wgpu::PrimitiveTopology::TriangleList,
wgpu::PolygonMode::Line,
color_format,
);
Self::ForFace { lines }
}
pub fn for_model( pub fn for_model(
device: &wgpu::Device, device: &wgpu::Device,
shaders: &Shaders, shaders: &Shaders,
@ -58,6 +79,9 @@ impl Pipelines {
render_pass: &mut wgpu::RenderPass, render_pass: &mut wgpu::RenderPass,
) { ) {
match self { match self {
Self::ForFace { lines } => {
lines.draw(geometry, render_pass);
}
Self::ForModel { model, mesh } => { Self::ForModel { model, mesh } => {
if config.draw_model { if config.draw_model {
model.draw(geometry, render_pass); model.draw(geometry, render_pass);

View File

@ -188,6 +188,12 @@ impl Renderer {
let shaders = Shaders::new(&device.device); let shaders = Shaders::new(&device.device);
let pipelines = match mode { let pipelines = match mode {
RenderMode::Face => Pipelines::for_face(
&device.device,
&shaders,
&pipeline_layout,
color_format,
),
RenderMode::Model => Pipelines::for_model( RenderMode::Model => Pipelines::for_model(
&device.device, &device.device,
&shaders, &shaders,
@ -381,6 +387,7 @@ impl Renderer {
} }
pub enum RenderMode { pub enum RenderMode {
Face,
Model, Model,
} }

View File

@ -35,6 +35,13 @@ fn vertex(in: VertexInput) -> VertexOutput {
const pi: f32 = 3.14159265359; const pi: f32 = 3.14159265359;
@fragment
fn frag_face(in: VertexOutput) -> FragmentOutput {
var out: FragmentOutput;
out.color = vec4<f32>(0.0, 0.0, 0.0, 1.0);
return out;
}
@fragment @fragment
fn frag_model(in: VertexOutput) -> FragmentOutput { fn frag_model(in: VertexOutput) -> FragmentOutput {
let light = vec3<f32>(0.0, 0.0, -1.0); let light = vec3<f32>(0.0, 0.0, -1.0);

View File

@ -17,6 +17,13 @@ impl Shaders {
Self { module } Self { module }
} }
pub fn face(&self) -> Shader {
Shader {
module: &self.module,
frag_entry: "frag_face",
}
}
pub fn model(&self) -> Shader { pub fn model(&self) -> Shader {
Shader { Shader {
module: &self.module, module: &self.module,

View File

@ -1,5 +1,6 @@
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use fj_interop::{Index, TriMesh, vertices_to_indexed_vertices}; use fj_interop::{Index, TriMesh, vertices_to_indexed_vertices};
use fj_math::{Point, Scalar};
#[derive(Debug)] #[derive(Debug)]
pub struct Vertices { pub struct Vertices {
@ -8,6 +9,24 @@ pub struct Vertices {
} }
impl Vertices { impl Vertices {
pub fn for_face(points: &[Point<2>]) -> Self {
let vertices = points
.iter()
.map(|point| {
let [x, y] = point.coords.components.map(Scalar::into_f32);
Vertex {
position: [x, y, 0.],
normal: [0., 0., 1.],
color: [0., 0., 0., 1.],
}
})
.collect();
let indices = (0..).take(points.len()).collect();
Self { vertices, indices }
}
pub fn for_model(tri_mesh: &TriMesh) -> Self { pub fn for_model(tri_mesh: &TriMesh) -> Self {
let (vertices, indices) = vertices_to_indexed_vertices( let (vertices, indices) = vertices_to_indexed_vertices(
tri_mesh.triangles.iter().flat_map(|triangle| { tri_mesh.triangles.iter().flat_map(|triangle| {

View File

@ -1,6 +1,7 @@
use std::{collections::BTreeMap, panic, thread}; use std::{collections::BTreeMap, panic, thread};
use fj_interop::TriMesh; use fj_interop::TriMesh;
use fj_math::Point;
use futures::executor::block_on; use futures::executor::block_on;
use winit::{ use winit::{
application::ApplicationHandler, application::ApplicationHandler,
@ -63,6 +64,14 @@ pub struct Viewer {
} }
impl Viewer { impl Viewer {
/// # Display a 2D face in a new window
pub fn display_face(&self, points: Vec<Point<2>>) {
// If there's an error, that means the display thread has closed down
// and we're on our way to shutting down as well. I don't think there's
// much we can do about that.
let _ = self.event_loop.send_event(ToDisplay::face(points));
}
/// # Display a 3D model in a new window /// # Display a 3D model in a new window
pub fn display_model(&self, tri_mesh: TriMesh) { pub fn display_model(&self, tri_mesh: TriMesh) {
// If there's an error, that means the display thread has closed down // If there's an error, that means the display thread has closed down

View File

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use fj_interop::TriMesh; use fj_interop::TriMesh;
use fj_math::Aabb; use fj_math::{Aabb, Point};
use tracing::warn; use tracing::warn;
use winit::{dpi::PhysicalSize, event_loop::ActiveEventLoop}; use winit::{dpi::PhysicalSize, event_loop::ActiveEventLoop};
@ -30,6 +30,11 @@ impl Window {
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
) -> Result<Self, WindowError> { ) -> Result<Self, WindowError> {
let (vertices, render_mode, aabb) = match &to_display { let (vertices, render_mode, aabb) = match &to_display {
ToDisplay::Face { points, aabb } => {
let vertices = Vertices::for_face(points);
let render_mode = RenderMode::Face;
(vertices, render_mode, aabb)
}
ToDisplay::Model { tri_mesh, aabb } => { ToDisplay::Model { tri_mesh, aabb } => {
let vertices = Vertices::for_model(tri_mesh); let vertices = Vertices::for_model(tri_mesh);
let render_mode = RenderMode::Model; let render_mode = RenderMode::Model;
@ -78,11 +83,11 @@ impl Window {
/// # Compute and store a focus point, unless one is already stored /// # Compute and store a focus point, unless one is already stored
pub fn add_focus_point(&mut self) { pub fn add_focus_point(&mut self) {
let ToDisplay::Model { tri_mesh, aabb } = &self.to_display; if let ToDisplay::Model { tri_mesh, aabb } = &self.to_display {
if self.focus_point.is_none() {
if self.focus_point.is_none() { self.focus_point =
self.focus_point = Some(self.camera.focus_point(self.cursor, tri_mesh, aabb));
Some(self.camera.focus_point(self.cursor, tri_mesh, aabb)); }
} }
} }
@ -175,6 +180,7 @@ impl Window {
} }
let aabb = match &self.to_display { let aabb = match &self.to_display {
ToDisplay::Face { points: _, aabb } => aabb,
ToDisplay::Model { tri_mesh: _, aabb } => aabb, ToDisplay::Model { tri_mesh: _, aabb } => aabb,
}; };
self.camera.update_planes(aabb); self.camera.update_planes(aabb);
@ -186,10 +192,23 @@ impl Window {
} }
pub enum ToDisplay { pub enum ToDisplay {
Model { tri_mesh: TriMesh, aabb: Aabb<3> }, Face {
points: Vec<Point<2>>,
aabb: Aabb<3>,
},
Model {
tri_mesh: TriMesh,
aabb: Aabb<3>,
},
} }
impl ToDisplay { impl ToDisplay {
pub fn face(points: Vec<Point<2>>) -> Self {
let aabb =
Aabb::<3>::from_points(points.iter().map(|point| point.to_xyz()));
Self::Face { points, aabb }
}
pub fn model(tri_mesh: TriMesh) -> Self { pub fn model(tri_mesh: TriMesh) -> Self {
let aabb = tri_mesh.aabb(); let aabb = tri_mesh.aabb();
Self::Model { tri_mesh, aabb } Self::Model { tri_mesh, aabb }