plexus icon indicating copy to clipboard operation
plexus copied to clipboard

Support writing and reading meshes in the PLY format.

Open olson-sean-k opened this issue 6 years ago • 6 comments

Currently, there is no way to save or restore Meshes. Serialization support via serde has been completed (not merged at this time), but is questionable and lacks interoperability and domain-specific features. Support for the PLY format should allow Meshes to be persisted to disk and shared with other software.

PLY allows meshes to associate arbitrary properties with their elements. This works well with Plexus' notion of arbitrary geometry. While topology (i.e., elements) will always have the same structure in Plexus, geometry (i.e., properties) can implement optional traits that allow for expression in the PLY format.

olson-sean-k avatar Dec 08 '17 20:12 olson-sean-k

Have you considered using some kind of generic generator? Some way to decouple the format from plexus mesh implementation? PLY, Obj, stl? please correct me if I am talking nonsense. Something like: https://doc.cgal.org/latest/Polyhedron/classCGAL_1_1Polyhedron__incremental__builder__3.html

I really like your library, this is pretty well done work.

LuisAyuso avatar May 17 '18 12:05 LuisAyuso

Have you considered using some kind of generic generator? ... Something like: ...

Thanks for the link! It'll take me some time to wrap my head around those docs, but I'll take a look and see if I can apply that here somehow. For PLY, I'd like to provide some mapping from user geometry to properties, probably via traits. Ideally this wouldn't be too intrusive and would be as automatic as possible. That may not be general enough of an approach for other formats though.

olson-sean-k avatar May 24 '18 18:05 olson-sean-k

I think serde might not be the best option for this use case and a more specialized API might be of interest. In a recent project of mine I have used meshio (Python) and I think something like that would be great to have in Rust, in its own crate standardizing mesh representation using expressive typing.

My idea is to have a deserializer generic over the mesh representation (MeshInsert trait or something like that) and provide a vec based one and a half edge data structure. This would allow to reuse deseralizers directly with a desired format. Serialization would be handled in a similar way. (It might take an attempt or two until a design is found so that no copying is needed.) I'll try to prototype this in a few days and provide more details.

leoschwarz avatar Jul 03 '18 13:07 leoschwarz

There's currently some working code on the ply branch, but it only provides decoding so far (not encoding). It also does not yet handle arcs nor edges; I expect this can be done by providing traits for this that are unused for buffers and are easily stubbed for graphs.

To support decoding, there are some core traits: VertexDecoder, FaceDecoder, and FromEncoding. These are generic w.r.t. mesh data structures and provide basic type information about geometry and indexing. Both buffer and graph types implement FromEncoding.

Each encoding then provides more specific traits, which can be implemented in terms of the core traits. For PLY, there is a general Encoding trait for metadata and then decoder traits for elements and properties. These are used with FromPly (and ToPly) to handle marshaling the data.

A simple encoding scheme takes approximately 80 LoC to implement this way. See PointEncoding for an example. The bulk of that implementation is here and here. Usage looks something like this:

let ply: &[u8] = include_bytes!("cube.ply");
let (graph, _) = MeshGraph::<Point3<f64>>::from_ply(PointEncoding::default(), ply).unwrap();

The PLY implementation is supported by ply-rs. Some of its types are re-exported and others are hidden. Some extension traits are provided to ease conversions and error handling (see read_scalar and read_list).

olson-sean-k avatar Jun 12 '19 00:06 olson-sean-k

(Most of) the changes described above landed in e57f096. There's still no support for serializing meshes into PLY format, but I'm happy enough with the way deserialization is implemented.

As always, naming is tricky, and I worry I've overloaded the term "encoding" too much. In particular, formats like PLY are referred to as "encodings", but the types that marshal data (handle the details of conversions) to and from PLY are also called "encodings". PointEncoding is an example of this. These should probably use a different name!

olson-sean-k avatar Jun 17 '19 17:06 olson-sean-k

I'm experimenting with both internal and public APIs for mutating and initializing mesh data structures on the transact branch. This temporary test demonstrates a builder API that is similar to CGAL's incremental polyhedron builder mentioned previously. This could act as the basic interface for initializing meshes and I plan to try implementing other APIs like FromEncoding and FromIndexer in terms of MeshBuilder.

Just a note, but the implementation for MeshGraph used in the linked code just wraps the mutation API. This leads to some awkwardness in the implementation, because Mutation doesn't use transitionary types like the builder API does. ~~Top-level types must support moving MeshGraph and Mutation in and out of Options. :nauseated_face: See the Drain trait.~~

olson-sean-k avatar Nov 19 '19 00:11 olson-sean-k