Merge GSOC Sculpt Branch: 28499-30319
authorJason Wilkins <Jason.A.Wilkins@gmail.com>
Wed, 14 Jul 2010 14:11:03 +0000 (14:11 +0000)
committerJason Wilkins <Jason.A.Wilkins@gmail.com>
Wed, 14 Jul 2010 14:11:03 +0000 (14:11 +0000)
https://svn.blender.org/svnroot/bf-blender/branches/soc-2010-jwilkins
See log of that branch for details.

55 files changed:
intern/ghost/SConscript
intern/ghost/intern/GHOST_WindowCarbon.cpp
intern/ghost/intern/GHOST_WindowCocoa.mm
intern/ghost/intern/GHOST_WindowWin32.cpp
release/scripts/ui/properties_data_modifier.py
release/scripts/ui/properties_texture.py
release/scripts/ui/space_userpref.py
release/scripts/ui/space_view3d_toolbar.py
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_brush.h
source/blender/blenkernel/BKE_paint.h
source/blender/blenkernel/intern/brush.c
source/blender/blenkernel/intern/colortools.c
source/blender/blenkernel/intern/icons.c
source/blender/blenkernel/intern/image.c
source/blender/blenlib/BLI_pbvh.h
source/blender/blenlib/intern/math_geom.c
source/blender/blenlib/intern/pbvh.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/gpencil/gpencil_buttons.c
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface_icons.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/interface/resources.c
source/blender/editors/sculpt_paint/SConscript
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/editors/sculpt_paint/paint_ops.c
source/blender/editors/sculpt_paint/paint_stroke.c
source/blender/editors/sculpt_paint/paint_utils.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/sculpt_paint/sculpt_intern.h
source/blender/editors/sculpt_paint/sculpt_undo.c [new file with mode: 0644]
source/blender/editors/space_image/image_buttons.c
source/blender/editors/space_logic/logic_window.c
source/blender/editors/space_nla/nla_buttons.c
source/blender/editors/space_node/drawnode.c
source/blender/gpu/intern/gpu_draw.c
source/blender/makesdna/DNA_ID.h
source/blender/makesdna/DNA_brush_types.h
source/blender/makesdna/DNA_color_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesdna/DNA_windowmanager_types.h
source/blender/makesrna/SConscript
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/rna_brush.c
source/blender/makesrna/intern/rna_image.c
source/blender/makesrna/intern/rna_sculpt_paint.c
source/blender/makesrna/intern/rna_ui_api.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/SConscript
source/blender/windowmanager/intern/wm_draw.c
source/blender/windowmanager/intern/wm_operators.c
source/blenderplayer/bad_level_call_stubs/stubs.c

index 1269c63..65c00b1 100644 (file)
@@ -56,9 +56,8 @@ else:
 
 if env['BF_GHOST_DEBUG']:
        defs.append('BF_GHOST_DEBUG')
-       
+
 incs = '. ../string #extern/glew/include #source/blender/imbuf #source/blender/makesdna ' + env['BF_OPENGL_INC']
 if window_system in ('win32-vc', 'win32-mingw', 'cygwin', 'linuxcross', 'win64-vc'):
        incs = env['BF_WINTAB_INC'] + ' ' + incs
 env.BlenderLib ('bf_ghost', sources, Split(incs), defines=defs, libtype=['intern','player'], priority = [40,15] ) 
-
index 376859d..a8295ec 100644 (file)
@@ -42,7 +42,7 @@ AGLContext GHOST_WindowCarbon::s_firstaglCtx = NULL;
 const GHOST_TInt32 GHOST_WindowCarbon::s_sizeRectSize = 16;
 #endif //GHOST_DRAW_CARBON_GUTTER
 
-static const GLint sPreferredFormatWindow[8] = {
+static const GLint sPreferredFormatWindow[10] = {
 AGL_RGBA,
 AGL_DOUBLEBUFFER,      
 AGL_ACCELERATED,
@@ -50,7 +50,7 @@ AGL_DEPTH_SIZE,               32,
 AGL_NONE,
 };
 
-static const GLint sPreferredFormatFullScreen[9] = {
+static const GLint sPreferredFormatFullScreen[11] = {
 AGL_RGBA,
 AGL_DOUBLEBUFFER,
 AGL_ACCELERATED,
index 0b22017..a97f762 100644 (file)
@@ -348,10 +348,11 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(
        
        pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
        //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
-       
+
        pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
        pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
        
+       
        if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
        
        if (numOfAASamples>0) {
index bbfa84d..1366aeb 100644 (file)
@@ -1093,7 +1093,7 @@ static int WeightPixelFormat(PIXELFORMATDESCRIPTOR& pfd) {
                !(pfd.dwFlags & PFD_DRAW_TO_WINDOW) ||
                !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */
                ( pfd.cDepthBits <= 8 ) ||
-               !(pfd.iPixelType == PFD_TYPE_RGBA) )
+               !(pfd.iPixelType == PFD_TYPE_RGBA))
                return 0;
 
        weight = 1;  /* it's usable */
index 37764e2..dfa0d80 100644 (file)
@@ -437,6 +437,7 @@ class DATA_PT_modifiers(DataButtonsPanel):
         col.prop(md, "levels", text="Preview")
         col.prop(md, "sculpt_levels", text="Sculpt")
         col.prop(md, "render_levels", text="Render")
+        col.prop(bpy.context.tool_settings.sculpt, "fast_navigate")
 
         if wide_ui:
             col = split.column()
index 86f2d86..2bfbe18 100644 (file)
@@ -275,15 +275,12 @@ class TEXTURE_PT_mapping(TextureSlotPanel):
 
         if type(idblock) == bpy.types.Brush:
             if context.sculpt_object:
+                layout.label(text="Brush Mapping:")
                 layout.prop(tex, "map_mode", expand=True)
 
                 row = layout.row()
                 row.active = tex.map_mode in ('FIXED', 'TILED')
                 row.prop(tex, "angle")
-
-                row = layout.row()
-                row.active = tex.map_mode in ('TILED', '3D')
-                row.column().prop(tex, "size")
         else:
             if type(idblock) == bpy.types.Material:
                 split = layout.split(percentage=0.3)
@@ -307,15 +304,17 @@ class TEXTURE_PT_mapping(TextureSlotPanel):
                 row.prop(tex, "y_mapping", text="")
                 row.prop(tex, "z_mapping", text="")
 
-            # any non brush
-            split = layout.split()
+        split = layout.split()
+
+        col = split.column()
+        col.prop(tex, "offset")
 
+        if wide_ui:
             col = split.column()
-            col.prop(tex, "offset")
+        else:
+            col.separator()
 
-            if wide_ui:
-                col = split.column()
-            col.prop(tex, "size")
+        col.prop(tex, "size")
 
 
 class TEXTURE_PT_influence(TextureSlotPanel):
index 44b6858..61f759a 100644 (file)
@@ -327,7 +327,21 @@ class USERPREF_PT_edit(bpy.types.Panel):
         row.separator()
         row.separator()
 
+        sculpt = context.tool_settings.sculpt
         col = row.column()
+        col.label(text="Paint and Sculpt:")
+        col.prop(edit, "sculpt_paint_use_unified_size", text="Unify Size")
+        col.prop(edit, "sculpt_paint_use_unified_strength", text="Unify Strength")
+        row = col.row(align=True)
+        row.label("Overlay Color:")
+        row.prop(edit, "sculpt_paint_overlay_col", text="")
+        col.prop(sculpt, "use_openmp", text="Threaded Sculpt")
+        col.prop(sculpt, "show_brush")
+
+        col.separator()
+        col.separator()
+        col.separator()
+
         col.label(text="Duplicate Data:")
         col.prop(edit, "duplicate_mesh", text="Mesh")
         col.prop(edit, "duplicate_surface", text="Surface")
index c503eaa..515fa23 100644 (file)
@@ -19,6 +19,7 @@
 # <pep8 compliant>
 import bpy
 
+narrowui = bpy.context.user_preferences.view.properties_width_check
 
 class View3DPanel(bpy.types.Panel):
     bl_space_type = 'VIEW_3D'
@@ -503,18 +504,26 @@ class VIEW3D_PT_tools_brush(PaintPanel):
 
         if not context.particle_edit_object:
             col = layout.split().column()
-            row = col.row()
 
-            if context.sculpt_object and brush:
-                defaultbrushes = 8
-            elif context.texture_paint_object and brush:
-                defaultbrushes = 4
+            if context.sculpt_object and context.tool_settings.sculpt:
+                col.template_ID_preview(settings, "brush", new="brush.add", filter="is_sculpt_brush", rows=3, cols=8)
+            elif context.texture_paint_object and context.tool_settings.image_paint:
+                col.template_ID_preview(settings, "brush", new="brush.add", filter="is_imapaint_brush", rows=3, cols=8)
+            elif context.vertex_paint_object and context.tool_settings.vertex_paint:
+                col.template_ID_preview(settings, "brush", new="brush.add", filter="is_vpaint_brush", rows=3, cols=8)
+            elif context.weight_paint_object and context.tool_settings.weight_paint:
+                col.template_ID_preview(settings, "brush", new="brush.add", filter="is_wpaint_brush", rows=3, cols=8)
             else:
-                defaultbrushes = 7
+                row = col.row()
 
-            row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaultbrushes)
+                if context.sculpt_object and brush:
+                    defaultbrushes = 8
+                elif context.texture_paint_object and brush:
+                    defaultbrushes = 4
+                else:
+                    defaultbrushes = 7
 
-            col.template_ID(settings, "brush", new="brush.add")
+                row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaultbrushes)
 
         # Particle Mode #
 
@@ -546,44 +555,152 @@ class VIEW3D_PT_tools_brush(PaintPanel):
         # Sculpt Mode #
 
         elif context.sculpt_object and brush:
+            edit = context.user_preferences.edit
+
             col = layout.column()
+
+
             col.separator()
 
             row = col.row(align=True)
-            row.prop(brush, "size", slider=True)
 
-            if brush.sculpt_tool != 'GRAB':
-                row.prop(brush, "use_size_pressure", toggle=True, text="")
+            if edit.sculpt_paint_use_unified_size:
+                if edit.sculpt_paint_unified_lock_brush_size:
+                    row.prop(edit, "sculpt_paint_unified_lock_brush_size", toggle=True, text="", icon='LOCKED')
+                    row.prop(edit, "sculpt_paint_unified_unprojected_radius", text="Unified Radius", slider=True)
+                else:
+                    row.prop(edit, "sculpt_paint_unified_lock_brush_size", toggle=True, text="", icon='UNLOCKED')
+                    row.prop(edit, "sculpt_paint_unified_size", text="Unified Radius", slider=True)
+
+            else:
+                if brush.lock_brush_size:
+                    row.prop(brush, "lock_brush_size", toggle=True, text="", icon='LOCKED')
+                    row.prop(brush, "unprojected_radius", text="Radius", slider=True)
+                else:
+                    row.prop(brush, "lock_brush_size", toggle=True, text="", icon='UNLOCKED')
+                    row.prop(brush, "size", text="Radius", slider=True)
+
+            row.prop(brush, "use_size_pressure", toggle=True, text="")
+
+
+            if brush.sculpt_tool not in ('SNAKE_HOOK', 'GRAB', 'ROTATE'):
+                col.separator()
 
                 row = col.row(align=True)
-                row.prop(brush, "strength", slider=True)
+
+                if brush.use_space and brush.sculpt_tool not in ('SMOOTH'):
+                    if brush.use_space_atten:
+                        row.prop(brush, "use_space_atten", toggle=True, text="", icon='LOCKED')
+                    else:
+                        row.prop(brush, "use_space_atten", toggle=True, text="", icon='UNLOCKED')
+
+                if edit.sculpt_paint_use_unified_strength:
+                    row.prop(edit, "sculpt_paint_unified_strength", text="Unified Strength", slider=True)
+                else:
+                    row.prop(brush, "strength", text="Strength", slider=True)
+
                 row.prop(brush, "use_strength_pressure", text="")
 
-                # XXX - TODO
-                #row = col.row(align=True)
-                #row.prop(brush, "jitter", slider=True)
-                #row.prop(brush, "use_jitter_pressure", toggle=True, text="")
 
-                col = layout.column()
 
-                if brush.sculpt_tool in ('DRAW', 'PINCH', 'INFLATE', 'LAYER', 'CLAY'):
-                    col.row().prop(brush, "direction", expand=True)
+            if brush.sculpt_tool not in ('SMOOTH'):
+                col.separator()
+
+                row = col.row(align=True)
+                row.prop(brush, "autosmooth_factor", slider=True)
+                row.prop(brush, "use_inverse_smooth_pressure", toggle=True, text="")
+
+
+
+            if brush.sculpt_tool in ('GRAB', 'SNAKE_HOOK'):
+                col.separator()
+
+                row = col.row(align=True)
+                row.prop(brush, "normal_weight", slider=True)
+
+
+
+            if brush.sculpt_tool in ('CREASE', 'BLOB'):
+                col.separator()
+
+                row = col.row(align=True)
+                row.prop(brush, "crease_pinch_factor", slider=True, text="Pinch")
+
+            if brush.sculpt_tool not in ('PINCH', 'INFLATE', 'SMOOTH'):
+                row = col.row(align=True)
+
+                col.separator()
+
+                if brush.use_original_normal:
+                    row.prop(brush, "use_original_normal", toggle=True, text="", icon='LOCKED')
+                else:
+                    row.prop(brush, "use_original_normal", toggle=True, text="", icon='UNLOCKED')
+
+                row.prop(brush, "sculpt_plane", text="")
+
+            #if brush.sculpt_tool in ('CLAY', 'CLAY_TUBES', 'FLATTEN', 'FILL', 'SCRAPE'):
+            if brush.sculpt_tool in ('CLAY', 'FLATTEN', 'FILL', 'SCRAPE'):
+                row = col.row(align=True)
+                row.prop(brush, "plane_offset", slider=True)
+                row.prop(brush, "use_offset_pressure", text="")
+
+                col.separator()
+
+                row= col.row()
+                row.prop(brush, "use_plane_trim", text="Trim")
+                row= col.row()
+                row.active=brush.use_plane_trim
+                row.prop(brush, "plane_trim", slider=True, text="Distance")
+
+            col.separator()
+
+            row= col.row()
+            row.prop(brush, "use_frontface", text="Front Faces Only")
+
+            #if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'LAYER', 'CLAY', 'CLAY_TUBES'):
+            if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'LAYER', 'CLAY'):
+                col.separator()
+                col.row().prop(brush, "direction", expand=True)
+            elif brush.sculpt_tool in ('FLATTEN'):
+                col.separator()
+                col.row().prop(brush, "flatten_contrast", expand=True)
+            elif brush.sculpt_tool in ('FILL'):
+                col.separator()
+                col.row().prop(brush, "fill_deepen", expand=True)
+            elif brush.sculpt_tool in ('SCRAPE'):
+                col.separator()
+                col.row().prop(brush, "scrape_peaks", expand=True)
+            elif brush.sculpt_tool in ('INFLATE'):
+                col.separator()
+                col.row().prop(brush, "inflate_deflate", expand=True)
+            elif brush.sculpt_tool in ('PINCH'):
+                col.separator()
+                col.row().prop(brush, "pinch_magnify", expand=True)
+
+
+
+            #if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'INFLATE', 'LAYER', 'CLAY', 'CLAY_TUBES'):
+            if brush.sculpt_tool in ('DRAW', 'CREASE', 'BLOB', 'INFLATE', 'LAYER', 'CLAY'):
+                col.separator()
 
-                if brush.sculpt_tool in ('DRAW', 'INFLATE', 'LAYER'):
-                    col.prop(brush, "use_accumulate")
+                col.prop(brush, "use_accumulate")
 
-                if brush.sculpt_tool == 'LAYER':
-                    ob = context.sculpt_object
-                    do_persistent = True
 
-                    # not supported yet for this case
-                    for md in ob.modifiers:
-                        if md.type == 'MULTIRES':
-                            do_persistent = False
 
-                    if do_persistent:
-                        col.prop(brush, "use_persistent")
-                        col.operator("sculpt.set_persistent_base")
+            if brush.sculpt_tool == 'LAYER':
+                col.separator()
+
+                ob = context.sculpt_object
+                do_persistent = True
+
+                # not supported yet for this case
+                for md in ob.modifiers:
+                    if md.type == 'MULTIRES':
+                        do_persistent = False
+
+                if do_persistent:
+                    col.prop(brush, "use_persistent")
+                    col.operator("sculpt.set_persistent_base")
 
         # Texture Paint Mode #
 
@@ -669,10 +786,88 @@ class VIEW3D_PT_tools_brush_texture(PaintPanel):
 
         col = layout.column()
 
-        col.template_ID_preview(brush, "texture", new="texture.new", rows=2, cols=4)
+        col.template_ID_preview(brush, "texture", new="texture.new", rows=3, cols=8)
 
         if context.sculpt_object:
-            col.row().prop(tex_slot, "map_mode", expand=True)
+            #XXX duplicated from properties_texture.py
+
+            wide_ui = context.region.width > narrowui
+
+
+            col.separator()
+
+
+            col.label(text="Brush Mapping:")
+            row = col.row(align=True)
+            row.prop(tex_slot, "map_mode", expand=True)
+
+            col.separator()
+
+            col = layout.column()
+            col.active = tex_slot.map_mode in ('FIXED')
+            col.label(text="Angle:")
+            
+            col = layout.column()
+            if not brush.use_anchor and brush.sculpt_tool not in ('GRAB', 'SNAKE_HOOK', 'THUMB', 'ROTATE') and tex_slot.map_mode in ('FIXED'):
+                col.prop(brush, "texture_angle_source", text="")
+            else:
+                col.prop(brush, "texture_angle_source_no_random", text="")
+
+            #row = col.row(align=True)
+            #row.label(text="Angle:")
+            #row.active = tex_slot.map_mode in ('FIXED', 'TILED')
+
+            #row = col.row(align=True)
+
+            #col = row.column()
+            #col.active = tex_slot.map_mode in ('FIXED')
+            #col.prop(brush, "use_rake", toggle=True, icon='PARTICLEMODE', text="")
+
+            col = layout.column()
+            col.prop(tex_slot, "angle", text="")
+            col.active = tex_slot.map_mode in ('FIXED', 'TILED')
+
+            #col = layout.column()
+            #col.prop(brush, "use_random_rotation")
+            #col.active = (not brush.use_rake) and (not brush.use_anchor) and brush.sculpt_tool not in ('GRAB', 'SNAKE_HOOK', 'THUMB', 'ROTATE') and tex_slot.map_mode in ('FIXED')
+
+            split = layout.split()
+
+            col = split.column()
+            col.prop(tex_slot, "offset")
+
+            if wide_ui:
+                col = split.column()
+            else:
+                col.separator()
+
+            col.prop(tex_slot, "size")
+
+            col = layout.column()
+
+            row = col.row(align=True)
+            row.label(text="Sample Bias:")
+            row = col.row(align=True)
+            row.prop(brush, "texture_sample_bias", slider=True, text="")
+
+            row = col.row(align=True)
+            row.label(text="Overlay:")
+            row.active = tex_slot.map_mode in ('FIXED', 'TILED')
+
+            row = col.row(align=True)
+
+            col = row.column()
+
+            if brush.use_texture_overlay:
+                col.prop(brush, "use_texture_overlay", toggle=True, text="", icon='MUTE_IPO_OFF')
+            else:
+                col.prop(brush, "use_texture_overlay", toggle=True, text="", icon='MUTE_IPO_ON')
+
+            col.active = tex_slot.map_mode in ('FIXED', 'TILED')
+
+            col = row.column()
+            col.prop(brush, "texture_overlay_alpha", text="Alpha")
+            col.active = tex_slot.map_mode in ('FIXED', 'TILED') and brush.use_texture_overlay
 
 
 class VIEW3D_PT_tools_brush_tool(PaintPanel):
@@ -696,15 +891,11 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel):
         col = layout.column(align=True)
 
         if context.sculpt_object:
-            col.prop(brush, "sculpt_tool", expand=True)
+            col.prop(brush, "sculpt_tool", expand=False, text="")
         elif context.texture_paint_object:
-            col.prop(brush, "imagepaint_tool", expand=True)
-            #col.prop_enum(settings, "tool", 'DRAW')
-            #col.prop_enum(settings, "tool", 'SOFTEN')
-            #col.prop_enum(settings, "tool", 'CLONE')
-            #col.prop_enum(settings, "tool", 'SMEAR')
+            col.prop(brush, "imagepaint_tool", expand=False, text="")
         elif context.vertex_paint_object or context.weight_paint_object:
-            col.prop(brush, "vertexpaint_tool", expand=True)
+            col.prop(brush, "vertexpaint_tool", expand=False, text="")
 
 
 class VIEW3D_PT_tools_brush_stroke(PaintPanel):
@@ -725,29 +916,83 @@ class VIEW3D_PT_tools_brush_stroke(PaintPanel):
         brush = settings.brush
         texture_paint = context.texture_paint_object
 
+        col = layout.column()
+
         if context.sculpt_object:
-            if brush.sculpt_tool != 'LAYER':
-                layout.prop(brush, "use_anchor")
-            layout.prop(brush, "use_rake")
+            col.label(text="Stroke Method:")
+            col.prop(brush, "stroke_method", text="")
+
+            if brush.use_anchor:
+                col.separator()
+                row = col.row()
+                row.prop(brush, "edge_to_edge", "Edge To Edge")
+
+            if brush.use_airbrush:
+                col.separator()
+                row = col.row()
+                row.prop(brush, "rate", text="Rate", slider=True)
+
+            if brush.use_space:
+                col.separator()
+                row = col.row()
+                row.active = brush.use_space
+                row.prop(brush, "spacing", text="Spacing")
+
+            if brush.sculpt_tool not in ('GRAB', 'THUMB', 'SNAKE_HOOK', 'ROTATE') and (not brush.use_anchor) and (not brush.restore_mesh):
+                col = layout.column()
+                col.separator()
 
-        layout.prop(brush, "use_airbrush")
-        col = layout.column()
-        col.active = brush.use_airbrush
-        col.prop(brush, "rate", slider=True)
+                col.prop(brush, "use_smooth_stroke")
+
+                sub = col.column()
+                sub.active = brush.use_smooth_stroke
+                sub.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
+                sub.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
+
+                col.separator()
+
+                row = col.row(align=True)
+                row.prop(brush, "jitter", slider=True)
+                row.prop(brush, "use_jitter_pressure", toggle=True, text="")
+
+        else:
+            row = col.row()
+            row.prop(brush, "use_airbrush")
+
+            row = col.row()
+            row.active = brush.use_airbrush and (not brush.use_space) and (not brush.use_anchor)
+            row.prop(brush, "rate", slider=True)
+
+            col.separator()
+
+            if not texture_paint:
+                row = col.row()
+                row.prop(brush, "use_smooth_stroke")
+
+                col = layout.column()
+                col.active = brush.use_smooth_stroke
+                col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
+                col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
+
+            col.separator()
 
-        if not texture_paint:
-            layout.prop(brush, "use_smooth_stroke")
             col = layout.column()
-            col.active = brush.use_smooth_stroke
-            col.prop(brush, "smooth_stroke_radius", text="Radius", slider=True)
-            col.prop(brush, "smooth_stroke_factor", text="Factor", slider=True)
+            col.active = (not brush.use_anchor) and (brush.sculpt_tool not in ('GRAB', 'THUMB', 'ROTATE', 'SNAKE_HOOK'))
 
-        layout.prop(brush, "use_space")
-        row = layout.row(align=True)
-        row.active = brush.use_space
-        row.prop(brush, "spacing", text="Distance", slider=True)
-        if texture_paint:
-            row.prop(brush, "use_spacing_pressure", toggle=True, text="")
+            row = col.row()
+            row.prop(brush, "use_space")
+
+            row = col.row()
+            row.active = brush.use_space
+            row.prop(brush, "spacing", text="Spacing")
+
+            #col.prop(brush, "use_space_atten", text="Adaptive Strength")
+            #col.prop(brush, "use_adaptive_space", text="Adaptive Spacing")
+
+            #col.separator()
+
+            #if texture_paint:
+            #    row.prop(brush, "use_spacing_pressure", toggle=True, text="")
 
 
 class VIEW3D_PT_tools_brush_curve(PaintPanel):
@@ -767,13 +1012,17 @@ class VIEW3D_PT_tools_brush_curve(PaintPanel):
         layout.template_curve_mapping(brush, "curve", brush=True)
 
         row = layout.row(align=True)
-        row.operator("brush.curve_preset", text="Sharp").shape = 'SHARP'
-        row.operator("brush.curve_preset", text="Smooth").shape = 'SMOOTH'
-        row.operator("brush.curve_preset", text="Max").shape = 'MAX'
-
+        row.operator("brush.curve_preset", icon="SMOOTHCURVE", text="").shape = 'SMOOTH'
+        row.operator("brush.curve_preset", icon="SPHERECURVE", text="").shape = 'ROUND'
+        row.operator("brush.curve_preset", icon="ROOTCURVE", text="").shape = 'ROOT'
+        row.operator("brush.curve_preset", icon="SHARPCURVE", text="").shape = 'SHARP'
+        row.operator("brush.curve_preset", icon="LINCURVE", text="").shape = 'LINE'
+        row.operator("brush.curve_preset", icon="NOCURVE", text="").shape = 'MAX'
+        row.operator("brush.curve_preset", icon="RNDCURVE", text="").shape = 'MID9'
 
 class VIEW3D_PT_sculpt_options(PaintPanel):
     bl_label = "Options"
+    bl_default_closed = True
 
     def poll(self, context):
         return (context.sculpt_object and context.tool_settings.sculpt)
@@ -781,25 +1030,100 @@ class VIEW3D_PT_sculpt_options(PaintPanel):
     def draw(self, context):
         layout = self.layout
 
+        wide_ui = context.region.width > narrowui
+
         sculpt = context.tool_settings.sculpt
+        settings = self.paint_settings(context)
+        brush = settings.brush
 
-        col = layout.column()
-        col.prop(sculpt, "show_brush")
-        col.prop(sculpt, "fast_navigate")
+        split = layout.split()
+
+        col = split.column()
+
+        edit = context.user_preferences.edit
+        col.label(text="Unified Settings:")
+        col.prop(edit, "sculpt_paint_use_unified_size", text="Size")
+        col.prop(edit, "sculpt_paint_use_unified_strength", text="Strength")
+
+        if wide_ui:
+            col = split.column()
+        else:
+            col.separator()
+
+        col.label(text="Lock:")
+        row = col.row(align=True)
+        row.prop(sculpt, "lock_x", text="X", toggle=True)
+        row.prop(sculpt, "lock_y", text="Y", toggle=True)
+        row.prop(sculpt, "lock_z", text="Z", toggle=True)
 
-        split = self.layout.split()
+class VIEW3D_PT_sculpt_symmetry(PaintPanel):
+    bl_label = "Symmetry"
+    bl_default_closed = True
+
+    def poll(self, context):
+        return (context.sculpt_object and context.tool_settings.sculpt)
+
+    def draw(self, context):
+        wide_ui = context.region.width > narrowui
+
+        layout = self.layout
+
+        sculpt = context.tool_settings.sculpt
+        settings = self.paint_settings(context)
+        brush = settings.brush
+
+        split = layout.split()
 
         col = split.column()
-        col.label(text="Symmetry:")
+
+        col.label(text="Mirror:")
         col.prop(sculpt, "symmetry_x", text="X")
         col.prop(sculpt, "symmetry_y", text="Y")
         col.prop(sculpt, "symmetry_z", text="Z")
 
