2.5/Sculpt:
authorNicholas Bishop <nicholasbishop@gmail.com>
Tue, 30 Jun 2009 23:06:50 +0000 (23:06 +0000)
committerNicholas Bishop <nicholasbishop@gmail.com>
Tue, 30 Jun 2009 23:06:50 +0000 (23:06 +0000)
== Re-added smooth stroke ==

UI: toggle is just in the sculpt menu for now. Also changes the sculpt paint cursor slightly, draws a line between previous and current locations.

It's a different implementation than in 2.4, works like this:

The stroke interpolates between the last mouse location and the current location, weighted towards the previous location. If the stroke gets within a certain radius of the current mouse location, the stroke stops. This radius allows for sharp turns in the stroke.

Todo: there are two hard-coded parameters that should become user settable, that's the weighting between previous and current locations, and most important, the no-update radius.

Note also that this option was added as a per-brush flag, worth discussing whether that's the correct place, or whether it should be a sculpt setting like symmetry?

== Improved stroke spacing ==
The previous implementation of stroke spacing simply guaranteed that stroke dots would not occur any closer than the space setting. It now forces stroke dots to always be the specified distance apart.

Todo: Performance gets pretty awful with a small spacing setting, this needs optimization.

source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_view3d/view3d_header.c
source/blender/makesdna/DNA_brush_types.h
source/blender/makesrna/intern/rna_brush.c

index 8f0b52ef3a19159ae9a70b6f0ad2452f26dd824a..a2b883eabfcc4fcb888932662cda951b76c46bdd 100644 (file)
@@ -156,7 +156,6 @@ typedef struct StrokeCache {
        float old_grab_location[3];
        int symmetry; /* Symmetry index between 0 and 7 */
        float view_normal[3], view_normal_symmetry[3];
-       int last_dot[2]; /* Last location of stroke application */
        int last_rake[2]; /* Last location of updating rake rotation */
 } StrokeCache;
 
@@ -849,15 +848,6 @@ static void do_symmetrical_brush_actions(Sculpt *sd, StrokeCache *cache)
        const char symm = sd->flags & 7;
        int i;
 
-       /* Brush spacing: only apply dot if next dot is far enough away */
-       if((sd->brush->flag & BRUSH_SPACE) && !(sd->brush->flag & BRUSH_ANCHORED) && !cache->first_time) {
-               int dx = cache->last_dot[0] - cache->mouse[0];
-               int dy = cache->last_dot[1] - cache->mouse[1];
-               if(sqrt(dx*dx+dy*dy) < sd->brush->spacing)
-                       return;
-       }
-       memcpy(cache->last_dot, cache->mouse, sizeof(int) * 2);
-
        VecCopyf(cache->location, cache->true_location);
        VecCopyf(cache->grab_delta_symmetry, cache->grab_delta);
        cache->symmetry = 0;
@@ -1050,16 +1040,21 @@ static void draw_paint_cursor(bContext *C, int x, int y, void *customdata)
 {
        Sculpt *sd= CTX_data_tool_settings(C)->sculpt;
        
-       glTranslatef((float)x, (float)y, 0.0f);
-       
        glColor4ub(255, 100, 100, 128);
        glEnable( GL_LINE_SMOOTH );
        glEnable(GL_BLEND);
+
+       glTranslatef((float)x, (float)y, 0.0f);
        glutil_draw_lined_arc(0.0, M_PI*2.0, sd->brush->size, 40);
+       glTranslatef((float)-x, (float)-y, 0.0f);
+
+       if(sd->session && sd->session->cache && sd->brush && (sd->brush->flag & BRUSH_SMOOTH_STROKE)) {
+               ARegion *ar = CTX_wm_region(C);
+               sdrawline(x, y, sd->session->cache->mouse[0] - ar->winrct.xmin, sd->session->cache->mouse[1] - ar->winrct.ymin);
+       }
+
        glDisable(GL_BLEND);
        glDisable( GL_LINE_SMOOTH );
-       
-       glTranslatef((float)-x, (float)-y, 0.0f);
 }
 
 static void toggle_paint_cursor(bContext *C)
