Plexus is a highly composable Rust library for polygonal mesh processing. See the website for the most recent API documentation and the user guide.
Plexus provides primitive topological structures that can be composed using generators and iterator expressions. Iterator expressions operate over a sequence of polygons with arbitrary vertex data. These polygons can be decomposed, tessellated, indexed, and collected into mesh data structures.
use decorum::R64; // See "Integrations".
use nalgebra::Point3;
use plexus::buffer::MeshBuffer;
use plexus::index::Flat3;
use plexus::prelude::*;
use plexus::primitive::generate::Position;
use plexus::primitive::sphere::UvSphere;
use crate::render::pipeline::{Color4, Vertex};
type E3 = Point3<R64>;
// Create a buffer of index and vertex data from a uv-sphere.
let buffer: MeshBuffer<Flat3, Vertex> = UvSphere::new(16, 8)
.polygons::<Position<E3>>()
.map_vertices(|position| Vertex::new(position, Color4::white()))
.triangulate()
.collect();
The above example uses a generator and iterator expression to transform the positional data of a sphere into a linear buffer for indexed drawing. See the sphere example for a rendered demonstration.
The MeshGraph
type represents polygonal meshes as an ergonomic half-edge
graph that supports arbitrary data in vertices, arcs (half-edges), edges,
and faces. Graphs can be traversed and manipulated in many ways that iterator
expressions and linear buffers cannot.
use plexus::graph::MeshGraph;
use plexus::prelude::*;
use plexus::primitive::Tetragon;
use ultraviolet::vec::Vec3;
type E3 = Vec3;
// Create a graph of a tetragon.
let mut graph = MeshGraph::<E3>::from(Tetragon::from([
(1.0, 1.0, 0.0),
(-1.0, 1.0, 0.0),
(-1.0, -1.0, 0.0),
(1.0, -1.0, 0.0),
]));
// Extrude the face of the tetragon.
let key = graph.faces().nth(0).unwrap().key();
let face = graph.face_mut(key).unwrap().extrude_with_offset(1.0).unwrap();
Plexus avoids exposing very basic topological operations like inserting individual vertices into a graph, because they can easily be done incorrectly. Instead, graphs are typically manipulated with more abstract operations like merging and splitting.
See the user guide for more details about graphs.
Plexus provides optional traits to support spatial operations by exposing
positional data in vertices. If the data exposed by the AsPosition
trait
supports these geometric traits, then geometric operations become available in
primitive and mesh data structure APIs.
use glam::Vec3A;
use plexus::geometry::{AsPosition, Vector};
use plexus::graph::GraphData;
use plexus::prelude::*;
type E3 = Vec3A;
#[derive(Clone, Copy, PartialEq)]
pub struct Vertex {
pub position: E3,
pub normal: Vector<E3>,
}
impl GraphData for Vertex {
type Vertex = Self;
type Arc = ();
type Edge = ();
type Face = ();
}
impl AsPosition for Vertex {
type Position = E3;
fn as_position(&self) -> &Self::Position {
&self.position
}
}
Data structures like MeshGraph
also provide functions that allow user code to
compute geometry without requiring any of these traits; the data in these
structures may be arbitrary, including no data at all.
Plexus integrates with the theon
crate to provide geometric traits and
support various mathematics crates in the Rust ecosystem. Any mathematics crate
can be used and, if it is supported by Theon, Plexus provides geometric APIs by
enabling Cargo features.
Feature | Default | Crate |
---|---|---|
geometry-cgmath |
No | cgmath |
geometry-glam |
No | glam |
geometry-mint |
No | mint |
geometry-nalgebra |
No | nalgebra |
geometry-ultraviolet |
No | ultraviolet |
Enabling the corresponding feature is recommended if using one of these supported crates.
Plexus also integrates with the decorum
crate for floating-point
representations that can be hashed for fast indexing. The R64
type is a
(totally ordered) real number with an f64
representation that cannot be NaN
nor infinity, for example. Geometric conversion traits are implemented for
supported types to allow for implicit conversions of scalar types.
Plexus provides support for polygonal mesh encodings. This allows mesh data
structures like MeshGraph
and MeshBuffer
to be serialized and deserialized
to and from various formats.
use nalgebra::Point3;
use plexus::encoding::ply::{FromPly, PositionEncoding};
use plexus::graph::MeshGraph;
use plexus::prelude::*;
use std::fs::File;
type E3 = Point3<f64>;
let ply = File::open("cube.ply").unwrap();
let encoding = PositionEncoding::<E3>::default();
let (graph, _) = MeshGraph::<E3>::from_ply(encoding, ply).unwrap();
Encoding support is optional and enabled via Cargo features.
Feature | Default | Encoding | Read | Write |
---|---|---|---|---|
encoding-ply |
No | PLY | Yes | No |
See the teapot example for a rendered demonstration of reading a mesh from the file system.