-        col = split.column()
-        col.label(text="Lock:")
-        col.prop(sculpt, "lock_x", text="X")
-        col.prop(sculpt, "lock_y", text="Y")
-        col.prop(sculpt, "lock_z", text="Z")
+        if wide_ui:
+            col = split.column()
+        else:
+            col.separator()
+
+        col.prop(sculpt, "radial_symm", text="Radial")
+
+        col = layout.column()
+
+        col.separator()
+
+        col.prop(sculpt, "use_symmetry_feather", text="Feather")
+
+class VIEW3D_PT_tools_brush_appearance(PaintPanel):
+    bl_label = "Appearance"
+    bl_default_closed = True
+
+    def poll(self, context):
+        return (context.sculpt_object and context.tool_settings.sculpt) or (context.vertex_paint_object and context.tool_settings.vertex_paint) or (context.weight_paint_object and context.tool_settings.weight_paint) or (context.texture_paint_object and context.tool_settings.image_paint)
+
+    def draw(self, context):
+        layout = self.layout
+
+        sculpt = context.tool_settings.sculpt
+        settings = self.paint_settings(context)
+        brush = settings.brush
+
+        col = layout.column();
+
+        if context.sculpt_object and context.tool_settings.sculpt:
+            #if brush.sculpt_tool in ('DRAW', 'INFLATE', 'CLAY', 'CLAY_TUBES', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN'):
+            if brush.sculpt_tool in ('DRAW', 'INFLATE', 'CLAY', 'PINCH', 'CREASE', 'BLOB', 'FLATTEN'):
+                col.prop(brush, "add_col", text="Add Color")
+                col.prop(brush, "sub_col", text="Substract Color")
+            else:
+                col.prop(brush, "add_col", text="Color")
+
+        col.separator()
+
+        col = layout.column()
+        col.label(text="Icon:")
+        #col.template_ID_preview(brush, "image_icon", open="image.open", filter="is_image_icon", rows=3, cols=8)
+        col.template_ID_preview(brush, "image_icon", open="image.open", rows=3, cols=8)
 
 # ********** default tools for weightpaint ****************
 
@@ -1049,9 +1373,11 @@ classes = [
     VIEW3D_PT_tools_posemode_options,
     VIEW3D_PT_tools_brush,
     VIEW3D_PT_tools_brush_texture,
-    VIEW3D_PT_tools_brush_tool,
     VIEW3D_PT_tools_brush_stroke,
     VIEW3D_PT_tools_brush_curve,
+    VIEW3D_PT_tools_brush_appearance,
+    VIEW3D_PT_tools_brush_tool,
+    VIEW3D_PT_sculpt_symmetry,
     VIEW3D_PT_sculpt_options,
     VIEW3D_PT_tools_vertexpaint,
     VIEW3D_PT_tools_weightpaint_options,
index 7ac5815..5936805 100644 (file)
@@ -43,9 +43,9 @@ struct bContext;
 struct ReportList;
 struct Scene;
 struct Main;
-       
+
 #define BLENDER_VERSION                        252
-#define BLENDER_SUBVERSION             5
+#define BLENDER_SUBVERSION             6 // XXX: this shouldn't be merged with trunk, this is so Sculpt branch can detect old files
 
 #define BLENDER_MINVERSION             250
 #define BLENDER_MINSUBVERSION  0
index fcc6362..6a20916 100644 (file)
@@ -84,5 +84,15 @@ unsigned int *brush_gen_texture_cache(struct Brush *br, int half_side);
 void brush_radial_control_invoke(struct wmOperator *op, struct Brush *br, float size_weight);
 int brush_radial_control_exec(struct wmOperator *op, struct Brush *br, float size_weight);
 
+/* unified strength and size */
+int sculpt_get_brush_size(struct Brush *brush);
+void sculpt_set_brush_size(struct Brush *brush, int size);
+int sculpt_get_lock_brush_size(struct Brush *brush);
+float sculpt_get_brush_unprojected_radius(struct Brush *brush);
+void sculpt_set_brush_unprojected_radius(struct Brush *brush, float unprojected_radius);
+float sculpt_get_brush_alpha(struct Brush *brush);
+void sculpt_set_brush_alpha(struct Brush *brush, float alpha);
+
+
 #endif
 
index cd412ca..f9954b3 100644 (file)
@@ -28,6 +28,8 @@
 #ifndef BKE_PAINT_H
 #define BKE_PAINT_H
 
+#include "DNA_vec_types.h"
+
 struct Brush;
 struct MFace;
 struct MultireModifierData;
@@ -96,6 +98,8 @@ typedef struct SculptSession {
        struct GPUDrawObject *drawobject;
 
        int modifiers_active;
+
+       rcti previous_r;
 } SculptSession;
 
 void free_sculptsession(struct Object *ob);
index 538012c..c423d42 100644 (file)
@@ -53,8 +53,7 @@
 #include "BKE_main.h"
 #include "BKE_paint.h"
 #include "BKE_texture.h"
-
-
+#include "BKE_icons.h"
 
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
@@ -70,29 +69,59 @@ Brush *add_brush(const char *name)
 
        brush= alloc_libblock(&G.main->brush, ID_BR, name);
 
-       brush->rgb[0]= 1.0f;
+       /* BRUSH SCULPT TOOL SETTINGS */
+       brush->sculpt_tool = SCULPT_TOOL_DRAW; /* sculpting defaults to the draw tool for new brushes */
+       brush->size= 35; /* radius of the brush in pixels */
+       brush->alpha= 0.5f; /* brush strength/intensity probably variable should be renamed? */
+       brush->autosmooth_factor= 0.0f;
+       brush->crease_pinch_factor= 0.5f;
+       brush->sculpt_plane = SCULPT_DISP_DIR_VIEW;
+       brush->plane_offset= 0.0f; /* how far above or below the plane that is found by averaging the faces */
+       brush->plane_trim= 0.5f;
+       brush->clone.alpha= 0.5f;
+       brush->normal_weight= 0.0f;
+
+       /* BRUSH PAINT TOOL SETTINGS */
+       brush->rgb[0]= 1.0f; /* default rgb color of the brush when painting - white */
        brush->rgb[1]= 1.0f;
        brush->rgb[2]= 1.0f;
-       brush->alpha= 0.2f;
-       brush->size= 25;
-       brush->spacing= 3.5f;
+
+       /* BRUSH STROKE SETTINGS */
+       brush->flag |= (BRUSH_SPACE|BRUSH_SPACE_ATTEN);
+       brush->spacing= 10; /* how far each brush dot should be spaced as a percentage of brush diameter */
+
        brush->smooth_stroke_radius= 75;
-       brush->smooth_stroke_factor= 0.9;
-       brush->rate= 0.1f;
+       brush->smooth_stroke_factor= 0.9f;
+
+       brush->rate= 0.1f; /* time delay between dots of paint or sculpting when doing airbrush mode */
+
        brush->jitter= 0.0f;
-       brush->clone.alpha= 0.5;
-       brush->sculpt_tool = SCULPT_TOOL_DRAW;
-       brush->flag |= BRUSH_SPACE;
 
-       brush_curve_preset(brush, CURVE_PRESET_SMOOTH);
-       
+       /* BRUSH TEXTURE SETTINGS */
        default_mtex(&brush->mtex);
 
+       brush->texture_sample_bias= 0; /* value to added to texture samples */
+
+       /* brush appearance  */
+
+       brush->image_icon= NULL;
+
+       brush->add_col[0]= 1.00; /* add mode color is light red */
+       brush->add_col[1]= 0.39;
+       brush->add_col[2]= 0.39;
+
+       brush->sub_col[0]= 0.39; /* subtract mode color is light blue */
+       brush->sub_col[1]= 0.39;
+       brush->sub_col[2]= 1.00;
+
+        /* the default alpha falloff curve */
+       brush_curve_preset(brush, CURVE_PRESET_SMOOTH);
+
        /* enable fake user by default */
        brush->id.flag |= LIB_FAKEUSER;
        brush_toggled_fake_user(brush);
-       
-       return brush;   
+
+       return brush;
 }
 
 Brush *copy_brush(Brush *brush)
@@ -118,7 +147,7 @@ Brush *copy_brush(Brush *brush)
 void free_brush(Brush *brush)
 {
        if(brush->mtex.tex) brush->mtex.tex->id.us--;
-       
+
        curvemapping_free(brush->curve);
 }
 
@@ -731,11 +760,19 @@ static void brush_apply_pressure(BrushPainter *painter, Brush *brush, float pres
                brush->spacing = MAX2(1.0, painter->startspacing*(1.5f-pressure));
 }
 
-static void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos)
+void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos)
 {
        if(brush->jitter){
-               jitterpos[0] = pos[0] + ((BLI_frand()-0.5f) * brush->size * brush->jitter * 2);
-               jitterpos[1] = pos[1] + ((BLI_frand()-0.5f) * brush->size * brush->jitter * 2);
+               float rand_pos[2];
+
+               // find random position within a circle of diameter 1
+               do {
+                       rand_pos[0] = BLI_frand()-0.5f;
+                       rand_pos[1] = BLI_frand()-0.5f;
+               } while (len_v2(rand_pos) > 0.5f);
+
+               jitterpos[0] = pos[0] + 2*rand_pos[0]*brush->size*brush->jitter;
+               jitterpos[1] = pos[1] + 2*rand_pos[1]*brush->size*brush->jitter;
        }
        else {
                VECCOPY2D(jitterpos, pos);
@@ -887,7 +924,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
 /* Uses the brush curve control to find a strength value between 0 and 1 */
 float brush_curve_strength_clamp(Brush *br, float p, const float len)
 {
-       if(p >= len)    p= 1.0f;
+       if(p >= len)    return 0;
        else                    p= p/len;
 
        p= curvemapping_evaluateF(br->curve, 0, p);
@@ -899,9 +936,12 @@ float brush_curve_strength_clamp(Brush *br, float p, const float len)
  * used for sculpt only */
 float brush_curve_strength(Brush *br, float p, const float len)
 {
-       if(p >= len)    p= 1.0f;
-       else                    p= p/len;
-       return curvemapping_evaluateF(br->curve, 0, p);
+    if(p >= len)
+        p= 1.0f;
+    else
+        p= p/len;
+
+    return curvemapping_evaluateF(br->curve, 0, p);
 }
 
 /* TODO: should probably be unified with BrushPainter stuff? */
@@ -915,7 +955,7 @@ unsigned int *brush_gen_texture_cache(Brush *br, int half_side)
 
        memset(&texres, 0, sizeof(TexResult));
        
-       if(mtex && mtex->tex) {
+       if(mtex->tex) {
                float x, y, step = 2.0 / side, co[3];
 
                texcache = MEM_callocN(sizeof(int) * side * side, "Brush texture cache");
@@ -993,9 +1033,9 @@ void brush_radial_control_invoke(wmOperator *op, Brush *br, float size_weight)
        float original_value= 0;
 
        if(mode == WM_RADIALCONTROL_SIZE)
-               original_value = br->size * size_weight;
+               original_value = sculpt_get_brush_size(br) * size_weight;
        else if(mode == WM_RADIALCONTROL_STRENGTH)
-               original_value = br->alpha;
+               original_value = sculpt_get_brush_alpha(br);
        else if(mode == WM_RADIALCONTROL_ANGLE) {
                MTex *mtex = brush_active_texture(br);
                if(mtex)
@@ -1013,9 +1053,15 @@ int brush_radial_control_exec(wmOperator *op, Brush *br, float size_weight)
        const float conv = 0.017453293;
 
        if(mode == WM_RADIALCONTROL_SIZE)
-               br->size = new_value * size_weight;
+               if (sculpt_get_lock_brush_size(br)) {
+                       float initial_value = RNA_float_get(op->ptr, "initial_value");
+                       const float unprojected_radius = sculpt_get_brush_unprojected_radius(br);
+                       sculpt_set_brush_unprojected_radius(br, unprojected_radius * new_value/initial_value * size_weight);
+               }
+               else
+                       sculpt_set_brush_size(br, new_value * size_weight);
        else if(mode == WM_RADIALCONTROL_STRENGTH)
-               br->alpha = new_value;
+               sculpt_set_brush_alpha(br, new_value);
        else if(mode == WM_RADIALCONTROL_ANGLE) {
                MTex *mtex = brush_active_texture(br);
                if(mtex)
index c8a01b1..a07c18f 100644 (file)
@@ -126,6 +126,9 @@ CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, floa
                cumap->cm[a].curve[1].x= maxx;
                cumap->cm[a].curve[1].y= maxy;
        }       
+
+       cumap->changed_timestamp = 0;
+
        return cumap;
 }
 
@@ -240,10 +243,12 @@ void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset)
 
        switch(preset) {
                case CURVE_PRESET_LINE: cuma->totpoint= 2; break;
-               case CURVE_PRESET_SHARP: cuma->totpoint= 3; break;
+               case CURVE_PRESET_SHARP: cuma->totpoint= 4; break;
                case CURVE_PRESET_SMOOTH: cuma->totpoint= 4; break;
                case CURVE_PRESET_MAX: cuma->totpoint= 2; break;
-               case CURVE_PRESET_MID9: cuma->totpoint= 9;
+               case CURVE_PRESET_MID9: cuma->totpoint= 9; break;
+               case CURVE_PRESET_ROUND: cuma->totpoint= 4; break;
+               case CURVE_PRESET_ROOT: cuma->totpoint= 4; break;
        }
 
        cuma->curve= MEM_callocN(cuma->totpoint*sizeof(CurveMapPoint), "curve points");
@@ -251,27 +256,29 @@ void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset)
        switch(preset) {
                case CURVE_PRESET_LINE:
                        cuma->curve[0].x= clipr->xmin;
-                       cuma->curve[0].y= clipr->ymin;
+                       cuma->curve[0].y= clipr->ymax;
                        cuma->curve[0].flag= 0;
                        cuma->curve[1].x= clipr->xmax;
-                       cuma->curve[1].y= clipr->ymax;
+                       cuma->curve[1].y= clipr->ymin;
                        cuma->curve[1].flag= 0;
                        break;
                case CURVE_PRESET_SHARP:
                        cuma->curve[0].x= 0;
                        cuma->curve[0].y= 1;
-                       cuma->curve[1].x= 0.33;
-                       cuma->curve[1].y= 0.33;
-                       cuma->curve[2].x= 1;
-                       cuma->curve[2].y= 0;
+                       cuma->curve[1].x= 0.25;
+                       cuma->curve[1].y= 0.50;
+                       cuma->curve[2].x= 0.75;
+                       cuma->curve[2].y= 0.04;
+                       cuma->curve[3].x= 1;
+                       cuma->curve[3].y= 0;
                        break;
                case CURVE_PRESET_SMOOTH:
                        cuma->curve[0].x= 0;
                        cuma->curve[0].y= 1;
                        cuma->curve[1].x= 0.25;
-                       cuma->curve[1].y= 0.92;
+                       cuma->curve[1].y= 0.94;
                        cuma->curve[2].x= 0.75;
-                       cuma->curve[2].y= 0.08;
+                       cuma->curve[2].y= 0.06;
                        cuma->curve[3].x= 1;
                        cuma->curve[3].y= 0;
                        break;
@@ -290,8 +297,29 @@ void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset)
                                        cuma->curve[i].y= 0.5;
                                }
                        }
+                       break;
+               case CURVE_PRESET_ROUND:
+                       cuma->curve[0].x= 0;
+                       cuma->curve[0].y= 1;
+                       cuma->curve[1].x= 0.5;
+                       cuma->curve[1].y= 0.90;
+                       cuma->curve[2].x= 0.86;
+                       cuma->curve[2].y= 0.5;
+                       cuma->curve[3].x= 1;
+                       cuma->curve[3].y= 0;
+                       break;
+               case CURVE_PRESET_ROOT:
+                       cuma->curve[0].x= 0;
+                       cuma->curve[0].y= 1;
+                       cuma->curve[1].x= 0.25;
+                       cuma->curve[1].y= 0.95;
+                       cuma->curve[2].x= 0.75;
+                       cuma->curve[2].y= 0.44;
+                       cuma->curve[3].x= 1;
+                       cuma->curve[3].y= 0;
+                       break;
        }
-       
+
        if(cuma->table) {
                MEM_freeN(cuma->table);
                cuma->table= NULL;
@@ -619,7 +647,9 @@ void curvemapping_changed(CurveMapping *cumap, int rem_doubles)
        float thresh= 0.01f*(clipr->xmax - clipr->xmin);
        float dx= 0.0f, dy= 0.0f;
        int a;
-       
+
+       cumap->changed_timestamp++;
+
        /* clamp with clip */
        if(cumap->flag & CUMA_DO_CLIP) {
                for(a=0; a<cuma->totpoint; a++) {
@@ -701,7 +731,7 @@ float curvemapping_evaluateF(CurveMapping *cumap, int cur, float value)
        if(cuma->table==NULL) {
                curvemap_make_table(cuma, &cumap->clipr);
                if(cuma->table==NULL)
-                       return value;
+                       return 1.0f-value;
        }
        return curvemap_evaluateF(cuma, value);
 }
index 29314fb..ad2c857 100644 (file)
@@ -38,6 +38,7 @@
 #include "DNA_material_types.h"
 #include "DNA_texture_types.h"
 #include "DNA_world_types.h"
+#include "DNA_brush_types.h"
 
 #include "BLI_ghash.h"
 
@@ -120,6 +121,7 @@ struct PreviewImage* BKE_previewimg_create()
 
        for (i=0; i<PREVIEW_MIPMAPS; ++i) {
                prv_img->changed[i] = 1;
+               prv_img->changed_timestamp[i] = 0;
        }
        return prv_img;
 }
@@ -202,7 +204,7 @@ PreviewImage* BKE_previewimg_get(ID *id)
                Image *img  = (Image*)id;
                if (!img->preview) img->preview = BKE_previewimg_create();
                prv_img = img->preview;
-       } 
+       }
 
        return prv_img;
 }
@@ -224,6 +226,7 @@ void BKE_icon_changed(int id)
                        int i;
                        for (i=0; i<PREVIEW_MIPMAPS; ++i) {
                                prv->changed[i] = 1;
+                               prv->changed_timestamp[i]++;
                        }
                }
        }       
index dc78dac..b66b5c6 100644 (file)
@@ -200,9 +200,9 @@ void free_image(Image *ima)
        }
        BKE_icon_delete(&ima->id);
        ima->id.icon_id = 0;
-       if (ima->preview) {
-               BKE_previewimg_free(&ima->preview);
-       }
+
+       BKE_previewimg_free(&ima->preview);
+
        for(a=0; a<IMA_MAX_RENDER_SLOT; a++) {
                if(ima->renders[a]) {
                        RE_FreeRenderResult(ima->renders[a]);
index 519e3dd..4797aeb 100644 (file)
@@ -36,12 +36,17 @@ struct ListBase;
 typedef struct PBVH PBVH;
 typedef struct PBVHNode PBVHNode;
 
+typedef struct {
+       float (*co)[3];
+} PBVHProxyNode;
+
 /* Callbacks */
 
 /* returns 1 if the search should continue from this node, 0 otherwise */
 typedef int (*BLI_pbvh_SearchCallback)(PBVHNode *node, void *data);
 
 typedef void (*BLI_pbvh_HitCallback)(PBVHNode *node, void *data);
+typedef void (*BLI_pbvh_HitOccludedCallback)(PBVHNode *node, void *data, float* tmin);
 
 /* Building */
 
@@ -70,7 +75,7 @@ void BLI_pbvh_search_gather(PBVH *bvh,
    it's up to the callback to find the primitive within the leaves that is
    hit first */
 
-void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data,
+void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitOccludedCallback cb, void *data,
                          float ray_start[3], float ray_normal[3], int original);
 int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
        float ray_start[3], float ray_normal[3], float *dist);
@@ -106,6 +111,8 @@ void BLI_pbvh_node_get_verts(PBVH *bvh, PBVHNode *node,
 void BLI_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]);
 void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]);
 
+float BLI_pbvh_node_get_tmin(PBVHNode* node);
+
 /* Update Normals/Bounding Box/Draw Buffers/Redraw and clear flags */
 
 void BLI_pbvh_update(PBVH *bvh, int flags, float (*face_nors)[3]);
@@ -159,13 +166,21 @@ typedef struct PBVHVertexIter {
        float *fno;
 } PBVHVertexIter;
 
+#ifdef _MSC_VER
+#pragma warning (disable:4127) // conditional expression is constant
+#endif
+
 #define BLI_pbvh_vertex_iter_begin(bvh, node, vi, mode) \
        { \
                struct DMGridData **grids; \
                struct MVert *verts; \
                int *grid_indices, totgrid, gridsize, *vert_indices, uniq_verts, totvert; \
                \
-               memset(&vi, 0, sizeof(PBVHVertexIter)); \
+               vi.grid= 0; \
+               vi.no= 0; \
+               vi.fno= 0; \
+               vi.mvert= 0; \
+               vi.skip= 0; \
                \
                BLI_pbvh_node_get_grids(bvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids, NULL); \
                BLI_pbvh_node_num_verts(bvh, node, &uniq_verts, &totvert); \
@@ -222,6 +237,13 @@ typedef struct PBVHVertexIter {
                } \
        }
 
+void BLI_pbvh_node_get_proxies(PBVHNode* node, PBVHProxyNode** proxies, int* proxy_count);
+void BLI_pbvh_node_free_proxies(PBVHNode* node);
+PBVHProxyNode* BLI_pbvh_node_add_proxy(PBVH* bvh, PBVHNode* node);
+void BLI_pbvh_gather_proxies(PBVH* pbvh, PBVHNode*** nodes,  int* totnode);
+
+//void BLI_pbvh_node_BB_reset(PBVHNode* node);
+//void BLI_pbvh_node_BB_expand(PBVHNode* node, float co[3]);
 
 #endif /* BLI_PBVH_H */
 
index 0a06cd1..e8fb922 100644 (file)
@@ -62,6 +62,7 @@ float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const floa
        n[0]= n1[1]*n2[2]-n1[2]*n2[1];
        n[1]= n1[2]*n2[0]-n1[0]*n2[2];
        n[2]= n1[0]*n2[1]-n1[1]*n2[0];
+
        return normalize_v3(n);
 }
 
@@ -401,16 +402,17 @@ int isect_line_tri_v3(float p1[3], float p2[3], float v0[3], float v1[3], float
        
        sub_v3_v3v3(s, p1, v0);
        
-       cross_v3_v3v3(q, s, e1);
-       *lambda = f * dot_v3v3(e2, q);
-       if ((*lambda < 0.0)||(*lambda > 1.0)) return 0;
-       
        u = f * dot_v3v3(s, p);
        if ((u < 0.0)||(u > 1.0)) return 0;
        
-       v = f * dot_v3v3(d, q);
+       cross_v3_v3v3(q, s, e1);
+       
+        v = f * dot_v3v3(d, q);
        if ((v < 0.0)||((u + v) > 1.0)) return 0;
 
+       *lambda = f * dot_v3v3(e2, q);
+       if ((*lambda < 0.0)||(*lambda > 1.0)) return 0;
+
        if(uv) {
                uv[0]= u;
                uv[1]= v;
@@ -440,17 +442,18 @@ int isect_ray_tri_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2
        
        sub_v3_v3v3(s, p1, v0);
        
-       cross_v3_v3v3(q, s, e1);
-       *lambda = f * dot_v3v3(e2, q);
-       if ((*lambda < 0.0)) return 0;
-       
        u = f * dot_v3v3(s, p);
        if ((u < 0.0)||(u > 1.0)) return 0;
        
+       cross_v3_v3v3(q, s, e1);
+       
        v = f * dot_v3v3(d, q);
        if ((v < 0.0)||((u + v) > 1.0)) return 0;
 
-       if(uv) {
+       *lambda = f * dot_v3v3(e2, q);
+       if ((*lambda < 0.0)) return 0;
+
+        if(uv) {
                uv[0]= u;
                uv[1]= v;
        }
@@ -460,36 +463,36 @@ int isect_ray_tri_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2
 
 int isect_ray_tri_epsilon_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2[3], float *lambda, float *uv, float epsilon)
 {
-       float p[3], s[3], e1[3], e2[3], q[3];
-       float a, f, u, v;
-       
-       sub_v3_v3v3(e1, v1, v0);
-       sub_v3_v3v3(e2, v2, v0);
-       
-       cross_v3_v3v3(p, d, e2);
-       a = dot_v3v3(e1, p);
-       if (a == 0.0f) return 0;
-       f = 1.0f/a;
-       
-       sub_v3_v3v3(s, p1, v0);
-       
-       cross_v3_v3v3(q, s, e1);
+    float p[3], s[3], e1[3], e2[3], q[3];
+    float a, f, u, v;
 
-       u = f * dot_v3v3(s, p);
-       if ((u < -epsilon)||(u > 1.0f+epsilon)) return 0;
-       
-       v = f * dot_v3v3(d, q);
-       if ((v < -epsilon)||((u + v) > 1.0f+epsilon)) return 0;
+    sub_v3_v3v3(e1, v1, v0);
+    sub_v3_v3v3(e2, v2, v0);
 
-       *lambda = f * dot_v3v3(e2, q);
-       if ((*lambda < 0.0f)) return 0;
+    cross_v3_v3v3(p, d, e2);
+    a = dot_v3v3(e1, p);
+    if (a == 0.0f) return 0;
+    f = 1.0f/a;
 
-       if(uv) {
-               uv[0]= u;
-               uv[1]= v;
-       }
-       
-       return 1;
+    sub_v3_v3v3(s, p1, v0);
+
+    u = f * dot_v3v3(s, p);
+    if ((u < -epsilon)||(u > 1.0f+epsilon)) return 0;
+
+    cross_v3_v3v3(q, s, e1);
+
+    v = f * dot_v3v3(d, q);
+    if ((v < -epsilon)||((u + v) > 1.0f+epsilon)) return 0;
+
+    *lambda = f * dot_v3v3(e2, q);
+    if ((*lambda < 0.0f)) return 0;
+
+    if(uv) {
+        uv[0]= u;
+        uv[1]= v;
+    }
+
+    return 1;
 }
 
 int isect_ray_tri_threshold_v3(float p1[3], float d[3], float v0[3], float v1[3], float v2[3], float *lambda, float *uv, float threshold)
index 426181e..7069eee 100644 (file)
@@ -92,6 +92,11 @@ struct PBVHNode {
        unsigned int uniq_verts, face_verts;
 
        char flag;
+
+       float tmin; // used for raycasting, is how close bb is to the ray point
+
+       int proxy_count;
+       PBVHProxyNode* proxies;
 };
 
 struct PBVH {
@@ -227,6 +232,17 @@ static void update_node_vb(PBVH *bvh, PBVHNode *node)
        node->vb= vb;
 }
 
+//void BLI_pbvh_node_BB_reset(PBVHNode* node)
+//{
+//     BB_reset(&node->vb);
+//}
+//
+//void BLI_pbvh_node_BB_expand(PBVHNode* node, float co[3])
+//{
+//     BB_expand(&node->vb, co);
+//}
+
+
 /* Adapted from BLI_kdopbvh.c */
 /* Returns the index of the first element on the right of the partition */
 static int partition_indices(int *prim_indices, int lo, int hi, int axis,
@@ -354,10 +370,10 @@ static void build_mesh_leaf_node(PBVH *bvh, PBVHNode *node)
        if(!G.background) {
                node->draw_buffers =
                        GPU_build_mesh_buffers(map, bvh->verts, bvh->faces,
-                                 node->prim_indices,
-                                 node->totprim, node->vert_indices,
-                                 node->uniq_verts,
-                                 node->uniq_verts + node->face_verts);
+                                       node->prim_indices,
+                                       node->totprim, node->vert_indices,
+                                       node->uniq_verts,
+                                       node->uniq_verts + node->face_verts);
        }
 
        node->flag |= PBVH_UpdateDrawBuffers;
@@ -641,13 +657,12 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter)
 {
        PBVHNode *node;
        int revisiting;
-       void *search_data;
 
        /* purpose here is to traverse tree, visiting child nodes before their
           parents, this order is necessary for e.g. computing bounding boxes */
 
        while(iter->stacksize) {
-               /* pop node */
+                /* pop node */
                iter->stacksize--;
                node= iter->stack[iter->stacksize].node;
 
@@ -662,10 +677,7 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter)
                if(revisiting)
                        return node;
 
-               /* check search callback */
-               search_data= iter->search_data;
-
-               if(iter->scb && !iter->scb(node, search_data))
+               if(iter->scb && !iter->scb(node, iter->search_data))
                        continue; /* don't traverse, outside of search zone */
 
                if(node->flag & PBVH_Leaf) {
@@ -685,6 +697,34 @@ static PBVHNode *pbvh_iter_next(PBVHIter *iter)
        return NULL;
 }
 
+static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter)
+{
+    PBVHNode *node;
+
+    while(iter->stacksize) {
+        /* pop node */
+        iter->stacksize--;
+        node= iter->stack[iter->stacksize].node;
+
+        /* on a mesh with no faces this can happen
+        * can remove this check if we know meshes have at least 1 face */
+        if(node==NULL) return NULL;
+
+        if(iter->scb && !iter->scb(node, iter->search_data)) continue; /* don't traverse, outside of search zone */
+
+        if(node->flag & PBVH_Leaf) {
+            /* immediately hit leaf node */
+            return node;
+        }
+        else {
+            pbvh_stack_push(iter, iter->bvh->nodes+node->children_offset+1, 0);
+            pbvh_stack_push(iter, iter->bvh->nodes+node->children_offset, 0);
+        }
+    }
+
+    return NULL;
+}
+
 void BLI_pbvh_search_gather(PBVH *bvh,
        BLI_pbvh_SearchCallback scb, void *search_data,
        PBVHNode ***r_array, int *r_tot)
@@ -736,12 +776,105 @@ void BLI_pbvh_search_callback(PBVH *bvh,
        pbvh_iter_begin(&iter, bvh, scb, search_data);
 
        while((node=pbvh_iter_next(&iter)))
-               if(node->flag & PBVH_Leaf)
+               if (node->flag & PBVH_Leaf)
                        hcb(node, hit_data);
 
        pbvh_iter_end(&iter);
 }
 
+typedef struct node_tree {
+    PBVHNode* data;
+
+    struct node_tree* left;
+    struct node_tree* right;
+} node_tree;
+
+static void node_tree_insert(node_tree* tree, node_tree* new_node)
+{
+    if (new_node->data->tmin < tree->data->tmin) {
+        if (tree->left) {
+            node_tree_insert(tree->left, new_node);
+        }
+        else {
+            tree->left = new_node;
+        }
+    }
+    else {
+        if (tree->right) {
+            node_tree_insert(tree->right, new_node);
+        }
+        else {
+            tree->right = new_node;
+        }
+    }
+}
+
+static void traverse_tree(node_tree* tree, BLI_pbvh_HitOccludedCallback hcb, void* hit_data, float* tmin)
+{
+    if (tree->left) traverse_tree(tree->left, hcb, hit_data, tmin);
+
+    hcb(tree->data, hit_data, tmin);
+
+    if (tree->right) traverse_tree(tree->right, hcb, hit_data, tmin);
+}
+
+static void free_tree(node_tree* tree)
+{
+    if (tree->left) {
+        free_tree(tree->left);
+        tree->left = 0;
+    }
+
+    if (tree->right) {
+        free_tree(tree->right);
+        tree->right = 0;
+    }
+
+    free(tree);
+}
+
+float BLI_pbvh_node_get_tmin(PBVHNode* node)
+{
+    return node->tmin;
+}
+
+void BLI_pbvh_search_callback_occluded(PBVH *bvh,
+       BLI_pbvh_SearchCallback scb, void *search_data,
+       BLI_pbvh_HitOccludedCallback hcb, void *hit_data)
+{
+       PBVHIter iter;
+       PBVHNode *node;
+       node_tree *tree = 0;
+
+       pbvh_iter_begin(&iter, bvh, scb, search_data);
+
+       while((node=pbvh_iter_next_occluded(&iter))) {
+               if(node->flag & PBVH_Leaf) {
+                       node_tree* new_node = malloc(sizeof(node_tree));
+
+                       new_node->data = node;
+
+                       new_node->left  = NULL;
+                       new_node->right = NULL;
+
+                       if (tree) {
+                               node_tree_insert(tree, new_node);
+                       }
+                       else {
+                               tree = new_node;
+                       }
+               }
+       }
+
+       pbvh_iter_end(&iter);
+
+       if (tree) {
+               float tmin = FLT_MAX;
+               traverse_tree(tree, hcb, hit_data, &tmin);
+               free_tree(tree);
+       }
+}
+
 static int update_search_cb(PBVHNode *node, void *data_v)
 {
        int flag= GET_INT_FROM_POINTER(data_v);
@@ -985,7 +1118,8 @@ void BLI_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *tot
        GHashIterator *hiter;
        GHash *map;
        void *face, **faces;
-       int i, tot;
+       unsigned i;
+        int tot;
 
        map = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "pbvh_get_grid_updates gh");
 
@@ -1086,6 +1220,18 @@ void BLI_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max
        copy_v3_v3(bb_max, node->orig_vb.bmax);
 }
 
