Expand description

Tools to help with generating vertex and index buffers.

Overview

While it would be possible for the tessellation algorithms to manually generate vertex and index buffers with a certain layout, it would mean that most code using the tessellators have to copy and convert all generated vertices in order to have their own vertex layout, or de-interleaved vertex formats, which is a very common use-case.

In order to flexibly and efficiently build geometry of various flavors, this module contains a number of builder interfaces that centered around the idea of building vertex and index buffers without having to know about the final vertex and index types.

See:

The traits above are what the tessellators interface with. It is very common to push vertices and indices into a pair of vectors, so to facilitate this pattern this module also provides:

  • The struct VertexBuffers is a simple pair of vectors of indices and vertices (generic parameters).
  • The struct BuffersBuilder which writes into a VertexBuffers and implements the various gemoetry builder traits. It takes care of filling the buffers while producing vertices is delegated to a vertex constructor.
  • The traits FillVertexConstructor, StrokeVertexConstructor and BuffersBuilder in order to generate any vertex type. In the first example below, a struct WithColor implements the FillVertexConstructor trait in order to create vertices composed of a 2d position and a color value from an input 2d position. This separates the construction of vertex values from the assembly of the vertex buffers. Another, simpler example of vertex constructor is the Positions constructor which just returns the vertex position untransformed.

Geometry builders are a practical way to add one last step to the tessellation pipeline, such as applying a transform or clipping the geometry.

While this is module designed to facilitate the generation of vertex buffers and index buffers, nothing prevents a given GeometryBuilder implementation to only generate a vertex buffer without indices, or write into a completely different format. These builder traits are at the end of the tessellation pipelines and are meant for users of this crate to be able to adapt the output of the tessellators to their own needs.

Do I need to implement geometry builders or vertex constructors?

If you only generate a vertex buffer and an index buffer (as a pair of standard Vec), then the simplest option is to work with custom vertex constructors and use VertexBuffers and BuffersBuilder.

For more specific or elaborate use cases where control over where the vertices as written is needed such as building de-interleaved vertex buffers or writing directly into a mapped GPU buffer, implementing custom geometry builders is the right thing to do.

Which of the vertex constructor or geometry builder traits to implement (fill/stroke/basic variants), depends on which tessellators the builder or constructor will interface with.

Examples

Generating custom vertices

The example below implements the FillVertexConstructor trait in order to use a custom vertex type MyVertex (containing position and color), storing the tessellation in a VertexBuffers<MyVertex, u16>, and tessellates two shapes with different colors.

extern crate lyon_tessellation as tess;
use tess::{FillVertexConstructor, VertexBuffers, BuffersBuilder, FillOptions, FillTessellator, FillVertex};
use tess::math::{Point, point};

// Our custom vertex.
#[derive(Copy, Clone, Debug)]
pub struct MyVertex {
  position: [f32; 2],
  color: [f32; 4],
}

// The vertex constructor. This is the object that will be used to create the custom
// verticex from the information provided by the tessellators.
struct WithColor([f32; 4]);

impl FillVertexConstructor<MyVertex> for WithColor {
    fn new_vertex(&mut self, vertex: FillVertex) -> MyVertex {
        MyVertex {
            position: vertex.position().to_array(),
            color: self.0,
        }
    }
}

fn main() {
    let mut output: VertexBuffers<MyVertex, u16> = VertexBuffers::new();
    let mut tessellator = FillTessellator::new();
    // Tessellate a red and a green circle.
    tessellator.tessellate_circle(
        point(0.0, 0.0),
        10.0,
        &FillOptions::tolerance(0.05),
        &mut BuffersBuilder::new(
            &mut output,
            WithColor([1.0, 0.0, 0.0, 1.0])
        ),
    );
    tessellator.tessellate_circle(
        point(10.0, 0.0),
        5.0,
        &FillOptions::tolerance(0.05),
        &mut BuffersBuilder::new(
            &mut output,
            WithColor([0.0, 1.0, 0.0, 1.0])
        ),
    );

    println!(" -- {} vertices, {} indices", output.vertices.len(), output.indices.len());
}

Generating a completely custom output

Using VertexBuffers<T> is convenient and probably fits a lot of use cases, but what if we do not want to write the geometry in a pair of vectors? Perhaps we want to write the geometry in a different data structure or directly into gpu-accessible buffers mapped on the CPU?

extern crate lyon_tessellation as tess;
use tess::{StrokeTessellator, GeometryBuilder, StrokeGeometryBuilder, StrokeOptions, Count, GeometryBuilderError, StrokeVertex, VertexId};
use tess::math::{Point, point};
use tess::path::polygon::Polygon;
use std::fmt::Debug;
use std::u32;

// A geometry builder that writes the result of the tessellation to stdout instead
// of filling vertex and index buffers.
pub struct ToStdOut {
    vertices: u32,
    indices: u32,
}

impl ToStdOut {
     pub fn new() -> Self { ToStdOut { vertices: 0, indices: 0 } }
}

impl GeometryBuilder for ToStdOut {
    fn begin_geometry(&mut self) {
        // Reset the vertex in index counters.
        self.vertices = 0;
        self.indices = 0;
        println!(" -- begin geometry");
    }

    fn end_geometry(&mut self) -> Count {
        println!(" -- end geometry, {} vertices, {} indices", self.vertices, self.indices);
        Count {
            vertices: self.vertices,
            indices: self.indices,
        }
    }

    fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
        println!("triangle ({}, {}, {})", a.offset(), b.offset(), c.offset());
        self.indices += 3;
    }

    fn abort_geometry(&mut self) {
        println!(" -- oops!");
    }
}

impl StrokeGeometryBuilder for ToStdOut {
    fn add_stroke_vertex(&mut self, vertex: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
        println!("vertex {:?}", vertex.position());
        if self.vertices >= u32::MAX {
            return Err(GeometryBuilderError::TooManyVertices);
        }
        self.vertices += 1;
        Ok(VertexId(self.vertices as u32 - 1))
    }
}

fn main() {
    let mut output = ToStdOut::new();
    let mut tessellator = StrokeTessellator::new();
    tessellator.tessellate_polygon(
        Polygon {
            points: &[point(0.0, 0.0), point(10.0, 0.0), point(5.0, 5.0)],
            closed: true,
        },
        &StrokeOptions::default(),
        &mut output,
    );
}

Structs

  • A temporary view on a VertexBuffers object which facilitate the population of vertex and index data.
  • Number of vertices and indices added during the tessellation.
  • A geometry builder that does not output any geometry.
  • A simple vertex constructor that just takes the position.
  • Structure that holds the vertex and index data.

Enums

Traits

Functions

Type Aliases