Skip to content

Commit

Permalink
initial subdivide impl for quads only
Browse files Browse the repository at this point in the history
  • Loading branch information
Bendzae committed Oct 12, 2024
1 parent 2528dea commit 56003b3
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 31 deletions.
37 changes: 28 additions & 9 deletions examples/extrude.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use bevy::prelude::*;
use bevy_inspector_egui::bevy_egui::EguiPlugin;
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
use glam::vec3;

use itertools::Itertools;
use smesh::prelude::*;
use smesh::{
adapters::bevy::{DebugRenderSMesh, SMeshDebugDrawPlugin, Selection},
prelude::*,
};
use transform::Pivot;

fn extrude_faces() -> SMeshResult<SMesh> {
Expand Down Expand Up @@ -52,13 +56,27 @@ fn init_system(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let extrude_mesh = extrude_faces().unwrap();
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(extrude_mesh)),
material: materials.add(StandardMaterial::from(Color::rgb(0.4, 0.4, 1.0))),
transform: Transform::from_translation(vec3(0.0, 0.0, 0.0)),
..default()
});
let mut extrude_mesh = extrude_faces().unwrap();
let v0 = extrude_mesh.vertices().next().unwrap();
extrude_mesh
.subdivide(extrude_mesh.vertices().collect_vec())
.unwrap();

extrude_mesh.recalculate_normals().unwrap();

commands.spawn((
PbrBundle {
mesh: meshes.add(Mesh::from(extrude_mesh.clone())),
material: materials.add(StandardMaterial::from(Color::rgb(0.4, 0.4, 1.0))),
transform: Transform::from_translation(vec3(0.0, 0.0, 0.0)),
..default()
},
DebugRenderSMesh {
mesh: extrude_mesh,
selection: Selection::Vertex(v0),
visible: true,
},
));