+void BLI_pbvh_node_get_proxies(PBVHNode* node, PBVHProxyNode** proxies, int* proxy_count)
+{
+       if (node->proxy_count > 0) {
+               if (proxies) *proxies = node->proxies;
+               if (proxy_count) *proxy_count = node->proxy_count;
+       }
+       else {
+               if (proxies) *proxies = 0;
+               if (proxy_count) *proxy_count = 0;
+       }
+}
+
 /********************************* Raycast ***********************************/
 
 typedef struct {
@@ -1100,16 +1246,13 @@ typedef struct {
 static int ray_aabb_intersect(PBVHNode *node, void *data_v)
 {
        RaycastData *ray = data_v;
-       float bb_min[3], bb_max[3], bbox[2][3];
+       float bbox[2][3];
        float tmin, tmax, tymin, tymax, tzmin, tzmax;
 
        if(ray->original)
-               BLI_pbvh_node_get_original_BB(node, bb_min, bb_max);
+               BLI_pbvh_node_get_original_BB(node, bbox[0], bbox[1]);
        else
-               BLI_pbvh_node_get_BB(node, bb_min, bb_max);
-       
-       copy_v3_v3(bbox[0], bb_min);
-       copy_v3_v3(bbox[1], bb_max);
+               BLI_pbvh_node_get_BB(node, bbox[0], bbox[1]);
 
        tmin = (bbox[ray->sign[0]][0] - ray->start[0]) * ray->inv_dir[0];
        tmax = (bbox[1-ray->sign[0]][0] - ray->start[0]) * ray->inv_dir[0];
@@ -1119,8 +1262,10 @@ static int ray_aabb_intersect(PBVHNode *node, void *data_v)
 
        if((tmin > tymax) || (tymin > tmax))
                return 0;
+
        if(tymin > tmin)
                tmin = tymin;
+
        if(tymax < tmax)
                tmax = tymax;
 
@@ -1129,20 +1274,20 @@ static int ray_aabb_intersect(PBVHNode *node, void *data_v)
 
        if((tmin > tzmax) || (tzmin > tmax))
                return 0;
-       
-       return 1;
 
-       /* XXX: Not sure about this? 
-          if(tzmin > tmin)
-          tmin = tzmin;
-          if(tzmax < tmax)
-          tmax = tzmax;
-          return ((tmin < t1) && (tmax > t0));
-       */
+       if(tzmin > tmin)
+               tmin = tzmin;
 
+       // XXX jwilkins: tmax does not need to be updated since we don't use it
+       // keeping this here for future reference
+       //if(tzmax < tmax) tmax = tzmax; 
+
+       node->tmin = tmin;
+
+       return 1;
 }
 
-void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data,
+void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitOccludedCallback cb, void *data,
                          float ray_start[3], float ray_normal[3], int original)
 {
        RaycastData rcd;
@@ -1156,37 +1301,24 @@ void BLI_pbvh_raycast(PBVH *bvh, BLI_pbvh_HitCallback cb, void *data,
        rcd.sign[2] = rcd.inv_dir[2] < 0;
        rcd.original = original;
 
-       BLI_pbvh_search_callback(bvh, ray_aabb_intersect, &rcd, cb, data);
+       BLI_pbvh_search_callback_occluded(bvh, ray_aabb_intersect, &rcd, cb, data);
 }
 
-/* XXX: Code largely copied from bvhutils.c, could be unified */
-/* Returns 1 if a better intersection has been found */
 static int ray_face_intersection(float ray_start[3], float ray_normal[3],
                                 float *t0, float *t1, float *t2, float *t3,
                                 float *fdist)
 {
-       int hit = 0;
-
-       do
-       {       
-               float dist = FLT_MAX;
-                       
-               if(!isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t1, t2,
-                                        &dist, NULL, 0.1f))
-                       dist = FLT_MAX;
-
-               if(dist >= 0 && dist < *fdist) {
-                       hit = 1;
-                       *fdist = dist;
-               }
-
-               t1 = t2;
-               t2 = t3;
-               t3 = NULL;
-
-       } while(t2);
-
-       return hit;
+    float dist;
+
+    if ((isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t1, t2, &dist, NULL, 0.1f) && dist < *fdist) ||
+        (t3 && isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t2, t3, &dist, NULL, 0.1f) && dist < *fdist))
+    {
+        *fdist = dist;
+        return 1;
+    }
+    else {
+        return 0;
+    }
 }
 
 int BLI_pbvh_node_raycast(PBVH *bvh, PBVHNode *node, float (*origco)[3],
@@ -1399,3 +1531,86 @@ int BLI_pbvh_isDeformed(PBVH *pbvh)
 {
        return pbvh->deformed;
 }
+/* Proxies */
+
+PBVHProxyNode* BLI_pbvh_node_add_proxy(PBVH* bvh, PBVHNode* node)
+{
+       int index, totverts;
+
+       #pragma omp critical
+       {
+
+               index = node->proxy_count;
+
+               node->proxy_count++;
+
+               if (node->proxies)
+                       node->proxies= MEM_reallocN(node->proxies, node->proxy_count*sizeof(PBVHProxyNode));
+               else
+                       node->proxies= MEM_mallocN(sizeof(PBVHProxyNode), "PBVHNodeProxy");
+
+               if (bvh->grids)
+                       totverts = node->totprim*bvh->gridsize*bvh->gridsize;
+               else
+                       totverts = node->uniq_verts;
+
+               node->proxies[index].co= MEM_callocN(sizeof(float[3])*totverts, "PBVHNodeProxy.co");
+       }
+
+       return node->proxies + index;
+}
+
+void BLI_pbvh_node_free_proxies(PBVHNode* node)
+{
+       #pragma omp critical
+       {
+               int p;
+
+               for (p= 0; p < node->proxy_count; p++) {
+                       MEM_freeN(node->proxies[p].co);
+                       node->proxies[p].co= 0;
+               }
+
+               MEM_freeN(node->proxies);
+               node->proxies = 0;
+
+               node->proxy_count= 0;
+       }
+}
+
+void BLI_pbvh_gather_proxies(PBVH* pbvh, PBVHNode*** r_array,  int* r_tot)
+{
+       PBVHNode **array= NULL, **newarray, *node;
+       int tot= 0, space= 0;
+       int n;
+
+       for (n= 0; n < pbvh->totnode; n++) {
+               node = pbvh->nodes + n;
+
+               if(node->proxy_count > 0) {
+                       if(tot == space) {
+                               /* resize array if needed */
+                               space= (tot == 0)? 32: space*2;
+                               newarray= MEM_callocN(sizeof(PBVHNode)*space, "BLI_pbvh_gather_proxies");
+
+                               if (array) {
+                                       memcpy(newarray, array, sizeof(PBVHNode)*tot);
+                                       MEM_freeN(array);
+                               }
+
+                               array= newarray;
+                       }
+
+                       array[tot]= node;
+                       tot++;
+               }
+       }
+
+       if(tot == 0 && array) {
+               MEM_freeN(array);
+               array= NULL;
+       }
+
+       *r_array= array;
+       *r_tot= tot;
+}
index f612216..7b29ab2 100644 (file)
@@ -1539,6 +1539,7 @@ static void lib_link_brush(FileData *fd, Main *main)
                        brush->id.flag -= LIB_NEEDLINK;
 
                        brush->mtex.tex= newlibadr_us(fd, brush->id.lib, brush->mtex.tex);
+                       brush->image_icon= newlibadr_us(fd, brush->id.lib, brush->image_icon);
                        brush->clone.image= newlibadr_us(fd, brush->id.lib, brush->clone.image);
                }
        }
@@ -10971,6 +10972,82 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                }
        }
 
+       {
+               /* GSOC 2010 Sculpt - New settings for Brush */
+
+               Brush *brush;
+               for (brush= main->brush.first; brush; brush= brush->id.next) {
+                       /* Sanity Check */
+
+                       // infinite number of dabs
+                       if (brush->spacing == 0)
+                               brush->spacing = 10;
+
+                       // will have no effect
+                       if (brush->alpha == 0)
+                               brush->alpha = 0.5f;
+
+                       // bad radius
+                       if (brush->unprojected_radius == 0)
+                               brush->unprojected_radius = 0.125;
+
+                       // unusable size
+                       if (brush->size == 0)
+                               brush->size = 35;
+
+                       // can't see overlay
+                       if (brush->texture_overlay_alpha == 0)
+                               brush->texture_overlay_alpha = 33;
+
+                       // same as draw brush
+                       if (brush->crease_pinch_factor == 0)
+                               brush->crease_pinch_factor = 0.5f;
+
+                       // will sculpt no vertexes
+                       if (brush->plane_trim == 0)
+                               brush->plane_trim = 0.5f;
+
+                       // same as smooth stroke off
+                       if (brush->smooth_stroke_radius == 0)
+                               brush->smooth_stroke_radius= 75;
+
+                       // will keep cursor in one spot
+                       if (brush->smooth_stroke_radius == 1)
+                               brush->smooth_stroke_factor= 0.9f;
+
+                       // same as dots
+                       if (brush->rate == 0)
+                               brush->rate = 0.1f;
+
+                       /* New Settings */
+                       if (main->versionfile < 252 || (main->versionfile == 252 && main->subversionfile < 6)) {
+                               brush->flag |= BRUSH_SPACE_ATTEN; // explicitly enable adaptive space
+
+                               // spacing was originally in pixels, convert it to percentage for new version
+                               // size should not be zero due to sanity check above
+                               brush->spacing = (int)(100*((float)brush->spacing) / ((float)brush->size));
+
+                               if (brush->add_col[0] == 0 &&
+                                       brush->add_col[1] == 0 &&
+                                       brush->add_col[2] == 0)
+                               {
+                                       brush->add_col[0] = 1.00;
+                                       brush->add_col[1] = 0.39;
+                                       brush->add_col[2] = 0.39;
+                               }
+
+                               if (brush->sub_col[0] == 0 &&
+                                       brush->sub_col[1] == 0 &&
+                                       brush->sub_col[2] == 0)
+                               {
+                                       brush->sub_col[0] = 0.39;
+                                       brush->sub_col[1] = 0.39;
+                                       brush->sub_col[2] = 1.00;
+                               }
+                       }
+               }
+       }
+
        /* WATCH IT!!!: pointers from libdata have not been converted yet here! */
        /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */
 
index 001773f..17bafa3 100644 (file)
@@ -243,7 +243,7 @@ static void draw_gpencil_panel (bContext *C, uiLayout *layout, bGPdata *gpd, Poi
        col= uiLayoutColumn(layout, 0);
                /* current Grease Pencil block */
                // TODO: show some info about who owns this?
-               uiTemplateID(col, C, ctx_ptr, "grease_pencil", "GPENCIL_OT_data_add", NULL, "GPENCIL_OT_data_unlink"); 
+               uiTemplateID(col, C, ctx_ptr, "grease_pencil", "GPENCIL_OT_data_add", NULL, "GPENCIL_OT_data_unlink", NULL); 
                
                /* add new layer button - can be used even when no data, since it can add a new block too */
                uiItemO(col, NULL, 0, "GPENCIL_OT_layer_add");
index 93e91d0..4b6b396 100644 (file)
@@ -669,11 +669,11 @@ uiBlock *uiLayoutAbsoluteBlock(uiLayout *layout);
 void uiTemplateHeader(uiLayout *layout, struct bContext *C, int menus);
 void uiTemplateDopeSheetFilter(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr);
 void uiTemplateID(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname,
-       char *newop, char *openop, char *unlinkop);
+       char *newop, char *openop, char *unlinkop, char *filterop);
 void uiTemplateIDBrowse(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname,
-                                 char *newop, char *openop, char *unlinkop);
+                                 char *newop, char *openop, char *unlinkop, char *filterop);
 void uiTemplateIDPreview(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname,
-       char *newop, char *openop, char *unlinkop, int rows, int cols);
+       char *newop, char *openop, char *unlinkop, char *filterop, int rows, int cols);
 void uiTemplateAnyID(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, 
        char *proptypename, char *text);
 void uiTemplatePathBuilder(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, char *propname, 
index 499fe3b..232b6f7 100644 (file)
@@ -46,6 +46,7 @@
 
 #include "DNA_screen_types.h"
 #include "DNA_userdef_types.h"
+#include "DNA_brush_types.h"
 
 #include "BKE_context.h"
 #include "BKE_global.h"
@@ -762,6 +763,7 @@ static void icon_create_mipmap(struct PreviewImage* prv_img, int miplevel)
                prv_img->w[miplevel] = size;
                prv_img->h[miplevel] = size;
                prv_img->changed[miplevel] = 1;
+               prv_img->changed_timestamp[miplevel] = 0;
                prv_img->rect[miplevel] = MEM_callocN(size*size*sizeof(unsigned int), "prv_rect"); 
        }
 }
@@ -976,6 +978,16 @@ int ui_id_icon_get(bContext *C, ID *id, int preview)
                        /* checks if not exists, or changed */
                        ui_id_icon_render(C, id, preview);
                        break;
+               case ID_BR:
+                       { /* use the image in the brush as the icon */
+                         /* XXX redundancy here can be reduced be rewriting this switch as an if */
+                               ID* ima_id = (ID*)((Brush*)id)->image_icon;
+                               id = ima_id ? ima_id : id;
+                               iconid= BKE_icon_getid(id);
+                               /* checks if not exists, or changed */
+                               ui_id_icon_render(C, id, preview);
+                       }
+                       break;
                default:
                        break;
        }
index 7a5e041..cbe1049 100644 (file)
@@ -42,6 +42,7 @@
 #include "BKE_main.h"
 #include "BKE_texture.h"
 #include "BKE_utildefines.h"
+#include "BKE_report.h"
 
 #include "ED_screen.h"
 #include "ED_render.h"
@@ -141,6 +142,8 @@ typedef struct TemplateID {
 
        ListBase *idlb;
        int prv_rows, prv_cols;
+
+       char filterop[64];
 } TemplateID;
 
 /* Search browse menu, assign  */
@@ -170,15 +173,53 @@ static void id_search_cb(const bContext *C, void *arg_template, char *str, uiSea
        /* ID listbase */
        for(id= lb->first; id; id= id->next) {
                if(!((flag & PROP_ID_SELF_CHECK) && id == id_from)) {
+                       int filter_yes;
+
+                       filter_yes= 0;
+
+                       /* use filter */
+                       if (template->filterop[0] != 0) {
+                               PointerRNA ptr;
+                               ReportList reports;
+                               FunctionRNA *func;
+                               ParameterList parms;
+
+                               RNA_id_pointer_create(id, &ptr);
+
+                               BKE_reports_init(&reports, RPT_PRINT);
+
+                               func= RNA_struct_find_function(&ptr, template->filterop);
+
+                               if (func) {
+                                       RNA_parameter_list_create(&parms, &ptr, func);
+
+                                       RNA_parameter_set_lookup(&parms, "context", &C);
+
+                                       if (RNA_function_call(C, &reports, &ptr, func, &parms) == 0) {
+                                               int* ret;
+                                               RNA_parameter_get_lookup(&parms, "ret", &ret);
 
-                       /* hide dot-datablocks */
-                       if(U.uiflag & USER_HIDE_DOT)
+                                               if (!(*ret)) {
+                                                       RNA_parameter_list_free(&parms);
+                                                       continue;
+                                               }
+                                               else {
+                                                       filter_yes= 1;
+                                               }
+                                       }
+
+                                       RNA_parameter_list_free(&parms);
+                               }
+                       }
+
+                       /* hide dot-datablocks, but only if filter does not force it visible */
+                       if(!filter_yes && U.uiflag & USER_HIDE_DOT)
                                if ((id->name[2]=='.') && (str[0] != '.'))
                                        continue;
 
                        if(BLI_strcasestr(id->name+2, str)) {
                                iconid= ui_id_icon_get((bContext*)C, id, 1);
-                
+
                                if(!uiSearchItemAdd(items, id->name+2, id, iconid))
                                        break;
                        }
@@ -340,7 +381,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
        }
 }
 
-static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag, char *newop, char *openop, char *unlinkop)
+static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, int flag, char *newop, char *openop, char *unlinkop, char *filterop)
 {
        uiBut *but;
        uiBlock *block;
@@ -480,7 +521,7 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str
        uiBlockEndAlign(block);
 }
 
-static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, int flag, int prv_rows, int prv_cols)
+static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char* filterop, int flag, int prv_rows, int prv_cols)
 {
        TemplateID *template;
        PropertyRNA *prop;
@@ -498,7 +539,12 @@ static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char
        template->prop= prop;
        template->prv_rows = prv_rows;
        template->prv_cols = prv_cols;
-       
+
+       if (filterop) 
+               BLI_strncpy(template->filterop, filterop, sizeof(template->filterop));
+       else
+               template->filterop[0] = 0;
+
        if(newop)
                flag |= UI_ID_ADD_NEW;
        if(openop)
@@ -512,26 +558,25 @@ static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, char
         */
        if(template->idlb) {
                uiLayoutRow(layout, 1);
-               template_ID(C, layout, template, type, flag, newop, openop, unlinkop);
+               template_ID(C, layout, template, type, flag, newop, openop, unlinkop, filterop);
        }
 
        MEM_freeN(template);
-       
 }
 
-void uiTemplateID(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop)
+void uiTemplateID(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char *filterop)
 {
-       ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE, 0, 0);
+       ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, filterop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE, 0, 0);
 }
 
-void uiTemplateIDBrowse(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop)
+void uiTemplateIDBrowse(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char *filterop)
 {
-       ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE|UI_ID_RENAME, 0, 0);
+       ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, filterop, UI_ID_BROWSE|UI_ID_RENAME, 0, 0);
 }
 
-void uiTemplateIDPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, int rows, int cols)
+void uiTemplateIDPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, char *propname, char *newop, char *openop, char *unlinkop, char *filterop, int rows, int cols)
 {
-       ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE|UI_ID_PREVIEWS, rows, cols);
+       ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, filterop, UI_ID_BROWSE|UI_ID_RENAME|UI_ID_DELETE|UI_ID_PREVIEWS, rows, cols);
 }
 
 /************************ ID Chooser Template ***************************/
index cf47feb..bea8d3b 100644 (file)
@@ -1463,7 +1463,7 @@ void init_userdef_do_versions(void)
                                SETCOL(btheme->tv3d.lastsel_point, 0xff, 0xff, 0xff, 255);
                }
        }
-       if (G.main->versionfile <= 252 || (G.main->versionfile == 252 && G.main->subversionfile < 5)) {
+       if (G.main->versionfile < 252 || (G.main->versionfile == 252 && G.main->subversionfile < 5)) {
                bTheme *btheme;
                
                /* interface_widgets.c */
@@ -1521,7 +1521,14 @@ void init_userdef_do_versions(void)
        /* this timer uses U */
 // XXX reset_autosave();
 
-}
+       /* GSOC Sculpt 2010 - Sanity check on Sculpt/Paint settings */
 
+       if (U.sculpt_paint_unified_alpha == 0)
+               U.sculpt_paint_unified_alpha = 0.5f;
 
+       if (U.sculpt_paint_unified_unprojected_radius == 0) 
+               U.sculpt_paint_unified_unprojected_radius = 0.125f;
 
+       if (U.sculpt_paint_unified_size == 0)
+               U.sculpt_paint_unified_size = 35;
+}
index 472ba36..3d9a514 100644 (file)
@@ -3,6 +3,8 @@ Import ('env')
 
 sources = env.Glob('*.c')
 
+defs = []
+
 incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
 incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
 incs += ' ../../render/extern/include'
@@ -12,7 +14,11 @@ if env['OURPLATFORM'] == 'linux2':
        cflags='-pthread'
        incs += ' ../../../extern/binreloc/include'
 
+if env['OURPLATFORM'] == 'linuxcross':
+    if env['WITH_BF_OPENMP']:
+        incs += ' ' + env['BF_OPENMP_INC']
+
 if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
-       incs += ' ' + env['BF_PTHREADS_INC']
+    incs += ' ' + env['BF_PTHREADS_INC']
 
-env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), [], libtype=['core'], priority=[40] )
+env.BlenderLib ( 'bf_editors_sculpt_paint', sources, Split(incs), defines=defs, libtype=['core'], priority=[40] )
index 41908bb..45c0396 100644 (file)
@@ -641,8 +641,8 @@ static int project_paint_PickColor(const ProjPaintState *ps, float pt[2], float
                }
        }
        else {
-               xi = (uv[0]*ibuf->x) + 0.5f;
-               yi = (uv[1]*ibuf->y) + 0.5f;
+               xi = (int)((uv[0]*ibuf->x) + 0.5f);
+               yi = (int)((uv[1]*ibuf->y) + 0.5f);
                
                //if (xi<0 || xi>=ibuf->x  ||  yi<0 || yi>=ibuf->y) return 0;
                
@@ -1053,15 +1053,15 @@ static void uv_image_outset(float (*orig_uv)[2], float (*outset_uv)[2], const fl
         * This is incorrect. Its already given radians but without it wont work.
         * need to look into a fix - campbell */
        if (is_quad) {
-               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * (M_PI/180.0f));
-               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f));
-               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f));
-               a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * (M_PI/180.0f));
+               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir4, dir1) * ((float)M_PI/180.0f));
+               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f));
+               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f));
+               a4 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir4) * ((float)M_PI/180.0f));
        }
        else {
-               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * (M_PI/180.0f));
-               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * (M_PI/180.0f));
-               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * (M_PI/180.0f));
+               a1 = shell_angle_to_dist(angle_normalized_v2v2(dir3, dir1) * ((float)M_PI/180.0f));
+               a2 = shell_angle_to_dist(angle_normalized_v2v2(dir1, dir2) * ((float)M_PI/180.0f));
+               a3 = shell_angle_to_dist(angle_normalized_v2v2(dir2, dir3) * ((float)M_PI/180.0f));
        }
        
        if (is_quad) {
@@ -1197,7 +1197,7 @@ static void screen_px_from_persp(
                w[2] *= wtot_inv;
        }
        else {
-               w[0] = w[1] = w[2] = 1.0/3.0; /* dummy values for zero area face */
+               w[0] = w[1] = w[2] = 1.0f/3.0f; /* dummy values for zero area face */
        }
        /* done re-weighting */
        
@@ -2513,11 +2513,11 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
                                                                                                w[0]=w[1]=w[2]= 0.0;
                                                                                                if (side) {
                                                                                                        w[fidx1?fidx1-1:0] = fac;
-                                                                                                       w[fidx2?fidx2-1:0] = 1.0-fac;
+                                                                                                       w[fidx2?fidx2-1:0] = 1.0f-fac;
                                                                                                }
                                                                                                else {
                                                                                                        w[fidx1] = fac;
-                                                                                                       w[fidx2] = 1.0-fac;
+                                                                                                       w[fidx2] = 1.0f-fac;
                                                                                                }
 #endif
                                                                                        }
