From a59a6e099459c311ee974c77fc362e5cdaced75a Mon Sep 17 00:00:00 2001 From: "francis.nowak1@gmail.com" Date: Sat, 3 May 2025 14:33:47 +0200 Subject: [PATCH] A simple wireframe object that takes a CpuMesh, for use in debugging. Unfortunately, this approach has some limitations: meshes with shared vertices will not be displayed correctly. --- src/renderer/object.rs | 5 +- src/renderer/object/wireframe.rs | 158 +++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 src/renderer/object/wireframe.rs diff --git a/src/renderer/object.rs b/src/renderer/object.rs index 7b53262f6..7f609650a 100644 --- a/src/renderer/object.rs +++ b/src/renderer/object.rs @@ -17,7 +17,6 @@ macro_rules! impl_object_body { } }; } - mod gm; #[doc(inline)] pub use gm::*; @@ -54,6 +53,10 @@ mod axes; #[doc(inline)] pub use axes::*; +mod wireframe; +#[doc(inline)] +pub use wireframe::*; + use crate::core::*; use crate::renderer::*; diff --git a/src/renderer/object/wireframe.rs b/src/renderer/object/wireframe.rs new file mode 100644 index 000000000..94115cf50 --- /dev/null +++ b/src/renderer/object/wireframe.rs @@ -0,0 +1,158 @@ +use crate::core::*; +use crate::renderer::*; + +pub struct Wireframe{ + width: f32, + positions: VertexBuffer, + barycentric: VertexBuffer, + context: Context, + aabb: AxisAlignedBoundingBox, + transformation: Mat4, +} +impl Wireframe { + pub fn new(context: &Context, mesh: &CpuMesh, width: f32) -> Self { + let positions = VertexBuffer::new_with_data( + context, + &mesh.positions.to_f32() + ); + let barycentric = VertexBuffer::new_with_data( + context, + &(0..positions.count()/3).flat_map(|_| [ + vec3(1., 0., 0.), + vec3(0., 1., 0.), + vec3(0., 0., 1.), + ]).collect::>() + ); + Self { + width, + positions, + barycentric, + context: context.clone(), + aabb: mesh.compute_aabb(), + transformation: Mat4::identity(), + } + } + pub fn transform_mut(&mut self) -> &mut Mat4 { + &mut self.transformation + } + pub fn line_width_mut(&mut self) -> &mut f32 { + &mut self.width + } +} + +struct WireframeMaterial(f32); + +impl Object for Wireframe { + fn render(&self, viewer: &dyn Viewer, lights: &[&dyn Light]) { + render_with_material(&self.context, viewer, &self, WireframeMaterial(self.width), lights); + } + + fn material_type(&self) -> MaterialType { + WireframeMaterial(0.).material_type() + } +} + +impl Material for WireframeMaterial { + fn fragment_shader_source(&self, lights: &[&dyn Light]) -> String { + r#" +layout (location = 0) out vec4 outColor; + +uniform float u_line_width = 0.5; + +in vec3 bary; +in vec3 pos; + +void main() { + vec3 d = fwidth(bary); + vec3 f = step(d * u_line_width, bary); + float b = min(min(f.x, f.y), f.z); + outColor = vec4(1.-b); +} + "#.into() + } + + fn id(&self) -> EffectMaterialId { + //TODO! + EffectMaterialId(0) + } + + fn use_uniforms(&self, program: &Program, viewer: &dyn Viewer, lights: &[&dyn Light]) { + program.use_uniform("u_line_width", self.0); + } + + fn render_states(&self) -> RenderStates { + RenderStates { + depth_test: DepthTest::Always, + blend: Blend::TRANSPARENCY, + cull: Cull::None, + ..Default::default() + } + } + + fn material_type(&self) -> MaterialType { + MaterialType::Transparent + } +} + +impl Geometry for Wireframe { + fn draw(&self, viewer: &dyn Viewer, program: &Program, render_states: RenderStates) { + program.use_uniform("viewProjection", viewer.projection() * viewer.view()); + program.use_uniform("modelMatrix", self.transformation); + program.use_vertex_attribute("position", &self.positions); + program.use_vertex_attribute("barycentric", &self.barycentric); + program.draw_arrays( + render_states, + viewer.viewport(), + self.positions.count(), + ); + } + + fn vertex_shader_source(&self) -> String { + r#" +uniform mat4 viewProjection; +uniform mat4 modelMatrix; + +in vec3 position; +in vec3 barycentric; + +out vec3 pos; +out vec3 bary; + +void main() { + vec4 worldPos = modelMatrix * vec4(position, 1.); + pos = worldPos.xyz; + bary = barycentric; + gl_Position = viewProjection * worldPos; +} + "#.into() + } + + fn id(&self) -> GeometryId { + //TODO! + GeometryId(0) + } + + fn render_with_material( + &self, + _material: &dyn Material, + _viewer: &dyn Viewer, + _lights: &[&dyn Light], + ) { + panic!("Wireframes must be rendered with the built-in material."); + } + + fn render_with_effect( + &self, + _material: &dyn Effect, + _viewer: &dyn Viewer, + _lights: &[&dyn Light], + _color_texture: Option, + _depth_texture: Option, + ) { + panic!("Wireframes do not support effects."); + } + + fn aabb(&self) -> AxisAlignedBoundingBox { + self.aabb.transformed(self.transformation) + } +}