// Light
commands.spawn(PointLightBundle {
Expand Down Expand Up @@ -87,7 +105,8 @@ fn main() {
.add_plugins((
DefaultPlugins,
PanOrbitCameraPlugin,
// WorldInspectorPlugin::default(),
SMeshDebugDrawPlugin,
EguiPlugin,
))
.add_systems(Startup, init_system)
.run();
Expand Down
4 changes: 3 additions & 1 deletion examples/visualizer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use bevy::color::palettes::css::{BLACK, WHITE};
use bevy::prelude::*;
use bevy_inspector_egui::bevy_egui::EguiPlugin;
use bevy_inspector_egui::quick::WorldInspectorPlugin;
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};

use itertools::Itertools;
Expand Down Expand Up @@ -53,7 +55,7 @@ fn main() {
DefaultPlugins,
PanOrbitCameraPlugin,
SMeshDebugDrawPlugin,
// WorldInspectorPlugin::default(),
EguiPlugin,
))
.add_systems(Startup, init_system)
.run();
Expand Down
13 changes: 6 additions & 7 deletions src/adapters/bevy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use bevy::{
node_bundles::{NodeBundle, TextBundle},
FlexDirection, Style, UiRect, Val,
},
utils::info,
};
use glam::{bool, Quat, Vec2, Vec3};
use itertools::Itertools;
Expand Down Expand Up @@ -182,14 +183,11 @@ fn debug_draw_smesh(
None => TURQUOISE,
}
};
let edge_normal = ((v_src.normal(mesh)? + v_dst.normal(mesh)?) / 2.0).normalize();
let mut edge_normal = ((v_src.normal(mesh)? + v_dst.normal(mesh)?) / 2.0).normalize();
if edge_normal.is_nan() {
edge_normal = Vec3::Y;
}
draw_halfedge(gizmos, v_src_pos, v_dst_pos, edge_normal, color);
// let color = if debug_smesh.selection == Selection::Halfedge(opposite?) {
// Color::ORANGE_RED
// } else {
// Color::TURQUOISE
// };
// draw_halfedge(&mut gizmos, v_dst_pos, v_src_pos, color);
}
// Faces
for face_id in mesh.faces() {
Expand Down Expand Up @@ -274,6 +272,7 @@ fn change_selection_inner(
let pos = (mesh.positions[v0] + mesh.positions[v1]) / 2.0;
let v = mesh.add_vertex(pos);
let he = mesh.insert_vertex(id, v);
mesh.recalculate_normals()?;
match he {
Ok(he) => {
d.selection = Selection::Halfedge(he);
Expand Down
63 changes: 50 additions & 13 deletions src/smesh/edit_operations.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::collections::{HashMap, HashSet};

use attribute::CustomAttributeMapOps;
use bevy::{log::info, reflect::List};
use bevy::{log::info, utils::info};
use glam::Vec3;
use itertools::Itertools;

use crate::{bail, prelude::*};
use crate::{adapters::bevy::Selection, bail, prelude::*};

/// Edit operations
impl SMesh {
Expand Down Expand Up @@ -213,17 +213,54 @@ impl SMesh {
Ok(new_edges)
}

// Returns the normal of the face. The first three vertices are used to
// compute the normal. If the vertices of the face are not coplanar,
// the result will not be correct.
fn calculate_face_normal(&self, face: FaceId) -> Option<Vec3> {
let verts = face.vertices(self).collect_vec();
if verts.len() >= 3 {
let v01 = self.positions[verts[0]] - self.positions[verts[1]];
let v12 = self.positions[verts[1]] - self.positions[verts[2]];
Some(v01.cross(v12).normalize())
} else {
None
pub fn subdivide<T: Into<MeshSelection>>(&mut self, selection: T) -> SMeshResult<()> {
let s: MeshSelection = selection.into();
let faces = s.clone().resolve_to_faces(self)?;
let halfedges = s.clone().resolve_to_halfedges(self)?;

let face_corners = self
.faces()
.map(|f| (f, f.halfedge().src_vert().run(self).unwrap()))
.collect::<HashMap<FaceId, VertexId>>();

let mut he_cache = HashSet::new();
for he in halfedges {
if he_cache.contains(&he) {
continue;
}
let p0 = he.src_vert().position(self)?;
let p1 = he.dst_vert().position(self)?;
let v = self.add_vertex(0.5 * (p0 + p1));
self.insert_vertex(he, v)?;
he_cache.insert(he);
he_cache.insert(he.opposite().run(self)?);
}
for f in faces {
let valence = f.valence(self) / 2;
if valence == 3 {
todo!()
}
if valence == 4 {
let center = self.get_face_centroid(f)?;
let v_c = self.add_vertex(center);
let corner = face_corners[&f];
let corner_edge = f
.halfedges(self)
.find(|he| he.src_vert().run(self).unwrap() == corner)
.unwrap();
let he_loop = self.halfedge_loop(corner_edge.next().run(self)?);
self.delete_only_face(f)?;
for (h0, h1) in he_loop.iter().circular_tuple_windows().step_by(2) {
self.make_quad(
h0.src_vert().run(self)?,
h1.src_vert().run(self)?,
h1.dst_vert().run(self)?,
v_c,
)?;
}
}
}

Ok(())
}
}
2 changes: 1 addition & 1 deletion src/smesh/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::prelude::{HalfedgeId, HalfedgeOps, RunQuery, SMesh};
const MAX_LOOP_ITERATIONS: usize = 100;

impl SMesh {
fn halfedge_loop(&self, h0: HalfedgeId) -> Vec<HalfedgeId> {
pub fn halfedge_loop(&self, h0: HalfedgeId) -> Vec<HalfedgeId> {
let mut ret = vec![h0];
let mut h = h0;

Expand Down
18 changes: 18 additions & 0 deletions src/smesh/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashSet;

use crate::{bail, prelude::*};
use bevy::utils::default;
use itertools::Itertools;

use super::mesh_query::{HalfedgeOps, RunQuery};

Expand Down Expand Up @@ -47,6 +48,23 @@ impl MeshSelection {
}
Ok(faces)
}

pub fn resolve_to_halfedges(&self, smesh: &SMesh) -> SMeshResult<HashSet<HalfedgeId>> {
let mut edges = self.halfedges.clone();
for face in &self.faces {
for he in face.halfedges(smesh) {
edges.insert(he);
}
}
for v in &self.vertices {
for he in v.halfedges(smesh) {
if self.vertices.contains(&he.dst_vert().run(smesh)?) {
edges.insert(he);
}
}
}
Ok(edges)
}
}

impl From<VertexId> for MeshSelection {
Expand Down
10 changes: 10 additions & 0 deletions src/smesh/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,14 @@ impl SMesh {

Ok(())
}
pub fn get_face_centroid(&self, face: FaceId) -> SMeshResult<Vec3> {
let face_vertices = face.vertices(self).collect_vec();
let mut centroid = Vec3::ZERO;
for v in face.vertices(self) {
let vertex_position = v.position(self)?;
centroid += vertex_position;
}
centroid /= face_vertices.len() as f32;
Ok(centroid)
}
}

0 comments on commit 56003b3

Please sign in to comment.