@@ -2571,11 +2571,12 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i
 static void project_paint_bucket_bounds(const ProjPaintState *ps, const float min[2], const float max[2], int bucketMin[2], int bucketMax[2])
 {
        /* divide by bucketWidth & bucketHeight so the bounds are offset in bucket grid units */
-       bucketMin[0] = (int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f; /* these offsets of 0.5 and 1.5 seem odd but they are correct */
-       bucketMin[1] = (int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f;
+       /* XXX: the offset of 0.5 is always truncated to zero and the offset of 1.5f is always truncated to 1, is this really correct?? - jwilkins */
+       bucketMin[0] = (int)((int)(((float)(min[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 0.5f); /* these offsets of 0.5 and 1.5 seem odd but they are correct */
+       bucketMin[1] = (int)((int)(((float)(min[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 0.5f);
        
-       bucketMax[0] = (int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f;
-       bucketMax[1] = (int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f;  
+       bucketMax[0] = (int)((int)(((float)(max[0] - ps->screenMin[0]) / ps->screen_width) * ps->buckets_x) + 1.5f);
+       bucketMax[1] = (int)((int)(((float)(max[1] - ps->screenMin[1]) / ps->screen_height) * ps->buckets_y) + 1.5f);
        
        /* incase the rect is outside the mesh 2d bounds */
        CLAMP(bucketMin[0], 0, ps->buckets_x);
@@ -3029,19 +3030,19 @@ static void project_paint_begin(ProjPaintState *ps)
        
        if(ps->source==PROJ_SRC_VIEW) {
 #ifdef PROJ_DEBUG_WINCLIP
-               CLAMP(ps->screenMin[0], -ps->brush->size, ps->winx + ps->brush->size);
-               CLAMP(ps->screenMax[0], -ps->brush->size, ps->winx + ps->brush->size);
+               CLAMP(ps->screenMin[0], (float)(-ps->brush->size), (float)(ps->winx + ps->brush->size));
+               CLAMP(ps->screenMax[0], (float)(-ps->brush->size), (float)(ps->winx + ps->brush->size));
 
-               CLAMP(ps->screenMin[1], -ps->brush->size, ps->winy + ps->brush->size);
-               CLAMP(ps->screenMax[1], -ps->brush->size, ps->winy + ps->brush->size);
+               CLAMP(ps->screenMin[1], (float)(-ps->brush->size), (float)(ps->winy + ps->brush->size));
+               CLAMP(ps->screenMax[1], (float)(-ps->brush->size), (float)(ps->winy + ps->brush->size));
 #endif
        }
        else { /* reprojection, use bounds */
                ps->screenMin[0]= 0;
-               ps->screenMax[0]= ps->winx;
+               ps->screenMax[0]= (float)(ps->winx);
 
                ps->screenMin[1]= 0;
-               ps->screenMax[1]= ps->winy;
+               ps->screenMax[1]= (float)(ps->winy);
        }
 
        /* only for convenience */
@@ -3497,7 +3498,7 @@ static int project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf
                        project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds);
                        
                        if (    (ps->source != PROJ_SRC_VIEW) ||
-                                       project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, ps->brush->size * ps->brush->size, bucket_bounds)
+                                       project_bucket_isect_circle(ps->context_bucket_x, ps->context_bucket_y, mval, (float)(ps->brush->size * ps->brush->size), bucket_bounds)
                        ) {
                                *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x);
                                ps->context_bucket_x++;
@@ -3545,7 +3546,7 @@ static void blend_color_mix(unsigned char *cp, const unsigned char *cp1, const u
 
 static void blend_color_mix_float(float *cp, const float *cp1, const float *cp2, const float fac)
 {
-       const float mfac= 1.0-fac;
+       const float mfac= 1.0f-fac;
        cp[0]= mfac*cp1[0] + fac*cp2[0];
        cp[1]= mfac*cp1[1] + fac*cp2[1];
        cp[2]= mfac*cp1[2] + fac*cp2[2];
@@ -3712,7 +3713,7 @@ static void *do_projectpaint_thread(void *ph_v)
        }
        
        /* avoid a square root with every dist comparison */
-       brush_size_sqared = ps->brush->size * ps->brush->size; 
+       brush_size_sqared = (float)(ps->brush->size * ps->brush->size);
        
        /* printf("brush bounds %d %d %d %d\n", bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */
        
@@ -3771,7 +3772,7 @@ static void *do_projectpaint_thread(void *ph_v)
                                                        falloff = 1.0f - falloff;
                                                        falloff = 1.0f - (falloff * falloff);
                                                        
-                                                       mask_short = projPixel->mask * (ps->brush->alpha * falloff);
+                                                       mask_short = (unsigned short)(projPixel->mask * (ps->brush->alpha * falloff));
                                                        if (mask_short > projPixel->mask_max) {
                                                                mask = ((float)mask_short)/65535.0f;
                                                                projPixel->mask_max = mask_short;
@@ -3932,8 +3933,8 @@ static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, i
        /* Use mouse coords as floats for projection painting */
        float pos[2];
        
-       pos[0] = mval_i[0];
-       pos[1] = mval_i[1];
+       pos[0] = (float)(mval_i[0]);
+       pos[1] = (float)(mval_i[1]);
        
        // we may want to use this later 
        // brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
@@ -4057,7 +4058,8 @@ static int imapaint_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo
 {
        float inrgb[3];
 
-       if ((x >= ibuf->x) || (y >= ibuf->y)) {
+       // XXX: signed unsigned mismatch
+       if ((x >= (unsigned int)(ibuf->x)) || (y >= (unsigned int)(ibuf->y))) {
                if (torus) imapaint_ibuf_get_set_rgb(ibuf, x, y, 1, 0, inrgb);
                else return 0;
        }
@@ -4611,8 +4613,8 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps)
                ps->normal_angle_inner= ps->normal_angle= settings->imapaint.normal_angle;
        }
 
-       ps->normal_angle_inner *=       M_PI_2 / 90;
-       ps->normal_angle *=                     M_PI_2 / 90;
+       ps->normal_angle_inner *=       (float)(M_PI_2 / 90);
+       ps->normal_angle *=                     (float)(M_PI_2 / 90);
        ps->normal_angle_range = ps->normal_angle - ps->normal_angle_inner;
 
        if(ps->normal_angle_range <= 0.0f)
@@ -4711,8 +4713,8 @@ static void paint_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
        int mouse[2], redraw;
 
        RNA_float_get_array(itemptr, "mouse", mousef);
-       mouse[0] = mousef[0];
-       mouse[1] = mousef[1];
+       mouse[0] = (int)(mousef[0]);
+       mouse[1] = (int)(mousef[1]);
        time= RNA_float_get(itemptr, "time");
        pressure= RNA_float_get(itemptr, "pressure");
 
@@ -4832,8 +4834,8 @@ static void paint_apply_event(bContext *C, wmOperator *op, wmEvent *event)
        /* fill in stroke */
        RNA_collection_add(op->ptr, "stroke", &itemptr);
 
-       mousef[0] = mouse[0];
-       mousef[1] = mouse[1];
+       mousef[0] = (float)(mouse[0]);
+       mousef[1] = (float)(mouse[1]);
        RNA_float_set_array(&itemptr, "mouse", mousef);
        RNA_float_set(&itemptr, "time", (float)(time - pop->starttime));
        RNA_float_set(&itemptr, "pressure", pressure);
@@ -4950,7 +4952,7 @@ static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
                glColor4ub(255, 255, 255, 128);
                glEnable( GL_LINE_SMOOTH );
                glEnable(GL_BLEND);
-               glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size*0.5f, 40);
+               glutil_draw_lined_arc(0, (float)(M_PI*2.0), brush->size*0.5f, 40);
                glDisable(GL_BLEND);
                glDisable( GL_LINE_SMOOTH );
                
@@ -4977,7 +4979,7 @@ static int paint_radial_control_invoke(bContext *C, wmOperator *op, wmEvent *eve
        ToolSettings *ts = CTX_data_scene(C)->toolsettings;
        get_imapaint_zoom(C, &zoom, &zoom);
        toggle_paint_cursor(C, !ts->imapaint.paintcursor);
-       brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5 * zoom);
+       brush_radial_control_invoke(op, paint_brush(&ts->imapaint.paint), 0.5f * zoom);
        return WM_radial_control_invoke(C, op, event);
 }
 
@@ -4997,7 +4999,7 @@ static int paint_radial_control_exec(bContext *C, wmOperator *op)
        int ret;
        char str[256];
        get_imapaint_zoom(C, &zoom, &zoom);
-       ret = brush_radial_control_exec(op, brush, 2.0 / zoom);
+       ret = brush_radial_control_exec(op, brush, 2.0f / zoom);
        WM_radial_control_string(op, str, 256);
        
        WM_event_add_notifier(C, NC_BRUSH|NA_EDITED, brush);
index b25eec7..0ae7c7f 100644 (file)
@@ -110,6 +110,13 @@ void PAINT_OT_face_select_all(struct wmOperatorType *ot);
 
 int facemask_paint_poll(struct bContext *C);
 
+/* stroke operator */
+typedef enum wmBrushStrokeMode {
+       WM_BRUSHSTROKE_NORMAL,
+       WM_BRUSHSTROKE_INVERT,
+       WM_BRUSHSTROKE_SMOOTH,
+} wmBrushStrokeMode;
+
 /* paint_undo.c */
 typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
 typedef void (*UndoFreeCb)(struct ListBase *lb);
index 5470ac8..5d52b0a 100644 (file)
 #include "sculpt_intern.h"
 
 #include <string.h>
+//#include <stdio.h>
 
 /* Brush operators */
 static int brush_add_exec(bContext *C, wmOperator *op)
 {
        /*int type = RNA_enum_get(op->ptr, "type");*/
-       Brush *br = NULL;
+       Paint *paint = paint_get_active(CTX_data_scene(C));
+       Brush *br = paint_brush(paint);
 
-       br = add_brush("Brush");
+       if (br)
+               br = copy_brush(br);
+       else
+               br = add_brush("Brush");
 
-       if(br)
-               paint_brush_set(paint_get_active(CTX_data_scene(C)), br);
+       paint_brush_set(paint_get_active(CTX_data_scene(C)), br);
 
        return OPERATOR_FINISHED;
 }
 
-static EnumPropertyItem brush_type_items[] = {
-       {OB_MODE_SCULPT, "SCULPT", ICON_SCULPTMODE_HLT, "Sculpt", ""},
-       {OB_MODE_VERTEX_PAINT, "VERTEX_PAINT", ICON_VPAINT_HLT, "Vertex Paint", ""},
-       {OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
-       {OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
-       {0, NULL, 0, NULL, NULL}};
-
 void BRUSH_OT_add(wmOperatorType *ot)
 {
        /* identifiers */
@@ -72,9 +69,43 @@ void BRUSH_OT_add(wmOperatorType *ot)
        ot->exec= brush_add_exec;
        
        /* flags */
-       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       ot->flag= OPTYPE_UNDO;
+}
+
+
+static int brush_scale_size_exec(bContext *C, wmOperator *op)
+{
+       /*int type = RNA_enum_get(op->ptr, "type");*/
+       Paint *paint = paint_get_active(CTX_data_scene(C));
+       Brush *br = paint_brush(paint);
+       float factor = RNA_float_get(op->ptr, "scalar");
+
+       if (br) {
+               if (U.sculpt_paint_settings & SCULPT_PAINT_USE_UNIFIED_SIZE) {
+                       U.sculpt_paint_unified_size *= factor;
+               }
+               else {
+                       br->size *= factor;
+               }
+       }
+
+       return OPERATOR_FINISHED;
+}
+
+void BRUSH_OT_scale_size(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Scale Sculpt/Paint Brush Size";
+       ot->description= "Change brush size by a scalar";
+       ot->idname= "BRUSH_OT_scale_size";
+       
+       /* api callbacks */
+       ot->exec= brush_scale_size_exec;
+       
+       /* flags */
+       ot->flag= OPTYPE_UNDO;
 
-       RNA_def_enum(ot->srna, "type", brush_type_items, OB_MODE_VERTEX_PAINT, "Type", "Which paint mode to create the brush for.");
+       RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2);
 }
 
 static int vertex_color_set_exec(bContext *C, wmOperator *op)
@@ -108,8 +139,10 @@ void ED_operatortypes_paint(void)
 {
        /* brush */
        WM_operatortype_append(BRUSH_OT_add);
+       WM_operatortype_append(BRUSH_OT_scale_size);
        WM_operatortype_append(BRUSH_OT_curve_preset);
 
+
        /* image */
        WM_operatortype_append(PAINT_OT_texture_paint_toggle);
        WM_operatortype_append(PAINT_OT_texture_paint_radial_control);
@@ -141,6 +174,7 @@ void ED_operatortypes_paint(void)
        WM_operatortype_append(PAINT_OT_face_select_all);
 }
 
+
 static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path)
 {
        wmKeyMapItem *kmi;
@@ -180,15 +214,13 @@ static void ed_keymap_paint_brush_switch(wmKeyMap *keymap, const char *path)
 static void ed_keymap_paint_brush_size(wmKeyMap *keymap, const char *path)
 {
        wmKeyMapItem *kmi;
-       
-       kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", LEFTBRACKETKEY, KM_PRESS, 0, 0);
-       RNA_string_set(kmi->ptr, "data_path", path);
-       RNA_float_set(kmi->ptr, "value", 0.9);
-       
-       kmi= WM_keymap_add_item(keymap, "WM_OT_context_scale_int", RIGHTBRACKETKEY, KM_PRESS, 0, 0);
-       RNA_string_set(kmi->ptr, "data_path", path);
-       RNA_float_set(kmi->ptr, "value", 10.0/9.0); // 1.1111....
-}      
+
+       kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0);
+       RNA_float_set(kmi->ptr, "scalar", 0.9);
+
+       kmi= WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0);
+       RNA_float_set(kmi->ptr, "scalar", 10.0/9.0); // 1.1111....
+}
 
 void ED_keymap_paint(wmKeyConfig *keyconf)
 {
@@ -200,12 +232,15 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
        keymap= WM_keymap_find(keyconf, "Sculpt", 0, 0);
        keymap->poll= sculpt_poll;
 
-       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr, "mode", WM_RADIALCONTROL_SIZE);
+       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, 0, 0)->ptr,        "mode", WM_RADIALCONTROL_SIZE);
        RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_RADIALCONTROL_STRENGTH);
-       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr, "mode", WM_RADIALCONTROL_ANGLE);
+       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0)->ptr,  "mode", WM_RADIALCONTROL_ANGLE);
 
-       WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
-       WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
+       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, 0,        0)->ptr, "mode", WM_BRUSHSTROKE_NORMAL);
+       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_CTRL,  0)->ptr, "mode", WM_BRUSHSTROKE_INVERT);
+       RNA_enum_set(WM_keymap_add_item(keymap, "SCULPT_OT_brush_stroke", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", WM_BRUSHSTROKE_SMOOTH);
+
+       //stroke_mode_modal_keymap(keyconf);
 
        for(i=0; i<=5; i++)
                RNA_int_set(WM_keymap_add_item(keymap, "OBJECT_OT_subdivision_set", ZEROKEY+i, KM_PRESS, KM_CTRL, 0)->ptr, "level", i);
@@ -221,7 +256,8 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
 
        ed_keymap_paint_brush_switch(keymap, "tool_settings.sculpt.active_brush_index");
        ed_keymap_paint_brush_size(keymap, "tool_settings.sculpt.brush.size");
-       
+
+       /* */
        kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", AKEY, KM_PRESS, 0, 0);
        RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.use_anchor");
 
@@ -254,11 +290,10 @@ void ED_keymap_paint(wmKeyConfig *keyconf)
        kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", LKEY, KM_PRESS, 0, 0);
        RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.active_brush_name");
        RNA_string_set(kmi->ptr, "value", "Layer");
-       
+
        kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", TKEY, KM_PRESS, KM_SHIFT, 0); // was just T in 2.4x
        RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.active_brush_name");
        RNA_string_set(kmi->ptr, "value", "Flatten");
-       
 
        /* Vertex Paint mode */
        keymap= WM_keymap_find(keyconf, "Vertex Paint", 0, 0);
index 441464c..e373a25 100644 (file)
@@ -20,7 +20,7 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Jason Wilkins, Tom Musgrove.
  *
  * ***** END GPL LICENSE BLOCK *****
  *
@@ -35,6 +35,7 @@
 
 #include "BKE_context.h"
 #include "BKE_paint.h"
+#include "BKE_brush.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -49,6 +50,9 @@
 #include "ED_view3d.h"
 
 #include "paint_intern.h"
+#include "sculpt_intern.h" // XXX, for expedience in getting this working, refactor later (or this just shows that this needs unification)
+
+#include "BKE_image.h"
 
 #include <float.h>
 #include <math.h>
@@ -96,54 +100,748 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata
        glDisable(GL_LINE_SMOOTH);
 }
 
-static void paint_draw_cursor(bContext *C, int x, int y, void *customdata)
+#if 0
+
+// grid texture for testing
+
+#define GRID_WIDTH   8
+#define GRID_LENGTH  8
+
+#define W (0xFFFFFFFF)
+#define G (0x00888888)
+#define E (0xE1E1E1E1)
+#define C (0xC3C3C3C3)
+#define O (0xB4B4B4B4)
+#define Q (0xA9A9A9A9)
+
+static unsigned grid_texture0[256] =
+{
+   W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,G,G,G,G,G,G,G,G,G,G,G,G,G,G,W,
+   W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,
+};
+
+static unsigned grid_texture1[64] =
 {
-       Paint *paint = paint_get_active(CTX_data_scene(C));
-       Brush *brush = paint_brush(paint);
+   C,C,C,C,C,C,C,C,
+   C,G,G,G,G,G,G,C,
+   C,G,G,G,G,G,G,C,
+   C,G,G,G,G,G,G,C,
+   C,G,G,G,G,G,G,C,
+   C,G,G,G,G,G,G,C,
+   C,G,G,G,G,G,G,C,
+   C,C,C,C,C,C,C,C,
+};
+
+static unsigned grid_texture2[16] =
+{
+   O,O,O,O,
+   O,G,G,O,
+   O,G,G,O,
+   O,O,O,O,
+};
 
-       if(!(paint->flags & PAINT_SHOW_BRUSH))
-               return;
+static unsigned grid_texture3[4] =
+{
+   Q,Q,
+   Q,Q,
+};
 
-       glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
-       glEnable(GL_LINE_SMOOTH);
-       glEnable(GL_BLEND);
+static unsigned grid_texture4[1] =
+{
+   Q,
+};
 
-       glTranslatef((float)x, (float)y, 0.0f);
-       glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
-       glTranslatef((float)-x, (float)-y, 0.0f);
+#undef W
+#undef G
+#undef E
+#undef C
+#undef O
+#undef Q
 
-       glDisable(GL_BLEND);
-       glDisable(GL_LINE_SMOOTH);
+static void load_grid()
+{
+       static GLuint overlay_texture;
+
+       if (!overlay_texture) {
+               //GLfloat largest_supported_anisotropy;
+
+               glGenTextures(1, &overlay_texture);
+               glBindTexture(GL_TEXTURE_2D, overlay_texture);
+               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture0);
+               glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB,  8,  8, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture1);
+               glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB,  4,  4, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture2);
+               glTexImage2D(GL_TEXTURE_2D, 3, GL_RGB,  2,  2, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture3);
+               glTexImage2D(GL_TEXTURE_2D, 4, GL_RGB,  1,  1, 0, GL_RGBA, GL_UNSIGNED_BYTE, grid_texture4);
+               glEnable(GL_TEXTURE_2D);
+               glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+
+               //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+               //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+
+               //glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy);
+               //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_supported_anisotropy);
+       }
+}
+
+#endif
+
+extern float get_tex_pixel(Brush* br, float u, float v);
+
+typedef struct Snapshot {
+       float size[3];
+       float ofs[3];
+       float rot;
+       int brush_size;
+       int winx;
+       int winy;
+       int brush_map_mode;
+       int curve_changed_timestamp;
+} Snapshot;
+
+static int same_snap(Snapshot* snap, Brush* brush, ViewContext* vc)
+{
+       MTex* mtex = &brush->mtex;
+
+       return 
+               (mtex->tex &&
+                   mtex->ofs[0] == snap->ofs[0] &&
+                   mtex->ofs[1] == snap->ofs[1] &&
+                   mtex->ofs[2] == snap->ofs[2] &&
+                   mtex->size[0] == snap->size[0] &&
+                   mtex->size[1] == snap->size[1] &&
+                   mtex->size[2] == snap->size[2] &&
+                   mtex->rot == snap->rot) &&
+               ((mtex->brush_map_mode == MTEX_MAP_MODE_FIXED && sculpt_get_brush_size(brush) <= snap->brush_size) || (sculpt_get_brush_size(brush) == snap->brush_size)) && // make brush smaller shouldn't cause a resample
+               mtex->brush_map_mode == snap->brush_map_mode &&
+               vc->ar->winx == snap->winx &&
+               vc->ar->winy == snap->winy;
+}
+
+static void make_snap(Snapshot* snap, Brush* brush, ViewContext* vc)
+{
+       if (brush->mtex.tex) {
+               snap->brush_map_mode = brush->mtex.brush_map_mode;
+               copy_v3_v3(snap->ofs, brush->mtex.ofs);
+               copy_v3_v3(snap->size, brush->mtex.size);
+               snap->rot = brush->mtex.rot;
+       }
+       else {
+               snap->brush_map_mode = -1;
+               snap->ofs[0]= snap->ofs[0]= snap->ofs[0]= -1;
+               snap->size[0]= snap->size[0]= snap->size[0]= -1;
+               snap->rot = -1;
+       }
+
+       snap->brush_size = sculpt_get_brush_size(brush);
+       snap->winx = vc->ar->winx;
+       snap->winy = vc->ar->winy;
+}
+
+int load_tex(Sculpt *sd, Brush* br, ViewContext* vc)
+{
+       static GLuint overlay_texture = 0;
+       static int init = 0;
+       static int tex_changed_timestamp = -1;
+       static int curve_changed_timestamp = -1;
+       static Snapshot snap;
+       static int old_size = -1;
+
+       GLubyte* buffer = 0;
+
+       int size;
+       int j;
+       int refresh;
+
+       if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED && !br->mtex.tex) return 0;
+
+       refresh = 
+               !overlay_texture ||
+               (br->mtex.tex && 
+                   (!br->mtex.tex->preview ||
+                     br->mtex.tex->preview->changed_timestamp[0] != tex_changed_timestamp)) ||
+               !br->curve ||
+               br->curve->changed_timestamp != curve_changed_timestamp ||
+               !same_snap(&snap, br, vc);
+
+       if (refresh) {
+               if (br->mtex.tex && br->mtex.tex->preview)
+                       tex_changed_timestamp = br->mtex.tex->preview->changed_timestamp[0];
+
+               if (br->curve)
+                       curve_changed_timestamp = br->curve->changed_timestamp;
+
+               make_snap(&snap, br, vc);
+
+               if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) {
+                       int s = sculpt_get_brush_size(br);
+                       int r = 1;
+
+                       for (s >>= 1; s > 0; s >>= 1)
+                               r++;
+
+                       size = (1<<r);
+
+                       if (size < 256)
+                               size = 256;
+
+                       if (size < old_size)
+                               size = old_size;
+               }
+               else
+                       size = 512;
+
+               if (old_size != size) {
+                       if (overlay_texture) {
+                               glDeleteTextures(1, &overlay_texture);
+                               overlay_texture = 0;
+                       }
+
+                       init = 0;
+
+                       old_size = size;
+               }
+
+               buffer = MEM_mallocN(sizeof(GLubyte)*size*size, "load_tex");
+
+               #pragma omp parallel for schedule(static) if (sd->flags & SCULPT_USE_OPENMP)
+               for (j= 0; j < size; j++) {
+                       int i;
+                       float y;
+                       float len;
+
+                       for (i= 0; i < size; i++) {
+
+                               // largely duplicated from tex_strength
+
+                               const float rotation = -br->mtex.rot;
+                               float diameter = sculpt_get_brush_size(br);
+                               int index = j*size + i;
+                               float x;
+                               float avg;
+
+                               x = (float)i/size;
+                               y = (float)j/size;
+
+                               x -= 0.5f;
+                               y -= 0.5f;
+
+                               if (br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) {
+                                       x *= vc->ar->winx / diameter;
+                                       y *= vc->ar->winy / diameter;
+                               }
+                               else {
+                                       x *= 2;
+                                       y *= 2;
+                               }
+
+                               len = sqrtf(x*x + y*y);
+
+                               if ((br->mtex.brush_map_mode == MTEX_MAP_MODE_TILED) || len <= 1) {
+                                       /* it is probably worth optimizing for those cases where 
+                                          the texture is not rotated by skipping the calls to
+                                          atan2, sqrtf, sin, and cos. */
+                                       if (br->mtex.tex && (rotation > 0.001 || rotation < -0.001)) {
+                                               const float angle    = atan2(y, x) + rotation;
+
+                                               x = len * cos(angle);
+                                               y = len * sin(angle);
+                                       }
+
+                                       x *= br->mtex.size[0];
+                                       y *= br->mtex.size[1];
+
+                                       x += br->mtex.ofs[0];
+                                       y += br->mtex.ofs[1];
+
+                                       avg = br->mtex.tex ? get_tex_pixel(br, x, y) : 1;
+
+                                       avg += br->texture_sample_bias;
+
+                                       if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED)
+                                               avg *= brush_curve_strength(br, len, 1); /* Falloff curve */
+
+                                       buffer[index] = (GLubyte)(255*avg);
+                               }
+                               else {
+                                       buffer[index] = 0;
+                               }
+                       }
+               }
+
+               if (!overlay_texture)
+                       glGenTextures(1, &overlay_texture);
+       }
+       else {
+               size= old_size;
+       }
+
+       glBindTexture(GL_TEXTURE_2D, overlay_texture);
+
+       if (refresh) {
+               if (!init) {
+                       glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, size, size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
+                       init = 1;
+               }
+               else {
+                       glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
+               }
+
+               if (buffer)
+                       MEM_freeN(buffer);
+       }
+
+       glEnable(GL_TEXTURE_2D);
+
+       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       if (br->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) {
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+       }
+
+       return 1;
+}
+
+/* Convert a point in model coordinates to 2D screen coordinates. */
+// XXX duplicated from sculpt.c, deal with this later.
+static void projectf(bglMats *mats, const float v[3], float p[2])
+{
+       double ux, uy, uz;
+
+       gluProject(v[0],v[1],v[2], mats->modelview, mats->projection,
+                  (GLint *)mats->viewport, &ux, &uy, &uz);
+       p[0]= ux;
+       p[1]= uy;
+}
+
+static int project_brush_radius(RegionView3D* rv3d, float radius, float location[3], bglMats* mats)
+{
+       float view[3], nonortho[3], ortho[3], offset[3], p1[2], p2[2];
+
+       viewvector(rv3d, location, view);
+
+       // create a vector that is not orthogonal to view
+
+       if (fabsf(view[0]) < 0.1) {
+               nonortho[0] = view[0] + 1;
+               nonortho[1] = view[1];
+               nonortho[2] = view[2];
+       }
+       else if (fabsf(view[1]) < 0.1) {
+               nonortho[0] = view[0];
+               nonortho[1] = view[1] + 1;
+               nonortho[2] = view[2];
+       }
+       else {
+               nonortho[0] = view[0];
+               nonortho[1] = view[1];
+               nonortho[2] = view[2] + 1;
+       }
+
+       // get a vector in the plane of the view
+       cross_v3_v3v3(ortho, nonortho, view);
+       normalize_v3(ortho);
+
+       // make a point on the surface of the brush tagent to the view
+       mul_v3_fl(ortho, radius);
+       add_v3_v3v3(offset, location, ortho);
+
+       // project the center of the brush, and the tagent point to the view onto the screen
+       projectf(mats, location, p1);
+       projectf(mats, offset, p2);
+
+       // the distance between these points is the size of the projected brush in pixels
+       return len_v2v2(p1, p2);
+}
+
+int sculpt_get_brush_geometry(bContext* C, int x, int y, int* pixel_radius, float location[3], float modelview[16], float projection[16], int viewport[4])
+{
+       struct PaintStroke *stroke;
+       float window[2];
+       int hit;
+
+       stroke = paint_stroke_new(C, NULL, NULL, NULL, NULL);
+
+       window[0] = x + stroke->vc.ar->winrct.xmin;
+       window[1] = y + stroke->vc.ar->winrct.ymin;
+
+       memcpy(modelview, stroke->vc.rv3d->viewmat, sizeof(float[16]));
+       memcpy(projection, stroke->vc.rv3d->winmat, sizeof(float[16]));
+       memcpy(viewport, stroke->mats.viewport, sizeof(int[4]));
+
+       if (stroke->vc.obact->sculpt && stroke->vc.obact->sculpt->pbvh && sculpt_stroke_get_location(C, stroke, location, window)) {
+               *pixel_radius = project_brush_radius(stroke->vc.rv3d, sculpt_get_brush_unprojected_radius(stroke->brush), location, &stroke->mats);
+
+               if (*pixel_radius == 0)
+                       *pixel_radius = sculpt_get_brush_size(stroke->brush);
+
+               mul_m4_v3(stroke->vc.obact->sculpt->ob->obmat, location);
+
+               hit = 1;
+       }
+       else {
+               Sculpt* sd    = CTX_data_tool_settings(C)->sculpt;
+               Brush*  brush = paint_brush(&sd->paint);
+
+               *pixel_radius = sculpt_get_brush_size(brush);
+               hit = 0;
+       }
+
+       paint_stroke_free(stroke);
+
+       return hit;
+}
+
+// XXX duplicated from sculpt.c
+float unproject_brush_radius(Object *ob, ViewContext *vc, float center[3], float offset)
+{
+       float delta[3], scale, loc[3];
+
+       mul_v3_m4v3(loc, ob->obmat, center);
+
+       initgrabz(vc->rv3d, loc[0], loc[1], loc[2]);
+       window_to_3d_delta(vc->ar, delta, offset, 0);
+
+       scale= fabsf(mat4_to_scale(ob->obmat));
+       scale= (scale == 0.0f)? 1.0f: scale;
+
+       return len_v3(delta)/scale;
+}
+
+// XXX paint cursor now does a lot of the same work that is needed during a sculpt stroke
+// problem: all this stuff was not intended to be used at this point, so things feel a
+// bit hacked.  I've put lots of stuff in Brush that probably better goes in Paint
+// Functions should be refactored so that they can be used between sculpt.c and
+// paint_stroke.c clearly and optimally and the lines of communication between the
+// two modules should be more clearly defined.
+static void paint_draw_cursor(bContext *C, int x, int y, void *unused)
+{
+       ViewContext vc;
+
+       (void)unused;
+
+       view3d_set_viewcontext(C, &vc);
+
+       if (vc.obact->sculpt) {
+               Paint *paint = paint_get_active(CTX_data_scene(C));
+               Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+               Brush *brush = paint_brush(paint);
+
+               int pixel_radius, viewport[4];
+               float location[3], modelview[16], projection[16];
+
+               int hit;
+
+               int flip;
+               int sign;
+
+               float* col;
+               float  alpha;
+
+               float visual_strength = sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush);
+
+               const float min_alpha = 0.20f;
+               const float max_alpha = 0.80f;
+
+               {
+                       const float u = 0.5f;
+                       const float v = 1 - u;
+                       const float r = 20;
+
+                       const float dx = sd->last_x - x;
+                       const float dy = sd->last_y - y;
+
+                       if (dx*dx + dy*dy >= r*r) {
+                               sd->last_angle = atan2(dx, dy);
+
+                               sd->last_x = u*sd->last_x + v*x;
+                               sd->last_y = u*sd->last_y + v*y;
+                       }
+               }
+
+               if(!sculpt_get_lock_brush_size(brush) && !(paint->flags & PAINT_SHOW_BRUSH)) 
+                       return;
+
+               hit = sculpt_get_brush_geometry(C, x, y, &pixel_radius, location, modelview, projection, viewport);
+
+               if (sculpt_get_lock_brush_size(brush))
+                       sculpt_set_brush_size(brush, pixel_radius);
+
+               // XXX: no way currently to know state of pen flip or invert key modifier without starting a stroke
+               flip = 1;
+
+               sign = flip * ((brush->flag & BRUSH_DIR_IN)? -1 : 1);
+
+               if (sign < 0 && ELEM4(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH))
+                       col = brush->sub_col;
+               else
+                       col = brush->add_col;
+
+               alpha = (paint->flags & PAINT_SHOW_BRUSH_ON_SURFACE) ? min_alpha + (visual_strength*(max_alpha-min_alpha)) : 0.50f;
+
+               if (ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_FIXED, MTEX_MAP_MODE_TILED) && brush->flag & BRUSH_TEXTURE_OVERLAY) {
+                       glPushAttrib(
+                               GL_COLOR_BUFFER_BIT|
+                               GL_CURRENT_BIT|
+                               GL_DEPTH_BUFFER_BIT|
+                               GL_ENABLE_BIT|
+                               GL_LINE_BIT|
+                               GL_POLYGON_BIT|
+                               GL_STENCIL_BUFFER_BIT|
+                               GL_TRANSFORM_BIT|
+                               GL_VIEWPORT_BIT|
+                               GL_TEXTURE_BIT);
+
+                       if (load_tex(sd, brush, &vc)) {
+                               glEnable(GL_BLEND);
+
+                               glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+                               glDepthMask(GL_FALSE);
+                               glDepthFunc(GL_ALWAYS);
+
+                               glMatrixMode(GL_TEXTURE);
+                               glPushMatrix();
+                               glLoadIdentity();
+
+                               if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) {
+                                       glTranslatef(0.5f, 0.5f, 0);
+
+                                       if (brush->flag & BRUSH_RAKE) {
+                                               glRotatef(sd->last_angle*(float)(180.0/M_PI), 0, 0, 1);
+                                       }
+                                       else {
+                                               glRotatef(sd->special_rotation*(float)(180.0/M_PI), 0, 0, 1);
+                                       }
+
+                                       glTranslatef(-0.5f, -0.5f, 0);
+
+                                       if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE)) {
+                                               glTranslatef(0.5f, 0.5f, 0);
+                                               glScalef(1.0f/sd->pressure_value, 1.0f/sd->pressure_value, 1);
+                                               glTranslatef(-0.5f, -0.5f, 0);
+                                       }
+                               }
+
+                               glColor4f(
+                                       U.sculpt_paint_overlay_col[0],
+                                       U.sculpt_paint_overlay_col[1],
+                                       U.sculpt_paint_overlay_col[2],
+                                       brush->texture_overlay_alpha / 100.0f);
+
+                               glBegin(GL_QUADS);
+                               if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_FIXED) {
+                                       if (sd->draw_anchored) {
+                                               glTexCoord2f(0, 0);
+                                               glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin);
+
+                                               glTexCoord2f(1, 0);
+                                               glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]-sd->anchored_size - vc.ar->winrct.ymin);
+
+                                               glTexCoord2f(1, 1);
+                                               glVertex2f(sd->anchored_initial_mouse[0]+sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin);
+
+                                               glTexCoord2f(0, 1);
+                                               glVertex2f(sd->anchored_initial_mouse[0]-sd->anchored_size - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1]+sd->anchored_size - vc.ar->winrct.ymin);
+                                       }
+                                       else {
+                                               glTexCoord2f(0, 0);
+                                               glVertex2f((float)x-sculpt_get_brush_size(brush), (float)y-sculpt_get_brush_size(brush));
+
+                                               glTexCoord2f(1, 0);
+                                               glVertex2f((float)x+sculpt_get_brush_size(brush), (float)y-sculpt_get_brush_size(brush));
+
+                                               glTexCoord2f(1, 1);
+                                               glVertex2f((float)x+sculpt_get_brush_size(brush), (float)y+sculpt_get_brush_size(brush));
+
+                                               glTexCoord2f(0, 1);
+                                               glVertex2f((float)x-sculpt_get_brush_size(brush), (float)y+sculpt_get_brush_size(brush));
+                                       }
+                               }
+                               else {
+                                       glTexCoord2f(0, 0);
+                                       glVertex2f(0, 0);
+
+                                       glTexCoord2f(1, 0);
+                                       glVertex2f(viewport[2], 0);
+
+                                       glTexCoord2f(1, 1);
+                                       glVertex2f(viewport[2], viewport[3]);
+
+                                       glTexCoord2f(0, 1);
+                                       glVertex2f(0, viewport[3]);
+                               }
+                               glEnd();
+
+                               glPopMatrix();
+                       }
+
+                       glPopAttrib();
+               }
+
+               if (hit) {
+                       float unprojected_radius;
+
+                       // XXX duplicated from brush_strength & paint_stroke_add_step, refactor later
+                       //wmEvent* event = CTX_wm_window(C)->eventstate;
+
+                       if (sd->draw_pressure && (brush->flag & BRUSH_ALPHA_PRESSURE))
+                               visual_strength *= sd->pressure_value;
+
+                       // don't show effect of strength past the soft limit
+                       if (visual_strength > 1) visual_strength = 1;
+
+                       if (sd->draw_anchored) {
+                               unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sd->anchored_size);
+                       }
+                       else {
+                               if (brush->flag & BRUSH_ANCHORED)
+                                       unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, 8);
+                               else
+                                       unprojected_radius = unproject_brush_radius(CTX_data_active_object(C), &vc, location, sculpt_get_brush_size(brush));
+                       }
+
+                       if (sd->draw_pressure && (brush->flag & BRUSH_SIZE_PRESSURE))
+                               unprojected_radius *= sd->pressure_value;
+
+                       if (!sculpt_get_lock_brush_size(brush))
+                               sculpt_set_brush_unprojected_radius(brush, unprojected_radius);
+
+                       if(!(paint->flags & PAINT_SHOW_BRUSH))
+                               return;
+
+               }
+
+               glPushAttrib(
+                       GL_COLOR_BUFFER_BIT|
+                       GL_CURRENT_BIT|
+                       GL_DEPTH_BUFFER_BIT|
+                       GL_ENABLE_BIT|
+                       GL_LINE_BIT|
+                       GL_POLYGON_BIT|
+                       GL_STENCIL_BUFFER_BIT|
+                       GL_TRANSFORM_BIT|
+                       GL_VIEWPORT_BIT|
+                       GL_TEXTURE_BIT);
+
+               glColor4f(col[0], col[1], col[2], alpha);
+
+               glEnable(GL_BLEND);
+
+               glEnable(GL_LINE_SMOOTH);
+
+               if (sd->draw_anchored) {
+                       glTranslatef(sd->anchored_initial_mouse[0] - vc.ar->winrct.xmin, sd->anchored_initial_mouse[1] - vc.ar->winrct.ymin, 0.0f);
+                       glutil_draw_lined_arc(0.0, M_PI*2.0, sd->anchored_size, 40);
+                       glTranslatef(-sd->anchored_initial_mouse[0] + vc.ar->winrct.xmin, -sd->anchored_initial_mouse[1] + vc.ar->winrct.xmin, 0.0f);
+               }
+               else {
+                       glTranslatef((float)x, (float)y, 0.0f);
+                       glutil_draw_lined_arc(0.0, M_PI*2.0, sculpt_get_brush_size(brush), 40);
+                       glTranslatef(-(float)x, -(float)y, 0.0f);
+               }
+
+               glPopAttrib();
+       }
+       else {
+               Paint *paint = paint_get_active(CTX_data_scene(C));
+               Brush *brush = paint_brush(paint);
+
+               if(!(paint->flags & PAINT_SHOW_BRUSH))
+                       return;
+
+               glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
+               glEnable(GL_LINE_SMOOTH);
+               glEnable(GL_BLEND);
+
+               glTranslatef((float)x, (float)y, 0.0f);
+               glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40); // XXX: for now use the brushes size instead of potentially using the unified size because the feature has been enabled for sculpt
+               glTranslatef((float)-x, (float)-y, 0.0f);
+
+               glDisable(GL_BLEND);
+               glDisable(GL_LINE_SMOOTH);
+       }
 }
 
 /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