@@ -1202,6 +1197,9 @@ static void sculpt_update_cache_invariants(Sculpt *sd, bContext *C, wmOperator *
        RNA_int_get_array(op->ptr, "initial_mouse", cache->initial_mouse);
        cache->depth = RNA_float_get(op->ptr, "depth");
 
+       cache->mouse[0] = cache->initial_mouse[0];
+       cache->mouse[1] = cache->initial_mouse[1];
+
        /* Truly temporary data that isn't stored in properties */
 
        view3d_set_viewcontext(C, &cache->vc);
@@ -1407,13 +1405,99 @@ static void sculpt_flush_update(bContext *C)
        ED_region_tag_redraw(ar);
 }
 
-static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
+/* Returns zero if no sculpt changes should be made, non-zero otherwise */
+static int sculpt_smooth_stroke(Sculpt *s, int output[2], wmEvent *event)
 {
+       output[0] = event->x;
+       output[1] = event->y;
+
+       if(s->brush->flag & BRUSH_SMOOTH_STROKE && s->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
+               StrokeCache *cache = s->session->cache;
+               float u = .9, v = 1.0 - u;
+               int dx = cache->mouse[0] - event->x, dy = cache->mouse[1] - event->y;
+               int radius = 50;
+
+               /* If the mouse is moving within the radius of the last move,
+                  don't update the mouse position. This allows sharp turns. */
+               if(dx*dx + dy*dy < radius*radius)
+                       return 0;
+
+               output[0] = event->x * v + cache->mouse[0] * u;
+               output[1] = event->y * v + cache->mouse[1] * u;
+       }
+
+       return 1;
+}
+
+/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
+int sculpt_space_stroke_enabled(Sculpt *s)
+{
+       Brush *br = s->brush;
+       return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
+}
+
+/* Put the location of the next sculpt stroke dot into the stroke RNA and apply it to the mesh */
+static void sculpt_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, int mouse[2])
+{
+       Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+       StrokeCache *cache = sd->session->cache;
        PointerRNA itemptr;
+       float cur_depth;
+       float center[3];
+
+       cur_depth = read_cached_depth(&cache->vc, mouse[0], mouse[1]);
+       unproject(sd->session->cache->mats, center, mouse[0], mouse[1], cur_depth);
+                               
+       /* Add to stroke */
+       RNA_collection_add(op->ptr, "stroke", &itemptr);
+       RNA_float_set_array(&itemptr, "location", center);
+       RNA_int_set_array(&itemptr, "mouse", mouse);
+       RNA_boolean_set(&itemptr, "flip", event->shift);
+       sculpt_update_cache_variants(sd, &itemptr);
+                               
+       sculpt_restore_mesh(sd);
+       do_symmetrical_brush_actions(sd, cache);
+                               
+       sculpt_post_stroke_free(sd->session);
+}
+
+/* For brushes with stroke spacing enabled, moves mouse in steps
+   towards the final mouse location. */
+static int sculpt_space_stroke(bContext *C, wmOperator *op, wmEvent *event, Sculpt *s, const int final_mouse[2])
+{
+       StrokeCache *cache = s->session->cache;
+       int cnt = 0;
+
+       if(sculpt_space_stroke_enabled(s)) {
+               float vec[2] = {final_mouse[0] - cache->mouse[0], final_mouse[1] - cache->mouse[1]};
+               int mouse[2] = {cache->mouse[0], cache->mouse[1]};
+               float length, scale;
+               int steps = 0, i;
+
+               /* Normalize the vector between the last stroke dot and the goal */
+               length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
+
+               if(length > FLT_EPSILON) {
+                       scale = s->brush->spacing / length;
+                       vec[0] *= scale;
+                       vec[1] *= scale;
+
+                       steps = (int)(length / s->brush->spacing);
+                       for(i = 0; i < steps; ++i, ++cnt) {
+                               mouse[0] += vec[0];
+                               mouse[1] += vec[1];
+                               sculpt_brush_stroke_add_step(C, op, event, mouse);
+                       }
+               }
+       }
+
+       return cnt;
+}
+
+static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
+{
        Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
        ARegion *ar = CTX_wm_region(C);
-       float center[3];
-       int mouse[2] = {event->x, event->y};
        float cur_depth;
 
        sculpt_update_mesh_elements(C);
@@ -1433,21 +1517,19 @@ static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, wmEvent *event
        }
 
        if(sd->session->cache) {
-               cur_depth = read_cached_depth(&sd->session->cache->vc, event->x, event->y);
-               unproject(sd->session->cache->mats, center, event->x, event->y, cur_depth);
-
-               /* Add to stroke */
-               RNA_collection_add(op->ptr, "stroke", &itemptr);
-               RNA_float_set_array(&itemptr, "location", center);
-               RNA_int_set_array(&itemptr, "mouse", mouse);
-               RNA_boolean_set(&itemptr, "flip", event->shift);
-               sculpt_update_cache_variants(sd, &itemptr);
+               int mouse[2];
 
-               sculpt_restore_mesh(sd);
-               do_symmetrical_brush_actions(CTX_data_tool_settings(C)->sculpt, sd->session->cache);
-
-               sculpt_flush_update(C);
-               sculpt_post_stroke_free(sd->session);
+               if(sculpt_smooth_stroke(sd, mouse, event)) {
+                       if(sculpt_space_stroke_enabled(sd)) {
+                               if(!sculpt_space_stroke(C, op, event, sd, mouse))
+                                       ED_region_tag_redraw(ar);
+                       }
+                       else
+                               sculpt_brush_stroke_add_step(C, op, event, mouse);
+                       sculpt_flush_update(C);
+               }
+               else
+                       ED_region_tag_redraw(ar);
        }
 
        /* Finished */
