Mesh Data Structures?

5 minute read Published: 2023-01-29

At the moment, we're generating vertices using the Lyon crate and sticking them right into a vertex buffer for Vulkan to use.

let mut geometry: VertexBuffers<Point, u16> = VertexBuffers::new();
let mut geometry_builder = simple_builder(&mut geometry);

let options = FillOptions::tolerance(0.001);
let mut tessellator = FillTessellator::new();

let mut builder = tessellator.builder(&options, &mut geometry_builder);

builder.add_circle([0.0, 0.0].into(), 1.0, Winding::Positive);

builder.build().unwrap();

The geometry generation is fine, I plan to use Lyon for this purpose for as long as I can. I'm still thinking about it, but I think my problem is how do I store this data for several meshes and how do I edit the vertex data while still keeping track of where faces are.

In seek of answers, I turned to the Blender development wiki and found this page, which reveals that Blender uses to methods of structuring mesh data. Mesh, which is how meshes are stored when they're not being modified, and BMesh which is how they're stored when they are being modified. I'm not sure if either of these are relevant for what I'm trying to achieve, or if I need a similarly separated system for static and editable mesh data, so I'm going to look into both and see.

A few hours (maybe a day) later...

This is a few hours later and I'm getting there. I've downloaded the source code for Blender and found that there's some wonderful documentation in there that you have to generate with doxygen. Using the docs, I managed to find this:

typedef struct Mesh {
  DNA_DEFINE_CXX_METHODS(Mesh)
 
  ID id;
  struct AnimData *adt;
 
  struct Ipo *ipo DNA_DEPRECATED;
  struct Key *key;
 
  struct Material **mat;
 
  int totvert;
  int totedge;
  int totpoly;
  int totloop;
 
  CustomData vdata, edata, pdata, ldata;
  ...

DNA_mesh_types.h

This is the start of the struct definition for Mesh (there's quite a bit more but I can barely understand it, a lot of it says it is deprecated anyway). So, I think the data of vertex positions, edges and such are stored in the *data properties at the bottom there. They all exist as CustomData. I've had a look in the files for CustomData and I can't for the life of me figure out how it works, it seems to be doing memory management stuff, but I don't know C++ too well. If I were to guess, I'd say vdata stores vertices, e stores edges, p stores polygons and l stores loops.

I also found the documentation and code for BMesh. There's a lot going on in here that I don't understand too, BMesh seems to store the same CustomData as Mesh but it also has BMVert, BMEdge, BMFace and BMLoop. The documentation explains that faces are made up of "a circular linked list of loops" so I went and found the definition of BMLoop (and trimmed out the documentation for easier reading).

typedef struct BMLoop {
  BMHeader head;
  struct BMVert *v;
  struct BMEdge *e;
  struct BMFace *f;
  struct BMLoop *radial_next, *radial_prev;
  struct BMLoop *next, *prev;
} BMLoop;

bmesh_class.h

So BMLoop stores several pointers. A pointer to an edge, which exists as part of the face it is making up. A pointer to a vertex, which must be one of the two vertices that make up the edge it references. A pointer to the face that it is a part of. Pointers forwards and backwards in the circle to other loops that make up the face (next & prev). And finally pointers forwards and backwards in the circle that use the same edge. The edge sharing pointers are interesting, feels like it's less obvious why they might be useful compared to the other properties but I'm sure there necessary somewhere.

Looking at the other struct definitions, there's quite a bit of back and forth referencing going on. For example, BMEdge has pointers to two BMVert pointers (v1 & v2), but it also has a BMLoop pointer. BMVert has a similar thing going on with a pointer BMEdge.

There's another thing I've missed which is that in a BMEdge, there's two BMDiskLink's, one for each vertex. This keeps a linked list of edges around a vertex.

If you are interested in taking a look at the BMesh docs yourself , there's this page on the wiki which is pretty handy, although quite confusing when not accompanied by the doxygen docs, which can be generated by running doxygen in \doc\doxygen in the Blender source. Also, I may have made some false assumptions throughout this because it's quite complicated, if you have any corrections I would love to hear them.

What next?

There are a few things to think about now:

I may have not got very far with this here but it was fun having a look around the source, even if I can't understand all that much.