-static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2])
+static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse_in[2])
 {
+       Paint *paint = paint_get_active(CTX_data_scene(C)); // XXX
+       Brush *brush = paint_brush(paint); // XXX
+
+       float mouse[2];
+
        PointerRNA itemptr;
-       float pressure = 1;
-       float center[3] = {0, 0, 0};
-       int flip= event->shift?1:0;
+
+       float location[3];
+
+       float pressure;
+       int   pen_flip;
+
+       ViewContext vc; // XXX
+
        PaintStroke *stroke = op->customdata;
 
-       /* XXX: can remove the if statement once all modes have this */
-       if(stroke->get_location)
-               stroke->get_location(C, stroke, center, mouse);
+       view3d_set_viewcontext(C, &vc); // XXX
 
        /* Tablet */
        if(event->custom == EVT_DATA_TABLET) {
                wmTabletData *wmtab= event->customdata;
-               if(wmtab->Active != EVT_TABLET_NONE)
-                       pressure= wmtab->Pressure;
-               if(wmtab->Active == EVT_TABLET_ERASER)
-                       flip = 1;
+
+               pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1;
+               pen_flip = (wmtab->Active == EVT_TABLET_ERASER);
+       }
+       else {
+               pressure = 1;
+               pen_flip = 0;
        }
-                               
+
+       // XXX: temporary check for sculpt mode until things are more unified
+       if (vc.obact->sculpt) {
+               float delta[3];
+
+               brush_jitter_pos(brush, mouse_in, mouse);
+
+               // XXX: meh, this is round about because brush_jitter_pos isn't written in the best way to be reused here
+               if (brush->flag & BRUSH_JITTER_PRESSURE) {
+                       sub_v3_v3v3(delta, mouse, mouse_in);
+                       mul_v3_fl(delta, pressure);
+                       add_v3_v3v3(mouse, mouse_in, delta);
+               }
+       }
+       else
+               copy_v3_v3(mouse, mouse_in);
+
+       /* XXX: can remove the if statement once all modes have this */
+       if(stroke->get_location)
+               stroke->get_location(C, stroke, location, mouse);
+       else
+               zero_v3(location);
+
        /* Add to stroke */
        RNA_collection_add(op->ptr, "stroke", &itemptr);
-       RNA_float_set_array(&itemptr, "location", center);
-       RNA_float_set_array(&itemptr, "mouse", mouse);
-       RNA_boolean_set(&itemptr, "flip", flip);
-       RNA_float_set(&itemptr, "pressure", pressure);
+
+       RNA_float_set_array(&itemptr, "location",     location);
+       RNA_float_set_array(&itemptr, "mouse",        mouse);
+       RNA_boolean_set    (&itemptr, "pen_flip",     pen_flip);
+       RNA_float_set      (&itemptr, "pressure", pressure);
 
        stroke->last_mouse_position[0] = mouse[0];
        stroke->last_mouse_position[1] = mouse[1];
@@ -154,10 +852,14 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev
 /* Returns zero if no sculpt changes should be made, non-zero otherwise */
 static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event)
 {
-       output[0] = event->x;
+       output[0] = event->x; 
        output[1] = event->y;
 
-       if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
+       if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) &&  
+           !ELEM4(stroke->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK) &&
+           !(stroke->brush->flag & BRUSH_ANCHORED) &&
+           !(stroke->brush->flag & BRUSH_RESTORE_MESH))
+       {
                float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u;
                float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y;
 
@@ -176,7 +878,9 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev
 /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
 static int paint_space_stroke_enabled(Brush *br)
 {
-       return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
+       return (br->flag & BRUSH_SPACE) &&
+              !(br->flag & BRUSH_ANCHORED) &&
+              !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK);
 }
 
 /* For brushes with stroke spacing enabled, moves mouse in steps
@@ -187,23 +891,34 @@ static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const
        int cnt = 0;
 
        if(paint_space_stroke_enabled(stroke->brush)) {
-               float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]};
-               float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]};
+               float mouse[2];
+               float vec[2];
                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]);
+               copy_v2_v2(mouse, stroke->last_mouse_position);
+               sub_v2_v2v2(vec, final_mouse, mouse);
+
+               length = len_v2(vec);
 
                if(length > FLT_EPSILON) {
-                       scale = stroke->brush->spacing / length;
-                       vec[0] *= scale;
-                       vec[1] *= scale;
+                       int steps;
+                       int i;
+                       float pressure = 1;
+
+                       // XXX duplicate code
+                       if(event->custom == EVT_DATA_TABLET) {
+                               wmTabletData *wmtab= event->customdata;
+                               if(wmtab->Active != EVT_TABLET_NONE)
+                                       pressure = stroke->brush->flag & BRUSH_SIZE_PRESSURE ? wmtab->Pressure : 1;
+                       }
+
+                       scale = (sculpt_get_brush_size(stroke->brush)*pressure*stroke->brush->spacing/50.0f) / length;
+                       mul_v2_fl(vec, scale);
+
+                       steps = (int)(1.0f / scale);
 
-                       steps = (int)(length / stroke->brush->spacing);
                        for(i = 0; i < steps; ++i, ++cnt) {
-                               mouse[0] += vec[0];
-                               mouse[1] += vec[1];
+                               add_v2_v2(mouse, vec);
                                paint_brush_stroke_add_step(C, op, event, mouse);
                        }
                }
@@ -283,14 +998,25 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
                                                //ED_region_tag_redraw(ar);
                                        }
                                }
-                               else
+                               else {
                                        paint_brush_stroke_add_step(C, op, event, mouse);
+                               }
                        }
                        else
                                ;//ED_region_tag_redraw(ar);
                }
        }
 
+       /* we want the stroke to have the first daub at the start location instead of waiting till we have moved the space distance */
+       if(first &&
+          stroke->stroke_started &&
+          paint_space_stroke_enabled(stroke->brush) &&
+          !(stroke->brush->flag & BRUSH_ANCHORED) &&
+          !(stroke->brush->flag & BRUSH_SMOOTH_STROKE))
+       {
+               paint_brush_stroke_add_step(C, op, event, mouse);
+       }
+       
        return OPERATOR_RUNNING_MODAL;
 }
 
index 85fbd59..21ea6cb 100644 (file)
@@ -216,6 +216,10 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot)
                {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""},
                {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""},
                {CURVE_PRESET_MAX, "MAX", 0, "Max", ""},
+               {CURVE_PRESET_MID9, "MID9", 0, "Mid9", ""},
+               {CURVE_PRESET_LINE, "LINE", 0, "Line", ""},
+               {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""},
+               {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""},
                {0, NULL, 0, NULL, NULL}};
 
        ot->name= "Preset";
@@ -225,7 +229,7 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot)
        ot->exec= brush_curve_preset_exec;
        ot->poll= brush_curve_preset_poll;
 
-       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       ot->flag= OPTYPE_UNDO;
 
        RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", "");
 }
index b53771c..eee0d52 100644 (file)
@@ -22,7 +22,7 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Jason Wilkins, Tom Musgrove.
  *
  * ***** END GPL LICENSE BLOCK *****
  *
@@ -38,6 +38,8 @@
 #include "BLI_ghash.h"
 #include "BLI_pbvh.h"
 #include "BLI_threads.h"
+#include "BLI_editVert.h"
+#include "BLI_rand.h"
 
 #include "DNA_key_types.h"
 #include "DNA_mesh_types.h"
@@ -73,6 +75,7 @@
 #include "ED_screen.h"
 #include "ED_sculpt.h"
 #include "ED_view3d.h"
+#include "ED_mesh.h"
 #include "paint_intern.h"
 #include "sculpt_intern.h"
 
@@ -81,6 +84,7 @@
 
 
 #include "RE_render_ext.h"
+#include "RE_shader_ext.h"
 
 #include "GPU_buffers.h"
 
 #include <stdlib.h>
 #include <string.h>
 
-/* Number of vertices to average in order to determine the flatten distance */
-#define FLATTEN_SAMPLE_SIZE 10
+#ifdef _OPENMP
+#include <omp.h>
+#endif
 
 /* ==== FORWARD DEFINITIONS =====
  *
  */
-static void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]);
+
+void ED_sculpt_force_update(bContext *C)
+{
+       Object *ob= CTX_data_active_object(C);
+
+       if(ob && (ob->mode & OB_MODE_SCULPT))
+               multires_force_update(ob);
+}
+
+/* Sculpt mode handles multires differently from regular meshes, but only if
+   it's the last modifier on the stack and it is not on the first level */
+struct MultiresModifierData *sculpt_multires_active(Scene *scene, Object *ob)
+{
+       ModifierData *md, *nmd;
+       
+       for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
+               if(md->type == eModifierType_Multires) {
+                       MultiresModifierData *mmd= (MultiresModifierData*)md;
+
+                       /* Check if any of the modifiers after multires are active
+                        * if not it can use the multires struct */
+                       for(nmd= md->next; nmd; nmd= nmd->next)
+                               if(modifier_isEnabled(scene, nmd, eModifierMode_Realtime))
+                                       break;
+
+                       if(!nmd && mmd->sculptlvl > 0)
+                               return mmd;
+               }
+       }
+
+       return NULL;
+}
+
+/* Checks whether full update mode (slower) needs to be used to work with modifiers */
+int sculpt_modifiers_active(Scene *scene, Object *ob)
+{
+       ModifierData *md;
+       MultiresModifierData *mmd= sculpt_multires_active(scene, ob);
+
+       /* check if there are any modifiers after what we are sculpting,
+          for a multires modifier with a deform modifier in front, we
+          do no need to recalculate the modifier stack. note that this
+          needs to be in sync with ccgDM_use_grid_pbvh! */
+       if(mmd)
+               md= mmd->modifier.next;
+       else
+               md= modifiers_getVirtualModifierList(ob);
+       
+       /* exception for shape keys because we can edit those */
+       for(; md; md= md->next) {
+               if(modifier_isEnabled(scene, md, eModifierMode_Realtime))
+                       if(md->type != eModifierType_ShapeKey)
+                               return 1;
+       }
+
+       return 0;
+}
 
 /* ===== STRUCTS =====
  *
@@ -121,10 +182,13 @@ typedef struct StrokeCache {
 
        /* Variants */
        float radius;
+       float radius_squared;
+       //float traced_location[3];
        float true_location[3];
        float location[3];
 
-       float flip;
+       float pen_flip;
+       float invert;
        float pressure;
        float mouse[2];
        float bstrength;
@@ -141,15 +205,32 @@ typedef struct StrokeCache {
        Brush *brush;
 
        float (*face_norms)[3]; /* Copy of the mesh faces' normals */
-       float rotation; /* Texture rotation (radians) for anchored and rake modes */
+       float special_rotation; /* Texture rotation (radians) for anchored and rake modes */
        int pixel_radius, previous_pixel_radius;
-       float grab_active_location[8][3];
        float grab_delta[3], grab_delta_symmetry[3];
        float old_grab_location[3], orig_grab_location[3];
-       int symmetry; /* Symmetry index between 0 and 7 */
-       float view_normal[3], view_normal_symmetry[3];
-       int last_rake[2]; /* Last location of updating rake rotation */
+
+       int symmetry; /* Symmetry index between 0 and 7 bit combo 0 is Brush only;
+               1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
+       int mirror_symmetry_pass; /* the symmetry pass we are currently on between 0 and 7*/
+       float true_view_normal[3];
+       float view_normal[3];
+       float last_area_normal[3];
+       float last_center[3];
+       int radial_symmetry_pass;
+       float symm_rot_mat[4][4];
+       float symm_rot_mat_inv[4][4];
+       float last_rake[2]; /* Last location of updating rake rotation */
        int original;
+
+       float vertex_rotation;
+
+       char saved_active_brush_name[24];
+       int alt_smooth;
+
+       float plane_trim_squared;
+
+       float autosmooth_overlap;
 } StrokeCache;
 
 /* ===== OPENGL =====
@@ -224,10 +305,12 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
                                  RegionView3D *rv3d, Object *ob)
 {
        PBVH *pbvh= ob->sculpt->pbvh;
-       BoundBox *bb = MEM_callocN(sizeof(BoundBox), "sculpt boundbox");
+       BoundBox bb;
        bglMats mats;
        rcti rect;
 
+       memset(&bb, 0, sizeof(BoundBox));
+
        view3d_get_transformation(ar, rv3d, ob, &mats);
        sculpt_get_redraw_rect(ar, rv3d,ob, &rect);
 
@@ -246,423 +329,369 @@ void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar,
        rect.ymax -= 2;
 #endif
 
-       view3d_calculate_clipping(bb, planes, &mats, &rect);
+       view3d_calculate_clipping(&bb, planes, &mats, &rect);
        mul_m4_fl(planes, -1.0f);
 
-       MEM_freeN(bb);
-
        /* clear redraw flag from nodes */
        if(pbvh)
                BLI_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL);
 }
 
-/************************** Undo *************************/
-
-typedef struct SculptUndoNode {
-       struct SculptUndoNode *next, *prev;
+/************************ Brush Testing *******************/
 
-       char idname[MAX_ID_NAME];       /* name instead of pointer*/
-       void *node;                                     /* only during push, not valid afterwards! */
+typedef struct SculptBrushTest {
+       float radius_squared;
+       float location[3];
+       float dist;
+} SculptBrushTest;
 
-       float (*co)[3];
-       short (*no)[3];
-       int totvert;
+static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
+{
+       test->radius_squared= ss->cache->radius_squared;
+       copy_v3_v3(test->location, ss->cache->location);
+}
 
-       /* non-multires */
-       int maxvert;                            /* to verify if totvert it still the same */
-       int *index;                                     /* to restore into right location */
+static int sculpt_brush_test(SculptBrushTest *test, float co[3])
+{
+       float distsq = len_squared_v3v3(co, test->location);
 
-       /* multires */
-       int maxgrid;                            /* same for grid */
-       int gridsize;                           /* same for grid */
-       int totgrid;                            /* to restore into right location */
-       int *grids;                                     /* to restore into right location */
+       if(distsq <= test->radius_squared) {
+               test->dist = sqrt(distsq);
+               return 1;
+       }
+       else {
+               return 0;
+       }
+}
 
-       /* layer brush */
-       float *layer_disp;
+static int sculpt_brush_test_sq(SculptBrushTest *test, float co[3])
+{
+       float distsq = len_squared_v3v3(co, test->location);
 
-       /* shape keys */
-       char *shapeName[32]; /* keep size in sync with keyblock dna */
-} SculptUndoNode;
+       if(distsq <= test->radius_squared) {
+               test->dist = distsq;
+               return 1;
+       }
+       else {
+               return 0;
+       }
+}
 
-static void update_cb(PBVHNode *node, void *data)
+static int sculpt_brush_test_fast(SculptBrushTest *test, float co[3])
 {
-       BLI_pbvh_node_mark_update(node);
+       return len_squared_v3v3(co, test->location) <= test->radius_squared;
 }
 
-/* Checks whether full update mode (slower) needs to be used to work with modifiers */
-static int sculpt_modifiers_active(Scene *scene, Object *ob)
+static int sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4])
 {
-       ModifierData *md;
-       MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
+       const static float side = 0.70710678118654752440084436210485; // sqrt(.5);
 
-       /* check if there are any modifiers after what we are sculpting,
-          for a multires modifier with a deform modifier in front, we
-          do no need to recalculate the modifier stack. note that this
-          needs to be in sync with ccgDM_use_grid_pbvh! */
-       if(mmd)
-               md= mmd->modifier.next;
-       else
-               md= modifiers_getVirtualModifierList(ob);
-       
-       /* exception for shape keys because we can edit those */
-       for(; md; md= md->next) {
-               if(modifier_isEnabled(scene, md, eModifierMode_Realtime))
-                       if(md->type != eModifierType_ShapeKey)
-                               return 1;
+       float local_co[3];
+
+       mul_v3_m4v3(local_co, local, co);
+
+       local_co[0] = fabs(local_co[0]);
+       local_co[1] = fabs(local_co[1]);
+       local_co[2] = fabs(local_co[2]);
+
+       if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) {
+               test->dist = MAX3(local_co[0], local_co[1], local_co[2]) / side;
+
+               return 1;
+       }
+       else {
+               return 0;
        }
-       
-       return 0;
 }
 
