0% found this document useful (0 votes)
495 views

Blender Bmesh Data Structure

The document discusses the development of a new modeling system called BMesh for Blender to address limitations of the existing EditMesh structure. BMesh aims to provide persistent adjacency information between topological entities, locally modifiable topology, support for n-gons and arbitrary non-manifold conditions while maintaining similar memory efficiency as EditMesh. It describes the design of BMesh which uses double linked circular lists to store adjacency relationships between vertices, edges, and faces to allow flexible modeling operations.

Uploaded by

Joshua Smith
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
495 views

Blender Bmesh Data Structure

The document discusses the development of a new modeling system called BMesh for Blender to address limitations of the existing EditMesh structure. BMesh aims to provide persistent adjacency information between topological entities, locally modifiable topology, support for n-gons and arbitrary non-manifold conditions while maintaining similar memory efficiency as EditMesh. It describes the design of BMesh which uses double linked circular lists to store adjacency relationships between vertices, edges, and faces to allow flexible modeling operations.

Uploaded by

Joshua Smith
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

Developer page Discussion Edit History

From BlenderWiki
< Dev:Source | Modeling Note This page is a little outdated, though still somewhat relevant. See the bmesh blog (http://bmeshblender.wordpress.com) ] for more up-to-date information. Also see The official doc page.

As the demand for more advanced construction and selection tools has grown the limitations of the EditMesh structure have become apparent. The lack of persistent adjacency information makes the implementation of advanced selection tools complicated. Furthermore to implement construction tools the developer is often times put in the situation of modifying the EditMesh structures directly instead of relying upon a small set of trusted modular modeling operators. This slows down development of new tools and produces code that is more difficult to debug and maintain. The goal of the BMesh modeling kernel is to address these shortcomings as well as provide a flexible system that can be reused by other parts of Blender.

The creation of a newer and more advanced modeling system benefits not only programmers implementing Mesh Tools but the Blender Community as well. New advanced construction tools such as per-edge bevel which were, for all practical purposes, impossible under the old EditMesh system can now become realities. Furthermore current EditMesh tools ported to the new system will, in general, be faster and more robust.

In designing the BMesh kernel and API I have identified the following as constraints that any data structure must

1 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

satisfy to be considered an adequate replacement for EditMesh. 1. Must handle all non-manifold conditions that EditMesh can without complicated hacks and/or extensions. 2. Mesh API must provide a set of low level construction operators which can be combined to create more sophisticated tools. 3. Represent only a modest increase in memory requirements over current EditMesh. The first constraint may be considered of little consequence to some, however Blender is currently used by individuals for a large range of work and the ability of the current EditMesh system to represent any non-manifold condition as well as wire edges and point clouds are features that cannot be abandoned without creating significant hardships for a non-trivial portion of our users. In contrast to the first constraint the second one represents a significant departure from the current EditMesh system where seemingly simple tasks like splitting a face or edge are handled by monolithic tools comprised of hundreds of lines of code. Furthermore these tools manipulate the mesh structure directly which, in addition to their size, makes them very difficult to write, debug and maintain. Finally any new mesh modelling system must be nearly as memory efficient as the current EditMesh otherwise users might find that Blender no longer meets their needs. In considering the design of the BMesh system several data structures were researched including the Half-Edge, Radial Edge and Partial Entity data structures. All three of these data structures fulfilled the second constraint, but failed at one or both of the other two. For instance, Half-Edge is an elegant and memory efficient boundary representation and therefore satisfies constraint number three. Unfortunately it is very restricted in the types of topology that it can trivially represent. At best Half-Edge can handle multiple open manifold shells with extensions to support wire edges and loose points. However without complex and unproven extensions Half-Edge seemingly cannot begin to represent the wide array of non-manifold conditions that Blender requires for its modeller. The other two systems reviewed were the Radial Edge and Partial Entity structures. Both of these systems satisfied the first constraint and can supposedly represent any non-manifold condition possible. However the Radial Edge system does so at a cost, and memory requirements for it would represent between an estimated 4x to 10x increase over current EditMesh. On the other hand the Partial Entity structure was developed to address the storage requirements of the Radial Edge structure and is able to cut memory usage roughly in half. Despite this it would still represent quite a significant increase in memory requirements over Blenders current system. Finally both the Radial Edge and Partial Entity structures store information about entities that are relevant to the CAD world but not important for the problem domains in which Blender is used. These entities add unnecessary complexity to the data structures and functions for creating, modifying and validating them. Despite their shortcomings, each of these systems have useful ideas that made it into the design of the BMesh system as described in this paper. Unlike these solutions however, elegance of data representation was sacrificed in order to build a system that was flexible enough to meet all of Blenders needs. Special note should be made about the fact that no support for faces with holes in them are present at this time. They have been initially omitted from the modelling system to speed the development of a proof of concept. Their importance will be evaluated at a later time.

The new structure has the following features: Persistent adjacency information Locally modifiable topology

2 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

Faces of arbitrary length (N-Gons) Trivially represents any non-manifold condition including wire edges. The second through fourth features all depend upon the first, and as such the system which provides this facility is the foundation of the bmesh data structure. Persistent adjacency information is stored using a system of double linked circular lists which maintain the relationships among topological entities. These lists are conceptually the same as those found in other boundary representations such as half-edge, radial edge and partial entity and like these other representations each topological entity is itself a node in the cycles to which it belongs so the memory requirements for storing adjacency information remains minimal. These loops around topological entities are referred to as cycles and the base of each cycle is the entity which the cycle is designed to answer adjacency queries for. For instance a cycle designed to answer the question, what edges share this vertex would have the vertex itself as the base and its edges as the nodes of the cycle. Note that it is not required to explicitly store all possible adjacency relationships and full connectivity information can be quickly derived from using two or more cycles in conjunction. The three cycles stored explicitly within the bmesh structure are the Disk Cycle, the Radial Cycle and the Loop Cycle. Outlined below are the properties of each cycle and a list of functions for dealing with them. It is important to note that functions marked with an asterisk (*) are not part of the Mesh Tools API and only used by the modelling kernel. Furthermore when structure definitions are listed they have had certain members omitted for the sake of clarity.

The Disk Cycle: A circle of edges around a vertex


BASE: BME_Edge->vert pointer This cycle is the most complicated in terms of its structure. Each BME_Edge contains two BME_CycleNode structures to keep track of that edges membership in the disk cycle of each of its vertices. However for any given vertex it may be represented by the v1 pointer in some edges in its disk cycle and the v2 pointer for others. The BME_disk family of functions contains some nice utilities for navigating disk cycles and hides this detail from mesh tool authors. Note that unlike half edge, the disk cycle is completely independent from face data. One advantage of this is that wire edges are fully integrated into the topology database. Another is that the disk cycle has no problems dealing with non-manifold conditions involving faces. Functions relating to this cycle: BME_disk_append_edge* BME_disk_remove_edge* BME_disk_nextedge BME_disk_getpointer
The Disk Cycle

The Loop Cycle: A circle of face edges around a polygon.

3 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

BASE: BME_Poly->loopbase pointer To understand the loop cycle one must understand the way that polygons are stored in the bmesh modeler. Every face is represented by the following two structures:
typedef struct BME_Poly{ struct BME_Poly *next, *prev; struct BME_Loop *loopbase; /*First editloop around Polygon.*/ } BME_Poly;</pre> <pre>typedef struct BME_Loop { struct BME_Loop *next, *prev; struct BME_CycleNode radial; struct BME_Vert *v; struct BME_Edge *e; struct BME_Poly *f; } BME_Loop;</pre>

The BME_Poly structure is part of a ListBase stored in the BME_Mesh structure. It does not store the vertices or edges associated with it explicitly. Instead it simply stores a pointer to the first BME_Loop in the faces loop cycle. The following diagram shows the arrangement of BME_Loops in a clockwise winding face. As can be seen from the diagram, the BME_Loop structure is similar to the half-edge, in that it is directed according to face winding and only stores references to one vertex. Since each loop is also associated with an BME_Edge, a property of loops that must hold true is that loop->v and loop->next->v must be both contained within loop->e. Regardless of this the vertex pointers of loops are aligned to the face, not edges and the first vertex in a polygon will always be face->loopbase->v while the second one will be face->loopbase->next->v and so on.
The Bmesh face structure

Functions relating to this cycle:

BME_cycle_XXX family of functions.

The Radial Cycle: A circle of faces around an edge


BASE: BME_Edge->loop->radial structure

4 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

The radial cycle is similar to the radial cycle in the radial edge data structure. Unlike the radial edge however, the radial cycle in bmesh does not require a large amount of memory to represent non-manifold conditions since bmesh does not keep track of region or shell information. Figure two illustrates the construction of the radial cycle. Although any number of faces, and therefore loops, may be associated with any given edge, each BME_Edge stores a pointer to only one of its BME_Loops. This BME_Loop is considered to be the base for this BME_Edges radial cycle, and by following the next and prev pointers in the radial member of each BME_Loop structure we are able to visit every face associated with a particular edge. Functions relating to this cycle: BME_radial_append* BME_radial_remove_loop* BME_radial_nextloop BME_radial_find_face
The Bmesh radial cycle

Order of Elements in Cycles


Note that the order of elements in all three cycles is undefined. This leads to slightly increased seek time for deriving some adjacency relations, however the advantage is that no intrinsic properties of the mesh are dependant upon the cycle order and all non-manifold conditions are represented trivially.

Tool initialization and cleanup


Unlike the EditMesh system, the Bmesh kernel has a very strict API for tool authors to interface with. Editing of a mesh is 'locked' under normal circumstances and any calls to the modeling API will fail unless they appear between a pair of calls to BME_model_begin() and BME_model_end(). These functions are intended to perform several ' House Keeping' functions, such as clearing flags, statistic handling, interfacing with the undo system, tesselating N-Gons and doing intermediate level mesh validation.

Euler Operators
The functions listed below represent the primitive or atomic operators that mesh tools use to manipulate the topology of the structure.* The purpose of these functions is to provide a trusted set of operators to manipulate the mesh topology and which can also be combined together like building blocks to create more sophisticated tools. It needs to be stressed that NO manipulation of a mesh structure should be done outside of these functions. In the BMesh system, each euler is named by an acronym which describes what it actually does. Furthermore each Euler has a logical inverse. BME_MF/KF: Make Face and Kill Face BME_ME/KE: Make Edge and Kill Edge BME_MV/KV: Make Vert and Kill Vert

5 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

BME_SEMV/JEKV: Split Edge, Make Vert and Join Edge, Kill Vert BME_SFME/JFKE: Split Face, Make Edge and Join Face, Kill Edge BME_loop_reverse: Reverse the loop of a BME_Poly. Its own inverse Using a combination of these eleven eulers any non-manifold modelling operation can be achieved Furthermore each operator exploits the persistent adjacency information stored in the BMesh structure to make modifications to only local topology. This means that the running time of each operator is constrained only by the complexity of local detail and not overall mesh density. Finally every operator ensures that the data structure that it produces as output is fully valid and therefore frees the tool author from having to manipulate and validate the mesh directly. A side benefit of this design is that memory effecient undo can be implemented by storing a list of what eulers are called in what order. To undo the effects of the last tool executed, all that needs to be done is to traverse this list backwards and execute the inverse of each command.

Example: Splitting a Face

Splitting a face using Eulers

In the figure above we are shown how euler operators can be combined together simply to create more sophisticated effects. We start with a 4 sided quadrilateral labelled F1. By running the Euler SEMV first on edges E1 then E2 we turn F1 into a polygon with six sides. Finally we run SFME to connect the two new vertices and split the face down the middle. Here is the source code for accomplishing this:
BME_Vert *v1, *v2; BME_Poly *f2; v1 = BME_SEMV(e1); v2 = BME_SEMV(e2); f2 = BME_SFME(f1, v1, v2);

The remarkable thing about this is that with just three lines of code the tools writer can now accomplish what would have taken dozens if not a hundred or more lines to accomplish using the old EditMesh system. Furthermore the tool writer is not modifying the mesh directly and the complexity of maintaining a consistent data structure is cleanly handled by the Eulers internal code. Euler Function Reference

6 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

BME_Vert *BME_SEMV(BME_Mesh *bm, BME_Edge *e) SPLIT EDGE MAKE VERT: Takes a given edge and splits it into two, creating a new vert. The original edge, OE, is relinked to be between V1 and NV. OE is then moved from V2's disk cycle to NV's. The new edge, NE, is linked to be between NV and V2 and added to both vertices disk cycles. Finally the radial cycle of OE is traversed, splitting faceloop it encounters. Returns - Pointer to a new BME_Vert

Split Edge Make Vert

int BME_JEKV(BME_Mesh *bm, BME_Edge *ke, BME_Vert *kv) JOIN EDGE KILL VERT: Takes a pointer to an edge (ke) and pointer to one of its vertices (kv) and collapses the edge on that vertex. First ke is removed from the disk cycle of both kv and tv. Then the edge oe is relinked to run between ov and tv and is added to the disk cycle of ov. Finally the radial cycle of oe is traversed and all of its face loops are updated. Note that in order for this euler to work, kv must have exactly only two edges coincident upon it (valance of 2). A more generalized edge collapse function can be built using a combination of BME_SFME, BME_JFKE and BME_JEKV. Returns - 1 for success, 0 for failure

Join Edge Kill Vert

BME_Poly *BME_SFME(BME_Mesh *bm, BME_Poly *f, BME_Vert *v1, BME_Vert *v2) SPLIT FACE MAKE EDGE: Takes as input two vertices in a single face. An edge is created which divides the original face into two distinct regions. One of the regions is assigned to the original face and it is closed off. The second region has a new face assigned to it. Note that if the input vertices share an edge this will create a face with only two edges. Returns - Pointer to a new BME_Poly

Split Face Make Edge

int *BME_JFKE(BME_Mesh *bm, BME_Poly *f1, BME_Poly *f2)

7 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

JOIN FACE KILL EDGE: Takes two faces joined by a single 2-manifold edge and fuses them together. The edge shared by the faces must not be connected to any other edges which have Both faces in its radial cycle. An illustration of this appears in the figure on the right. In this diagram, situation A is the only one in which JFKE will return with a value indicating success. If the tool author wants to join two seperate faces which have multiple edges joining them as in situation B they should run JEKV on the excess edge(s) first. In the case of situation none of the edges joining the two faces can be safely removed because it would cause a face that loops back on itself.

Join Face Kill Edge

Also note that the order of arguments decides whether or not certain per-face attributes are present in the resultant face. For instance vertex winding, material index, smooth flags, ect are inherited from f1, not f2. Returns - 1 for success, 0 for failure

Important Note about Edges and Faces Tool authors should be made aware that the Bmesh data structures can behave in unexpected ways with regards to edges and faces. For instance, a face with only two edges is perfectly valid. From this it logically follows that between any two vertices an arbitrary number of edges can exist. While these unique properties can be exploited to create advanced modelling functions, tool authors should be very careful to clean up both faces with 2 edges and vertex pairs with multiple edges by the time their tool finishes execution. Although failure to do this won't be harmful at all to the stability of the modelling system, it is not considered a good thing to leave the structure in a state that is not intuitively understandable by the average end user. <NOTE: I am considering removing 2 edged faces from the modelling system alltogather>

The modelling kernel, aside from reliance on some low level Blender functionality, exists very much apart from the rest of the program. To integrate it in any meaningful way several large changes need to be made to Blender's code. The following sections discuss the areas that have been identified as the most pressing so far and discusses current efforts or plans to address them. It should be noted that although these areas are being discussed separately, they are in fact tightly interrelated.

Changes to Mesh DNA


So far what has been discussed is the data representations used by the Bmesh modelling system for editing operations. However, to efficiently store this data changes must be made to Blender's Mesh structure. Since this data is meant to be compact and memory efficient as possible, adjacency data will not be stored. Furthermore,

8 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

the current structures for representing vertices and edges, MVerts and MEdges respectively, need little or no modification from their current form. Therefore the real departure from the current system will be in the storage of faces. To store polygon data, two new structures must be introduced, the MPoly and MLoop:
typedef struct MPoly { int len, loopbase, flag; int pad; } MPoly; typedef struct MLoop { int v, e, f; int pad; } MLoop;

Note that these two structures are essentially more compact forms of the BME_Poly and BME_Loop structures. Both MPoly's and MLoops are stored in Mesh as single blocks of memory. All the loops in a polygon are stored contiguously in the mloop array, and the index for the base loop is stored in the MPoly structure along with the length.

Drawing support
In order to draw polygonal data with N-sides it needs to be first tessellated into simpler geometry. The eventual goal is to have a general purpose tessellator that can be used by any part of Blender and is not specifically tied to any mesh format. Currently the Bmesh code uses a very naive method to tessellate n-gon data and stores the result in an array pointed to by BME_Poly. This is then converted to an EditMesh whenever BME_model_end() is called. This conversion is just a stopgap measure for the moment to make testing of the kernel and tools associated with it possible. The eventual goal is to get derived mesh and modifiers working with the new mesh data structures directly and without any hacks or kludges. A better tesselator is required as the current one is very bad. This will be addressed soon.

Custom Face Data and Drawing support


Custom face data can be added simply by adding new custom data to face loops. This has the added avantage of greatly simplifying editing of geometry with UVs/VCols/Ect, and will in most cases not require the tool author to think about them at all. This change will not be possible without first addressing the derived mesh and modifier stack systems.

A proof of concept was completed as of mid January 2007. At the time the code used a heavily modified EditMesh as its base, and a video of it being used can be seen here: http://briggs.zanqdo.com/nonmanifold3.avi Once the basic framework proved successful the code was separated completely from EditMesh and several low level changes were made to support future development. Current efforts are underway to get the kernel to a state where a patch can be produced for others to review and comment on. Retrieved from "http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh" Categories: Development | Meshes | Script

9 of 10

2/23/2011 12:08 PM

Dev:Source/Modeling/Bmesh - BlenderWiki

http://wiki.blender.org/index.php/Dev:Source/Modeling/Bmesh

This page was last modified on 22 November 2010, at 15:21.

10 of 10

2/23/2011 12:08 PM

You might also like