[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [53442] trunk/blender: Add dynamic topology support to sculpt mode

Nicholas Bishop nicholasbishop at gmail.com
Sun Dec 30 19:29:08 CET 2012


Revision: 53442
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=53442
Author:   nicholasbishop
Date:     2012-12-30 18:29:07 +0000 (Sun, 30 Dec 2012)
Log Message:
-----------
Add dynamic topology support to sculpt mode

* User documentation:
  wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.66/Dynamic_Topology_Sculpting

* Code review for this and the other dynamic-topology commits:
  https://codereview.appspot.com/6947064/
  Thanks to Sergey for doing code review!

* Add SCULPT_OT_dynamic_topology_toggle operator to enable or disable
  dynamic topology mode

* Most brushes need little modification for dynamic topology, but for
  some it won't work well and is disabled. This decision is made in
  sculpt_stroke_dynamic_topology().

* For brushes that need original data (e.g. grab brush) the topology
  is not updated during the stroke, but some changes to original
  vertex data is accessed were made since BMesh works a little
  differently from mesh/multires. This is abstracted with
  SculptOrigVertData and associated functions.

* Smooth brush gets yet another set of functions, for mesh and
  multires and dynamic topology and, separetely, masking

* For most brushes, the topology is updated during the stroke right
  before the regular brush action takes place. This is handled in
  sculpt_topology_update().

* Exiting sculpt mode also disables dynamic topology

* Sculpt undo works differently with BMesh. Since the contents of
  nodes in the PBVH do not remain static during a sculpt session, the
  SculptUndoNodes do not correspond with PBVHNodes if dynamic topology
  is enabled. Rather, each SculptUndoNode is associated with a
  BMLogEntry.

* Sculpt undo gets a few new cases: entering and exiting dynamic
  topology does an undo push of all mesh data. Symmetrize will
  similarly push a full copy of BMesh data, although it does so
  through the BMLog API.

* Undo and redo in dynamic-topology mode will do a full recalculation
  of the PBVH.

* Add some documentation to doc/sculpt.org. This could stand to be
  expanded a lot more, for now it mostly contains test cases for the
  undo system.

* Add SCULPT_OT_optimize operator to recalculate the BVH. The BVH gets
  less optimal more quickly with dynamic topology than regular
  sculpting. There is no doubt more clever stuff we can do to optimize
  it on the fly, but for now this gives the user a nicer way to
  recalculate it than toggling modes.

Modified Paths:
--------------
    trunk/blender/source/blender/editors/sculpt_paint/sculpt.c
    trunk/blender/source/blender/editors/sculpt_paint/sculpt_intern.h
    trunk/blender/source/blender/editors/sculpt_paint/sculpt_undo.c

Added Paths:
-----------
    trunk/blender/doc/sculpt.org

Added: trunk/blender/doc/sculpt.org
===================================================================
--- trunk/blender/doc/sculpt.org	                        (rev 0)
+++ trunk/blender/doc/sculpt.org	2012-12-30 18:29:07 UTC (rev 53442)
@@ -0,0 +1,199 @@
+* Dynamic-Topology Sculpting
+** Undo System
+   Dynamic-topology sculpting is lightly integrated with the existing
+   sculpt undo stack. There are three new undo/redo operations:
+   entering dynamic-topology mode, exiting dynamic-topology mode, and
+   BMLog operations.
+
+*** BMLog
+	BMLog is an undo/redo record tied to BMesh. The source is in
+	bmesh/intern/bmesh_log.c. The BMLog keeps track of the creation
+	and deletion of vertices and faces (which are limited to triangles
+	for now) as well as vertex movement.
+
+*** Entering dynamic-topology mode
+	
+
+*** Exiting dynamic-topology mode
+	When exiting dynamic-topology mode, it is important to write out
+	vertices and faces in a consistent order. This is needed because
+	regular sculpt undo only records vertex locations, with the
+	assumption that the order of vertices and faces (and the PBVH node
+	that owns them) remains static.
+
+	To accomplish this, the BMesh elements are reordered by their
+	unique IDs when exiting dynamic-topology mode.
+
+	A slightly different approach is needed when exiting
+	dynamic-topology mode via undo. Again, the mesh needs to have a
+	consistent ordering so as to match any regular sculpt undo
+	nodes. A simple re-ordering isn't enough though because
+	dynamic-topology mode splits all faces into triangles. The
+	simplest solution here is to directly store a full copy of the
+	mesh's vertex and face arrays. When undoing out of
+	dynamic-topology mode, the arrays are directly copied to the mesh
+	rather than doing any conversion from the BMesh.
+
+*** Test Scenarios
+	There are quite a few cases that the undo system has to handle. In
+	the interest of not breaking things when making future changes and
+	fixes, here are some manual testing steps to try. The expected
+	results are not described as they should be fairly obvious
+	(undo/redo results match original, no unfree'd memory, no graphics
+	glitches, etc.)
+
+	Note: when a bug is found in the undo system, this list should be
+	updated with a new test case with steps to reproduce the failure.
+
+**** Test 0
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Undo
+   - Redo
+   - Undo
+   - Redo
+   - Quit Blender
+
+**** Test 1
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Disable dynamic topology
+   - Undo
+   - Undo
+   - Undo
+   - Redo
+   - Redo
+   - Redo
+   - Quit Blender
+
+**** Test 2
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Enter editmode
+   - Quit Blender
+
+**** Test 3
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Disable dynamic topology
+   - Enter editmode
+   - Select all and move the mesh
+   - Enter sculpt mode
+   - Undo
+   - Redo
+   - XXX: this currently gives unexpected results. After undoing, a
+     redo doesn't get you back to the mesh as it was after moving in
+     editmode. It does not, however, crash or leak memory.
+	 
+**** Test 4
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Disable dynamic topology
+   - Undo
+   - Paint a single brush stroke
+   - Quit Blender	 
+	 
+**** Test 5
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Disable dynamic topology
+   - Undo
+   - Paint a single brush stroke
+   - Quit Blender
+
+**** Test 6
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Undo (undoes paint stroke)
+   - Undo (disables dynamic topology)
+   - Undo (used to do global undo, currently disabled though)
+   - Redo
+   - Redo
+   - Redo
+   - Quit Blender
+
+**** Test 7
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Disable dynamic topology
+   - Paint a single brush stroke
+   - Undo
+   - Undo
+   - Undo
+   - Undo
+   - Redo
+   - Redo
+   - Redo
+   - Redo
+   - Quit Blender
+
+**** Test 8
+   - Subdivide default cube
+   - Enter sculpt mode
+   - Paint a single brush stroke
+   - Enable dynamic topology
+   - Undo
+   - Undo
+   - Redo
+   - Redo
+   - Quit Blender
+
+**** Test 9
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Disable dynamic topology
+   - Undo
+   - Symmetrize
+   - Paint a single brush stroke
+   - Quit Blender
+
+**** Test 10
+   - Enter edit mode on default cube
+   - Exit edit mode and enter sculpt mode
+   - Enable dynamic topology
+   - Disable dynamic topology
+   - Undo
+   - Undo
+   - Enter edit mode
+   - Quit Blender
+
+**** Test 11
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Switch to mask brush (MKEY)
+   - Paint a single brush stroke
+   - Disable dynamic topology
+   - Undo
+   - Undo
+   - Undo
+   - Redo
+   - Redo
+   - Redo
+   - Quit Blender
+
+**** Test 13
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Hide part of the mesh (HKEY)
+   - Disable dynamic topology
+   - Undo
+   - Undo
+   - Undo
+   - Redo
+   - Redo
+   - Redo
+   - Quit Blender
+
+**** Test 14
+   - Enter sculpt mode on default cube
+   - Enable dynamic topology
+   - Paint a single brush stroke
+   - Save the file
+   - Undo

Modified: trunk/blender/source/blender/editors/sculpt_paint/sculpt.c
===================================================================
--- trunk/blender/source/blender/editors/sculpt_paint/sculpt.c	2012-12-30 18:28:36 UTC (rev 53441)
+++ trunk/blender/source/blender/editors/sculpt_paint/sculpt.c	2012-12-30 18:29:07 UTC (rev 53442)
@@ -65,6 +65,7 @@
 #include "BKE_report.h"
 #include "BKE_lattice.h" /* for armature_deform_verts */
 #include "BKE_node.h"
+#include "BKE_object.h"
 #include "BKE_subsurf.h"
 
 #include "BIF_glutil.h"
@@ -86,6 +87,8 @@
 
 #include "GPU_buffers.h"
 
+#include "bmesh.h"
+
 #include <math.h>
 #include <stdlib.h>
 #include <string.h>
@@ -98,8 +101,13 @@
 {
 	Object *ob = CTX_data_active_object(C);
 
-	if (ob && (ob->mode & OB_MODE_SCULPT))
+	if (ob && (ob->mode & OB_MODE_SCULPT)) {
 		multires_force_update(ob);
+
+		/* Set reorder=false so that saving the file doesn't reorder
+		 * the BMesh's elements */
+		sculptsession_bm_to_me(ob, FALSE);
+	}
 }
 
 float *ED_sculpt_get_last_stroke(struct Object *ob)
@@ -172,7 +180,8 @@
 	Mesh *me = (Mesh *)ob->data;
 	MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
 
-	if (mmd) return 0;
+	if (mmd || ob->sculpt->bm)
+		return 0;
 
 	/* non-locked shape keys could be handled in the same way as deformed mesh */
 	if ((ob->shapeflag & OB_SHAPE_LOCK) == 0 && me->key && ob->shapenr)
@@ -281,12 +290,130 @@
 	rcti previous_r; /* previous redraw rectangle */
 } StrokeCache;
 
