Since the dawn of time (i.e. since SharpConstruct) sculpt symmetry essentially works as though multiple brushes are operating on the mesh simultaneously. This is not necessarily the most efficient method, but it is easy to implement and is generally fairly robust. It is even serviceable with dynamic topology, which is somewhat surprising considering that no attempt is made to keep the topology symmetric. That said, imperfections do begin to pop up with dynamic topology, so a workaround would be nice.
Naturally the first thing I had to do was go all Photo Booth on Suzanne.
While there are certainly fancy options such as dynamically mirroring the mesh while sculpting and constantly re-stitching the center, a simpler alternative is to add an operator that the user can manually invoke to symmetrize the mesh. (I was inordinately happy to discover that symmetrize is an actual word.)
I’ve just gotten a basic implementation working, code available as usual from my Blender Github repository, symmetrize branch.
To test it, just select a mesh and spacebar-search for the symmetrize operator. Only works in object mode for now. Once the algorithm is solid for meshes I’ll add a BMesh version. Note that right now it’s hardcoded to symmetrize across the YZ plane from X- to X+.
It’s actually a bit harder to symmetrize than you might expect; there are a significant number of cases to think through. A few examples follow:
A mesh consisting of a hexagon, pentagon, and three quads being symmetrized.
Any polygon that is already symmetric across the axis will end up just as it started. The pentagon and square demonstrate this.
In general, a polygon that is not symmetric across the axis gets chopped in half — everything on one side of the axis gets thrown away, and the rest is copied over and mirrored. The hexagon at left demonstrates this.
An exception is made for asymmetric polygons that have some symmetric edges. For example, the two quads adjacent to the square do not get split down the middle, as that would require the nice square to become a hexagon. Instead, a triangle is added to separate the asymmetric polygon from the symmetric one, and then a mirrored copy is added.
There are actually more cases — I’m still working on dealing with polygons that cross the axis multiple times. Loose edges should also be mirrored. It may be helpful to add additional cleanups too, such as optional merging of thin polygons that are sometimes generated near the axis.
As noted, this is just a regular mesh operator for now, not yet part of the dynamic topology branch. I’ll merge it in once the tool is nicely stabilized, but I should mention that I don’t currently have a lot of time to work on Blender.
Pushed some changes tonight that improve the output of dyntopo when collapse edges is enabled. Should fix the random “flaps” of isolated triangles. Various crashes also fixed.
The past couple days I’ve been looking into the performance of the remesh modifier. Although I’m committed to getting dynamic-topology sculpting working, the remesh modifier is also a useful approach to clay-like sculpting. Ideally it should support quite high density levels at fast speeds. Below I’ll get very detailed about what I’ve found, but spoiler alert: it turns out there’s quite a lot we can do to make remesh faster.
As a follow-up to yesterday’s post about better flat-shaded drawing for regular sculpt mode, you can now toggle between smooth and flat shading in dyntopo. This is just a display setting for now, doesn’t alter the actual mesh at all.
Edge collapse is now available again, but disabled by default. When making large alterations to the mesh (e.g. with the snake hook brush), you can enable the “Collapse Short Edges” toggle to get smoother and more regular topology. The trade-off is that, while enabled, brushes will tend to erase fine details. For example, if you use the crease brush to define a sharp indent in the mesh, you probably don’t want to collapse short edges in that area.
Added a “Simplify” brush that only modifies topology. It’s pretty much the same as using a regular brush with zero strength and Collapse Short Edges enabled. (Note that as this is a new brush it won’t show up in your brush list by default, you have to add a simplify brush manually.)
Spent a little time today looking at improving one small aspect of sculpt drawing: flat-shaded faces in solid view mode with VBO enabled.
In current trunk, flat shading in sculpt mode looks quite different depending on whether VBO is enabled or not. When VBO is not enabled, and immediate mode is used to draw the mesh, each face (triangle or quad) gets drawn separately. Before drawing the face, a single averaged normal and mask value is calculated. The normal and mask values are used for each vertex of the face. In contrast, VBO drawing uses a shared vertex normal and mask value for every face that uses the vertex. This behavior is correct for smooth shading, but looks strange for flat shading.
The benefit to using a shared vertex value is lower storage requirements; the mesh’s faces can simply reference vertices with an index (and the index can usually be a short, so two bytes per index.) When flat shading is enabled, however, OpenGL uses only a single vertex’s value for the entire face. On any particular face this generally looks OK, but the problem is evident when adjacent quads appear to have the same normal despite facing different directions.
Click to embiggen Left: current trunk. Middle: averaged normals and interpolated mask. Right: averaged normals and averaged mask.
I’ve implemented a simple fix for this, available from the sculpt-flat-shading branch on Github. If the mesh is flat shaded, the index buffer is scrapped in favor of duplicating vertex values. The GPU storage requirements are in general slightly higher, but the output looks much more correct.
In the image above you can see two variations of the fix. Both have flat-shaded normals, but the middle uses interpolated mask values while the right uses a single average mask value. Using an averaged mask value seems to look a bit better, although it’s technically a bit inaccurate as sculpt brushes operate on vertex mask values, not the average.
One further issue which affects smooth shading is that interpolation over quads doesn’t look very good with OpenGL. Mask values and normals don’t look smooth because OpenGL tessellates quads into two triangles. The vertex values are interpolated within each triangle, without regard to the missing corner. It’s technically possible to solve this with shaders. Instead of using regular vertex attributes, the vertices just get texture coordinates and the attributes are put into a texture. A bilinear (or even bicubic) interpolation can then be coded in the shader. Such fanciness is probably of questionable usefulness though, and at the very least will have to wait on the completion of other improvements to the various drawing systems in Blender.
My current plan is to wait until 2.64 is released, then commit the flat-shading fix (with averaged mask values.)
OK, time for some updates. Based in part of some useful feedback on BlenderArtists, I’ve spent the past week experimenting with various approaches to the topological update step. I think it’s now getting much closer to what people want to see; in particular the subdivision is localized to the area directly around the brush.
A sphere with varying levels of detail. The small clay stroke in the center has edge lengths a couple orders of magnitude smaller than the large edges to the right.
Until now, the topological updates were based on the paper “Freestyle: Sculpting meshes with self-adaptive topology”. This paper assumes that the entire mesh will have a consistent detail size. Note that since the algorithm collapses short edges, you can lose detail if you change your brush to a coarser detail size.
In contrast, the approach I am using now is to only do edge splits. If you create very dense topology and then set a coarse detail size, the dense topology will remain and not be collapsed. To handle situations where you want to make the topology less dense, a separate simplify brush will be added. (I haven’t done that yet, but the code is largely already written.)
In order to get a good boundary between a dense portion of the mesh and larger polygons, I used a trick that Farsthary described in his unlimited clay research: identify nearby pole vertices (in this case, defined as having nine or more adjacent edges) and split the adjacent edges. This isolates the pole vertex and prevents it from distorting the mesh. This wasn’t an issue with edge collapses, which implicitly force triangles to be relatively regular, but in a subdivision-only approach it turns out this is a really critical step. Without it the mesh quickly develops degenerate triangles that can’t be smoothed away.
Some other changes: the topology update now occurs before each sculpt step, so the result should match the brush shape a bit better. The brush also avoids the accumulation problem now, so repeatedly brushing over an area won’t continually build the surface up (unless the brush accumulate option is on, of course.)
I’m still looking into surface relaxation; I suspect a smoother result can be achieved while still maintaining detail, but haven’t found a good way to do it yet. My current short-term goal is to do some brush work: add the reduce/simplify mentioned earlier and fix the guaranteed-crash brushes like grab.
As usual, the code is available from the dyntopo branch on Github.
The last major change that was needed to get undo working was rewriting the edge-collapse function in dyntopo. Originally it was coded using the vertex/edge splicing euler operators. The two vertices of the edge were spliced (merged), then duplicate edges were spliced, and various cleanups on faces were performed. This was already tricky and error-prone, and getting undo working for splice was not going too well. As a workaround, I replaced all the splicing code by simply creating new faces and deleting the original vertex/edge. Simpler code and the undo code was already working for that case. That said, proper logging of splicing is something that will need to be revisited if the BMLog were considered as a replacement for editmesh-undo.
There are of course still bugs to work through, but this is a good validation of the logging technique.
I’m continuing to work on the BMesh logging system that will provide undo/redo for dynamic-topology sculpting. One aspect of this logging system is assigning unique IDs to elements. In order to do this efficiently, I wrote a small C++ library to efficiently store and query non-overlapping ranges. This code is now used in the bmesh-undo branch to keep track of unused IDs. Since the code is fairly generic and might be useful elsewhere, I’ve pushed the RangeTree code into its own repository on Github.
Another recent change in the bmesh-undo branch is an easier way to examine the BMesh log. I added a new view to the outliner which shows changes to the mesh while in edit mode:
This is of course just a temporary debugging tool, not something that would be released. Note that low-level actions like mesh-element deletion and flag setting are now nicely grouped into high-level actions like “Translate” and “Extrude Region and Move”. Pressing CTRL+A/CTRL+SHIFT+A while in edit mode will now step back by high-level actions, and seems to work fairly well. (Again, the CTRL+A hotkey is just a temporary debugging tool.)
Back on the dynamic topology side, I am continuing to make updates to integrate the BMesh logging system with sculpt’s undo system. It is starting to work a bit, but still lots of glitches and crashes to deal with yet. I’m hoping that by improving the testing tools in the bmesh-undo branch I can work out a lot of these issues separately from dynamic-topology sculpting.