olson-sean-k / plexus

Polygonal mesh processing.
https://plexus.rs
MIT License
162 stars 14 forks source link
geometry graphics half-edge mesh polygon rust topology
Plexus


Plexus is a highly composable Rust library for polygonal mesh processing. See the website for the most recent API documentation and the user guide.

GitHub docs.rs crates.io Gitter

Primitives

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.

Half-Edge Graphs

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.

Geometric Traits

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.

Integrations

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.

Encodings

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.