+/************** Access to original unmodified vertex data *************/
 
+typedef struct {
+	BMLog *bm_log;
+
+	SculptUndoNode *unode;
+	float (*coords)[3];
+	short (*normals)[3];
+	float *vmasks;
+
+	/* Original coordinate, normal, and mask */
+	const float *co;
+	float mask;
+	short no[3];
+} SculptOrigVertData;
+
+
+/* Initialize a SculptOrigVertData for accessing original vertex data;
+ * handles BMesh, mesh, and multires */
+static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data,
+											 Object *ob,
+											 SculptUndoNode *unode)
+{
+	SculptSession *ss = ob->sculpt;
+	BMesh *bm = ss->bm;
+
+	memset(data, 0, sizeof(*data));
+	data->unode = unode;
+
+	if (bm) {
+		data->bm_log = ss->bm_log;
+	}
+	else {
+		data->coords = data->unode->co;
+		data->normals = data->unode->no;
+		data->vmasks = data->unode->mask;
+	}
+}
+
+/* Initialize a SculptOrigVertData for accessing original vertex data;
+ * handles BMesh, mesh, and multires */
+static void sculpt_orig_vert_data_init(SculptOrigVertData *data,
+									   Object *ob,
+									   PBVHNode *node)
+{
+	SculptUndoNode *unode;
+	unode = sculpt_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
+	sculpt_orig_vert_data_unode_init(data, ob, unode);
+									 
+}
+
+/* Update a SculptOrigVertData for a particular vertex from the PBVH
+ * iterator */
+static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data,
+										 PBVHVertexIter *iter)
+{
+	if (orig_data->unode->type == SCULPT_UNDO_COORDS) {
+		if (orig_data->coords) {
+			orig_data->co = orig_data->coords[iter->i];
+		}
+		else {
+			orig_data->co = BM_log_original_vert_co(orig_data->bm_log, iter->bm_vert);
+		}
+
+		if (orig_data->normals) {
+			copy_v3_v3_short(orig_data->no, orig_data->normals[iter->i]);
+		}
+		else {
+			/* TODO: log doesn't store normals yet */
+			normal_float_to_short_v3(orig_data->no, iter->bm_vert->no);
+		}
+	}
+	else if (orig_data->unode->type == SCULPT_UNDO_MASK) {
+		if (orig_data->vmasks) {
+			orig_data->mask = orig_data->vmasks[iter->i];
+		}
+		else {
+			orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert);
+		}
+	}
+}
+
+/**********************************************************************/
+
+/* Returns true if the stroke will use dynamic topology, false
+   otherwise.
+
+   Factors: some brushes like grab cannot do dynamic topology.
+   Others, like smooth, are better without. Same goes for alt-
+   key smoothing. */
+static int sculpt_stroke_dynamic_topology(const SculptSession *ss,
+										  const Brush *brush)
+{
+	return ((BLI_pbvh_type(ss->pbvh) == PBVH_BMESH) &&
+
+			(!ss->cache || (!ss->cache->alt_smooth)) &&
+
+			/* Requires mesh restore, which doesn't work with
+			 * dynamic-topology */
+			!(brush->flag & BRUSH_ANCHORED) &&
+			!(brush->flag & BRUSH_RESTORE_MESH) &&
+
+			(!ELEM6(brush->sculpt_tool,
+					/* These brushes, as currently coded, cannot
+					 * support dynamic topology */
+					SCULPT_TOOL_GRAB,
+					SCULPT_TOOL_ROTATE,
+					SCULPT_TOOL_THUMB,
+					SCULPT_TOOL_LAYER,
+
+					/* These brushes could handle dynamic topology,
+					 * but user feedback indicates it's better not
+					 * to */
+					SCULPT_TOOL_SMOOTH,
+					SCULPT_TOOL_MASK)));
+}
+
 /*** paint mesh ***/
 
-static void paint_mesh_restore_co(Sculpt *sd, SculptSession *ss)
+static void paint_mesh_restore_co(Sculpt *sd, Object *ob)
 {
+	SculptSession *ss = ob->sculpt;
 	StrokeCache *cache = ss->cache;
+	const Brush *brush = paint_brush(&sd->paint);
 	int i;
 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list