-static void sculpt_undo_restore(bContext *C, ListBase *lb)
+static float frontface(Brush *brush, float sculpt_normal[3], short no[3], float fno[3])
 {
-       Scene *scene = CTX_data_scene(C);
-       Object *ob = CTX_data_active_object(C);
-       DerivedMesh *dm = mesh_get_derived_final(scene, ob, 0);
-       SculptSession *ss = ob->sculpt;
-       SculptUndoNode *unode;
-       MVert *mvert;
-       MultiresModifierData *mmd;
-       int *index;
-       int i, j, update= 0;
-
-       sculpt_update_mesh_elements(scene, ob, 0);
+       if (brush->flag & BRUSH_FRONTFACE) {
+               float dot;
 
-       for(unode=lb->first; unode; unode=unode->next) {
-               if(!(strcmp(unode->idname, ob->id.name)==0))
-                       continue;
+               if (no) {
+                       float tmp[3];
 
-               if(unode->maxvert) {
-                       char *shapeName= (char*)unode->shapeName;
+                       normal_short_to_float_v3(tmp, no);
+                       dot= dot_v3v3(tmp, sculpt_normal);
+               }
+               else {
+                       dot= dot_v3v3(fno, sculpt_normal);
+               }
 
-                       /* regular mesh restore */
-                       if(ss->totvert != unode->maxvert)
-                               continue;
+               return dot > 0 ? dot : 0;
+       }
+       else {
+               return 1;
+       }
+}
 
-                       if (ss->kb && strcmp(ss->kb->name, shapeName)) {
-                               /* shape key has been changed before calling undo operator */
+#if 0
 
-                               Key *key= ob_get_key(ob);
-                               KeyBlock *kb= key_get_named_keyblock(key, shapeName);
+static int sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3])
+{
+       if (sculpt_brush_test_fast(test, co)) {
+               float t1[3], t2[3], t3[3], dist;
 
-                               if (kb) {
-                                       ob->shapenr= BLI_findindex(&key->block, kb) + 1;
-                                       ob->shapeflag|= OB_SHAPE_LOCK;
+               sub_v3_v3v3(t1, location, co);
+               sub_v3_v3v3(t2, x2, location);
 
-                                       sculpt_update_mesh_elements(scene, ob, 0);
-                                       WM_event_add_notifier(C, NC_OBJECT|ND_DATA, ob);
-                               } else {
-                                       /* key has been removed -- skip this undo node */
-                                       continue;
-                               }
-                       }
+               cross_v3_v3v3(t3, an, t1);
 
-                       index= unode->index;
-                       mvert= ss->mvert;
+               dist = len_v3(t3)/len_v3(t2);
 
-                       if (ss->kb) {
-                               float (*vertCos)[3];
-                               vertCos= key_to_vertcos(ob, ss->kb);
+               test->dist = dist;
 
-                               for(i=0; i<unode->totvert; i++)
-                                       swap_v3_v3(vertCos[index[i]], unode->co[i]);
+               return 1;
+       }
 
-                               /* propagate new coords to keyblock */
-                               sculpt_vertcos_to_key(ob, ss->kb, vertCos);
+       return 0;
+}
 
-                               /* pbvh uses it's own mvert array, so coords should be */
-                               /* propagated to pbvh here */
-                               BLI_pbvh_apply_vertCos(ss->pbvh, vertCos);
+#endif
 
-                               MEM_freeN(vertCos);
-                       } else {
-                               for(i=0; i<unode->totvert; i++) {
-                                       swap_v3_v3(mvert[index[i]].co, unode->co[i]);
-                                       mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
-                               }
-                       }
-               }
-               else if(unode->maxgrid && dm->getGridData) {
-                       /* multires restore */
-                       DMGridData **grids, *grid;
-                       float (*co)[3];
-                       int gridsize;
+/* ===== Sculpting =====
+ *
+ */
+  
 
-                       if(dm->getNumGrids(dm) != unode->maxgrid)
-                               continue;
-                       if(dm->getGridSize(dm) != unode->gridsize)
-                               continue;
+static float overlapped_curve(Brush* br, float x)
+{
+       int i;
+       const int n = 100 / br->spacing;
+       const float h = br->spacing / 50.0f;
+       const float x0 = x-1;
 
-                       grids= dm->getGridData(dm);
-                       gridsize= dm->getGridSize(dm);
+       float sum;
 
-                       co = unode->co;
-                       for(j=0; j<unode->totgrid; j++) {
-                               grid= grids[unode->grids[j]];
+       sum = 0;
+       for (i= 0; i < n; i++) {
+               float xx;
 
-                               for(i=0; i<gridsize*gridsize; i++, co++)
-                                       swap_v3_v3(grid[i].co, co[0]);
-                       }
-               }
+               xx = fabs(x0 + i*h);
 
-               update= 1;
+               if (xx < 1.0f)
+                       sum += brush_curve_strength(br, xx, 1);
        }
 
-       if(update) {
-               /* we update all nodes still, should be more clever, but also
-                  needs to work correct when exiting/entering sculpt mode and
-                  the nodes get recreated, though in that case it could do all */
-               BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, NULL);
-               BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB|PBVH_UpdateOriginalBB|PBVH_UpdateRedraw, NULL);
+       return sum;
+}
+
+static float integrate_overlap(Brush* br)
+{
+       int i;
+       int m= 10;
+       float g = 1.0f/m;
+       float overlap;
+       float max;
 
-               if((mmd=sculpt_multires_active(scene, ob)))
-                       multires_mark_as_modified(ob);
+       overlap= 0;
+       max= 0;
+       for(i= 0; i < m; i++) {
+               overlap = overlapped_curve(br, i*g);
 
-               if(ss->modifiers_active || ((Mesh*)ob->data)->id.us > 1)
-                       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+               if (overlap > max)
+                       max = overlap;
        }
+
+       return max;
 }
 
-static void sculpt_undo_free(ListBase *lb)
+/* Uses symm to selectively flip any axis of a coordinate. */
+static void flip_coord(float out[3], float in[3], const char symm)
 {
-       SculptUndoNode *unode;
-
-       for(unode=lb->first; unode; unode=unode->next) {
-               if(unode->co)
-                       MEM_freeN(unode->co);
-               if(unode->no)
-                       MEM_freeN(unode->no);
-               if(unode->index)
-                       MEM_freeN(unode->index);
-               if(unode->grids)
-                       MEM_freeN(unode->grids);
-               if(unode->layer_disp)
-                       MEM_freeN(unode->layer_disp);
-       }
+       if(symm & SCULPT_SYMM_X)
+               out[0]= -in[0];
+       else
+               out[0]= in[0];
+       if(symm & SCULPT_SYMM_Y)
+               out[1]= -in[1];
+       else
+               out[1]= in[1];
+       if(symm & SCULPT_SYMM_Z)
+               out[2]= -in[2];
+       else
+               out[2]= in[2];
 }
 
-static SculptUndoNode *sculpt_undo_get_node(SculptSession *ss, PBVHNode *node)
+float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle)
 {
-       ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
-       SculptUndoNode *unode;
+       float mirror[3];
+       float distsq;
+       float mat[4][4];
+       
+       //flip_coord(mirror, cache->traced_location, symm);
+       flip_coord(mirror, cache->true_location, symm);
 
-       if(!lb)
-               return NULL;
+       unit_m4(mat);
+       rotate_m4(mat, axis, angle);
 
-       for(unode=lb->first; unode; unode=unode->next)
-               if(unode->node == node)
-                       return unode;
+       mul_m4_v3(mat, mirror);
 
-       return NULL;
+       //distsq = len_squared_v3v3(mirror, cache->traced_location);
+       distsq = len_squared_v3v3(mirror, cache->true_location);
+
+       if (distsq <= 4*(cache->radius_squared))
+               return (2*(cache->radius) - sqrt(distsq))  /  (2*(cache->radius));
+       else
+               return 0;
 }
 
-static SculptUndoNode *sculpt_undo_push_node(SculptSession *ss, PBVHNode *node)
+static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis)
 {
-       ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
-       Object *ob= ss->ob;
-       SculptUndoNode *unode;
-       int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
-
-       /* list is manipulated by multiple threads, so we lock */
-       BLI_lock_thread(LOCK_CUSTOM1);
+       int i;
+       float overlap;
 
-       if((unode= sculpt_undo_get_node(ss, node))) {
-               BLI_unlock_thread(LOCK_CUSTOM1);
-               return unode;
+       overlap = 0;
+       for(i = 1; i < sd->radial_symm[axis-'X']; ++i) {
+               const float angle = 2*M_PI*i/sd->radial_symm[axis-'X'];
+               overlap += calc_overlap(cache, symm, axis, angle);
        }
 
-       unode= MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode");
-       strcpy(unode->idname, ob->id.name);
-       unode->node= node;
+       return overlap;
+}
+
+static float calc_symmetry_feather(Sculpt *sd, StrokeCache* cache)
+{
+       if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
+               float overlap;
+               int symm = cache->symmetry;
+               int i;
+
+               overlap = 0;
+               for (i = 0; i <= symm; i++) {
+                       if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
 
-       BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert);
-       BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid,
-               &maxgrid, &gridsize, NULL, NULL);
+                               overlap += calc_overlap(cache, i, 0, 0);
 
-       unode->totvert= totvert;
-       /* we will use this while sculpting, is mapalloc slow to access then? */
-       unode->co= MEM_mapallocN(sizeof(float)*3*allvert, "SculptUndoNode.co");
-       unode->no= MEM_mapallocN(sizeof(short)*3*allvert, "SculptUndoNode.no");
-       undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float)*3 + sizeof(short)*3 + sizeof(int))*allvert);
-       BLI_addtail(lb, unode);
+                               overlap += calc_radial_symmetry_feather(sd, cache, i, 'X');
+                               overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y');
+                               overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z');
+                       }
+               }
 
-       if(maxgrid) {
-               /* multires */
-               unode->maxgrid= maxgrid;
-               unode->totgrid= totgrid;
-               unode->gridsize= gridsize;
-               unode->grids= MEM_mapallocN(sizeof(int)*totgrid, "SculptUndoNode.grids");
+               return 1/overlap;
        }
        else {
-               /* regular mesh */
-               unode->maxvert= ss->totvert;
-               unode->index= MEM_mapallocN(sizeof(int)*allvert, "SculptUndoNode.index");
+               return 1;
        }
+}
+
+/* Return modified brush strength. Includes the direction of the brush, positive
+   values pull vertices, negative values push. Uses tablet pressure and a
+   special multiplier found experimentally to scale the strength factor. */
+static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather, float overlap)
+{
+       Brush *brush = paint_brush(&sd->paint);
 
-       BLI_unlock_thread(LOCK_CUSTOM1);
+       /* Primary strength input; square it to make lower values more sensitive */
+       float alpha        = sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush);
+       float dir          = brush->flag & BRUSH_DIR_IN ? -1 : 1;
+       float pressure     = brush->flag & BRUSH_ALPHA_PRESSURE ? cache->pressure : 1;
+       float pen_flip     = cache->pen_flip ? -1 : 1;
+       float invert       = cache->invert ? -1 : 1;
+       float flip         = dir * invert * pen_flip;
 
-       /* copy threaded, hopefully this is the performance critical part */
-       {
-               PBVHVertexIter vd;
+       switch(brush->sculpt_tool){
+               case SCULPT_TOOL_CLAY:
+               case SCULPT_TOOL_CLAY_TUBES:
+               case SCULPT_TOOL_DRAW:
+               case SCULPT_TOOL_LAYER:
+                       return alpha * flip * pressure * overlap * feather;
 
-               BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) {
-                       copy_v3_v3(unode->co[vd.i], vd.co);
-                       if(vd.no) VECCOPY(unode->no[vd.i], vd.no)
-                       else normal_float_to_short_v3(unode->no[vd.i], vd.fno);
-                       if(vd.vert_indices) unode->index[vd.i]= vd.vert_indices[vd.i];
-               }
-               BLI_pbvh_vertex_iter_end;
-       }
+               case SCULPT_TOOL_CREASE:
+               case SCULPT_TOOL_BLOB:
+                       return alpha * flip * pressure * overlap * feather;
+
+               case SCULPT_TOOL_INFLATE:
+                       if (flip > 0) {
+                               return 0.250f * alpha * flip * pressure * overlap * feather;
+                       }
+                       else {
+                               return 0.125f * alpha * flip * pressure * overlap * feather;
+                       }
 
-       if(unode->grids)
-               memcpy(unode->grids, grids, sizeof(int)*totgrid);
+               case SCULPT_TOOL_FILL:
+               case SCULPT_TOOL_SCRAPE:
+               case SCULPT_TOOL_FLATTEN:
+                       if (flip > 0) {
+                               overlap = (1+overlap) / 2;
+                               return alpha * flip * pressure * overlap * feather;
+                       }
+                       else {
+                               /* reduce strength for DEEPEN, PEAKS, and CONTRAST */
+                               return 0.5f * alpha * flip * pressure * overlap * feather; 
+                       }
 
-       /* store active shape key */
-       if(ss->kb) BLI_strncpy((char*)unode->shapeName, ss->kb->name, sizeof(ss->kb->name));
-       else unode->shapeName[0]= '\0';
+               case SCULPT_TOOL_SMOOTH:
+                       return alpha * pressure * feather;
 
-       return unode;
-}
+               case SCULPT_TOOL_PINCH:
+                       if (flip > 0) {
+                               return alpha * flip * pressure * overlap * feather;
+                       }
+                       else {
+                               return 0.25f * alpha * flip * pressure * overlap * feather;
+                       }
 
-static void sculpt_undo_push_begin(SculptSession *ss, char *name)
-{
-       undo_paint_push_begin(UNDO_PAINT_MESH, name,
-               sculpt_undo_restore, sculpt_undo_free);
-}
+               case SCULPT_TOOL_NUDGE:
+                       overlap = (1+overlap) / 2;
+                       return alpha * pressure * overlap * feather;
 
-static void sculpt_undo_push_end(SculptSession *ss)
-{
-       ListBase *lb= undo_paint_push_get_list(UNDO_PAINT_MESH);
-       SculptUndoNode *unode;
+               case SCULPT_TOOL_THUMB:
+                       return alpha*pressure*feather;
 
-       /* we don't need normals in the undo stack */
-       for(unode=lb->first; unode; unode=unode->next) {
-               if(unode->no) {
-                       MEM_freeN(unode->no);
-                       unode->no= NULL;
-               }
+               case SCULPT_TOOL_SNAKE_HOOK:
+                       return feather;
 
-               if(unode->layer_disp) {
-                       MEM_freeN(unode->layer_disp);
-                       unode->layer_disp= NULL;
-               }
-       }
+               case SCULPT_TOOL_GRAB:
+               case SCULPT_TOOL_ROTATE:
+                       return feather;
 
-       undo_paint_push_end(UNDO_PAINT_MESH);
+               default:
+                       return 0;
+       }
 }
 
-void ED_sculpt_force_update(bContext *C)
+float get_tex_pixel(Brush* br, float u, float v)
 {
-       Object *ob= CTX_data_active_object(C);
+       TexResult texres;
+       float co[3];
+       int hasrgb;
 
-       if(ob && (ob->mode & OB_MODE_SCULPT))
-               multires_force_update(ob);
-}
+       co[0] = u;
+       co[1] = v;
+       co[2] = 0;
 
-/************************ Brush Testing *******************/
+       memset(&texres, 0, sizeof(TexResult));
+       hasrgb = multitex_ext(br->mtex.tex, co, NULL, NULL, 1, &texres);
 
-typedef struct SculptBrushTest {
-       float radius_squared;
-       float location[3];
+       if (hasrgb & TEX_RGB)
+               texres.tin = (0.35*texres.tr + 0.45*texres.tg + 0.2*texres.tb)*texres.ta;
 
-       float dist;
-} SculptBrushTest;
+       return texres.tin;
+}
 
-static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test)
+#if 0
+
+/* Get a pixel from the texcache at (px, py) */
+static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py)
 {
-       test->radius_squared= ss->cache->radius*ss->cache->radius;
-       copy_v3_v3(test->location, ss->cache->location);
+       unsigned *p;
+       p = ss->texcache + py * ss->texcache_side + px;
+       return ((unsigned char*)(p))[0];
 }
 
-static int sculpt_brush_test(SculptBrushTest *test, float co[3])
+static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v)
 {
-       float distsq, delta[3];
-
-       sub_v3_v3v3(delta, co, test->location);
-       distsq = INPR(delta, delta);
+       unsigned x, y, x2, y2;
+       const int tc_max = ss->texcache_side - 1;
+       float urat, vrat, uopp;
 
-       if(distsq < test->radius_squared) {
-               test->dist = sqrt(distsq);
-               return 1;
-       }
-
-       return 0;
-}
-
-/* ===== Sculpting =====
- *
- */
-
-/* Return modified brush strength. Includes the direction of the brush, positive
-   values pull vertices, negative values push. Uses tablet pressure and a
-   special multiplier found experimentally to scale the strength factor. */
-static float brush_strength(Sculpt *sd, StrokeCache *cache)
-{
-       Brush *brush = paint_brush(&sd->paint);
-       /* Primary strength input; square it to make lower values more sensitive */
-       float alpha = brush->alpha * brush->alpha;
-
-       float dir= brush->flag & BRUSH_DIR_IN ? -1 : 1;
-       float pressure= 1;
-       float flip= cache->flip ? -1:1;
-
-       if(brush->flag & BRUSH_ALPHA_PRESSURE)
-               pressure *= cache->pressure;
-       
-       switch(brush->sculpt_tool){
-       case SCULPT_TOOL_DRAW:
-       case SCULPT_TOOL_INFLATE:
-       case SCULPT_TOOL_CLAY:
-       case SCULPT_TOOL_FLATTEN:
-       case SCULPT_TOOL_LAYER:
-               return alpha * dir * pressure * flip; /*XXX: not sure why? was multiplied by G.vd->grid */;
-       case SCULPT_TOOL_SMOOTH:
-               return alpha * 4 * pressure;
-       case SCULPT_TOOL_PINCH:
-               return alpha / 2 * dir * pressure * flip;
-       case SCULPT_TOOL_GRAB:
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-/* Uses symm to selectively flip any axis of a coordinate. */
-static void flip_coord(float out[3], float in[3], const char symm)
-{
-       if(symm & SCULPT_SYMM_X)
-               out[0]= -in[0];
-       else
-               out[0]= in[0];
-       if(symm & SCULPT_SYMM_Y)
-               out[1]= -in[1];
-       else
-               out[1]= in[1];
-       if(symm & SCULPT_SYMM_Z)
-               out[2]= -in[2];
-       else
-               out[2]= in[2];
-}
-
-/* Get a pixel from the texcache at (px, py) */
-static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py)
-{
-       unsigned *p;
-       p = ss->texcache + py * ss->texcache_side + px;
-       return ((unsigned char*)(p))[0];
-}
-
-static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v)
-{
-       int x, y, x2, y2;
-       const int tc_max = ss->texcache_side - 1;
-       float urat, vrat, uopp;
-
-       if(u < 0) u = 0;
-       else if(u >= ss->texcache_side) u = tc_max;
-       if(v < 0) v = 0;
-       else if(v >= ss->texcache_side) v = tc_max;
+       if(u < 0) u = 0;
+       else if(u >= ss->texcache_side) u = tc_max;
+       if(v < 0) v = 0;
+       else if(v >= ss->texcache_side) v = tc_max;
 
        x = floor(u);
        y = floor(v);
@@ -682,76 +711,97 @@ static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float
                 get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0;
 }
 
+#endif
+
 /* Return a multiplier for brush strength on a particular vertex. */
 static float tex_strength(SculptSession *ss, Brush *br, float *point, const float len)
 {
-       MTex *tex = &br->mtex;
+       MTex *mtex = &br->mtex;
        float avg= 1;
 
-       if(!tex) {
+       if(!mtex->tex) {
                avg= 1;
        }
-       else if(tex->brush_map_mode == MTEX_MAP_MODE_3D) {
+       else if(mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
                float jnk;
 
                /* Get strength by feeding the vertex 
                   location directly into a texture */
-               externtex(tex, point, &avg,
+               externtex(mtex, point, &avg,
                          &jnk, &jnk, &jnk, &jnk);
        }
        else if(ss->texcache) {
-               const float bsize= ss->cache->pixel_radius * 2;
-               const float rot= tex->rot + ss->cache->rotation;
-               int px, py;
-               float flip[3], point_2d[2];
-
-               /* If the active area is being applied for symmetry, flip it
-                  across the symmetry axis in order to project it. This insures
-                  that the brush texture will be oriented correctly. */
-               copy_v3_v3(flip, point);
-               flip_coord(flip, flip, ss->cache->symmetry);
-               projectf(ss->cache->mats, flip, point_2d);
-
-               /* For Tile and Drag modes, get the 2D screen coordinates of the
-                  and scale them up or down to the texture size. */
-               if(tex->brush_map_mode == MTEX_MAP_MODE_TILED) {
-                       const int sx= (const int)tex->size[0];
-                       const int sy= (const int)tex->size[1];
-                       
-                       float fx= point_2d[0];
-                       float fy= point_2d[1];
-                       
-                       float angle= atan2(fy, fx) - rot;
-                       float flen= sqrtf(fx*fx + fy*fy);
-                       
-                       if(rot<0.001 && rot>-0.001) {
-                               px= point_2d[0];
-                               py= point_2d[1];
-                       } else {
-                               px= flen * cos(angle) + 2000;
-                               py= flen * sin(angle) + 2000;
-                       }
-                       if(sx != 1)
-                               px %= sx-1;
-                       if(sy != 1)
-                               py %= sy-1;
-                       avg= get_texcache_pixel_bilinear(ss, ss->texcache_side*px/sx, ss->texcache_side*py/sy);
-               }
-               else if(tex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
-                       float fx= (point_2d[0] - ss->cache->tex_mouse[0]) / bsize;
-                       float fy= (point_2d[1] - ss->cache->tex_mouse[1]) / bsize;
-
-                       float angle= atan2(fy, fx) - rot;
-                       float flen= sqrtf(fx*fx + fy*fy);
-                       
-                       fx = flen * cos(angle) + 0.5;
-                       fy = flen * sin(angle) + 0.5;
+               float rotation = -mtex->rot;
+               float x, y, point_2d[3];
+               float diameter;
+
+               /* if the active area is being applied for symmetry, flip it
+                  across the symmetry axis and rotate it back to the orignal
+                  position in order to project it. This insures that the 
+                  brush texture will be oriented correctly. */
+
+               flip_coord(point_2d, point, ss->cache->mirror_symmetry_pass);
+
+               if (ss->cache->radial_symmetry_pass)
+                       mul_m4_v3(ss->cache->symm_rot_mat_inv, point_2d);
+
+               projectf(ss->cache->mats, point_2d, point_2d);
+
+               /* if fixed mode, keep coordinates relative to mouse */
+               if(mtex->brush_map_mode == MTEX_MAP_MODE_FIXED) {
+                       rotation += ss->cache->special_rotation;
+
+                       point_2d[0] -= ss->cache->tex_mouse[0];
+                       point_2d[1] -= ss->cache->tex_mouse[1];
+
+                       diameter = ss->cache->pixel_radius; // use pressure adjusted size for fixed mode
 
-                       avg= get_texcache_pixel_bilinear(ss, fx * ss->texcache_side, fy * ss->texcache_side);
+                       x = point_2d[0];
+                       y = point_2d[1];
                }
+               else /* else (mtex->brush_map_mode == MTEX_MAP_MODE_TILED),
+                       leave the coordinates relative to the screen */
+               {
+                       diameter = sculpt_get_brush_size(br); // use unadjusted size for tiled mode
+               
+                       x = point_2d[0] - ss->cache->vc->ar->winrct.xmin;
+                       y = point_2d[1] - ss->cache->vc->ar->winrct.ymin;
+               }
+
+               x /= ss->cache->vc->ar->winx;
+               y /= ss->cache->vc->ar->winy;
+
+               if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
+                       x -= 0.5f;
+                       y -= 0.5f;
+               }
+               
+               x *= ss->cache->vc->ar->winx / diameter;
+               y *= ss->cache->vc->ar->winy / diameter;
+
+               /* it is probably worth optimizing for those cases where 
+                  the texture is not rotated by skipping the calls to
+                  atan2, sqrtf, sin, and cos. */
+               if (rotation > 0.001 || rotation < -0.001) {
+                       const float angle    = atan2(y, x) + rotation;
+                       const float flen     = sqrtf(x*x + y*y);
+
+                       x = flen * cos(angle);
+                       y = flen * sin(angle);
+               }
+
+               x *= br->mtex.size[0];
+               y *= br->mtex.size[1];
+
+               x += br->mtex.ofs[0];
+               y += br->mtex.ofs[1];
+
+               avg = get_tex_pixel(br, x, y);
        }
 
-       avg*= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */
+       avg += br->texture_sample_bias;
+
+       avg *= brush_curve_strength(br, len, ss->cache->radius); /* Falloff curve */
 
        return avg;
 }
@@ -787,7 +837,7 @@ static int sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
        
        sub_v3_v3v3(t, center, nearest);
 
-       return t[0] * t[0] + t[1] * t[1] + t[2] * t[2] < data->radius_squared;
+       return dot_v3v3(t, t) < data->radius_squared;
 }
 
 /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */
@@ -803,7 +853,7 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float *co, const float va
                        co[i]= 0.0f;
                else
                        co[i]= val[i];
-       }               
+       }
 }
 
 static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3])
@@ -815,52 +865,48 @@ static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], floa
        }
 }
 
-/* For draw/layer/flatten; finds average normal for all active vertices */
-static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3], PBVHNode **nodes, int totnode)
+static void calc_area_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode)
 {
-       PBVH *bvh= ss->pbvh;
-       StrokeCache *cache = ss->cache;
-       const int view = 0; /* XXX: should probably be a flag, not number: brush_type==SCULPT_TOOL_DRAW ? sculptmode_brush()->view : 0; */
-       float out[3] = {0.0f, 0.0f, 0.0f};
-       float out_flip[3] = {0.0f, 0.0f, 0.0f};
-       float out_dir[3];
        int n;
 
-       copy_v3_v3(out_dir, cache->view_normal_symmetry);
+       float out_flip[3] = {0.0f, 0.0f, 0.0f};
+
+       zero_v3(an);
 
-       /* threaded loop over nodes */
-       //#pragma omp parallel for private(n) schedule(static)
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
        for(n=0; n<totnode; n++) {
                PBVHVertexIter vd;
                SculptBrushTest test;
                SculptUndoNode *unode;
-               float fno[3];
-               float nout[3] = {0.0f, 0.0f, 0.0f};
-               float nout_flip[3] = {0.0f, 0.0f, 0.0f};
-               
-               // XXX push instead of get for thread safety in draw
-               // brush .. lame, but also not harmful really
-               unode= sculpt_undo_push_node(ss, nodes[n]);
+               float private_an[3] = {0.0f, 0.0f, 0.0f};
+               float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
+
+               unode = sculpt_undo_push_node(ss, nodes[n]);
                sculpt_brush_test_init(ss, &test);
 
                if(ss->cache->original) {
-                       BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                               if(sculpt_brush_test(&test, unode->co[vd.i])) {
+                       BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                               if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+                                       float fno[3];
+
                                        normal_short_to_float_v3(fno, unode->no[vd.i]);
-                                       add_norm_if(out_dir, nout, nout_flip, fno);
+                                       add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
                                }
                        }
                        BLI_pbvh_vertex_iter_end;
                }
                else {
-                       BLI_pbvh_vertex_iter_begin(bvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                               if(sculpt_brush_test(&test, vd.co)) {
+                       BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                               if(sculpt_brush_test_fast(&test, vd.co)) {
                                        if(vd.no) {
+                                               float fno[3];
+
                                                normal_short_to_float_v3(fno, vd.no);
-                                               add_norm_if(out_dir, nout, nout_flip, fno);
+                                               add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
+                                       }
+                                       else {
+                                               add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno);
                                        }
-                                       else
-                                               add_norm_if(out_dir, nout, nout_flip, vd.fno);
                                }
                        }
                        BLI_pbvh_vertex_iter_end;
@@ -868,72 +914,70 @@ static void calc_area_normal(Sculpt *sd, SculptSession *ss, float area_normal[3]
 
                //#pragma omp critical
                {
-                       /* we sum per node and add together later for threads */
-                       add_v3_v3(out, nout);
-                       add_v3_v3(out_flip, nout_flip);
+                       add_v3_v3(an, private_an);
+                       add_v3_v3(out_flip, private_out_flip);
                }
        }
 
-       if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) {
-               copy_v3_v3(out, out_flip);
-       }
-       
-       normalize_v3(out);
+       if (is_zero_v3(an))
+               copy_v3_v3(an, out_flip);
 
-       out[0] = out_dir[0] * view + out[0] * (10-view);
-       out[1] = out_dir[1] * view + out[1] * (10-view);
-       out[2] = out_dir[2] * view + out[2] * (10-view);
-       
-       normalize_v3(out);
-       copy_v3_v3(area_normal, out);
+       normalize_v3(an);
 }
 
