Add buttons to recompute / flip model normals
This commit is contained in:
4
TODO.md
4
TODO.md
@@ -88,3 +88,7 @@
|
|||||||
- [ ] Update readme
|
- [ ] Update readme
|
||||||
- [ ] Make post processing async
|
- [ ] Make post processing async
|
||||||
- [ ] Instance meshes in save files / rendering
|
- [ ] Instance meshes in save files / rendering
|
||||||
|
- [x] Allow recalculating normals
|
||||||
|
- [ ] Alert when model with invalid normals is loaded
|
||||||
|
- [ ] Cleanup self intersection resolution
|
||||||
|
- [ ] Dont fail to load an stl without normals
|
||||||
|
@@ -40,6 +40,7 @@ impl LineDispatch for NormalsDispatch {
|
|||||||
|
|
||||||
let models = resources.models.read();
|
let models = resources.models.read();
|
||||||
let ids = models.iter().map(|x| x.id).collect::<Vec<_>>();
|
let ids = models.iter().map(|x| x.id).collect::<Vec<_>>();
|
||||||
|
let dirty = models.iter().any(|x| x.dirty);
|
||||||
let transforms = models
|
let transforms = models
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| *x.mesh.transformation_matrix())
|
.map(|x| *x.mesh.transformation_matrix())
|
||||||
@@ -48,6 +49,7 @@ impl LineDispatch for NormalsDispatch {
|
|||||||
if ids != self.last_models
|
if ids != self.last_models
|
||||||
|| transforms != self.last_transforms
|
|| transforms != self.last_transforms
|
||||||
|| show_normals != self.last_normals
|
|| show_normals != self.last_normals
|
||||||
|
|| dirty
|
||||||
{
|
{
|
||||||
self.last_models = ids;
|
self.last_models = ids;
|
||||||
self.last_transforms = transforms;
|
self.last_transforms = transforms;
|
||||||
|
@@ -19,6 +19,10 @@ pub struct RenderedMesh {
|
|||||||
pub color: Color32,
|
pub color: Color32,
|
||||||
pub hidden: bool,
|
pub hidden: bool,
|
||||||
pub locked_scale: bool,
|
pub locked_scale: bool,
|
||||||
|
/// Used when the normals are recomputed / flipped and the solid_line
|
||||||
|
/// normals dispatch needs to know to regenerate the lines. Should only be
|
||||||
|
/// set for one frame.
|
||||||
|
pub dirty: bool,
|
||||||
|
|
||||||
vertices: Vec<ModelVertex>,
|
vertices: Vec<ModelVertex>,
|
||||||
index: Vec<u32>,
|
index: Vec<u32>,
|
||||||
@@ -47,6 +51,7 @@ impl RenderedMesh {
|
|||||||
color: Color32::WHITE,
|
color: Color32::WHITE,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
locked_scale: true,
|
locked_scale: true,
|
||||||
|
dirty: false,
|
||||||
index,
|
index,
|
||||||
vertices,
|
vertices,
|
||||||
buffers: None,
|
buffers: None,
|
||||||
@@ -90,6 +95,16 @@ impl RenderedMesh {
|
|||||||
self.mesh.set_position(pos);
|
self.mesh.set_position(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn recompute_normals(&mut self) {
|
||||||
|
self.mesh.recompute_normals();
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flip_normals(&mut self) {
|
||||||
|
self.mesh.flip_normals();
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_get_buffers(&self) -> Option<&RenderedMeshBuffers> {
|
pub fn try_get_buffers(&self) -> Option<&RenderedMeshBuffers> {
|
||||||
self.buffers.as_ref()
|
self.buffers.as_ref()
|
||||||
}
|
}
|
||||||
@@ -127,6 +142,7 @@ impl Clone for RenderedMesh {
|
|||||||
color: self.color,
|
color: self.color,
|
||||||
hidden: self.hidden,
|
hidden: self.hidden,
|
||||||
locked_scale: self.locked_scale,
|
locked_scale: self.locked_scale,
|
||||||
|
dirty: false,
|
||||||
vertices: self.vertices.clone(),
|
vertices: self.vertices.clone(),
|
||||||
index: self.index.clone(),
|
index: self.index.clone(),
|
||||||
buffers: None,
|
buffers: None,
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
use const_format::concatcp;
|
use const_format::concatcp;
|
||||||
use egui::{Context, Grid, Id, Ui};
|
use egui::{Context, Grid, Id, Ui};
|
||||||
use egui_phosphor::regular::{ARROW_LINE_DOWN, COPY, DICE_THREE, EYE, EYE_SLASH, TRASH};
|
use egui_phosphor::regular::{
|
||||||
|
ARROWS_CLOCKWISE, ARROW_LINE_DOWN, COPY, DICE_THREE, EYE, EYE_SLASH, FLIP_HORIZONTAL, TRASH,
|
||||||
|
VECTOR_THREE,
|
||||||
|
};
|
||||||
use slicer::Pos;
|
use slicer::Pos;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -28,6 +31,8 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
|
|||||||
let width = ui.available_width();
|
let width = ui.available_width();
|
||||||
|
|
||||||
for (i, mesh) in meshes.iter_mut().enumerate() {
|
for (i, mesh) in meshes.iter_mut().enumerate() {
|
||||||
|
mesh.dirty = false;
|
||||||
|
|
||||||
let id = Id::new(format!("model_show_{}", mesh.id));
|
let id = Id::new(format!("model_show_{}", mesh.id));
|
||||||
let open = ui.data_mut(|map| *map.get_temp_mut_or_insert_with(id, || false));
|
let open = ui.data_mut(|map| *map.get_temp_mut_or_insert_with(id, || false));
|
||||||
|
|
||||||
@@ -53,16 +58,35 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
|
|||||||
.with_row_color(|row, style| (row % 2 == 0).then_some(style.visuals.faint_bg_color))
|
.with_row_color(|row, style| (row % 2 == 0).then_some(style.visuals.faint_bg_color))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.label("Actions");
|
ui.label("Actions");
|
||||||
ui.horizontal(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.button(concatcp!(TRASH, " Delete"))
|
ui.horizontal(|ui| {
|
||||||
.clicked()
|
ui.button(concatcp!(TRASH, " Delete"))
|
||||||
.then(|| action = Action::Remove(i));
|
.clicked()
|
||||||
ui.button(concatcp!(COPY, " Duplicate"))
|
.then(|| action = Action::Remove(i));
|
||||||
.clicked()
|
ui.button(concatcp!(COPY, " Duplicate"))
|
||||||
.then(|| action = Action::Duplicate(i));
|
.clicked()
|
||||||
ui.button(concatcp!(ARROW_LINE_DOWN, " Align to Bed"))
|
.then(|| action = Action::Duplicate(i));
|
||||||
.clicked()
|
ui.button(concatcp!(ARROW_LINE_DOWN, " Align to Bed"))
|
||||||
.then(|| mesh.align_to_bed());
|
.clicked()
|
||||||
|
.then(|| mesh.align_to_bed());
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.menu_button(concatcp!(VECTOR_THREE, " Normals"), |ui| {
|
||||||
|
if ui
|
||||||
|
.button(concatcp!(ARROWS_CLOCKWISE, " Recompute"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
mesh.recompute_normals();
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button(concatcp!(FLIP_HORIZONTAL, " Flip")).clicked() {
|
||||||
|
mesh.flip_normals();
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
|
|
||||||
|
@@ -80,6 +80,40 @@ impl Mesh {
|
|||||||
self.faces().len()
|
self.faces().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes a copy of the mesh with normals computed from the triangles
|
||||||
|
/// directly. The copy makes this operation kinda expensive.
|
||||||
|
pub fn recompute_normals(&mut self) {
|
||||||
|
let vertices = self.vertices();
|
||||||
|
let normals = self
|
||||||
|
.faces()
|
||||||
|
.iter()
|
||||||
|
.map(|f| {
|
||||||
|
let edge1 = vertices[f[2] as usize] - vertices[f[1] as usize];
|
||||||
|
let edge2 = vertices[f[0] as usize] - vertices[f[1] as usize];
|
||||||
|
edge1.cross(&edge2).normalize()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.overwrite_normals(normals)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes a copy of the mesh with normals computed from the triangles
|
||||||
|
/// directly. The copy makes this operation kinda expensive.
|
||||||
|
pub fn flip_normals(&mut self) {
|
||||||
|
let normals = self.normals().iter().map(|f| -f).collect();
|
||||||
|
self.overwrite_normals(normals);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The alternaitve is to put normals in a separate Arc to avoid cloning all
|
||||||
|
// verts and faces here, but its proooobly fine.
|
||||||
|
fn overwrite_normals(&mut self, normals: Vec<Vector3<f32>>) {
|
||||||
|
self.inner = Arc::new(MeshInner {
|
||||||
|
vertices: self.inner.vertices.clone(),
|
||||||
|
faces: self.inner.faces.clone(),
|
||||||
|
normals: normals.into_boxed_slice(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Intersect the mesh with a plane with linier time complexity. You
|
/// Intersect the mesh with a plane with linier time complexity. You
|
||||||
/// should probably use the [`crate::segments::Segments`] struct as it can
|
/// should probably use the [`crate::segments::Segments`] struct as it can
|
||||||
/// massively accelerate slicing of high face count triangles.
|
/// massively accelerate slicing of high face count triangles.
|
||||||
|
Reference in New Issue
Block a user