index 79ea90864f32b2bc008fa5138590119391efdb26..5edcd203e166452a590baf1e9cf5063a2d6ec094 100644 (file)
@@ -4557,6 +4557,7 @@ static void view3d_sculpt_menu(bContext *C, uiLayout *layout, void *arg_unused)
        uiItemR(layout, NULL, 0, &rna, "rake", 0, 0, 0);
        uiItemR(layout, NULL, 0, &rna, "anchored", 0, 0, 0);
        uiItemR(layout, NULL, 0, &rna, "space", 0, 0, 0);
+       uiItemR(layout, NULL, 0, &rna, "smooth_stroke", 0, 0, 0);
 
        uiItemR(layout, NULL, 0, &rna, "flip_direction", 0, 0, 0);
 }
index 8ce0b439b29cc75c0e6a419ef1d8f8e6965389df..93a974c1180469a74b1d202c481223435b82e37c 100644 (file)
@@ -84,6 +84,7 @@ typedef struct Brush {
 #define BRUSH_ANCHORED         256
 #define BRUSH_DIR_IN           512
 #define BRUSH_SPACE            1024
+#define BRUSH_SMOOTH_STROKE    2048
 
 /* Brush.blend */
 #define BRUSH_BLEND_MIX                0
index 90617d0183368edabb9cbf54f3dfb872e58bd87c..7355261c5aa80aeddad3a32443608eefc25d973a 100644 (file)
@@ -184,6 +184,10 @@ void rna_def_brush(BlenderRNA *brna)
        prop= RNA_def_property(srna, "space", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SPACE);
        RNA_def_property_ui_text(prop, "Space", "Limit brush application to the distance specified by spacing.");
+
+       prop= RNA_def_property(srna, "smooth_stroke", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_SMOOTH_STROKE);
+       RNA_def_property_ui_text(prop, "Smooth Stroke", "Brush lags behind mouse and follows a smoother path.");
        
        /* not exposed in the interface yet
        prop= RNA_def_property(srna, "fixed_tex", PROP_BOOLEAN, PROP_NONE);