-static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+/* This initializes the faces to be moved for this sculpt for draw/layer/flatten; then it
+ finds average normal for all active vertices - note that this is called once for each mirroring direction */
+static void calc_sculpt_normal(Sculpt *sd, SculptSession *ss, float an[3], PBVHNode **nodes, int totnode)
 {
        Brush *brush = paint_brush(&sd->paint);
-       float offset[3], area_normal[3];
-       float bstrength= ss->cache->bstrength;
-       int n;
 
-       /* area normal */
-       calc_area_normal(sd, ss, area_normal, nodes, totnode);
+       if (ss->cache->mirror_symmetry_pass == 0 &&
+           ss->cache->radial_symmetry_pass == 0 &&
+          (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
+       {
+               switch (brush->sculpt_plane) {
+                       case SCULPT_DISP_DIR_VIEW:
+                               viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an);
+                               break;
 
-       /* offset with as much as possible factored in already */
-       offset[0]= area_normal[0]*ss->cache->radius*ss->cache->scale[0]*bstrength;
-       offset[1]= area_normal[1]*ss->cache->radius*ss->cache->scale[1]*bstrength;
-       offset[2]= area_normal[2]*ss->cache->radius*ss->cache->scale[2]*bstrength;
+                       case SCULPT_DISP_DIR_X:
+                               an[1] = 0.0;
+                               an[2] = 0.0;
+                               an[0] = 1.0;
+                               break;
 
-       /* threaded loop over nodes */
-       //#pragma omp parallel for private(n) schedule(static)
-       for(n=0; n<totnode; n++) {
-               PBVHVertexIter vd;
-               SculptBrushTest test;
-               
-               sculpt_undo_push_node(ss, nodes[n]);
-               sculpt_brush_test_init(ss, &test);
+                       case SCULPT_DISP_DIR_Y:
+                               an[0] = 0.0;
+                               an[2] = 0.0;
+                               an[1] = 1.0;
+                               break;
 
-               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                       if(sculpt_brush_test(&test, vd.co)) {
-                               /* offset vertex */
-                               float fade = tex_strength(ss, brush, vd.co, test.dist);
-                               float val[3]= {vd.co[0] + offset[0]*fade,
-                                                          vd.co[1] + offset[1]*fade,
-                                                          vd.co[2] + offset[2]*fade};
+                       case SCULPT_DISP_DIR_Z:
+                               an[0] = 0.0;
+                               an[1] = 0.0;
+                               an[2] = 1.0;
+                               break;
 
-                               sculpt_clip(sd, ss, vd.co, val);
-                               if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
-                       }
+                       case SCULPT_DISP_DIR_AREA:
+                               calc_area_normal(sd, ss, an, nodes, totnode);
+
+                       default:
+                               break;
                }
-               BLI_pbvh_vertex_iter_end;
 
-               BLI_pbvh_node_mark_update(nodes[n]);
+               copy_v3_v3(ss->cache->last_area_normal, an);
+       }
+       else {
+               copy_v3_v3(an, ss->cache->last_area_normal);
+               flip_coord(an, an, ss->cache->mirror_symmetry_pass);
+               mul_m4_v3(ss->cache->symm_rot_mat, an);
        }
 }
 
 /* For the smooth brush, uses the neighboring vertices around vert to calculate
    a smoothed location for vert. Skips corner vertices (used by only one
    polygon.) */
-static void neighbor_average(SculptSession *ss, float avg[3], const int vert)
+static void neighbor_average(SculptSession *ss, float avg[3], const unsigned vert)
 {
        int i, skip= -1, total=0;
        IndexNode *node= ss->fmap[vert].first;
@@ -974,52 +1018,58 @@ static void neighbor_average(SculptSession *ss, float avg[3], const int vert)
                copy_v3_v3(avg, ss->mvert[vert].co);
 }
 
-static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node)
+static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength)
 {
        Brush *brush = paint_brush(&sd->paint);
-       float bstrength= ss->cache->bstrength;
        PBVHVertexIter vd;
        SculptBrushTest test;
        
+       CLAMP(bstrength, 0.0f, 1.0f);
+
        sculpt_brush_test_init(ss, &test);
 
        BLI_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
                if(sculpt_brush_test(&test, vd.co)) {
-                       float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
+                       const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
                        float avg[3], val[3];
 
-                       CLAMP(fade, 0.0f, 1.0f);
-                       
                        neighbor_average(ss, avg, vd.vert_indices[vd.i]);
-                       val[0] = vd.co[0]+(avg[0]-vd.co[0])*fade;
-                       val[1] = vd.co[1]+(avg[1]-vd.co[1])*fade;
-                       val[2] = vd.co[2]+(avg[2]-vd.co[2])*fade;
-                       
-                       sculpt_clip(sd, ss, vd.co, val);                        
-                       if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       sub_v3_v3v3(val, avg, vd.co);
+                       mul_v3_fl(val, fade);
+
+                       add_v3_v3(val, vd.co);
+
+                       sculpt_clip(sd, ss, vd.co, val);
+
+                       if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
                }
        }
        BLI_pbvh_vertex_iter_end;
 }
 
-static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node)
+static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, float bstrength)
 {
        Brush *brush = paint_brush(&sd->paint);
        SculptBrushTest test;
        DMGridData **griddata, *data;
        DMGridAdjacency *gridadj, *adj;
-       float bstrength= ss->cache->bstrength;
-       float co[3], (*tmpgrid)[3];
+       float (*tmpgrid)[3], (*tmprow)[3];
        int v1, v2, v3, v4;
        int *grid_indices, totgrid, gridsize, i, x, y;
-                       
+
        sculpt_brush_test_init(ss, &test);
 
+       CLAMP(bstrength, 0.0f, 1.0f);
+
        BLI_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid,
                NULL, &gridsize, &griddata, &gridadj);
 
        //#pragma omp critical
-       tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid");
+       {
+               tmpgrid= MEM_mallocN(sizeof(float)*3*gridsize*gridsize, "tmpgrid");
+               tmprow=  MEM_mallocN(sizeof(float)*3*gridsize, "tmprow");
+       }
 
        for(i = 0; i < totgrid; ++i) {
                data = griddata[grid_indices[i]];
@@ -1027,75 +1077,106 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no
 
                memset(tmpgrid, 0, sizeof(float)*3*gridsize*gridsize);
 
-               /* average grid values */
-               for(y = 0; y < gridsize-1; ++y)  {
-                       for(x = 0; x < gridsize-1; ++x)  {
+               for (y= 0; y < gridsize-1; y++) {
+                       float tmp[3];
+
+                       v1 = y*gridsize;
+                       add_v3_v3v3(tmprow[0], data[v1].co, data[v1+gridsize].co);
+
+                       for (x= 0; x < gridsize-1; x++) {
                                v1 = x + y*gridsize;
-                               v2 = (x + 1) + y*gridsize;
-                               v3 = (x + 1) + (y + 1)*gridsize;
-                               v4 = x + (y + 1)*gridsize;
+                               v2 = v1 + 1;
+                               v3 = v1 + gridsize;
+                               v4 = v3 + 1;
 
-                               cent_quad_v3(co, data[v1].co, data[v2].co, data[v3].co, data[v4].co);
-                               mul_v3_fl(co, 0.25f);
+                               add_v3_v3v3(tmprow[x+1], data[v2].co, data[v4].co);
+                               add_v3_v3v3(tmp, tmprow[x+1], tmprow[x]);
 
-                               add_v3_v3(tmpgrid[v1], co);
-                               add_v3_v3(tmpgrid[v2], co);
-                               add_v3_v3(tmpgrid[v3], co);
-                               add_v3_v3(tmpgrid[v4], co);
+                               add_v3_v3(tmpgrid[v1], tmp);
+                               add_v3_v3(tmpgrid[v2], tmp);
+                               add_v3_v3(tmpgrid[v3], tmp);
+                               add_v3_v3(tmpgrid[v4], tmp);
                        }
                }
 
                /* blend with existing coordinates */
                for(y = 0; y < gridsize; ++y)  {
                        for(x = 0; x < gridsize; ++x)  {
-                               if(x == 0 && adj->index[0] == -1) continue;
-                               if(x == gridsize - 1 && adj->index[2] == -1) continue;
-                               if(y == 0 && adj->index[3] == -1) continue;
-                               if(y == gridsize - 1 && adj->index[1] == -1) continue;
+                               float *co;
+                               float *fno;
+                               int index;
+
+                               if(x == 0 && adj->index[0] == -1)
+                                       continue;
+
+                               if(x == gridsize - 1 && adj->index[2] == -1)
+                                       continue;
 
-                               copy_v3_v3(co, data[x + y*gridsize].co);
+                               if(y == 0 && adj->index[3] == -1)
+                                       continue;
+
+                               if(y == gridsize - 1 && adj->index[1] == -1)
+                                       continue;
+
+                               index = x + y*gridsize;
+                               co=  data[index].co;
+                               fno= data[index].no;
 
                                if(sculpt_brush_test(&test, co)) {
-                                       float fade = tex_strength(ss, brush, co, test.dist)*bstrength;
-                                       float avg[3], val[3];
+                                       const float fade = bstrength*tex_strength(ss, brush, co, test.dist)*frontface(brush, ss->cache->view_normal, NULL, fno);
+                                       float *avg, val[3];
+                                       float n;
+
+                                       avg = tmpgrid[x + y*gridsize];
+
+                                       n = 1/16.0f;
 
-                                       copy_v3_v3(avg, tmpgrid[x + y*gridsize]);
                                        if(x == 0 || x == gridsize - 1)
-                                               mul_v3_fl(avg, 2.0f);
+                                               n *= 2;
+
                                        if(y == 0 || y == gridsize - 1)
-                                               mul_v3_fl(avg, 2.0f);
+                                               n *= 2;
+
+                                       mul_v3_fl(avg, n);
 
-                                       CLAMP(fade, 0.0f, 1.0f);
+                                       sub_v3_v3v3(val, avg, co);
+                                       mul_v3_fl(val, fade);
 
-                                       val[0] = co[0]+(avg[0]-co[0])*fade;
-                                       val[1] = co[1]+(avg[1]-co[1])*fade;
-                                       val[2] = co[2]+(avg[2]-co[2])*fade;
-                                       
-                                       sculpt_clip(sd, ss, data[x + y*gridsize].co, val);
+                                       add_v3_v3(val, co);
+
+                                       sculpt_clip(sd, ss, co, val);
                                }
                        }
                }
        }
 
        //#pragma omp critical
-       MEM_freeN(tmpgrid);
+       {
+               MEM_freeN(tmpgrid);
+               MEM_freeN(tmprow);
+       }
 }
 
-static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void smooth(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float bstrength)
 {
-       int iteration, n;
+       const int max_iterations = 4;
+       const float fract = 1.0f/max_iterations;
+       int iteration, n, count;
+       float last;
 
-       for(iteration = 0; iteration < 2; ++iteration) {
-               //#pragma omp parallel for private(n) schedule(static)
-               for(n=0; n<totnode; n++) {
-                       sculpt_undo_push_node(ss, nodes[n]);
+       CLAMP(bstrength, 0, 1);
 
-                       if(ss->multires)
-                               do_multires_smooth_brush(sd, ss, nodes[n]);
-                       else if(ss->fmap)
-                               do_mesh_smooth_brush(sd, ss, nodes[n]);
+       count = (int)(bstrength*max_iterations);
+       last  = max_iterations*(bstrength - count*fract);
 
-                       BLI_pbvh_node_mark_update(nodes[n]);
+       for(iteration = 1; iteration <= count; ++iteration) {
+               #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+               for(n=0; n<totnode; n++) {
+                       if(ss->multires) {
+                               do_multires_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last);
+                       }
+                       else if(ss->fmap)
+                               do_mesh_smooth_brush(sd, ss, nodes[n], iteration != count ? 1.0f : last);
                }
 
                if(ss->multires)
@@ -1103,317 +1184,1086 @@ static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int
        }
 }
 
-static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       smooth(sd, ss, nodes, totnode, ss->cache->bstrength);
+}
+
+static void do_draw_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
        Brush *brush = paint_brush(&sd->paint);
+       float offset[3], area_normal[3];
        float bstrength= ss->cache->bstrength;
        int n;
 
-       //#pragma omp parallel for private(n) schedule(static)
+       calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+       
+       /* offset with as much as possible factored in already */
+       mul_v3_v3fl(offset, area_normal, ss->cache->radius);
+       mul_v3_v3(offset, ss->cache->scale);
+       mul_v3_fl(offset, bstrength);
+
+       /* threaded loop over nodes */
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
        for(n=0; n<totnode; n++) {
                PBVHVertexIter vd;
                SculptBrushTest test;
-               
-               sculpt_undo_push_node(ss, nodes[n]);
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
                sculpt_brush_test_init(ss, &test);
 
                BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                       if(sculpt_brush_test(&test, vd.co)) {
-                               float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
-                               float val[3]= {vd.co[0]+(test.location[0]-vd.co[0])*fade,
-                                                          vd.co[1]+(test.location[1]-vd.co[1])*fade,
-                                                          vd.co[2]+(test.location[2]-vd.co[2])*fade};
+                       if (sculpt_brush_test(&test, vd.co)) {
+                       //if(sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, area_normal)) {
+                               /* offset vertex */
+                               float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
+
+                               mul_v3_v3fl(proxy[vd.i], offset, fade);
 
-                               sculpt_clip(sd, ss, vd.co, val);                        
-                               if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
                        }
                }
                BLI_pbvh_vertex_iter_end;
-
-               BLI_pbvh_node_mark_update(nodes[n]);
        }
 }
 
-static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_crease_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
        Brush *brush = paint_brush(&sd->paint);
+       float offset[3], area_normal[3];
        float bstrength= ss->cache->bstrength;
-       float grab_delta[3];
+       float flippedbstrength, crease_correction;
        int n;
+
+       calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
        
-       copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+       /* offset with as much as possible factored in already */
+       mul_v3_v3fl(offset, area_normal, ss->cache->radius);
+       mul_v3_v3(offset, ss->cache->scale);
+       mul_v3_fl(offset, bstrength);
+       
+       /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */
+       
+       if(sculpt_get_brush_alpha(brush) > 0.0f)
+               crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor/(sculpt_get_brush_alpha(brush)*sculpt_get_brush_alpha(brush));
+       else
+               crease_correction = brush->crease_pinch_factor*brush->crease_pinch_factor;
+
+       /* we always want crease to pinch or blob to relax even when draw is negative */
+       flippedbstrength = (bstrength < 0) ? -crease_correction*bstrength : crease_correction*bstrength;
+
+       if(brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f;
 
-       //#pragma omp parallel for private(n) schedule(static)
+       /* threaded loop over nodes */
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
        for(n=0; n<totnode; n++) {
                PBVHVertexIter vd;
                SculptBrushTest test;
-               float (*origco)[3];
-               
-               origco= sculpt_undo_push_node(ss, nodes[n])->co;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
                sculpt_brush_test_init(ss, &test);
 
                BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                       if(sculpt_brush_test(&test, origco[vd.i])) {
-                               float fade = tex_strength(ss, brush, origco[vd.i], test.dist)*bstrength;
-                               float add[3]= {vd.co[0]+fade*grab_delta[0],
-                                                          vd.co[1]+fade*grab_delta[1],
-                                                          vd.co[2]+fade*grab_delta[2]};
+                       if(sculpt_brush_test(&test, vd.co)) {
+                               /* offset vertex */
+                               const float fade = tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
+                               float val1[3];
+                               float val2[3];
 
-                               sculpt_clip(sd, ss, vd.co, add);                        
-                               if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                               /* first we pinch */
+                               sub_v3_v3v3(val1, test.location, vd.co);
+                               //mul_v3_v3(val1, ss->cache->scale);
+                               mul_v3_fl(val1, fade*flippedbstrength);
+
+                               /* then we draw */
+                               mul_v3_v3fl(val2, offset, fade);
+
+                               add_v3_v3v3(proxy[vd.i], val1, val2);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
                        }
                }
                BLI_pbvh_vertex_iter_end;
-
-               BLI_pbvh_node_mark_update(nodes[n]);
        }
 }
 
-static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_pinch_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
        Brush *brush = paint_brush(&sd->paint);
        float bstrength= ss->cache->bstrength;
-       float area_normal[3], offset[3];
-       float lim= ss->cache->radius / 4;
        int n;
 
-       if(ss->cache->flip)
-               lim = -lim;
-
-       calc_area_normal(sd, ss, area_normal, nodes, totnode);
-
-       offset[0]= ss->cache->scale[0]*area_normal[0];
-       offset[1]= ss->cache->scale[1]*area_normal[1];
-       offset[2]= ss->cache->scale[2]*area_normal[2];
-
-       //#pragma omp parallel for private(n) schedule(static)
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
        for(n=0; n<totnode; n++) {
                PBVHVertexIter vd;
                SculptBrushTest test;
-               SculptUndoNode *unode;
-               float (*origco)[3], *layer_disp;
-               
-               unode= sculpt_undo_push_node(ss, nodes[n]);
-               origco=unode->co;
-               if(!unode->layer_disp)
-                       unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp");
-               layer_disp= unode->layer_disp;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
 
                sculpt_brush_test_init(ss, &test);
 
                BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
                        if(sculpt_brush_test(&test, vd.co)) {
-                               float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
-                               float *disp= &layer_disp[vd.i];
+                               float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
                                float val[3];
-                               
-                               *disp+= fade;
-                               
-                               /* Don't let the displacement go past the limit */
-                               if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim))
-                                       *disp = lim;
-                               
-                               if(ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) {
-                                       int index= vd.vert_indices[vd.i];
 
-                                       /* persistent base */
-                                       val[0] = ss->layer_co[index][0] + (*disp)*offset[0];
-                                       val[1] = ss->layer_co[index][1] + (*disp)*offset[1];
-                                       val[2] = ss->layer_co[index][2] + (*disp)*offset[2];
-                               }
-                               else {
-                                       val[0] = origco[vd.i][0] + (*disp)*offset[0];
-                                       val[1] = origco[vd.i][1] + (*disp)*offset[1];
-                                       val[2] = origco[vd.i][2] + (*disp)*offset[2];
-                               }
+                               sub_v3_v3v3(val, test.location, vd.co);
+                               mul_v3_v3fl(proxy[vd.i], val, fade);
 
-                               sculpt_clip(sd, ss, vd.co, val);
-                               if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
-                       }
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                               }
                }
                BLI_pbvh_vertex_iter_end;
-
-               BLI_pbvh_node_mark_update(nodes[n]);
        }
 }
 
-static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+static void do_grab_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
-       Brush *brush = paint_brush(&sd->paint);
+       Brush *brush= paint_brush(&sd->paint);
        float bstrength= ss->cache->bstrength;
+       float grab_delta[3], an[3];
        int n;
+       float len;
+
+       if (brush->normal_weight > 0)
+               calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+       copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
 
-       //#pragma omp parallel for private(n) schedule(static)
+       len = len_v3(grab_delta);
+
+       if (brush->normal_weight > 0) {
+               mul_v3_fl(an, len*brush->normal_weight);
+               mul_v3_fl(grab_delta, 1.0f - brush->normal_weight);
+               add_v3_v3(grab_delta, an);
+       }
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
        for(n=0; n<totnode; n++) {
                PBVHVertexIter vd;
+               SculptUndoNode* unode;
                SculptBrushTest test;
-               
-               sculpt_undo_push_node(ss, nodes[n]);
+               float (*origco)[3];
+               short (*origno)[3];
+               float (*proxy)[3];
+
+               unode=  sculpt_undo_push_node(ss, nodes[n]);
+               origco= unode->co;
+               origno= unode->no;
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, origco[vd.i])) {
+                               const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL);
+
+                               mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_nudge_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush = paint_brush(&sd->paint);
+       float bstrength = ss->cache->bstrength;
+       float grab_delta[3];
+       int n;
+       float an[3];
+       float tmp[3], cono[3];
+
+       copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+       calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+       cross_v3_v3v3(tmp, an, grab_delta);
+       cross_v3_v3v3(cono, tmp, an);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n = 0; n < totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, vd.co)) {
+                               const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno);
+
+                               mul_v3_v3fl(proxy[vd.i], cono, fade);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_snake_hook_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush = paint_brush(&sd->paint);
+       float bstrength = ss->cache->bstrength;
+       float grab_delta[3], an[3];
+       int n;
+       float len;
+
+       if (brush->normal_weight > 0)
+               calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+       copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+       len = len_v3(grab_delta);
+
+       if (bstrength < 0)
+               negate_v3(grab_delta);
+
+       if (brush->normal_weight > 0) {
+               mul_v3_fl(an, len*brush->normal_weight);
+               mul_v3_fl(grab_delta, 1.0f - brush->normal_weight);
+               add_v3_v3(grab_delta, an);
+       }
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n = 0; n < totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, vd.co)) {
+                               const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, an, vd.no, vd.fno);
+
+                               mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_thumb_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush = paint_brush(&sd->paint);
+       float bstrength = ss->cache->bstrength;
+       float grab_delta[3];
+       int n;
+       float an[3];
+       float tmp[3], cono[3];
+
+       copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+       calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+       cross_v3_v3v3(tmp, an, grab_delta);
+       cross_v3_v3v3(cono, tmp, an);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n = 0; n < totnode; n++) {
+               PBVHVertexIter vd;
+               SculptUndoNode* unode;
+               SculptBrushTest test;
+               float (*origco)[3];
+               short (*origno)[3];
+               float (*proxy)[3];
+
+               unode=  sculpt_undo_push_node(ss, nodes[n]);
+               origco= unode->co;
+               origno= unode->no;
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, origco[vd.i])) {
+                               const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL);
+
+                               mul_v3_v3fl(proxy[vd.i], cono, fade);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_rotate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush= paint_brush(&sd->paint);
+       float bstrength= ss->cache->bstrength;
+       float an[3];
+       int n;
+       float m[3][3];
+       static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 };
+       float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass];
+
+       calc_sculpt_normal(sd, ss, an, nodes, totnode);
+
+       axis_angle_to_mat3(m, an, angle);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n=0; n<totnode; n++) {
+               PBVHVertexIter vd;
+               SculptUndoNode* unode;
+               SculptBrushTest test;
+               float (*origco)[3];
+               short (*origno)[3];
+               float (*proxy)[3];
+
+               unode=  sculpt_undo_push_node(ss, nodes[n]);
+               origco= unode->co;
+               origno= unode->no;
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, origco[vd.i])) {
+                               const float fade = bstrength*tex_strength(ss, brush, origco[vd.i], test.dist)*frontface(brush, an, origno[vd.i], NULL);
+
+                               mul_v3_m3v3(proxy[vd.i], m, origco[vd.i]);
+                               sub_v3_v3(proxy[vd.i], origco[vd.i]);
+                               mul_v3_fl(proxy[vd.i], fade);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_layer_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush = paint_brush(&sd->paint);
+       float bstrength= ss->cache->bstrength;
+       float area_normal[3], offset[3];
+       float lim= ss->cache->radius / 4;
+       int n;
+
+       if(bstrength < 0)
+               lim = -lim;
+
+       calc_sculpt_normal(sd, ss, area_normal, nodes, totnode);
+
+       mul_v3_v3v3(offset, ss->cache->scale, area_normal);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n=0; n<totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               SculptUndoNode *unode;
+               float (*origco)[3], *layer_disp;
+               //float (*proxy)[3]; // XXX layer brush needs conversion to proxy but its more complicated
+
+               //proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+               
+               unode= sculpt_undo_push_node(ss, nodes[n]);
+               origco=unode->co;
+               if(!unode->layer_disp)
+                       {
+                               #pragma omp critical 
+                               unode->layer_disp= MEM_callocN(sizeof(float)*unode->totvert, "layer disp");
+                       }
+
+               layer_disp= unode->layer_disp;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, vd.co)) {
+                               const float fade = bstrength*ss->cache->radius*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, area_normal, vd.no, vd.fno);
+                               float *disp= &layer_disp[vd.i];
+                               float val[3];
+
+                               *disp+= fade;
+
+                               /* Don't let the displacement go past the limit */
+                               if((lim < 0 && *disp < lim) || (lim > 0 && *disp > lim))
+                                       *disp = lim;
+
+                               mul_v3_v3fl(val, offset, *disp);
+
+                               if(ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) {
+                                       int index= vd.vert_indices[vd.i];
+
+                                       /* persistent base */
+                                       add_v3_v3(val, ss->layer_co[index]);
+                               }
+                               else {
+                                       add_v3_v3(val, origco[vd.i]);
+                               }
+
+                               sculpt_clip(sd, ss, vd.co, val);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void do_inflate_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush = paint_brush(&sd->paint);
+       float bstrength= ss->cache->bstrength;
+       int n;
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n=0; n<totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if(sculpt_brush_test(&test, vd.co)) {
+                               const float fade = bstrength*tex_strength(ss, brush, vd.co, test.dist)*frontface(brush, ss->cache->view_normal, vd.no, vd.fno);
+                               float val[3];
+
+                               if(vd.fno) copy_v3_v3(val, vd.fno);
+                               else normal_short_to_float_v3(val, vd.no);
+                               
+                               mul_v3_fl(val, fade * ss->cache->radius);
+                               mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale);
+
+                               if(vd.mvert)
+                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
+}
+
+static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float fc[3])
+{
+       int n;
+
+       float count = 0;
+
+       zero_v3(fc);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n=0; n<totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               SculptUndoNode *unode;
+               float private_fc[3] = {0.0f, 0.0f, 0.0f};
+               int private_count = 0;
+
+               unode = sculpt_undo_push_node(ss, nodes[n]);
+               sculpt_brush_test_init(ss, &test);
+
+               if(ss->cache->original) {
+                       BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                               if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+                                       add_v3_v3(private_fc, vd.co);
+                                       private_count++;
+                               }
+                       }
+                       BLI_pbvh_vertex_iter_end;
+               }
+               else {
+                       BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                               if(sculpt_brush_test_fast(&test, vd.co)) {
+                                       add_v3_v3(private_fc, vd.co);
+                                       private_count++;
+                               }
+                       }
+                       BLI_pbvh_vertex_iter_end;
+               }
+
+               #pragma omp critical
+               {
+                       add_v3_v3(fc, private_fc);
+                       count += private_count;
+               }
+       }
+
+       mul_v3_fl(fc, 1.0f / count);
+}
+
+/* this calculates flatten center and area normal together, 
+amortizing the memory bandwidth and loop overhead to calculate both at the same time */
+static void calc_area_normal_and_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3])
+{
+       int n;
+
+       // an
+       float out_flip[3] = {0.0f, 0.0f, 0.0f};
+
+       // fc
+       float count = 0;
+
+       // an
+       zero_v3(an);
+
+       // fc
+       zero_v3(fc);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n=0; n<totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               SculptUndoNode *unode;
+               float private_an[3] = {0.0f, 0.0f, 0.0f};
+               float private_out_flip[3] = {0.0f, 0.0f, 0.0f};
+               float private_fc[3] = {0.0f, 0.0f, 0.0f};
+               int private_count = 0;
+
+               unode = sculpt_undo_push_node(ss, nodes[n]);
+               sculpt_brush_test_init(ss, &test);
+
+               if(ss->cache->original) {
+                       BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                               if(sculpt_brush_test_fast(&test, unode->co[vd.i])) {
+                                       // an
+                                       float fno[3];
+
+                                       normal_short_to_float_v3(fno, unode->no[vd.i]);
+                                       add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
+
+                                       // fc
+                                       add_v3_v3(private_fc, vd.co);
+                                       private_count++;
+                               }
+                       }
+                       BLI_pbvh_vertex_iter_end;
+               }
+               else {
+                       BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                               if(sculpt_brush_test_fast(&test, vd.co)) {
+                                       // an
+                                       if(vd.no) {
+                                               float fno[3];
+
+                                               normal_short_to_float_v3(fno, vd.no);
+                                               add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno);
+                                       }
+                                       else {
+                                               add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno);
+                                       }
+
+                                       // fc
+                                       add_v3_v3(private_fc, vd.co);
+                                       private_count++;
+                               }
+                       }
+                       BLI_pbvh_vertex_iter_end;
+               }
+
+               #pragma omp critical
+               {
+                       // an
+                       add_v3_v3(an, private_an);
+                       add_v3_v3(out_flip, private_out_flip);
+
+                       // fc
+                       add_v3_v3(fc, private_fc);
+                       count += private_count;
+               }
+       }
+
+       // an
+       if (is_zero_v3(an))
+               copy_v3_v3(an, out_flip);
+
+       normalize_v3(an);
+
+       // fc
+       mul_v3_fl(fc, 1.0f / count);
+}
+
+static void calc_sculpt_plane(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float an[3], float fc[3])
+{
+       Brush *brush = paint_brush(&sd->paint);
+
+       if (ss->cache->mirror_symmetry_pass == 0 &&
+           ss->cache->radial_symmetry_pass == 0 &&
+          (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL)))
+       {
+               switch (brush->sculpt_plane) {
+                       case SCULPT_DISP_DIR_VIEW:
+                               viewvector(ss->cache->vc->rv3d, ss->cache->vc->rv3d->twmat[3], an);
+                               break;
+
+                       case SCULPT_DISP_DIR_X:
+                               an[1] = 0.0;
+                               an[2] = 0.0;
+                               an[0] = 1.0;
+                               break;
+
+                       case SCULPT_DISP_DIR_Y:
+                               an[0] = 0.0;
+                               an[2] = 0.0;
+                               an[1] = 1.0;
+                               break;
+
+                       case SCULPT_DISP_DIR_Z:
+                               an[0] = 0.0;
+                               an[1] = 0.0;
+                               an[2] = 1.0;
+                               break;
+
+                       case SCULPT_DISP_DIR_AREA:
+                               calc_area_normal_and_flatten_center(sd, ss, nodes, totnode, an, fc);
+
+                       default:
+                               break;
+               }
+
+               // fc
+               /* flatten center has not been calculated yet if we are not using the area normal */
+               if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA)
+                       calc_flatten_center(sd, ss, nodes, totnode, fc);
+
+               // an
+               copy_v3_v3(ss->cache->last_area_normal, an);
+
+               // fc
+               copy_v3_v3(ss->cache->last_center, fc);
+       }
+       else {
+               // an
+               copy_v3_v3(an, ss->cache->last_area_normal);
+
+               // fc
+               copy_v3_v3(fc, ss->cache->last_center);
+
+               // an
+               flip_coord(an, an, ss->cache->mirror_symmetry_pass);
+
+               // fc
+               flip_coord(fc, fc, ss->cache->mirror_symmetry_pass);
+
+               // an
+               mul_m4_v3(ss->cache->symm_rot_mat, an);
+
+               // fc
+               mul_m4_v3(ss->cache->symm_rot_mat, fc);
+       }
+}
+
+/* Projects a point onto a plane along the plane's normal */
+static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3])
+{
+    sub_v3_v3v3(intr, co, plane_center);
+    mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr));
+    sub_v3_v3v3(intr, co, intr); 
+}
+
+static int plane_trim(StrokeCache *cache, Brush *brush, float val[3])
+{
+       return !(brush->flag & BRUSH_PLANE_TRIM) || (dot_v3v3(val, val) <= cache->radius_squared*cache->plane_trim_squared);
+}
+
+static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip)
+{
+    float delta[3];
+    float d;
+
+    sub_v3_v3v3(delta, co, plane_center);
+    d = dot_v3v3(plane_normal, delta);
+
+    if (flip) d = -d;
+
+    return d <= 0.0f;
+}
+
+static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3])
+{
+    float delta[3];
+
+    sub_v3_v3v3(delta, co, plane_center);
+    return  dot_v3v3(plane_normal, delta) <= 0.0f;
+}
+
+static float get_offset(Sculpt *sd, SculptSession *ss)
+{
+       Brush* brush = paint_brush(&sd->paint);
+
+       float rv = brush->plane_offset;
+
+       if (brush->flag & BRUSH_OFFSET_PRESSURE) {
+               rv *= ss->cache->pressure;
+       }
+
+       return rv;
+}
+
+static void do_flatten_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
+{
+       Brush *brush = paint_brush(&sd->paint);
+
+       float bstrength = ss->cache->bstrength;
+       const float radius = ss->cache->radius;
+
+       float an[3];
+       float fc[3];
+
+       float offset = get_offset(sd, ss);
+
+       float displace;
+
+       int n;
+
+       float temp[3];
+
+       calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+       displace = radius*offset;
+
+       mul_v3_v3v3(temp, an, ss->cache->scale);
+       mul_v3_fl(temp, displace);
+       add_v3_v3(fc, temp);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for(n = 0; n < totnode; n++) {
+               PBVHVertexIter  vd;
+               SculptBrushTest test;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
                sculpt_brush_test_init(ss, &test);
 
                BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                       if(sculpt_brush_test(&test, vd.co)) {
-                               float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
-                               float add[3];
+                       if (sculpt_brush_test_sq(&test, vd.co)) {
+                               float intr[3];
+                               float val[3];
 
-                               if(vd.fno) copy_v3_v3(add, vd.fno);
-                               else normal_short_to_float_v3(add, vd.no);
-                               
-                               mul_v3_fl(add, fade * ss->cache->radius);
-                               add[0]*= ss->cache->scale[0];
-                               add[1]*= ss->cache->scale[1];
-                               add[2]*= ss->cache->scale[2];
-                               add_v3_v3(add, vd.co);
-                               
-                               sculpt_clip(sd, ss, vd.co, add);
-                               if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                               point_plane_project(intr, vd.co, an, fc);
+
+                               sub_v3_v3v3(val, intr, vd.co);
+
+                               if (plane_trim(ss->cache, brush, val)) {
+                                       const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
+
+                                       mul_v3_v3fl(proxy[vd.i], val, fade);
+
+                                       if(vd.mvert)
+                                               vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                               }
                        }
                }
                BLI_pbvh_vertex_iter_end;
-
-               BLI_pbvh_node_mark_update(nodes[n]);
        }
 }
 
