Skip to content

Commit

Permalink
add loop
Browse files Browse the repository at this point in the history
  • Loading branch information
besok committed Jan 20, 2025
1 parent bd1c02a commit 93a735d
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 140 deletions.
11 changes: 6 additions & 5 deletions examples/subdivide_cells/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,22 @@ use tessellate::mesh::parts::vertex::Vertex;

fn main() -> TessResult<()> {

let bunny = files::ply::import_ply("examples/import_models/bunny.ply")?;
let mut bunny_b = bunny.subdivide().by_butterfly()?;
let _ = bunny_b.transform(Mat4::from_translation(Vec3::new(0.3, 0.3, 0.)))?;
// let bunny = files::ply::import_ply("examples/import_models/bunny.ply")?;
let bunny = mesh::shape::icosahedron::Icosahedron::create(Vertex::default(), 0.2, Attributes::default());
let mut bunny_b = bunny.subdivide_by_loop(1)?;
let _ = bunny_b.transform(Mat4::from_translation(Vec3::new(0.5, 0.5, 0.)))?;

println!("Bunny vertices: {}", bunny.vertices().len());
println!("Bunny vertices: {}", bunny_b.vertices().len());

let meshes = vec![
bunny,
bunny.into(),
bunny_b
];

let options = GpuOptions::new(
CameraOptions::new_position(Vec3::new(1., 1., 1.)),
LightOptions::new_position(Vec3::new(0., 0., 0.))
LightOptions::new_position(Vec3::new(1., 1., 0.))
.with_show_source(true)
.clone(),
);
Expand Down
8 changes: 4 additions & 4 deletions src/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::mesh::distance::distance_between_surfaces;
use crate::mesh::material::Color;
use crate::mesh::normals::MeshNormals;
use crate::mesh::parts::edge::Edge;
use crate::mesh::subdivision::MeshSubdivision;
use crate::mesh::subdivision::by_loop;
use crate::mesh::tables::MeshTables;
use parts::bbox::BoundingBox;
use parts::edge::MeshEdge;
Expand Down Expand Up @@ -296,8 +296,8 @@ impl Mesh {
/// # Returns
///
/// A `MeshSubdivision` instance for further subdivision operations.
pub fn subdivide(&self) -> MeshSubdivision {
MeshSubdivision::new(self)
pub fn subdivide_by_loop(&self, iterations:usize) -> MeshResult<Mesh> {
(0..iterations).try_fold(by_loop(self)?, |mesh, _| by_loop(&mesh))
}

pub fn contains(&self, v: &Vertex) -> bool {
Expand Down Expand Up @@ -388,7 +388,7 @@ impl Mesh {
pub fn get(&self, idx: usize) -> MeshResult<&Vertex> {
self.vertices
.get(idx)
.ok_or(MeshError::InvalidIndex("Invalid vertex index".to_string()))
.ok_or(MeshError::idx_vertex(idx))
}
pub fn triangulate(&self) -> MeshResult<Mesh> {
let faces = self
Expand Down
13 changes: 5 additions & 8 deletions src/mesh/shape/sphere.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::mesh::parts::face::Face;
use crate::mesh::shape::icosahedron::Icosahedron;
use crate::mesh::HasMesh;
use crate::mesh::{HasMesh, MeshResult};
use crate::mesh::Mesh;
use std::f32::consts::PI;
use std::ops::Deref;
Expand Down Expand Up @@ -72,20 +72,17 @@ impl Sphere {
mesh: Mesh::from_vertices(vertices, faces,attributes),
}
}
pub fn create_ico<V: Into<Vertex>>(center: V, radius: f32, subdivisions: usize, attrs: Attributes) -> Self {
pub fn create_ico<V: Into<Vertex>>(center: V, radius: f32, subdivisions: usize, attrs: Attributes) -> MeshResult<Self> {
let center = center.into();
let mut ico = Icosahedron::create(center, radius, attrs);
let mesh = (0..subdivisions).fold(ico.mesh_mut(), |acc, _| {
let _ = acc.subdivide().by_linear();
acc
});
let mesh = ico.subdivide_by_loop(subdivisions)?;

Sphere {
Ok(Sphere {
radius,
center,
segments: subdivisions,
mesh:mesh.clone(),
}
})
}

pub fn create<V: Into<Vertex>>(center: V, radius: f32,color:Color) -> Self {
Expand Down
191 changes: 68 additions & 123 deletions src/mesh/subdivision.rs
Original file line number Diff line number Diff line change
@@ -1,137 +1,82 @@
use std::collections::HashSet;
use crate::mesh::parts::edge::MeshEdge;
use crate::mesh::parts::face::Face;
use crate::mesh::{Mesh, MeshError, MeshResult};
use egui::ahash::{HashMap, HashMapExt};
use crate::mesh::parts::vertex::Vertex;

/// Subdivides the mesh using the Loop subdivision scheme.
///
/// This method applies the Loop subdivision algorithm to the mesh,
/// which is a technique used to create a smoother mesh by adding new vertices
/// and adjusting the positions of existing vertices.
///
/// # Returns
///
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs.
pub fn by_loop(mesh: &Mesh) -> MeshResult<Mesh> {
let trimesh = mesh.triangulate()?;
let start_idx = trimesh.vertices().len();
let mut new_vertices = vec![];
let mut new_faces = vec![];
let mut cache = HashMap::new();
let mut add_edge = |lhs:usize,rhs:usize|{
if let Some(idx) = cache.get(&(lhs,rhs)){
Ok::<usize, MeshError>(*idx)
}
else{
let v1 = trimesh.get(lhs)?;
let v2 = trimesh.get(rhs)?;

let next = new_vertices.len() + start_idx;
new_vertices.push((*v1 + *v2) / 2.0);
cache.insert((lhs,rhs),next);
cache.insert((rhs,lhs),next);
Ok(next)
}
};
for face in trimesh.faces(){

pub struct MeshSubdivision<'a> {
mesh: &'a Mesh,
}

impl<'a> MeshSubdivision<'a> {
pub fn new(mesh: &'a Mesh) -> Self {
MeshSubdivision { mesh }
}

/// Subdivides the mesh using the Butterfly subdivision scheme.
///
/// This method applies the Butterfly subdivision algorithm to the mesh,
/// which is a technique used to create a smoother mesh by adding new vertices
/// and adjusting the positions of existing vertices.
///
/// # Returns
///
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs.
pub fn by_butterfly(&self) -> MeshResult<Mesh> {
let mesh = self.mesh.triangulate()?;
let mut new_vertices = self.mesh.vertices.clone();
let mut new_faces = vec![];

let table = mesh.try_tables()?;

let mut cache = HashMap::new();

for face in mesh.faces() {
let mut mid_points_extra = vec![];
let mut edges = face.edges().into_iter().collect::<Vec<_>>();

for MeshEdge(lhs, rhs) in edges {
if let Some(idx) = cache.get(&(lhs, rhs)) {
mid_points_extra.push(*idx);
} else {
let v1 = mesh.get(lhs)?;
let v2 = mesh.get(rhs)?;

let mut mid = (*v1 + *v2) / 2.0;
let adjacent_vs: Vec<usize> = table
.edge_faces(lhs, rhs)?
.iter()
.flat_map(|f| f.flatten())
.filter(|v| v != &lhs && v != &rhs)
.collect();
if adjacent_vs.len() >= 2 {
let a1 = adjacent_vs[0];
let a2 = adjacent_vs[1];

let mut b1_vs = Vec::new();
for face in table.vertex_faces(a1)? {
if !face.flatten().contains(&lhs) && !face.flatten().contains(&rhs) {
b1_vs = face
.flatten()
.iter()
.filter(|v| *v != &a1)
.cloned()
.collect();
}
}

let mut b2_vs = Vec::new();
for face in table.vertex_faces(a2)? {
if !face.flatten().contains(&lhs) && !face.flatten().contains(&rhs) {
b2_vs = face
.flatten()
.iter()
.filter(|v| *v != &a2)
.cloned()
.collect();
}
}

if !b1_vs.is_empty() && !b2_vs.is_empty() {
let a1 = mesh.get(a1)?;
let a2 = mesh.get(a2)?;
let b1 = mesh.get(b1_vs[0])?;
let b2 = mesh.get(b2_vs[0])?;
let [a,b,c] = face.flatten()[..] else {
return Err(MeshError::WrongMesh("Invalid number of vertices".to_string()))
};
let m1 = add_edge(a,b)?;
let m2 = add_edge(b,c)?;
let m3 = add_edge(c,a)?;

mid = mid + (*a1 + *a2 - *b1 - *b2) * 0.125;
}
}
new_faces.push(Face::new3(a,m1,m3));
new_faces.push(Face::new3(b,m2,m1));
new_faces.push(Face::new3(c,m3,m2));
new_faces.push(Face::new3(m1,m2,m3));

let idx = new_vertices.len();
new_vertices.push(mid);
mid_points_extra.push(idx);
cache.insert((lhs, rhs), idx);
cache.insert((rhs, lhs), idx);
}
}

mid_points_extra.push(lhs);
mid_points_extra.push(rhs);
}
let mut vertex_neighbors = HashMap::new();
for (idx,v) in trimesh.vertices().iter().enumerate(){
vertex_neighbors.insert(idx,HashSet::new());
}

let [m_ab, a, b, m_bc, _b, c, m_ca, _c, _a] = mid_points_extra[..] else {
return Err(MeshError::WrongMesh("Invalid number of mid points".to_string()));
};
for MeshEdge(lhs,rhs) in trimesh.edges().iter(){
vertex_neighbors.get_mut(&lhs).ok_or(MeshError::idx_vertex(*lhs))?.insert(rhs);
vertex_neighbors.get_mut(&rhs).ok_or(MeshError::idx_vertex(*rhs))?.insert(lhs);
}

new_faces.push(Face::new3(a, m_ab, m_ca));
new_faces.push(Face::new3(b, m_bc, m_ab));
new_faces.push(Face::new3(c, m_ca, m_bc));
new_faces.push(Face::new3(m_ab, m_bc, m_ca));
let mut updated_vertices = trimesh.vertices.clone();
for (idx,neighbours) in vertex_neighbors.iter(){
let n = neighbours.len() as f32;
let beta = if n > 3.0 {
3.0 / (8.0 * n)
} else {
3.0 / 16.0
};
let mut new_vertex = *trimesh.get(*idx)? * (1.0 - n * beta);
let mut coef = Vertex::default();
for neighbor in neighbours {
coef = coef + *trimesh.get(**neighbor)?;
}

Ok(Mesh::from_vertices(new_vertices, new_faces, self.mesh.attributes.clone()))
coef = coef * beta;
updated_vertices[*idx] = new_vertex + coef;
}

/// Subdivides the mesh using the Loop subdivision scheme.
///
/// This method applies the Loop subdivision algorithm to the mesh,
/// which is a technique used to create a smoother mesh by adding new vertices
/// and adjusting the positions of existing vertices.
///
/// # Returns
///
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs.
pub fn by_loop(&self) -> MeshResult<Mesh> {
Ok(self.mesh.clone())
}
/// Subdivides the mesh using a linear subdivision scheme.
///
/// This method applies a linear subdivision algorithm to the mesh,
/// which is a technique used to create a smoother mesh by adding new vertices
/// and adjusting the positions of existing vertices.
///
/// # Returns
///
/// A `MeshResult` containing the subdivided `Mesh` if successful, or a `MeshError` if an error occurs.
pub fn by_linear(&self) -> MeshResult<Mesh> {
Ok(self.mesh.clone())
}
Ok(Mesh::from_vertices([updated_vertices, new_vertices].concat(), new_faces, trimesh.attributes.clone()))
}

0 comments on commit 93a735d

Please sign in to comment.