Caution
Library is still work in progress
SMesh is a polygon mesh manipulation library based on the Surface Mesh data structure and the pmp library and the halfedge-mesh implementation of the blackjack project.
| SMesh Version | Bevy Version |
|---|---|
| 0.2.7 | 0.17 |
| 0.2.5 | 0.16 |
The goal of this library is to provide a flexible mesh abstraction and set of operations to be used for procedural modeling and procedural generation of 3D meshes.
parameterized procedural mesh generation
cargo run --example tree
mesh extrusion and manipulation example with the visual debug tools enabled
cargo run --example extrude
UV Unwrapping Example
Interactive example demonstrating all UV unwrapping methods on four different primitives with a checkerboard texture:
cargo run --example uv_unwrap --features xatlasPress SPACE to cycle through UV methods: Planar Z, Planar Y, Cylindrical Y, Spherical, Cube, and XAtlas Auto. The four primitives (Cube, Sphere, Cylinder, and a complex extruded shape) update simultaneously to show how each method performs on different geometry.
Preface: Mesh elements in SMesh are identified by a unique typesafe id, which can be of type:
VertexId, HalfedgeId and FaceId.
SMesh has a simple api to add vertices to your mesh and connect them to faces: Add vertices
let mut smesh = SMesh::new();
let v0 = smesh.add_vertex(vec3(-1.0, -1.0, 0.0)); // Returns a unique VertexId
let v1 = smesh.add_vertex(vec3(1.0, -1.0, 0.0));
let v2 = smesh.add_vertex(vec3(1.0, 1.0, 0.0));
let v3 = smesh.add_vertex(vec3(-1.0, 1.0, 0.0));Build face
smesh.add_face(vec![v0, v1, v2, v3])?;SMesh provides a chainable api to query mesh elements using the typical halfedge-mesh relationships:
get outgoing halfedge of a vertex
let outgoing_halfedge_query = v0.halfedge(); // returns a MeshQueryBuilder<HalfedgeId>you can execute the query on a smesh instance by using .run(&smesh)
let outgoing_halfedge = v0.halfedge().run(&smesh)?; // returns a HalfedgeIdchaining queries
let vertex = v0.halfedge_to(v1).cw_rotated_neighbour().dst_vert().run(&smesh)?; // returns a VertexIdSMesh provides a comprehensive set of mesh manipulation operations:
Topological Operations
// Insert a vertex along an edge
let new_halfedge = mesh.insert_vertex(halfedge_id, vertex_id)?;
// Delete mesh elements
mesh.delete_vertex(vertex_id)?;
mesh.delete_edge(halfedge_id)?;
mesh.delete_face(face_id)?;
// Edge collapse (for triangle meshes)
if mesh.is_collapse_ok(halfedge_id).is_ok() {
mesh.collapse(halfedge_id)?;
}
// Remove edge (merges adjacent faces)
if mesh.is_removal_ok(halfedge_id).is_ok() {
mesh.remove_edge(halfedge_id)?;
}Edit Operations
// Extrude a single face
let new_face = mesh.extrude(face_id)?;
// Extrude multiple faces
let new_faces = mesh.extrude_faces(vec![face1, face2, face3])?;
// Extrude a boundary edge
let new_edge = mesh.extrude_edge(halfedge_id)?;
// Extrude a chain of connected boundary edges
let new_edges = mesh.extrude_edge_chain(vec![edge1, edge2, edge3])?;
// Subdivide faces
let selection = MeshSelection::from(vec![face1, face2]);
let new_selection = mesh.subdivide(selection)?;
// Combine two meshes
mesh.combine_with(other_mesh)?;Transform Operations
All transform operations work with selections (vertices, edges, or faces):
use smesh::smesh::transform::Pivot;
// Translate
mesh.translate(face_id, Vec3::new(0.0, 1.0, 0.0))?;
// Scale around a pivot point
mesh.scale(
vec![face1, face2],
Vec3::new(2.0, 2.0, 2.0),
Pivot::SelectionCog
)?;
// Rotate using a quaternion
mesh.rotate(
vertex_id,
Quat::from_rotation_y(PI / 4.0),
Pivot::Origin
)?;
// Set position relative to a pivot
mesh.set_position(selection, Vec3::new(5.0, 0.0, 0.0), Pivot::MeshCog)?;Available pivot options: Pivot::Origin, Pivot::MeshCog, Pivot::SelectionCog, Pivot::Point(Vec3)
Primitives
SMesh includes built-in primitive generators:
use smesh::smesh::primitives::*;
// Create a cube with subdivisions
let (cube, data) = Cube {
subdivision: U16Vec3::new(2, 2, 2)
}.generate()?;
// Create an icosphere
let (sphere, data) = Icosphere {
subdivisions: 3
}.generate()?;
// Create a cylinder
let (cylinder, data) = Cylinder {
segments: 16,
height: 2.0,
radius: 0.5
}.generate()?;
// Create other primitives
let (quad, data) = Quad.generate()?;
let (circle, data) = Circle { segments: 32 }.generate()?;Please check the examples for more :)
SMesh supports both simple projection-based UV mapping and automatic UV unwrapping via xatlas.
Simple Projection Methods (built-in, no features required):
use smesh::smesh::uv_operations::ProjectionAxis;
// Planar projection
mesh.planar_project_uvs(ProjectionAxis::Z)?;
// Cylindrical projection
mesh.cylindrical_project_uvs(ProjectionAxis::Y)?;
// Spherical projection
mesh.spherical_project_uvs(Vec3::ZERO)?;
// Cube projection
mesh.cube_project_uvs(Vec3::ZERO)?;Automatic UV Unwrapping via xatlas (requires xatlas feature):
[dependencies]
smesh = { version = "0.2", features = ["xatlas"] }Then generate UVs for your mesh:
// Simple automatic unwrapping
mesh.auto_uv_unwrap()?;
// With custom options
use smesh::smesh::xatlas_integration::XatlasOptions;
let options = XatlasOptions::default();
mesh.auto_uv_unwrap_with_options(options)?;I aim to provide a flexible rust implementation of the Surface Mesh with a focus on usefulness for procedural mesh generation. Other goals are:
- Ergonomic and easy-to-use api
- Port most operations from the pmp library and blackjack
- Support most operations that are possible in modern 3D modeling software like blender
- Integration with the bevy game engine
- Target manifold tri & quad meshes for now