-static void calc_flatten_center(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, float co[3])
+static void do_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
-       float outer_dist[FLATTEN_SAMPLE_SIZE];
-       float outer_co[FLATTEN_SAMPLE_SIZE][3];
-       int i, n;
+       Brush *brush = paint_brush(&sd->paint);
+
+       float bstrength = ss->cache->bstrength;
+       float radius    = ss->cache->radius;
+       float offset    = get_offset(sd, ss);
        
-       for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) {
-               zero_v3(outer_co[i]);
-               outer_dist[i]= -1.0f;
+       float displace;
+
+       float an[3]; // area normal
+       float fc[3]; // flatten center
+
+       int n;
+
+       float temp[3];
+       //float p[3];
+
+       int flip;
+
+       calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+       flip = bstrength < 0;
+
+       if (flip) {
+               bstrength = -bstrength;
+               radius    = -radius;
        }
-               
-       //#pragma omp parallel for private(n) schedule(static)
-       for(n=0; n<totnode; n++) {
+
+       displace = radius * (0.25f+offset);
+
+       mul_v3_v3v3(temp, an, ss->cache->scale);
+       mul_v3_fl(temp, displace);
+       add_v3_v3(fc, temp);
+
+       //add_v3_v3v3(p, ss->cache->location, an);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for (n = 0; n < totnode; n++) {
                PBVHVertexIter vd;
                SculptBrushTest test;
-               int j;
-               
-               sculpt_undo_push_node(ss, nodes[n]);
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
                sculpt_brush_test_init(ss, &test);
 
                BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                       if(sculpt_brush_test(&test, vd.co)) {
-                               for(j = 0; j < FLATTEN_SAMPLE_SIZE; ++j) {
-                                       if(test.dist > outer_dist[j]) {
-                                               copy_v3_v3(outer_co[j], vd.co);
-                                               outer_dist[j] = test.dist;
-                                               break;
+                       if (sculpt_brush_test_sq(&test, vd.co)) {
+                               if (plane_point_side_flip(vd.co, an, fc, flip)) {
+                               //if (sculpt_brush_test_cyl(&test, vd.co, ss->cache->location, p)) {
+                                       float intr[3];
+                                       float val[3];
+
+                                       point_plane_project(intr, vd.co, an, fc);
+
+                                       sub_v3_v3v3(val, intr, vd.co);
+
+                                       if (plane_trim(ss->cache, brush, val)) {
+                                               const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
+
+                                               mul_v3_v3fl(proxy[vd.i], val, fade);
+
+                                               if(vd.mvert)
+                                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
                                        }
                                }
                        }
                }
                BLI_pbvh_vertex_iter_end;
-
-               BLI_pbvh_node_mark_update(nodes[n]);
        }
-       
-       co[0] = co[1] = co[2] = 0.0f;
-       for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i)
-               if(outer_dist[i] >= 0.0f)
-                       add_v3_v3(co, outer_co[i]);
-       mul_v3_fl(co, 1.0f / FLATTEN_SAMPLE_SIZE);
 }
 
-/* Projects a point onto a plane along the plane's normal */
-static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3])
+static void do_clay_tubes_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
-       float p1[3], sub1[3], sub2[3];
+       Brush *brush = paint_brush(&sd->paint);
+
+       float bstrength = ss->cache->bstrength;
+       float radius    = ss->cache->radius;
+       float offset    = get_offset(sd, ss);
+       
+       float displace;
+
+       float sn[3]; // sculpt normal
+       float an[3]; // area normal
+       float fc[3]; // flatten center
+
+       int n;
+
+       float temp[3];
+       float mat[4][4];
+       float scale[4][4];
+       float tmat[4][4];
+
+       int flip;
+
+       calc_sculpt_plane(sd, ss, nodes, totnode, sn, fc);
+
+       if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL))
+               calc_area_normal(sd, ss, an, nodes, totnode);
+       else
+               copy_v3_v3(an, sn);
+
+       if (ss->cache->first_time)
+               return; // delay the first daub because grab delta is not setup
+
+       flip = bstrength < 0;
+
+       if (flip) {
+               bstrength = -bstrength;
+               radius    = -radius;
+       }
+
+       displace = radius * (0.25f+offset);
+
+       mul_v3_v3v3(temp, sn, ss->cache->scale);
+       mul_v3_fl(temp, displace);
+       add_v3_v3(fc, temp);
+
+       cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); mat[0][3] = 0;
+       cross_v3_v3v3(mat[1], an, mat[0]); mat[1][3] = 0;
+       copy_v3_v3(mat[2], an); mat[2][3] = 0;
+       copy_v3_v3(mat[3], ss->cache->location);  mat[3][3] = 1;
+       normalize_m4(mat);
+       scale_m4_fl(scale, ss->cache->radius);
+       mul_m4_m4m4(tmat, scale, mat);
+       invert_m4_m4(mat, tmat);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for (n = 0; n < totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if (sculpt_brush_test_cube(&test, vd.co, mat)) {
+                               if (plane_point_side_flip(vd.co, sn, fc, flip)) {
+                                       float intr[3];
+                                       float val[3];
+
+                                       point_plane_project(intr, vd.co, sn, fc);
+
+                                       sub_v3_v3v3(val, intr, vd.co);
+
+                                       if (plane_trim(ss->cache, brush, val)) {
+                                               const float fade = bstrength*tex_strength(ss, brush, vd.co, ss->cache->radius*test.dist)*frontface(brush, an, vd.no, vd.fno);
+
+                                               mul_v3_v3fl(proxy[vd.i], val, fade);
 
-       /* Find the intersection between squash-plane and vertex (along the area normal) */
-       sub_v3_v3v3(p1, co, plane_normal);
-       sub_v3_v3v3(sub1, plane_center, p1);
-       sub_v3_v3v3(sub2, co, p1);
-       sub_v3_v3v3(intr, co, p1);
-       mul_v3_fl(intr, dot_v3v3(plane_normal, sub1) / dot_v3v3(plane_normal, sub2));
-       add_v3_v3(intr, p1);
+                                               if(vd.mvert)
+                                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                                       }
+                               }
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
 }
 
-static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3], int flip)
+static void do_fill_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
-       float delta[3];
-       float d;
+       Brush *brush = paint_brush(&sd->paint);
+
+       float bstrength = ss->cache->bstrength;
+       const float radius = ss->cache->radius;
+
+       float an[3];
+       float fc[3];
+       float offset = get_offset(sd, ss);
+
+       float displace;
+
+       int n;
+
+       float temp[3];
+
+       calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+       displace = radius*offset;
+
+       mul_v3_v3v3(temp, an, ss->cache->scale);
+       mul_v3_fl(temp, displace);
+       add_v3_v3(fc, temp);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for (n = 0; n < totnode; n++) {
+               PBVHVertexIter vd;
+               SculptBrushTest test;
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
+               sculpt_brush_test_init(ss, &test);
+
+               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                       if (sculpt_brush_test_sq(&test, vd.co)) {
+                               if (plane_point_side(vd.co, an, fc)) {
+                                       float intr[3];
+                                       float val[3];
 
-       sub_v3_v3v3(delta, co, plane_center);
-       d = dot_v3v3(plane_normal, delta);
+                                       point_plane_project(intr, vd.co, an, fc);
 
-       if(flip)
-               d = -d;
+                                       sub_v3_v3v3(val, intr, vd.co);
+
+                                       if (plane_trim(ss->cache, brush, val)) {
+                                               const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
 
-       return d <= 0.0f;
+                                               mul_v3_v3fl(proxy[vd.i], val, fade);
+
+                                               if(vd.mvert)
+                                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                                       }
+                               }
+                       }
+               }
+               BLI_pbvh_vertex_iter_end;
+       }
 }
 
-static void do_flatten_clay_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode, int clay)
+static void do_scrape_brush(Sculpt *sd, SculptSession *ss, PBVHNode **nodes, int totnode)
 {
-       /* area_normal and cntr define the plane towards which vertices are squashed */
        Brush *brush = paint_brush(&sd->paint);
-       float bstrength= ss->cache->bstrength;
-       float area_normal[3];
-       float cntr[3], cntr2[3] = {0}, bstr = 0;
-       int n, flip = 0;
 
-       calc_area_normal(sd, ss, area_normal, nodes, totnode);
-       calc_flatten_center(sd, ss, nodes, totnode, cntr);
+       float bstrength = ss->cache->bstrength;
+       const float radius = ss->cache->radius;
 
-       if(clay) {
-               bstr= brush_strength(sd, ss->cache);
-               /* Limit clay application to here */
-               cntr2[0]=cntr[0]+area_normal[0]*bstr*ss->cache->scale[0];
-               cntr2[1]=cntr[1]+area_normal[1]*bstr*ss->cache->scale[1];
-               cntr2[2]=cntr[2]+area_normal[2]*bstr*ss->cache->scale[2];
-               flip = bstr < 0;
-       }
+       float an[3];
+       float fc[3];
+       float offset = get_offset(sd, ss);
 
-       //#pragma omp parallel for private(n) schedule(static)
-       for(n=0; n<totnode; n++) {
+       float displace;
+
+       int n;
+
+       float temp[3];
+
+       calc_sculpt_plane(sd, ss, nodes, totnode, an, fc);
+
+       displace = -radius*offset;
+
+       mul_v3_v3v3(temp, an, ss->cache->scale);
+       mul_v3_fl(temp, displace);
+       add_v3_v3(fc, temp);
+
+       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+       for (n = 0; n < totnode; n++) {
                PBVHVertexIter vd;
                SculptBrushTest test;
-               
-               sculpt_undo_push_node(ss, nodes[n]);
+               float (*proxy)[3];
+
+               proxy= BLI_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co;
+
                sculpt_brush_test_init(ss, &test);
 
                BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
-                       if(sculpt_brush_test(&test, vd.co)) {
-                               float intr[3], val[3];
-                               
-                               if(!clay || plane_point_side(vd.co, area_normal, cntr2, flip)) {
-                                       const float fade = tex_strength(ss, brush, vd.co, test.dist)*bstrength;
+                       if (sculpt_brush_test_sq(&test, vd.co)) {
+                               if (!plane_point_side(vd.co, an, fc)) {
+                                       float intr[3];
+                                       float val[3];
 
-                                       /* Find the intersection between squash-plane and vertex (along the area normal) */             
-                                       point_plane_project(intr, vd.co, area_normal, cntr);
+                                       point_plane_project(intr, vd.co, an, fc);
 
                                        sub_v3_v3v3(val, intr, vd.co);
 
-                                       if(clay) {
-                                               if(bstr > FLT_EPSILON)
-                                                       mul_v3_fl(val, fade / bstr);
-                                               else
-                                                       mul_v3_fl(val, fade);
-                                               /* Clay displacement */
-                                               val[0]+=area_normal[0] * ss->cache->scale[0]*fade;
-                                               val[1]+=area_normal[1] * ss->cache->scale[1]*fade;
-                                               val[2]+=area_normal[2] * ss->cache->scale[2]*fade;
-                                       }
-                                       else
-                                               mul_v3_fl(val, fabs(fade));
+                                       if (plane_trim(ss->cache, brush, val)) {
+                                               const float fade = bstrength*tex_strength(ss, brush, vd.co, sqrt(test.dist))*frontface(brush, an, vd.no, vd.fno);
 
-                                       add_v3_v3(val, vd.co);
+                                               mul_v3_v3fl(proxy[vd.i], val, fade);
 
-                                       sculpt_clip(sd, ss, vd.co, val);
-                                       if(vd.mvert) vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                                               if(vd.mvert)
+                                                       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+                                       }
                                }
                        }
                }
                BLI_pbvh_vertex_iter_end;
-
-               BLI_pbvh_node_mark_update(nodes[n]);
        }
 }
 
-static void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
+void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3])
 {
        Mesh *me= (Mesh*)ob->data;
        float (*ofs)[3]= NULL;
@@ -1475,36 +2325,27 @@ static void sculpt_update_keyblock(SculptSession *ss)
        }
 }
 
-static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
+static void do_brush_action(Sculpt *sd, SculptSession *ss, Brush *brush)
 {
        SculptSearchSphereData data;
-       Brush *brush = paint_brush(&sd->paint);
-       PBVHNode **nodes= NULL;
-       int totnode;
+       PBVHNode **nodes = NULL;
+       int n, totnode;
 
+       /* Build a list of all nodes that are potentially within the brush's area of influence */
        data.ss = ss;
        data.sd = sd;
-       data.radius_squared = ss->cache->radius * ss->cache->radius;
-
-       /* Build a list of all nodes that are potentially within the brush's
-          area of influence */
-       if(brush->sculpt_tool == SCULPT_TOOL_GRAB) {
-               data.original= 1;
-               BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data,
-                               &nodes, &totnode);
-
-               if(cache->first_time)
-                       copy_v3_v3(ss->cache->grab_active_location[ss->cache->symmetry], ss->cache->location);
-               else
-                       copy_v3_v3(ss->cache->location, ss->cache->grab_active_location[ss->cache->symmetry]);
-       }
-       else {
-               BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data,
-                       &nodes, &totnode);
-       }
+       data.radius_squared = ss->cache->radius_squared;
+       data.original = ELEM4(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER);
+       BLI_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
 
        /* Only act if some verts are inside the brush area */
-       if(totnode) {
+       if (totnode) {
+               #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+               for (n= 0; n < totnode; n++) {
+                       sculpt_undo_push_node(ss, nodes[n]);
+                       BLI_pbvh_node_mark_update(nodes[n]);
+               }
+
                /* Apply one type of brush action */
                switch(brush->sculpt_tool){
                case SCULPT_TOOL_DRAW:
@@ -1513,6 +2354,12 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
                case SCULPT_TOOL_SMOOTH:
                        do_smooth_brush(sd, ss, nodes, totnode);
                        break;
+               case SCULPT_TOOL_CREASE:
+                       do_crease_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_BLOB:
+                       do_crease_brush(sd, ss, nodes, totnode);
+                       break;
                case SCULPT_TOOL_PINCH:
                        do_pinch_brush(sd, ss, nodes, totnode);
                        break;
@@ -1522,57 +2369,244 @@ static void do_brush_action(Sculpt *sd, SculptSession *ss, StrokeCache *cache)
                case SCULPT_TOOL_GRAB:
                        do_grab_brush(sd, ss, nodes, totnode);
                        break;
+               case SCULPT_TOOL_ROTATE:
+                       do_rotate_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_SNAKE_HOOK:
+                       do_snake_hook_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_NUDGE:
+                       do_nudge_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_THUMB:
+                       do_thumb_brush(sd, ss, nodes, totnode);
+                       break;
                case SCULPT_TOOL_LAYER:
                        do_layer_brush(sd, ss, nodes, totnode);
                        break;
                case SCULPT_TOOL_FLATTEN:
-                       do_flatten_clay_brush(sd, ss, nodes, totnode, 0);
+                       do_flatten_brush(sd, ss, nodes, totnode);
                        break;
                case SCULPT_TOOL_CLAY:
-                       do_flatten_clay_brush(sd, ss, nodes, totnode, 1);
+                       do_clay_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_CLAY_TUBES:
+                       do_clay_tubes_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_FILL:
+                       do_fill_brush(sd, ss, nodes, totnode);
+                       break;
+               case SCULPT_TOOL_SCRAPE:
+                       do_scrape_brush(sd, ss, nodes, totnode);
                        break;
                }
 
+               if (brush->sculpt_tool != SCULPT_TOOL_SMOOTH && brush->autosmooth_factor > 0) {
+                       if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) {
+                               smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*(1-ss->cache->pressure)*ss->cache->autosmooth_overlap);
+                       }
+                       else {
+                               smooth(sd, ss, nodes, totnode, brush->autosmooth_factor*ss->cache->autosmooth_overlap);
+                       }
+               }
+
+               /* copy the modified vertices from mesh to the active key */
+               if(ss->kb)
+                       mesh_to_key(ss->ob->data, ss->kb);
+
                /* optimization: we could avoid copying new coords to keyblock at each */
                /* stroke step if there are no modifiers due to pbvh is used for displaying */
                /* so to increase speed we'll copy new coords to keyblock when stroke is done */
                if(ss->kb && ss->modifiers_active) sculpt_update_keyblock(ss);
 
-               if(nodes)
-                       MEM_freeN(nodes);
+               MEM_freeN(nodes);
+       }
+}
+
+static void sculpt_combine_proxies(Sculpt *sd, SculptSession *ss)
+{
+       Brush *brush= paint_brush(&sd->paint);
+       PBVHNode** nodes;
+       int totnode;
+       int n;
+
+       BLI_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode);
+
+       switch (brush->sculpt_tool) {
+               case SCULPT_TOOL_GRAB:
+               case SCULPT_TOOL_ROTATE:
+               case SCULPT_TOOL_THUMB:
+                       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+                       for (n= 0; n < totnode; n++) {
+                               PBVHVertexIter vd;
+                               PBVHProxyNode* proxies;
+                               int proxy_count;
+                               float (*origco)[3];
+
+                               origco= sculpt_undo_push_node(ss, nodes[n])->co;
+
+                               BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
+
+                               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                                       float val[3];
+                                       int p;
+
+                                       copy_v3_v3(val, origco[vd.i]);
+
+                                       for (p= 0; p < proxy_count; p++)
+                                               add_v3_v3(val, proxies[p].co[vd.i]);
+
+                                       sculpt_clip(sd, ss, vd.co, val);
+                               }
+                               BLI_pbvh_vertex_iter_end;
+
+                               BLI_pbvh_node_free_proxies(nodes[n]);
+                       }
+
+                       break;
+
+               case SCULPT_TOOL_DRAW:
+               case SCULPT_TOOL_CLAY:
+               case SCULPT_TOOL_CLAY_TUBES:
+               case SCULPT_TOOL_CREASE:
+               case SCULPT_TOOL_BLOB:
+               case SCULPT_TOOL_FILL:
+               case SCULPT_TOOL_FLATTEN:
+               case SCULPT_TOOL_INFLATE:
+               case SCULPT_TOOL_NUDGE:
+               case SCULPT_TOOL_PINCH:
+               case SCULPT_TOOL_SCRAPE:
+               case SCULPT_TOOL_SNAKE_HOOK:
+                       #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
+                       for (n= 0; n < totnode; n++) {
+                               PBVHVertexIter vd;
+                               PBVHProxyNode* proxies;
+                               int proxy_count;
+
+                               BLI_pbvh_node_get_proxies(nodes[n], &proxies, &proxy_count);
+
+                               BLI_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
+                                       float val[3];
+                                       int p;
+
+                                       copy_v3_v3(val, vd.co);
+
+                                       for (p= 0; p < proxy_count; p++)
+                                               add_v3_v3(val, proxies[p].co[vd.i]);
+
+                                       sculpt_clip(sd, ss, vd.co, val);
+                               }
+                               BLI_pbvh_vertex_iter_end;
+
+                               BLI_pbvh_node_free_proxies(nodes[n]);
+
+                       }
+
+                       break;
+
+               case SCULPT_TOOL_SMOOTH:
+               case SCULPT_TOOL_LAYER:
+               default:
+                       break;
        }
+
+       if (nodes)
+               MEM_freeN(nodes);
 }
 
+//static int max_overlap_count(Sculpt *sd)
+//{
+//     int count[3];
+//     int i, j;
+//
+//     for (i= 0; i < 3; i++) {
+//             count[i] = sd->radial_symm[i];
+//
+//             for (j= 0; j < 3; j++) {
+//                     if (i != j && sd->flags & (SCULPT_SYMM_X<<i))
+//                             count[i] *= 2;
+//             }
+//     }
+//
+//     return MAX3(count[0], count[1], count[2]);
+//}
+
 /* Flip all the editdata across the axis/axes specified by symm. Used to
    calculate multiple modifications to the mesh when symmetry is enabled. */
-static void calc_brushdata_symm(StrokeCache *cache, const char symm)
+static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, const char axis, const float angle, const float feather)
 {
        flip_coord(cache->location, cache->true_location, symm);
-       flip_coord(cache->view_normal_symmetry, cache->view_normal, symm);
        flip_coord(cache->grab_delta_symmetry, cache->grab_delta, symm);
-       cache->symmetry= symm;
+       flip_coord(cache->view_normal, cache->true_view_normal, symm);
+
+       // XXX This reduces the length of the grab delta if it approaches the line of symmetry
+       // XXX However, a different approach appears to be needed
+       //if (sd->flags & SCULPT_SYMMETRY_FEATHER) {
+       //      float frac = 1.0f/max_overlap_count(sd);
+       //      float reduce = (feather-frac)/(1-frac);
+
+       //      printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce);
+
+       //      if (frac < 1)
+       //              mul_v3_fl(cache->grab_delta_symmetry, reduce);
+       //}
+
+       unit_m4(cache->symm_rot_mat);
+       unit_m4(cache->symm_rot_mat_inv);
+       rotate_m4(cache->symm_rot_mat, axis, angle);
+       rotate_m4(cache->symm_rot_mat_inv, axis, -angle);
+
+       mul_m4_v3(cache->symm_rot_mat, cache->location);
+       mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry);
+}
+
+static void do_radial_symmetry(Sculpt *sd, SculptSession *ss, Brush *brush, const char symm, const int axis, const float feather)
+{
+       int i;
+
+       for(i = 1; i < sd->radial_symm[axis-'X']; ++i) {
+               const float angle = 2*M_PI*i/sd->radial_symm[axis-'X'];
+               ss->cache->radial_symmetry_pass= i;
+               calc_brushdata_symm(sd, ss->cache, symm, axis, angle, feather);
+               do_brush_action(sd, ss, brush);
+       }
 }
 
 static void do_symmetrical_brush_actions(Sculpt *sd, SculptSession *ss)
 {
+       Brush *brush = paint_brush(&sd->paint);
        StrokeCache *cache = ss->cache;
        const char symm = sd->flags & 7;
        int i;
 
-       copy_v3_v3(cache->location, cache->true_location);
-       copy_v3_v3(cache->grab_delta_symmetry, cache->grab_delta);
-       cache->symmetry = 0;
-       cache->bstrength = brush_strength(sd, cache);
-       do_brush_action(sd, ss, cache);
+       float feather = calc_symmetry_feather(sd, ss->cache);
+       float accum   = integrate_overlap(brush);
+       float overlap = (brush->flag & BRUSH_SPACE_ATTEN && brush->flag & BRUSH_SPACE && !(brush->flag & BRUSH_ANCHORED)) && (brush->spacing < 100) ? 1.0f/accum : 1; // spacing is integer percentage of radius, divide by 50 to get normalized diameter
+
+       ss->cache->autosmooth_overlap = overlap;
+
+       cache->bstrength= brush_strength(sd, cache, feather, overlap);
+
+       cache->symmetry= symm;
+
+       /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ 
+       for(i = 0; i <= symm; ++i) {
+               if(i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) {
+                       cache->mirror_symmetry_pass= i;
+                       cache->radial_symmetry_pass= 0;
 
-       for(i = 1; i <= symm; ++i) {
-               if(symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) {
-                       calc_brushdata_symm(cache, i);
-                       do_brush_action(sd, ss, cache);
+                       calc_brushdata_symm(sd, cache, i, 0, 0, feather);
+                       do_brush_action(sd, ss, brush);
+
+                       do_radial_symmetry(sd, ss, brush, i, 'X', feather);
+                       do_radial_symmetry(sd, ss, brush, i, 'Y', feather);
+                       do_radial_symmetry(sd, ss, brush, i, 'Z', feather);
                }
        }
 
-       cache->first_time = 0;
+       sculpt_combine_proxies(sd, ss);
+
+       cache->first_time= 0;
 }
 
 static void sculpt_update_tex(Sculpt *sd, SculptSession *ss)